about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMax Heller <max.a.heller@gmail.com>2023-08-05 09:07:29 -0400
committerMax Heller <max.a.heller@gmail.com>2023-08-05 09:07:29 -0400
commitbed1114b8ba258acb243c938e7b2915424426474 (patch)
tree08ea9a7bacbf269d87e005b14cd79e67926b81dd
parentd48606fefed56931414313d4f10ac8b53756c5df (diff)
downloadrust-bed1114b8ba258acb243c938e7b2915424426474.tar.gz
rust-bed1114b8ba258acb243c938e7b2915424426474.zip
handle omitted lifetime params
-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 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![[r#"
-            ct CONST
-            ct X
+            lt 'a
             ma makro!(…) macro_rules! makro
             kw crate::
             kw self::