Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit b07490f

Browse files
committed
made the add_missing_impl_members and add_missing_default_members assists transform default generic types
1 parent 5ce65a1 commit b07490f

File tree

2 files changed

+157
-32
lines changed

2 files changed

+157
-32
lines changed

crates/ide-assists/src/handlers/add_missing_impl_members.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,115 @@ impl Foo<T> for S<T> {
830830
)
831831
}
832832

833+
#[test]
834+
fn test_qualify_generic_default_parameter() {
835+
check_assist(
836+
add_missing_impl_members,
837+
r#"
838+
mod m {
839+
pub struct S;
840+
pub trait Foo<T = S> {
841+
fn bar(&self, other: &T);
842+
}
843+
}
844+
845+
struct S;
846+
impl m::Foo for S { $0 }"#,
847+
r#"
848+
mod m {
849+
pub struct S;
850+
pub trait Foo<T = S> {
851+
fn bar(&self, other: &T);
852+
}
853+
}
854+
855+
struct S;
856+
impl m::Foo for S {
857+
fn bar(&self, other: &m::S) {
858+
${0:todo!()}
859+
}
860+
}"#,
861+
)
862+
}
863+
864+
#[test]
865+
fn test_qualify_generic_default_parameter_2() {
866+
check_assist(
867+
add_missing_impl_members,
868+
r#"
869+
mod m {
870+
pub struct Wrapper<T, V> {
871+
one: T,
872+
another: V
873+
};
874+
pub struct S;
875+
pub trait Foo<T = Wrapper<S, bool>> {
876+
fn bar(&self, other: &T);
877+
}
878+
}
879+
880+
struct S;
881+
impl m::Foo for S { $0 }"#,
882+
r#"
883+
mod m {
884+
pub struct Wrapper<T, V> {
885+
one: T,
886+
another: V
887+
};
888+
pub struct S;
889+
pub trait Foo<T = Wrapper<S, bool>> {
890+
fn bar(&self, other: &T);
891+
}
892+
}
893+
894+
struct S;
895+
impl m::Foo for S {
896+
fn bar(&self, other: &m::Wrapper<m::S, bool>) {
897+
${0:todo!()}
898+
}
899+
}"#,
900+
);
901+
}
902+
903+
#[test]
904+
fn test_qualify_generic_default_parameter_3() {
905+
check_assist(
906+
add_missing_impl_members,
907+
r#"
908+
mod m {
909+
pub struct Wrapper<T, V> {
910+
one: T,
911+
another: V
912+
};
913+
pub struct S;
914+
pub trait Foo<T = S, V = Wrapper<T, S>> {
915+
fn bar(&self, other: &V);
916+
}
917+
}
918+
919+
struct S;
920+
impl m::Foo for S { $0 }"#,
921+
r#"
922+
mod m {
923+
pub struct Wrapper<T, V> {
924+
one: T,
925+
another: V
926+
};
927+
pub struct S;
928+
pub trait Foo<T = S, V = Wrapper<T, S>> {
929+
fn bar(&self, other: &V);
930+
}
931+
}
932+
933+
struct S;
934+
impl m::Foo for S {
935+
fn bar(&self, other: &m::Wrapper<m::S, m::S>) {
936+
${0:todo!()}
937+
}
938+
}"#,
939+
);
940+
}
941+
833942
#[test]
834943
fn test_assoc_type_bounds_are_removed() {
835944
check_assist(

crates/ide-db/src/path_transform.rs

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ struct AstSubsts {
1717
lifetimes: Vec<ast::LifetimeArg>,
1818
}
1919

20+
struct TypeOrConstSubst {
21+
subst: ast::Type,
22+
to_be_transformed: bool,
23+
}
24+
2025
type LifetimeName = String;
2126

2227
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
@@ -123,13 +128,21 @@ impl<'a> PathTransform<'a> {
123128
// the resulting change can be applied correctly.
124129
.zip(self.substs.types_and_consts.iter().map(Some).chain(std::iter::repeat(None)))
125130
.filter_map(|(k, v)| match (k.split(db), v) {
126-
(_, Some(v)) => Some((k, v.ty()?.clone())),
131+
(_, Some(v)) => {
132+
let subst =
133+
TypeOrConstSubst { subst: v.ty()?.clone(), to_be_transformed: false };
134+
Some((k, subst))
135+
}
127136
(Either::Right(t), None) => {
128137
let default = t.default(db)?;
129-
let v = ast::make::ty(
130-
&default.display_source_code(db, source_module.into(), false).ok()?,
131-
);
132-
Some((k, v))
138+
let subst = TypeOrConstSubst {
139+
subst: ast::make::ty(
140+
&default.display_source_code(db, source_module.into(), false).ok()?,
141+
)
142+
.clone_for_update(),
143+
to_be_transformed: true,
144+
};
145+
Some((k, subst))
133146
}
134147
(Either::Left(_), None) => None, // FIXME: get default const value
135148
})
@@ -151,45 +164,44 @@ impl<'a> PathTransform<'a> {
151164
}
152165

153166
struct Ctx<'a> {
154-
type_and_const_substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
167+
type_and_const_substs: FxHashMap<hir::TypeOrConstParam, TypeOrConstSubst>,
155168
lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
156169
target_module: hir::Module,
157170
source_scope: &'a SemanticsScope<'a>,
158171
}
159172

173+
fn preorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
174+
item.preorder().filter_map(|event| match event {
175+
syntax::WalkEvent::Enter(_) => None,
176+
syntax::WalkEvent::Leave(node) => Some(node),
177+
})
178+
}
179+
160180
impl<'a> Ctx<'a> {
161181
fn apply(&self, item: &SyntaxNode) {
182+
for (_, subst) in &self.type_and_const_substs {
183+
if subst.to_be_transformed {
184+
let paths =
185+
preorder(&subst.subst.syntax()).filter_map(ast::Path::cast).collect::<Vec<_>>();
186+
for path in paths {
187+
self.transform_path(path);
188+
}
189+
}
190+
}
191+
162192
// `transform_path` may update a node's parent and that would break the
163193
// tree traversal. Thus all paths in the tree are collected into a vec
164194
// so that such operation is safe.
165-
let paths = item
166-
.preorder()
167-
.filter_map(|event| match event {
168-
syntax::WalkEvent::Enter(_) => None,
169-
syntax::WalkEvent::Leave(node) => Some(node),
170-
})
171-
.filter_map(ast::Path::cast)
172-
.collect::<Vec<_>>();
173-
195+
let paths = preorder(item).filter_map(ast::Path::cast).collect::<Vec<_>>();
174196
for path in paths {
175197
self.transform_path(path);
176198
}
177199

178-
item.preorder()
179-
.filter_map(|event| match event {
180-
syntax::WalkEvent::Enter(_) => None,
181-
syntax::WalkEvent::Leave(node) => Some(node),
182-
})
183-
.filter_map(ast::Lifetime::cast)
184-
.for_each(|lifetime| {
185-
if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string())
186-
{
187-
ted::replace(
188-
lifetime.syntax(),
189-
subst.clone_subtree().clone_for_update().syntax(),
190-
);
191-
}
192-
});
200+
preorder(item).filter_map(ast::Lifetime::cast).for_each(|lifetime| {
201+
if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string()) {
202+
ted::replace(lifetime.syntax(), subst.clone_subtree().clone_for_update().syntax());
203+
}
204+
});
193205
}
194206

195207
fn transform_path(&self, path: ast::Path) -> Option<()> {
@@ -208,7 +220,9 @@ impl<'a> Ctx<'a> {
208220

209221
match resolution {
210222
hir::PathResolution::TypeParam(tp) => {
211-
if let Some(subst) = self.type_and_const_substs.get(&tp.merge()) {
223+
if let Some(TypeOrConstSubst { subst, .. }) =
224+
self.type_and_const_substs.get(&tp.merge())
225+
{
212226
let parent = path.syntax().parent()?;
213227
if let Some(parent) = ast::Path::cast(parent.clone()) {
214228
// Path inside path means that there is an associated
@@ -276,7 +290,9 @@ impl<'a> Ctx<'a> {
276290
ted::replace(path.syntax(), res.syntax())
277291
}
278292
hir::PathResolution::ConstParam(cp) => {
279-
if let Some(subst) = self.type_and_const_substs.get(&cp.merge()) {
293+
if let Some(TypeOrConstSubst { subst, .. }) =
294+
self.type_and_const_substs.get(&cp.merge())
295+
{
280296
ted::replace(path.syntax(), subst.clone_subtree().clone_for_update().syntax());
281297
}
282298
}

0 commit comments

Comments
 (0)