From a977cb1ce8b8453213eb5311610136ee812e5cf7 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 4 Jun 2025 10:08:41 +0800 Subject: [PATCH 1/3] Add AsRef and Borrow for generate_mut_trait_impl - AsRef -> AsMut - Borrow -> BorrowMut Example ==================== ```rust //- minicore: as_ref struct Foo(i32); impl core::convert::AsRef$0 for Foo { fn as_ref(&self) -> &i32 { &self.0 } } ``` -> ```rust 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 } } ``` --- .../src/handlers/generate_mut_trait_impl.rs | 114 +++++++++++++++--- 1 file changed, 96 insertions(+), 18 deletions(-) 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..789fb8288c2e 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -48,36 +48,42 @@ 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 (apply_trait, new_apply_trait) = impl_def + .syntax() + .descendants() + .filter_map(ast::NameRef::cast) + .find_map(process_trait_name)?; + 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()? { + let famous_defs = FamousDefs(&ctx.sema, scope.krate()); + if trait_type != get_famous(&apply_trait.text(), famous_defs)? { return None; } } // 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(), + apply_trait.syntax(), + make::path_segment(make::name_ref(new_apply_trait)).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 +93,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 +108,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 `{new_apply_trait}` impl from this `{apply_trait}` trait"), target, |edit| { edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}")); @@ -112,6 +116,43 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> ) } +fn get_famous(apply_trait: &str, famous: FamousDefs<'_, '_>) -> Option { + match apply_trait { + "Index" => famous.core_convert_Index(), + "AsRef" => famous.core_convert_AsRef(), + "Borrow" => famous.core_borrow_Borrow(), + _ => None, + } +} + +fn process_trait_name(name: ast::NameRef) -> Option<(ast::NameRef, &'static str)> { + let new_name = match &*name.text() { + "Index" => "IndexMut", + "AsRef" => "AsMut", + "Borrow" => "BorrowMut", + _ => return None, + }; + Some((name, new_name)) +} + +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 +227,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 +355,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] {} "#, ); } From 28f1e624821d829bae3511f63aaaa86952500a6f Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 16 Jul 2025 16:25:46 +0800 Subject: [PATCH 2/3] Remove redundant type parameters --- .../ide-assists/src/handlers/generate_mut_trait_impl.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 789fb8288c2e..415a7fce0623 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -236,7 +236,7 @@ impl core::ops::Index for [T; 3] where T: Copy { //- minicore: as_ref struct Foo(i32); -impl core::convert::AsRef$0 for Foo { +impl core::convert::AsRef$0 for Foo { fn as_ref(&self) -> &i32 { &self.0 } @@ -245,13 +245,13 @@ impl core::convert::AsRef$0 for Foo { r#" struct Foo(i32); -$0impl core::convert::AsMut for Foo { +$0impl core::convert::AsMut for Foo { fn as_mut(&mut self) -> &mut i32 { &self.0 } } -impl core::convert::AsRef for Foo { +impl core::convert::AsRef for Foo { fn as_ref(&self) -> &i32 { &self.0 } @@ -362,7 +362,7 @@ impl Index$0 for [T; 3] {} r#" pub trait AsRef {} -impl AsRef$0 for [T; 3] {} +impl AsRef$0 for [T; 3] {} "#, ); } From fc5dd8b7984c48e7e35b73807510ecb422b62a94 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Wed, 16 Jul 2025 17:15:19 +0800 Subject: [PATCH 3/3] Use hir::Trait in parameter --- .../src/handlers/generate_mut_trait_impl.rs | 61 ++++++++----------- 1 file changed, 24 insertions(+), 37 deletions(-) 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 415a7fce0623..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,27 +48,19 @@ 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 (apply_trait, new_apply_trait) = impl_def - .syntax() - .descendants() - .filter_map(ast::NameRef::cast) - .find_map(process_trait_name)?; - - 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())?; - let famous_defs = FamousDefs(&ctx.sema, scope.krate()); - if trait_type != get_famous(&apply_trait.text(), famous_defs)? { - 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 - ted::replace( - apply_trait.syntax(), - make::path_segment(make::name_ref(new_apply_trait)).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, new_trait_method_name) = impl_def @@ -108,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"), - format!("Generate `{new_apply_trait}` impl from this `{apply_trait}` trait"), + format!("Generate `{trait_new}` impl from this `{trait_name}` trait"), target, |edit| { edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}")); @@ -116,23 +108,18 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> ) } -fn get_famous(apply_trait: &str, famous: FamousDefs<'_, '_>) -> Option { - match apply_trait { - "Index" => famous.core_convert_Index(), - "AsRef" => famous.core_convert_AsRef(), - "Borrow" => famous.core_borrow_Borrow(), - _ => None, +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"); } -} - -fn process_trait_name(name: ast::NameRef) -> Option<(ast::NameRef, &'static str)> { - let new_name = match &*name.text() { - "Index" => "IndexMut", - "AsRef" => "AsMut", - "Borrow" => "BorrowMut", - _ => return None, - }; - Some((name, new_name)) + 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)> {