From b07490ffe9bdf2b4d71006bee09d2b0dc6ab0a19 Mon Sep 17 00:00:00 2001 From: ponyii Date: Wed, 14 Jun 2023 17:37:34 +0400 Subject: [PATCH] made the `add_missing_impl_members` and `add_missing_default_members` assists transform default generic types --- .../src/handlers/add_missing_impl_members.rs | 109 ++++++++++++++++++ crates/ide-db/src/path_transform.rs | 80 ++++++++----- 2 files changed, 157 insertions(+), 32 deletions(-) diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 4d572c604b0..e827f277b1e 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -830,6 +830,115 @@ fn bar(&self, this: &T, that: &Self) { ) } + #[test] + fn test_qualify_generic_default_parameter() { + check_assist( + add_missing_impl_members, + r#" +mod m { + pub struct S; + pub trait Foo { + fn bar(&self, other: &T); + } +} + +struct S; +impl m::Foo for S { $0 }"#, + r#" +mod m { + pub struct S; + pub trait Foo { + fn bar(&self, other: &T); + } +} + +struct S; +impl m::Foo for S { + fn bar(&self, other: &m::S) { + ${0:todo!()} + } +}"#, + ) + } + + #[test] + fn test_qualify_generic_default_parameter_2() { + check_assist( + add_missing_impl_members, + r#" +mod m { + pub struct Wrapper { + one: T, + another: V + }; + pub struct S; + pub trait Foo> { + fn bar(&self, other: &T); + } +} + +struct S; +impl m::Foo for S { $0 }"#, + r#" +mod m { + pub struct Wrapper { + one: T, + another: V + }; + pub struct S; + pub trait Foo> { + fn bar(&self, other: &T); + } +} + +struct S; +impl m::Foo for S { + fn bar(&self, other: &m::Wrapper) { + ${0:todo!()} + } +}"#, + ); + } + + #[test] + fn test_qualify_generic_default_parameter_3() { + check_assist( + add_missing_impl_members, + r#" +mod m { + pub struct Wrapper { + one: T, + another: V + }; + pub struct S; + pub trait Foo> { + fn bar(&self, other: &V); + } +} + +struct S; +impl m::Foo for S { $0 }"#, + r#" +mod m { + pub struct Wrapper { + one: T, + another: V + }; + pub struct S; + pub trait Foo> { + fn bar(&self, other: &V); + } +} + +struct S; +impl m::Foo for S { + fn bar(&self, other: &m::Wrapper) { + ${0:todo!()} + } +}"#, + ); + } + #[test] fn test_assoc_type_bounds_are_removed() { check_assist( diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 887c2feee16..f990fbd0e8f 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -17,6 +17,11 @@ struct AstSubsts { lifetimes: Vec, } +struct TypeOrConstSubst { + subst: ast::Type, + to_be_transformed: bool, +} + type LifetimeName = String; /// `PathTransform` substitutes path in SyntaxNodes in bulk. @@ -123,13 +128,21 @@ fn build_ctx(&self) -> Ctx<'a> { // the resulting change can be applied correctly. .zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None))) .filter_map(|(k, v)| match (k.split(db), v) { - (_, Some(v)) => Some((k, v.ty()?.clone())), + (_, Some(v)) => { + let subst = + TypeOrConstSubst { subst: v.ty()?.clone(), to_be_transformed: false }; + Some((k, subst)) + } (Either::Right(t), None) => { let default = t.default(db)?; - let v = ast::make::ty( - &default.display_source_code(db, source_module.into(), false).ok()?, - ); - Some((k, v)) + let subst = TypeOrConstSubst { + subst: ast::make::ty( + &default.display_source_code(db, source_module.into(), false).ok()?, + ) + .clone_for_update(), + to_be_transformed: true, + }; + Some((k, subst)) } (Either::Left(_), None) => None, // FIXME: get default const value }) @@ -151,45 +164,44 @@ fn build_ctx(&self) -> Ctx<'a> { } struct Ctx<'a> { - type_and_const_substs: FxHashMap, + type_and_const_substs: FxHashMap, lifetime_substs: FxHashMap, target_module: hir::Module, source_scope: &'a SemanticsScope<'a>, } +fn preorder(item: &SyntaxNode) -> impl Iterator { + item.preorder().filter_map(|event| match event { + syntax::WalkEvent::Enter(_) => None, + syntax::WalkEvent::Leave(node) => Some(node), + }) +} + impl<'a> Ctx<'a> { fn apply(&self, item: &SyntaxNode) { + for (_, subst) in &self.type_and_const_substs { + if subst.to_be_transformed { + let paths = + preorder(&subst.subst.syntax()).filter_map(ast::Path::cast).collect::>(); + for path in paths { + self.transform_path(path); + } + } + } + // `transform_path` may update a node's parent and that would break the // tree traversal. Thus all paths in the tree are collected into a vec // so that such operation is safe. - let paths = item - .preorder() - .filter_map(|event| match event { - syntax::WalkEvent::Enter(_) => None, - syntax::WalkEvent::Leave(node) => Some(node), - }) - .filter_map(ast::Path::cast) - .collect::>(); - + let paths = preorder(item).filter_map(ast::Path::cast).collect::>(); for path in paths { self.transform_path(path); } - item.preorder() - .filter_map(|event| match event { - syntax::WalkEvent::Enter(_) => None, - syntax::WalkEvent::Leave(node) => Some(node), - }) - .filter_map(ast::Lifetime::cast) - .for_each(|lifetime| { - if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) - { - ted::replace( - lifetime.syntax(), - subst.clone_subtree().clone_for_update().syntax(), - ); - } - }); + preorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| { + if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) { + ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax()); + } + }); } fn transform_path(&self, path: ast::Path) -> Option<()> { @@ -208,7 +220,9 @@ fn transform_path(&self, path: ast::Path) -> Option<()> { match resolution { hir::PathResolution::TypeParam(tp) => { - if let Some(subst) = self.type_and_const_substs.get(&tp.merge()) { + if let Some(TypeOrConstSubst { subst, .. }) = + self.type_and_const_substs.get(&tp.merge()) + { let parent = path.syntax().parent()?; if let Some(parent) = ast::Path::cast(parent.clone()) { // Path inside path means that there is an associated @@ -276,7 +290,9 @@ fn transform_path(&self, path: ast::Path) -> Option<()> { ted::replace(path.syntax(), res.syntax()) } hir::PathResolution::ConstParam(cp) => { - if let Some(subst) = self.type_and_const_substs.get(&cp.merge()) { + if let Some(TypeOrConstSubst { subst, .. }) = + self.type_and_const_substs.get(&cp.merge()) + { ted::replace(path.syntax(), subst.clone_subtree().clone_for_update().syntax()); } }