diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index bab2ccf3f337..ac07d35e9ec9 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -1,4 +1,4 @@ -use ide_db::famous_defs::FamousDefs; +use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait}; use syntax::{ AstNode, ast::{self, edit_in_place::Indent, make}, @@ -48,36 +48,34 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> let impl_def = ctx.find_node_at_offset::()?.clone_for_update(); let indent = impl_def.indent_level(); - let trait_ = impl_def.trait_()?; - if let ast::Type::PathType(trait_path) = trait_ { - let trait_type = ctx.sema.resolve_trait(&trait_path.path()?)?; - let scope = ctx.sema.scope(trait_path.syntax())?; - if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_convert_Index()? { - return None; - } - } + let ast::Type::PathType(path) = impl_def.trait_()? else { + return None; + }; + let trait_name = path.path()?.segment()?.name_ref()?; + + let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?; + let famous = FamousDefs(&ctx.sema, scope.krate()); + + let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; + let trait_new = get_trait_mut(&trait_, famous)?; // Index -> IndexMut - let index_trait = impl_def - .syntax() - .descendants() - .filter_map(ast::NameRef::cast) - .find(|it| it.text() == "Index")?; - ted::replace( - index_trait.syntax(), - make::path_segment(make::name_ref("IndexMut")).clone_for_update().syntax(), - ); + ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax()); // index -> index_mut - let trait_method_name = impl_def + let (trait_method_name, new_trait_method_name) = impl_def .syntax() .descendants() .filter_map(ast::Name::cast) - .find(|it| it.text() == "index")?; - ted::replace(trait_method_name.syntax(), make::name("index_mut").clone_for_update().syntax()); + .find_map(process_method_name)?; + ted::replace( + trait_method_name.syntax(), + make::name(new_trait_method_name).clone_for_update().syntax(), + ); - let type_alias = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast)?; - ted::remove(type_alias.syntax()); + if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) { + ted::remove(type_alias.syntax()); + } // &self -> &mut self let mut_self_param = make::mut_self_param(); @@ -87,10 +85,8 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> // &Self::Output -> &mut Self::Output let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?; - ted::replace( - ret_type.syntax(), - make::ret_type(make::ty("&mut Self::Output")).clone_for_update().syntax(), - ); + let new_ret_type = process_ret_type(&ret_type)?; + ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax()); let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it { ast::AssocItem::Fn(f) => Some(f), @@ -104,7 +100,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> let target = impl_def.syntax().text_range(); acc.add( AssistId::generate("generate_mut_trait_impl"), - "Generate `IndexMut` impl from this `Index` trait", + format!("Generate `{trait_new}` impl from this `{trait_name}` trait"), target, |edit| { edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}")); @@ -112,6 +108,38 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> ) } +fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> { + let trait_ = Some(apply_trait); + if trait_ == famous.core_convert_Index().as_ref() { + return Some("IndexMut"); + } + if trait_ == famous.core_convert_AsRef().as_ref() { + return Some("AsMut"); + } + if trait_ == famous.core_borrow_Borrow().as_ref() { + return Some("BorrowMut"); + } + None +} + +fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> { + let new_name = match &*name.text() { + "index" => "index_mut", + "as_ref" => "as_mut", + "borrow" => "borrow_mut", + _ => return None, + }; + Some((name, new_name)) +} + +fn process_ret_type(ref_ty: &ast::RetType) -> Option { + let ty = ref_ty.ty()?; + let ast::Type::RefType(ref_type) = ty else { + return None; + }; + Some(make::ty_ref(ref_type.ty()?, true)) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -186,6 +214,35 @@ impl core::ops::Index for [T; 3] where T: Copy { var_name } } +"#, + ); + + check_assist( + generate_mut_trait_impl, + r#" +//- minicore: as_ref +struct Foo(i32); + +impl core::convert::AsRef$0 for Foo { + fn as_ref(&self) -> &i32 { + &self.0 + } +} +"#, + r#" +struct Foo(i32); + +$0impl core::convert::AsMut for Foo { + fn as_mut(&mut self) -> &mut i32 { + &self.0 + } +} + +impl core::convert::AsRef for Foo { + fn as_ref(&self) -> &i32 { + &self.0 + } +} "#, ); } @@ -285,6 +342,14 @@ mod foo { pub trait Index {} impl Index$0 for [T; 3] {} +"#, + ); + check_assist_not_applicable( + generate_mut_trait_impl, + r#" +pub trait AsRef {} + +impl AsRef$0 for [T; 3] {} "#, ); }