Skip to content

Commit 95228d2

Browse files
committed
Auto merge of #14875 - ponyii:fix/implement-missing-members-do-not-transform-lifetimes, r=Veykril
fix: implemeted lifetime transformation fot assits A part of rust-lang/rust-analyzer#13363 I expect to implement transformation of const params in a separate PR Other assists and a completion affected: - `generate_function` currently just ignores lifetimes and, consequently, is not affected - `inline_call` and `replace_derive_with...` don't seem to need lifetime transformation - `trait_impl` (a completion) is fixed and tested
2 parents cee7a72 + 7f45ccc commit 95228d2

File tree

4 files changed

+151
-29
lines changed

4 files changed

+151
-29
lines changed

crates/hir/src/lib.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,14 +2641,22 @@ impl GenericDef {
26412641
Either::Right(x) => GenericParam::TypeParam(x),
26422642
}
26432643
});
2644-
let lt_params = generics
2644+
self.lifetime_params(db)
2645+
.into_iter()
2646+
.map(GenericParam::LifetimeParam)
2647+
.chain(ty_params)
2648+
.collect()
2649+
}
2650+
2651+
pub fn lifetime_params(self, db: &dyn HirDatabase) -> Vec<LifetimeParam> {
2652+
let generics = db.generic_params(self.into());
2653+
generics
26452654
.lifetimes
26462655
.iter()
26472656
.map(|(local_id, _)| LifetimeParam {
26482657
id: LifetimeParamId { parent: self.into(), local_id },
26492658
})
2650-
.map(GenericParam::LifetimeParam);
2651-
lt_params.chain(ty_params).collect()
2659+
.collect()
26522660
}
26532661

26542662
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
@@ -359,6 +359,59 @@ impl<U> Foo<U> for S {
359359
);
360360
}
361361

362+
#[test]
363+
fn test_lifetime_substitution() {
364+
check_assist(
365+
add_missing_impl_members,
366+
r#"
367+
pub trait Trait<'a, 'b, A, B, C> {
368+
fn foo(&self, one: &'a A, anoter: &'b B) -> &'a C;
369+
}
370+
371+
impl<'x, 'y, T, V, U> Trait<'x, 'y, T, V, U> for () {$0}"#,
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 () {
378+
fn foo(&self, one: &'x T, anoter: &'y V) -> &'x U {
379+
${0:todo!()}
380+
}
381+
}"#,
382+
);
383+
}
384+
385+
#[test]
386+
fn test_lifetime_substitution_with_body() {
387+
check_assist(
388+
add_missing_default_members,
389+
r#"
390+
pub trait Trait<'a, 'b, A, B, C: Default> {
391+
fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
392+
let value: &'a i32 = &0;
393+
(C::default(), value)
394+
}
395+
}
396+
397+
impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {$0}"#,
398+
r#"
399+
pub trait Trait<'a, 'b, A, B, C: Default> {
400+
fn foo(&self, _one: &'a A, _anoter: &'b B) -> (C, &'a i32) {
401+
let value: &'a i32 = &0;
402+
(C::default(), value)
403+
}
404+
}
405+
406+
impl<'x, 'y, T, V, U: Default> Trait<'x, 'y, T, V, U> for () {
407+
$0fn foo(&self, _one: &'x T, _anoter: &'y V) -> (U, &'x i32) {
408+
let value: &'x i32 = &0;
409+
(<U>::default(), value)
410+
}
411+
}"#,
412+
);
413+
}
414+
362415
#[test]
363416
fn test_cursor_after_empty_impl_def() {
364417
check_assist(

crates/ide-completion/src/completions/item_list/trait_impl.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,33 @@ impl Test for () {
833833
);
834834
}
835835

836+
#[test]
837+
fn fn_with_lifetimes() {
838+
check_edit(
839+
"fn foo",
840+
r#"
841+
trait Test<'a, 'b, T> {
842+
fn foo(&self, a: &'a T, b: &'b T) -> &'a T;
843+
}
844+
845+
impl<'x, 'y, A> Test<'x, 'y, A> for () {
846+
t$0
847+
}
848+
"#,
849+
r#"
850+
trait Test<'a, 'b, T> {
851+
fn foo(&self, a: &'a T, b: &'b T) -> &'a T;
852+
}
853+
854+
impl<'x, 'y, A> Test<'x, 'y, A> for () {
855+
fn foo(&self, a: &'x A, b: &'y A) -> &'x A {
856+
$0
857+
}
858+
}
859+
"#,
860+
);
861+
}
862+
836863
#[test]
837864
fn complete_without_name() {
838865
let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {

crates/ide-db/src/path_transform.rs

Lines changed: 60 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 AstSubsts {
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: AstSubsts,
3846
target_scope: &'a SemanticsScope<'a>,
3947
source_scope: &'a SemanticsScope<'a>,
4048
}
@@ -72,7 +80,12 @@ 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 {
84+
source_scope,
85+
target_scope,
86+
generic_def: None,
87+
substs: AstSubsts::default(),
88+
}
7689
}
7790

7891
pub fn apply(&self, syntax: &SyntaxNode) {
@@ -91,11 +104,11 @@ impl<'a> PathTransform<'a> {
91104
let target_module = self.target_scope.module();
92105
let source_module = self.source_scope.module();
93106
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
107+
// this is a trait impl, so we need to skip the first type parameter (i.e. Self) -- this is a bit hacky
95108
Some(hir::GenericDef::Trait(_)) => 1,
96109
_ => 0,
97110
};
98-
let substs_by_param: FxHashMap<_, _> = self
111+
let type_substs: FxHashMap<_, _> = self
99112
.generic_def
100113
.into_iter()
101114
.flat_map(|it| it.type_params(db))
@@ -106,31 +119,35 @@ impl<'a> PathTransform<'a> {
106119
// can still hit those trailing values and check if they actually have
107120
// a default type. If they do, go for that type from `hir` to `ast` so
108121
// the resulting change can be applied correctly.
109-
.zip(self.substs.iter().map(Some).chain(std::iter::repeat(None)))
122+
.zip(self.substs.types.iter().map(Some).chain(std::iter::repeat(None)))
110123
.filter_map(|(k, v)| match k.split(db) {
111-
Either::Left(_) => None,
124+
Either::Left(_) => None, // FIXME: map const types too
112125
Either::Right(t) => match v {
113-
Some(v) => Some((k, v.clone())),
126+
Some(v) => Some((k, v.ty()?.clone())),
114127
None => {
115128
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-
))
129+
let v = ast::make::ty(
130+
&default.display_source_code(db, source_module.into(), false).ok()?,
131+
);
132+
Some((k, v))
124133
}
125134
},
126135
})
127136
.collect();
128-
Ctx { substs: substs_by_param, target_module, source_scope: self.source_scope }
137+
let lifetime_substs: FxHashMap<_, _> = self
138+
.generic_def
139+
.into_iter()
140+
.flat_map(|it| it.lifetime_params(db))
141+
.zip(self.substs.lifetimes.clone())
142+
.filter_map(|(k, v)| Some((k.name(db).display(db.upcast()).to_string(), v.lifetime()?)))
143+
.collect();
144+
Ctx { type_substs, lifetime_substs, target_module, source_scope: self.source_scope }
129145
}
130146
}
131147

