From 8d8d12120d057f4b0b6585147695a177178d1f70 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Sat, 4 Nov 2023 11:32:15 +0800 Subject: [PATCH 1/2] feat: add generate_mut_trait_impl assist --- .../src/handlers/generate_mut_trait_impl.rs | 195 ++++++++++++++++++ crates/ide-assists/src/lib.rs | 2 + crates/ide-assists/src/tests/generated.rs | 35 ++++ crates/syntax/src/ast/make.rs | 4 + 4 files changed, 236 insertions(+) create mode 100644 crates/ide-assists/src/handlers/generate_mut_trait_impl.rs diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs new file mode 100644 index 000000000000..ceb9d22c1b9f --- /dev/null +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -0,0 +1,195 @@ +use syntax::{ + ast::{self, make}, + ted, AstNode, +}; + +use crate::{AssistContext, AssistId, AssistKind, Assists}; + +// Generate proper `index_mut` method body refer to `index` method body may impossible due to the unpredicable case [#15581]. +// Here just leave the `index_mut` method body be same as `index` method body, user can modify it manually to meet their need. + +// Assist: generate_mut_trait_impl +// +// Adds a IndexMut impl from the `Index` trait. +// +// ``` +// pub enum Axis { X = 0, Y = 1, Z = 2 } +// +// impl Index$0 for [T; 3] { +// type Output = T; +// +// fn index(&self, index: Axis) -> &Self::Output { +// &self[index as usize] +// } +// } +// ``` +// -> +// ``` +// pub enum Axis { X = 0, Y = 1, Z = 2 } +// +// $0impl IndexMut for [T; 3] { +// fn index_mut(&mut self, index: Axis) -> &mut Self::Output { +// &self[index as usize] +// } +// } +// +// impl Index for [T; 3] { +// type Output = T; +// +// fn index(&self, index: Axis) -> &Self::Output { +// &self[index as usize] +// } +// } +// ``` +pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let impl_def = ctx.find_node_at_offset::()?.clone_for_update(); + + let trait_ = impl_def.trait_()?; + if let ast::Type::PathType(trait_type) = trait_.clone() { + let trait_name = trait_type.path()?.segment()?.name_ref()?.to_string(); + if trait_name != "Index" { + 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(), + ); + + // index -> index_mut + let 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()); + + let 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(); + let self_param: ast::SelfParam = + impl_def.syntax().descendants().find_map(ast::SelfParam::cast)?; + ted::replace(self_param.syntax(), mut_self_param.clone_for_update().syntax()); + + // &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 fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it { + ast::AssocItem::Fn(f) => Some(f), + _ => None, + })?; + + let assoc_list = make::assoc_item_list().clone_for_update(); + assoc_list.add_item(syntax::ast::AssocItem::Fn(fn_)); + ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax()); + + let target = impl_def.syntax().text_range(); + acc.add( + AssistId("generate_mut_trait_impl", AssistKind::Generate), + "Generate `IndexMut` impl from this `Index` trait", + target, + |edit| { + edit.insert(target.start(), format!("$0{}\n\n", impl_def.to_string())); + }, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn test_generate_mut_trait_impl() { + check_assist( + generate_mut_trait_impl, + r#" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +impl Index$0 for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#, + r#" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +$0impl IndexMut for [T; 3] { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + &self[index as usize] + } +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#, + ); + + check_assist( + generate_mut_trait_impl, + r#" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +impl Index$0 for [T; 3] where T: Copy { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + let var_name = &self[index as usize]; + var_name + } +} +"#, + r#" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +$0impl IndexMut for [T; 3] where T: Copy { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + let var_name = &self[index as usize]; + var_name + } +} + +impl Index for [T; 3] where T: Copy { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + let var_name = &self[index as usize]; + var_name + } +} +"#, + ); + } + + #[test] + fn test_generate_mut_trait_impl_not_applicable() { + check_assist_not_applicable( + generate_mut_trait_impl, + r#" +impl Add$0 for [T; 3] {} +"#, + ); + } +} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index d2b291631c8a..e6f03214ed30 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -160,6 +160,7 @@ mod handlers { mod generate_getter_or_setter; mod generate_impl; mod generate_is_empty_from_len; + mod generate_mut_trait_impl; mod generate_new; mod generate_delegate_methods; mod generate_trait_from_impl; @@ -274,6 +275,7 @@ mod handlers { generate_function::generate_function, generate_impl::generate_impl, generate_impl::generate_trait_impl, + generate_mut_trait_impl::generate_mut_trait_impl, generate_is_empty_from_len::generate_is_empty_from_len, generate_new::generate_new, generate_trait_from_impl::generate_trait_from_impl, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 8523632acfbd..f7b394d481cc 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1538,6 +1538,41 @@ impl MyStruct { ) } +#[test] +fn doctest_generate_mut_trait_impl() { + check_doc_test( + "generate_mut_trait_impl", + r#####" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +impl Index$0 for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#####, + r#####" +pub enum Axis { X = 0, Y = 1, Z = 2 } + +$0impl IndexMut for [T; 3] { + fn index_mut(&mut self, index: Axis) -> &mut Self::Output { + &self[index as usize] + } +} + +impl Index for [T; 3] { + type Output = T; + + fn index(&self, index: Axis) -> &Self::Output { + &self[index as usize] + } +} +"#####, + ) +} + #[test] fn doctest_generate_new() { check_doc_test( diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 96f685d8d6d8..31a858b91a7d 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -852,6 +852,10 @@ pub fn self_param() -> ast::SelfParam { ast_from_text("fn f(&self) { }") } +pub fn mut_self_param() -> ast::SelfParam { + ast_from_text("fn f(&mut self) { }") +} + pub fn ret_type(ty: ast::Type) -> ast::RetType { ast_from_text(&format!("fn f() -> {ty} {{ }}")) } From b84940b199c45799fc9b6b01ed30615c28e98546 Mon Sep 17 00:00:00 2001 From: Young-Flash <871946895@qq.com> Date: Sun, 5 Nov 2023 12:20:32 +0800 Subject: [PATCH 2/2] make generate_mut_trait_impl assist trigged for std trait only --- .../src/handlers/generate_mut_trait_impl.rs | 35 +++++++++++-------- crates/ide-assists/src/tests/generated.rs | 7 ++-- crates/ide-db/src/famous_defs.rs | 4 +++ 3 files changed, 29 insertions(+), 17 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 ceb9d22c1b9f..cb8ef395650b 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs @@ -1,3 +1,4 @@ +use ide_db::famous_defs::FamousDefs; use syntax::{ ast::{self, make}, ted, AstNode, @@ -5,7 +6,7 @@ use syntax::{ use crate::{AssistContext, AssistId, AssistKind, Assists}; -// Generate proper `index_mut` method body refer to `index` method body may impossible due to the unpredicable case [#15581]. +// FIXME: Generate proper `index_mut` method body refer to `index` method body may impossible due to the unpredicable case [#15581]. // Here just leave the `index_mut` method body be same as `index` method body, user can modify it manually to meet their need. // Assist: generate_mut_trait_impl @@ -13,9 +14,10 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // Adds a IndexMut impl from the `Index` trait. // // ``` +// # //- minicore: index // pub enum Axis { X = 0, Y = 1, Z = 2 } // -// impl Index$0 for [T; 3] { +// impl core::ops::Index$0 for [T; 3] { // type Output = T; // // fn index(&self, index: Axis) -> &Self::Output { @@ -27,13 +29,13 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` // pub enum Axis { X = 0, Y = 1, Z = 2 } // -// $0impl IndexMut for [T; 3] { +// $0impl core::ops::IndexMut for [T; 3] { // fn index_mut(&mut self, index: Axis) -> &mut Self::Output { // &self[index as usize] // } // } // -// impl Index for [T; 3] { +// impl core::ops::Index for [T; 3] { // type Output = T; // // fn index(&self, index: Axis) -> &Self::Output { @@ -45,9 +47,10 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> let impl_def = ctx.find_node_at_offset::()?.clone_for_update(); let trait_ = impl_def.trait_()?; - if let ast::Type::PathType(trait_type) = trait_.clone() { - let trait_name = trait_type.path()?.segment()?.name_ref()?.to_string(); - if trait_name != "Index" { + if let ast::Type::PathType(trait_path) = trait_.clone() { + 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; } } @@ -118,9 +121,10 @@ mod tests { check_assist( generate_mut_trait_impl, r#" +//- minicore: index pub enum Axis { X = 0, Y = 1, Z = 2 } -impl Index$0 for [T; 3] { +impl core::ops::Index$0 for [T; 3] { type Output = T; fn index(&self, index: Axis) -> &Self::Output { @@ -131,13 +135,13 @@ impl Index$0 for [T; 3] { r#" pub enum Axis { X = 0, Y = 1, Z = 2 } -$0impl IndexMut for [T; 3] { +$0impl core::ops::IndexMut for [T; 3] { fn index_mut(&mut self, index: Axis) -> &mut Self::Output { &self[index as usize] } } -impl Index for [T; 3] { +impl core::ops::Index for [T; 3] { type Output = T; fn index(&self, index: Axis) -> &Self::Output { @@ -150,9 +154,10 @@ impl Index for [T; 3] { check_assist( generate_mut_trait_impl, r#" +//- minicore: index pub enum Axis { X = 0, Y = 1, Z = 2 } -impl Index$0 for [T; 3] where T: Copy { +impl core::ops::Index$0 for [T; 3] where T: Copy { type Output = T; fn index(&self, index: Axis) -> &Self::Output { @@ -164,14 +169,14 @@ impl Index$0 for [T; 3] where T: Copy { r#" pub enum Axis { X = 0, Y = 1, Z = 2 } -$0impl IndexMut for [T; 3] where T: Copy { +$0impl core::ops::IndexMut for [T; 3] where T: Copy { fn index_mut(&mut self, index: Axis) -> &mut Self::Output { let var_name = &self[index as usize]; var_name } } -impl Index for [T; 3] where T: Copy { +impl core::ops::Index for [T; 3] where T: Copy { type Output = T; fn index(&self, index: Axis) -> &Self::Output { @@ -188,7 +193,9 @@ impl Index for [T; 3] where T: Copy { check_assist_not_applicable( generate_mut_trait_impl, r#" -impl Add$0 for [T; 3] {} +pub trait Index {} + +impl Index$0 for [T; 3] {} "#, ); } diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index f7b394d481cc..da5822bba9c8 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1543,9 +1543,10 @@ fn doctest_generate_mut_trait_impl() { check_doc_test( "generate_mut_trait_impl", r#####" +//- minicore: index pub enum Axis { X = 0, Y = 1, Z = 2 } -impl Index$0 for [T; 3] { +impl core::ops::Index$0 for [T; 3] { type Output = T; fn index(&self, index: Axis) -> &Self::Output { @@ -1556,13 +1557,13 @@ impl Index$0 for [T; 3] { r#####" pub enum Axis { X = 0, Y = 1, Z = 2 } -$0impl IndexMut for [T; 3] { +$0impl core::ops::IndexMut for [T; 3] { fn index_mut(&mut self, index: Axis) -> &mut Self::Output { &self[index as usize] } } -impl Index for [T; 3] { +impl core::ops::Index for [T; 3] { type Output = T; fn index(&self, index: Axis) -> &Self::Output { diff --git a/crates/ide-db/src/famous_defs.rs b/crates/ide-db/src/famous_defs.rs index b63dde2c21e7..722517a7677b 100644 --- a/crates/ide-db/src/famous_defs.rs +++ b/crates/ide-db/src/famous_defs.rs @@ -54,6 +54,10 @@ impl FamousDefs<'_, '_> { self.find_trait("core:convert:Into") } + pub fn core_convert_Index(&self) -> Option { + self.find_trait("core:ops:Index") + } + pub fn core_option_Option(&self) -> Option { self.find_enum("core:option:Option") }