Skip to content

Commit 83de70f

Browse files
authored
Merge pull request #19917 from A4-Tacks/ext-generate-mut
Add AsRef and Borrow for generate_mut_trait_impl
2 parents 250aee3 + 9909a37 commit 83de70f

File tree

1 file changed

+93
-28
lines changed

1 file changed

+93
-28
lines changed

src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs

Lines changed: 93 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use ide_db::famous_defs::FamousDefs;
1+
use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
22
use syntax::{
33
AstNode,
44
ast::{self, edit_in_place::Indent, make},
@@ -48,36 +48,34 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
4848
let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update();
4949
let indent = impl_def.indent_level();
5050

51-
let trait_ = impl_def.trait_()?;
52-
if let ast::Type::PathType(trait_path) = trait_ {
53-
let trait_type = ctx.sema.resolve_trait(&trait_path.path()?)?;
54-
let scope = ctx.sema.scope(trait_path.syntax())?;
55-
if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_convert_Index()? {
56-
return None;
57-
}
58-
}
51+
let ast::Type::PathType(path) = impl_def.trait_()? else {
52+
return None;
53+
};
54+
let trait_name = path.path()?.segment()?.name_ref()?;
55+
56+
let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?;
57+
let famous = FamousDefs(&ctx.sema, scope.krate());
58+
59+
let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
60+
let trait_new = get_trait_mut(&trait_, famous)?;
5961

6062
// Index -> IndexMut
61-
let index_trait = impl_def
62-
.syntax()
63-
.descendants()
64-
.filter_map(ast::NameRef::cast)
65-
.find(|it| it.text() == "Index")?;
66-
ted::replace(
67-
index_trait.syntax(),
68-
make::path_segment(make::name_ref("IndexMut")).clone_for_update().syntax(),
69-
);
63+
ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax());
7064

7165
// index -> index_mut
72-
let trait_method_name = impl_def
66+
let (trait_method_name, new_trait_method_name) = impl_def
7367
.syntax()
7468
.descendants()
7569
.filter_map(ast::Name::cast)
76-
.find(|it| it.text() == "index")?;
77-
ted::replace(trait_method_name.syntax(), make::name("index_mut").clone_for_update().syntax());
70+
.find_map(process_method_name)?;
71+
ted::replace(
72+
trait_method_name.syntax(),
73+
make::name(new_trait_method_name).clone_for_update().syntax(),
74+
);
7875

79-
let type_alias = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast)?;
80-
ted::remove(type_alias.syntax());
76+
if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) {
77+
ted::remove(type_alias.syntax());
78+
}
8179

8280
// &self -> &mut self
8381
let mut_self_param = make::mut_self_param();
@@ -87,10 +85,8 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
8785

8886
// &Self::Output -> &mut Self::Output
8987
let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?;
90-
ted::replace(
91-
ret_type.syntax(),
92-
make::ret_type(make::ty("&mut Self::Output")).clone_for_update().syntax(),
93-
);
88+
let new_ret_type = process_ret_type(&ret_type)?;
89+
ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax());
9490

9591
let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it {
9692
ast::AssocItem::Fn(f) => Some(f),
@@ -104,14 +100,46 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
104100
let target = impl_def.syntax().text_range();
105101
acc.add(
106102
AssistId::generate("generate_mut_trait_impl"),
107-
"Generate `IndexMut` impl from this `Index` trait",
103+
format!("Generate `{trait_new}` impl from this `{trait_name}` trait"),
108104
target,
109105
|edit| {
110106
edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}"));
111107
},
112108
)
113109
}
114110

111+
fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> {
112+
let trait_ = Some(apply_trait);
113+
if trait_ == famous.core_convert_Index().as_ref() {
114+
return Some("IndexMut");
115+
}
116+
if trait_ == famous.core_convert_AsRef().as_ref() {
117+
return Some("AsMut");
118+
}
119+
if trait_ == famous.core_borrow_Borrow().as_ref() {
120+
return Some("BorrowMut");
121+
}
122+
None
123+
}
124+
125+
fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> {
126+
let new_name = match &*name.text() {
127+
"index" => "index_mut",
128+
"as_ref" => "as_mut",
129+
"borrow" => "borrow_mut",
130+
_ => return None,
131+
};
132+
Some((name, new_name))
133+
}
134+
135+
fn process_ret_type(ref_ty: &ast::RetType) -> Option<ast::Type> {
136+
let ty = ref_ty.ty()?;
137+
let ast::Type::RefType(ref_type) = ty else {
138+
return None;
139+
};
140+
Some(make::ty_ref(ref_type.ty()?, true))
141+
}
142+
115143
#[cfg(test)]
116144
mod tests {
117145
use crate::tests::{check_assist, check_assist_not_applicable};
@@ -186,6 +214,35 @@ impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
186214
var_name
187215
}
188216
}
217+
"#,
218+
);
219+
220+
check_assist(
221+
generate_mut_trait_impl,
222+
r#"
223+
//- minicore: as_ref
224+
struct Foo(i32);
225+
226+
impl core::convert::AsRef$0<i32> for Foo {
227+
fn as_ref(&self) -> &i32 {
228+
&self.0
229+
}
230+
}
231+
"#,
232+
r#"
233+
struct Foo(i32);
234+
235+
$0impl core::convert::AsMut<i32> for Foo {
236+
fn as_mut(&mut self) -> &mut i32 {
237+
&self.0
238+
}
239+
}
240+
241+
impl core::convert::AsRef<i32> for Foo {
242+
fn as_ref(&self) -> &i32 {
243+
&self.0
244+
}
245+
}
189246
"#,
190247
);
191248
}
@@ -285,6 +342,14 @@ mod foo {
285342
pub trait Index<Idx: ?Sized> {}
286343
287344
impl<T> Index$0<i32> for [T; 3] {}
345+
"#,
346+
);
347+
check_assist_not_applicable(
348+
generate_mut_trait_impl,
349+
r#"
350+
pub trait AsRef<T: ?Sized> {}
351+
352+
impl AsRef$0<i32> for [T; 3] {}
288353
"#,
289354
);
290355
}

0 commit comments

Comments
 (0)