132148
struct Ctx<'a> {
133-
substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
149+
type_substs: FxHashMap<hir::TypeOrConstParam, ast::Type>,
150+
lifetime_substs: FxHashMap<LifetimeName, ast::Lifetime>,
134151
target_module: hir::Module,
135152
source_scope: &'a SemanticsScope<'a>,
136153
}
@@ -152,7 +169,24 @@ impl<'a> Ctx<'a> {
152169
for path in paths {
153170
self.transform_path(path);
154171
}
172+
173+
item.preorder()
174+
.filter_map(|event| match event {
175+
syntax::WalkEvent::Enter(_) => None,
176+
syntax::WalkEvent::Leave(node) => Some(node),
177+
})
178+
.filter_map(ast::Lifetime::cast)
179+
.for_each(|lifetime| {
180+
if let Some(subst) = self.lifetime_substs.get(&lifetime.syntax().text().to_string())
181+
{
182+
ted::replace(
183+
lifetime.syntax(),
184+
subst.clone_subtree().clone_for_update().syntax(),
185+
);
186+
}
187+
});
155188
}
189+
156190
fn transform_path(&self, path: ast::Path) -> Option<()> {
157191
if path.qualifier().is_some() {
158192
return None;
@@ -169,7 +203,7 @@ impl<'a> Ctx<'a> {
169203

170204
match resolution {
171205
hir::PathResolution::TypeParam(tp) => {
172-
if let Some(subst) = self.substs.get(&tp.merge()) {
206+
if let Some(subst) = self.type_substs.get(&tp.merge()) {
173207
let parent = path.syntax().parent()?;
174208
if let Some(parent) = ast::Path::cast(parent.clone()) {
175209
// Path inside path means that there is an associated
@@ -250,7 +284,7 @@ impl<'a> Ctx<'a> {
250284

251285
// FIXME: It would probably be nicer if we could get this via HIR (i.e. get the
252286
// 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>> {
287+
fn get_syntactic_substs(impl_def: ast::Impl) -> Option<AstSubsts> {
254288
let target_trait = impl_def.trait_()?;
255289
let path_type = match target_trait {
256290
ast::Type::PathType(path) => path,
@@ -261,13 +295,13 @@ fn get_syntactic_substs(impl_def: ast::Impl) -> Option<Vec<ast::Type>> {
261295
get_type_args_from_arg_list(generic_arg_list)
262296
}
263297

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-
}
298+
fn get_type_args_from_arg_list(generic_arg_list: ast::GenericArgList) -> Option<AstSubsts> {
299+
let mut result = AstSubsts::default();
300+
generic_arg_list.generic_args().for_each(|generic_arg| match generic_arg {
301+
ast::GenericArg::TypeArg(type_arg) => result.types.push(type_arg),
302+
ast::GenericArg::LifetimeArg(l_arg) => result.lifetimes.push(l_arg),
303+
_ => (), // FIXME: don't filter out const params
304+
});
271305

272306
Some(result)
273307
}

0 commit comments

Comments
 (0)