diff options
| author | Max Heller <max.a.heller@gmail.com> | 2023-08-05 09:07:29 -0400 |
|---|---|---|
| committer | Max Heller <max.a.heller@gmail.com> | 2023-08-05 09:07:29 -0400 |
| commit | bed1114b8ba258acb243c938e7b2915424426474 (patch) | |
| tree | 08ea9a7bacbf269d87e005b14cd79e67926b81dd | |
| parent | d48606fefed56931414313d4f10ac8b53756c5df (diff) | |
| download | rust-bed1114b8ba258acb243c938e7b2915424426474.tar.gz rust-bed1114b8ba258acb243c938e7b2915424426474.zip | |
handle omitted lifetime params
| -rw-r--r-- | crates/ide-completion/src/completions/type.rs | 11 | ||||
| -rw-r--r-- | crates/ide-completion/src/context/analysis.rs | 24 | ||||
| -rw-r--r-- | crates/ide-completion/src/tests/type_pos.rs | 341 |
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 69b1e1fd11f..f4efaecba8a 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 8de4d0827f9..dfceb67f209 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 31ff9cf956e..4b441391094 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