Skip to content

Commit fe8f862

Browse files
committed
made the add_missing_impl_members and add_missing_default_members assists transform lifetimes
1 parent e5c56cd commit fe8f862

File tree

3 files changed

+116
-29
lines changed

3 files changed

+116
-29
lines changed

crates/hir/src/lib.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2637,14 +2637,19 @@ impl GenericDef {
26372637
Either::Right(x) => GenericParam::TypeParam(x),
26382638
}
26392639
});
2640-
let lt_params = generics
2640+
self.lifetime_params(db).into_iter().chain(ty_params).collect()
2641+
}
2642+
2643+
pub fn lifetime_params(self, db: &dyn HirDatabase) -> Vec<GenericParam> {
2644+
let generics = db.generic_params(self.into());
2645+
generics
26412646
.lifetimes
26422647
.iter()
26432648
.map(|(local_id, _)| LifetimeParam {
26442649
id: LifetimeParamId { parent: self.into(), local_id },
26452650
})
2646-
.map(GenericParam::LifetimeParam);
2647-
lt_params.chain(ty_params).collect()
2651+
.map(GenericParam::LifetimeParam)
2652+
.collect()
26482653
}
26492654

26502655
pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,59 @@ impl<U> Foo<U> for S {
365365
);
366366
}
367367

