Unnamed repository; edit this file 'description' to name the repository.
handle omitted lifetime params
Max Heller 2023-08-05
parent d48606f · commit bed1114
-rw-r--r--crates/ide-completion/src/completions/type.rs11
-rw-r--r--crates/ide-completion/src/context/analysis.rs24
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs341
3 files changed, 221 insertions, 155 deletions
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 69b1e1fd11..f4efaecba8 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -20,7 +20,16 @@ pub(crate) fn complete_type_path(
let scope_def_applicable = |def| {
use hir::{GenericParam::*, ModuleDef::*};
match def {
- ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false,
+ ScopeDef::GenericParam(LifetimeParam(_)) => {
+ matches!(
+ location,
+ TypeLocation::GenericArgList(Some((
+ _,
+ Some(ast::GenericParam::LifetimeParam(_))
+ )))
+ )
+ }
+ ScopeDef::Label(_) => false,
// no values in type places
ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false,
// unless its a constant in a generic arg list position
diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs
index 8de4d0827f..dfceb67f20 100644
--- a/crates/ide-completion/src/context/analysis.rs
+++ b/crates/ide-completion/src/context/analysis.rs
@@ -783,9 +783,27 @@ fn classify_name_ref(
_ => None,
}
}?;
- // Determine the index of the parameter in the `GenericArgList`
- // (subtract 1 because `siblings` includes the node itself)
- let param_idx = arg.syntax().siblings(Direction::Prev).count() - 1;
+ // Determine the index of the argument in the `GenericArgList` and match it with
+ // the corresponding parameter in the `GenericParamList`.
+ // Since lifetime parameters are often omitted, ignore them for the purposes of
+ // matching the argument with its parameter unless a lifetime argument is provided
+ // explicitly. That is, for `struct S<'a, 'b, T>`, match `S::<$0>` to to `T` and
+ // `S::<'a, $0, _>` to `'b`.
+ let mut explicit_lifetime_arg = false;
+ let arg_idx = arg
+ .syntax()
+ .siblings(Direction::Prev)
+ // Skip the node itself
+ .skip(1)
+ .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true })
+ .count();
+ let param_idx = if explicit_lifetime_arg {
+ arg_idx
+ } else {
+ // Lifetimes parameters always precede type and generic parameters,
+ // so offset the argument index by the total number of lifetime params
+ arg_idx + params.lifetime_params().count()
+ };
params.generic_params().nth(param_idx)
})();
(args, param)
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 31ff9cf956..4b44139109 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -718,216 +718,255 @@ fn completes_const_and_type_generics_separately() {
// Function generic params
check(
r#"
-struct Foo;
-const X: usize = 0;
-fn foo<T, const N: usize>() {}
-fn main() {
- foo::<F$0, _>();
-}
- "#,
+ struct Foo;
+ const X: usize = 0;
+ fn foo<T, const N: usize>() {}
+ fn main() {
+ foo::<F$0, _>();
+ }
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+ struct Foo;
+ const X: usize = 0;
+ fn foo<T, const N: usize>() {}
+ fn main() {
+ foo::<_, $0>();
+ }
+ "#,
expect![[r#"
- en Enum
- ma makro!(…) macro_rules! makro
- md module
- st Foo
- st Record
- st Tuple
- st Unit
- tt Trait
- un Union
- bt u32
- kw crate::
- kw self::
- "#]],
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
);
+
+ // Method generic params
check(
r#"
-struct Foo;
-const X: usize = 0;
-fn foo<T, const N: usize>() {}
-fn main() {
- foo::<_, $0>();
-}
- "#,
+ const X: usize = 0;
+ struct Foo;
+ impl Foo { fn bar<const N: usize, T>(self) {} }
+ fn main() {
+ Foo.bar::<_, $0>();
+ }
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ impl Foo { fn bar<const N: usize, T>(self) {} }
+ fn main() {
+ Foo.bar::<X$0, _>();
+ }
+ "#,
expect![[r#"
- ct CONST
- ct X
- ma makro!(…) macro_rules! makro
- kw crate::
- kw self::
- "#]],
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
);
- // Method generic params
+ // Associated type generic params
check(
r#"
-const X: usize = 0;
-struct Foo;
-impl Foo { fn bar<const N: usize, T>(self) {} }
-fn main() {
- Foo.bar::<_, $0>();
-}
- "#,
+ const X: usize = 0;
+ struct Foo;
+ trait Bar {
+ type Baz<T, const X: usize>;
+ }
+ fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {}
+ "#,
+ expect![[r#"
+ en Enum
+ ma makro!(…) macro_rules! makro
+ md module
+ st Foo
+ st Record
+ st Tuple
+ st Unit
+ tt Bar
+ tt Trait
+ un Union
+ bt u32
+ kw crate::
+ kw self::
+ "#]],
+ );
+ check(
+ r#"
+ const X: usize = 0;
+ struct Foo;
+ trait Bar {
+ type Baz<T, const X: usize>;
+ }
+ fn foo<T: Bar<Baz<(), $0> = ()>>() {}
+ "#,
expect![[r#"
- en Enum
- ma makro!(…) macro_rules! makro
- md module
- st Foo
- st Record
- st Tuple
- st Unit
- tt Trait
- un Union
- bt u32
- kw crate::
- kw self::
- "#]],
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
);
+
+ // Type generic params
check(
r#"
-const X: usize = 0;
-struct Foo;
-impl Foo { fn bar<const N: usize, T>(self) {} }
-fn main() {
- Foo.bar::<X$0, _>();
-}
- "#,
+ const X: usize = 0;
+ struct Foo<T, const N: usize>(T);
+ fn main() {
+ let _: Foo::<_, $0> = Foo(());
+ }
+ "#,
expect![[r#"
- ct CONST
- ct X
- ma makro!(…) macro_rules! makro
- kw crate::
- kw self::
- "#]],
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
);
- // Associated type generic params
+ // Type alias generic params
check(
r#"
-const X: usize = 0;
-struct Foo;
-trait Bar {
- type Baz<T, const X: usize>;
-}
-fn foo(_: impl Bar<Baz<F$0, 0> = ()>) {}
- "#,
+ const X: usize = 0;
+ struct Foo<T, const N: usize>(T);
+ type Bar<const X: usize, U> = Foo<U, X>;
+ fn main() {
+ let _: Bar::<X$0, _> = Bar(());
+ }
+ "#,
expect![[r#"
- en Enum
- ma makro!(…) macro_rules! makro
- md module
- st Foo
- st Record
- st Tuple
- st Unit
- tt Bar
- tt Trait
- un Union
- bt u32
- kw crate::
- kw self::
- "#]],
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
);
+
+ // Enum variant params
check(
r#"
-const X: usize = 0;
-struct Foo;
-trait Bar {
- type Baz<T, const X: usize>;
-}
-fn foo<T: Bar<Baz<(), $0> = ()>>() {}
- "#,
+ const X: usize = 0;
+ enum Foo<T, const N: usize> { A(T), B }
+ fn main() {
+ Foo::B::<(), $0>;
+ }
+ "#,
expect![[r#"
- ct CONST
- ct X
- ma makro!(…) macro_rules! makro
- kw crate::
- kw self::
- "#]],
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
);
- // Type generic params
+ // Trait params
check(
r#"
-const X: usize = 0;
-struct Foo<T, const N: usize>(T);
-fn main() {
- let _: Foo::<_, $0> = Foo(());
-}
- "#,
+ const X: usize = 0;
+ trait Foo<T, const N: usize> {}
+ impl Foo<(), $0> for () {}
+ "#,
expect![[r#"
- ct CONST
- ct X
- ma makro!(…) macro_rules! makro
- kw crate::
- kw self::
- "#]],
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
);
- // Type alias generic params
+ // Trait alias params
check(
r#"
-const X: usize = 0;
-struct Foo<T, const N: usize>(T);
-type Bar<const X: usize, U> = Foo<U, X>;
-fn main() {
- let _: Bar::<X$0, _> = Bar(());
-}
- "#,
+ #![feature(trait_alias)]
+ const X: usize = 0;
+ trait Foo<T, const N: usize> {}
+ trait Bar<const M: usize, U> = Foo<U, M>;
+ fn foo<T: Bar<X$0, ()>>() {}
+ "#,
expect![[r#"
- ct CONST
- ct X
- ma makro!(…) macro_rules! makro
- kw crate::
- kw self::
- "#]],
+ ct CONST
+ ct X
+ ma makro!(…) macro_rules! makro
+ kw crate::
+ kw self::
+ "#]],
);
- // Enum variant params
+ // Omitted lifetime params
check(
r#"
-const X: usize = 0;
-enum Foo<T, const N: usize> { A(T), B }
-fn main() {
- Foo::B::<(), $0>;
-}
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<F$0, _>; }
"#,
expect![[r#"
ct CONST
- ct X
ma makro!(…) macro_rules! makro
kw crate::
kw self::
"#]],
);
-
- // Trait params
+ // Explicit lifetime params
check(
r#"
-const X: usize = 0;
-trait Foo<T, const N: usize> {}
-impl Foo<(), $0> for () {}
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<'static, 'static, F$0, _>; }
"#,
expect![[r#"
ct CONST
- ct X
ma makro!(…) macro_rules! makro
kw crate::
kw self::
"#]],
);
-
- // Trait alias params
check(
r#"
-#![feature(trait_alias)]
-const X: usize = 0;
-trait Foo<T, const N: usize> {}
-trait Bar<const M: usize, U> = Foo<U, M>;
-fn foo<T: Bar<X$0, ()>>() {}
+struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>);
+fn foo<'a>() { S::<'static, F$0, _, _>; }
"#,
expect![[r#"
- ct CONST
- ct X
+ lt 'a
ma makro!(…) macro_rules! makro
kw crate::
kw self::