368+
#[test]
369+
fn test_lifetime_substitution() {
370+
check_assist(
371+
add_missing_impl_members,
372+
r#"
373+
pub trait Trait<'a, 'b, A, B, C> {
374+
fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
375+
}
376+
377+
impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {$0}"#,
378+
r#"
379+
pub trait Trait<'a, 'b, A, B, C> {
380+
fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
381+
}
382+
383+
impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {
384+
fn foo(&self, one: &'x T, anoter: &'y V) -> &'x U {
385+
${0:todo!()}
386+
}
387+
}"#,
388+
);
389+
}
390+
391+
#[test]
392+
fn test_lifetime_substitution_with_body() {
393+
check_assist(
394+
add_missing_default_members,
395+
r#"
396+
pub trait Trait<'a, 'b, A, B, C: Default> {
397+
fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
398+
let value: &'a i32 = &0;
399+
(C::default(), value)
400+
}
401+
}
402+
403+
impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {$0}"#,
404+
r#"
405+
pub trait Trait<'a, 'b, A, B, C: Default> {
406+
fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
407+
let value: &'a i32 = &0;
408+
(C::default(), value)
409+
}
410+
}
411+
412+
impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
413+
$0fn foo(&self, _one: &'x T, _anoter: &'y V) -> (U, &'x i32) {
414+
let value: &'x i32 = &0;
415+
(<U>::default(), value)
416+
}
417+
}"#,
418+
);
419+
}
420+
368421
#[test]
369422
fn test_cursor_after_empty_impl_def() {
370423
check_assist(

crates/ide-db/src/path_transform.rs

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@ use syntax::{
99
ted, SyntaxNode,
1010
};
1111

12+
#[derive(Default)]
13+
struct Substs {
14+
types: Vec<ast::TypeArg>,
15+
lifetimes: Vec<ast::LifetimeArg>,
16+
}
17+
18+
type LifetimeName = String;
19+
1220
/// `PathTransform` substitutes path in SyntaxNodes in bulk.
1321
///
1422
/// This is mostly useful for IDE code generation. If you paste some existing
@@ -34,7 +42,7 @@ use syntax::{
3442
/// ```
3543
pub struct PathTransform<'a> {
3644
generic_def: Option<hir::GenericDef>,
37-
substs: Vec<ast::Type>,
45+
substs: Substs,
3846
target_scope: &'a SemanticsScope<'a>,
3947
source_scope: &'a SemanticsScope<'a>,
4048
}
@@ -72,7 +80,7 @@ impl<'a> PathTransform<'a> {
7280
target_scope: &'a SemanticsScope<'a>,
7381
source_scope: &'a SemanticsScope<'a>,
7482
) -> PathTransform<'a> {
75-
PathTransform { source_scope, target_scope, generic_def: None, substs: Vec::new() }
83+
PathTransform { source_scope, target_scope, generic_def: None, substs: Substs::default() }
7684
}
7785

7886
pub fn apply(&self, syntax: &SyntaxNode) {
@@ -91,11 +99,11 @@ impl<'a> PathTransform<'a> {
9199
let target_module = self.target_scope.module();
92100
let source_module = self.source_scope.module();
93101
let skip = match self.generic_def {
94-
// this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
102+
// this is a trait impl, so we need to skip the first type parameter (i.e. Self) -- this is a bit hacky
95103
Some(hir::GenericDef::Trait(_)) => 1,
96104
_ => 0,
97105
};
98-
let substs_by_param: FxHashMap<_, _> = self
106+
let type_substs: FxHashMap<_, _> = self
99107
.generic_def
100108
.into_iter()
101109
.flat_map(|it| it.type_params(db))
@@ -106,31 +114,35 @@ impl<'a> PathTransform<'a> {
106114
// can still hit those trailing values and check if they actually have
107115
// a default type. If they do, go for that type from `hir` to `ast` so
108116
// the resulting change can be applied correctly.
109-
.zip(self.substs.iter().map(Some).chain(std::iter::repeat(None)))
117+
.zip(self.substs.types.iter().map(Some).chain(std::iter::repeat(None)))
110118
.filter_map(|(k, v)| match k.split(db) {
111-
Either::Left(_) => None,
119+
Either::Left(_) => None, // FIXME: map const types too
112120
Either::Right(t) => match v {
113-
Some(v) => Some((k, v.clone())),
121+
Some(v) => Some((k, v.ty()?.clone())),
114122
None => {
115123
let default = t.default(db)?;
116-
Some((
117-
k,
118-
ast::make::ty(
119-
&default
120-
.display_source_code(db, source_module.into(), false)
121-
.ok()?,
122-
),
123-
))
124+
let v = ast::make::ty(
125+
&default.display_source_code(db, source_module.into(), false).ok()?,
126+
);
127+
Some((k, v))
124128
}
125129
},
126130
})
127131
.collect();
128-
Ctx { substs: substs_by_param, target_module, source_scope: self.source_scope }
132+
let lifetime_substs: FxHashMap<_, _> = self
133+
.generic_def
134+
.into_iter()
135+
.flat_map(|it| it.lifetime_params(db))
136+
.zip(self.substs.lifetimes.clone())
137+
.filter_map(|(k, v)| Some((k.name(db).to_string(), v.lifetime()?)))
138+
.collect();
139+
Ctx { type_substs, lifetime_substs, target_module, source_scope: self.source_scope }
129140
}
130141
}
131142

132143
struct Ctx<'a> {
133-
substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
144+
type_substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
145+
lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
134146
target_module: hir::Module,
135147
source_scope: &'a SemanticsScope<'a>,
136148
}
@@ -152,7 +164,24 @@ impl<'a> Ctx<'a> {
152164
for path in paths {
153165
self.transform_path(path);
154166
}
167+
168+
item.preorder()
169+
.filter_map(|event| match event {
170+
syntax::WalkEvent::Enter(_) => None,
171+
syntax::WalkEvent::Leave(node) => Some(node),
172+
})
173+
.filter_map(ast::Lifetime::cast)
174+
.for_each(|lifetime| {
175+
if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string())
176+
{
177+
ted::replace(
178+
lifetime.syntax(),
179+
subst.clone_subtree().clone_for_update().syntax(),
180+
);
181+
}
182+
});
155183
}
184+
156185
fn transform_path(&self, path: ast::Path) -> Option<()> {
157186
if path.qualifier().is_some() {
158187
return None;
@@ -169,7 +198,7 @@ impl<'a> Ctx<'a> {
169198

170199
match resolution {
171200
hir::PathResolution::TypeParam(tp) => {
172-
if let Some(subst) = self.substs.get(&tp.merge()) {
201+
if let Some(subst) = self.type_substs.get(&tp.merge()) {
173202
let parent = path.syntax().parent()?;
174203
if let Some(parent) = ast::Path::cast(parent.clone()) {
175204
// Path inside path means that there is an associated
@@ -250,7 +279,7 @@ impl<'a> Ctx<'a> {
250279

251280
// FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
252281
// trait ref, and then go from the types in the substs back to the syntax).
253-
fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
282+
fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Substs> {
254283
let target_trait = impl_def.trait_()?;
255284
let path_type = match target_trait {
256285
ast::Type::PathType(path) => path,
@@ -261,13 +290,13 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
261290
get_type_args_from_arg_list(generic_arg_list)
262291
}
263292

264-
fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<Vec<ast::Type>> {
265-
let mut result = Vec::new();
266-
for generic_arg in generic_arg_list.generic_args() {
267-
if let ast::GenericArg::TypeArg(type_arg) = generic_arg {
268-
result.push(type_arg.ty()?)
269-
}
270-
}
293+
fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<Substs> {
294+
let mut result = Substs::default();
295+
generic_arg_list.generic_args().for_each(|generic_arg| match generic_arg {
296+
ast::GenericArg::TypeArg(type_arg) => result.types.push(type_arg),
297+
ast::GenericArg::LifetimeArg(l_arg) => result.lifetimes.push(l_arg),
298+
_ => (), // FIXME: don't filter out const params
299+
});
271300

272301
Some(result)
273302
}

0 commit comments

Comments
 (0)