about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/generics.rs14
-rw-r--r--crates/hir/src/lib.rs12
-rw-r--r--crates/ide-completion/src/completions/type.rs63
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs176
4 files changed, 220 insertions, 45 deletions
diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs
index 0b2e78bdcfb..eec960aa7dd 100644
--- a/crates/hir-def/src/generics.rs
+++ b/crates/hir-def/src/generics.rs
@@ -47,6 +47,7 @@ pub struct LifetimeParamData {
 pub struct ConstParamData {
     pub name: Name,
     pub ty: Interned<TypeRef>,
+    pub has_default: bool,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
@@ -70,6 +71,13 @@ impl TypeOrConstParamData {
         }
     }
 
+    pub fn has_default(&self) -> bool {
+        match self {
+            TypeOrConstParamData::TypeParamData(x) => x.default.is_some(),
+            TypeOrConstParamData::ConstParamData(x) => x.has_default,
+        }
+    }
+
     pub fn type_param(&self) -> Option<&TypeParamData> {
         match self {
             TypeOrConstParamData::TypeParamData(x) => Some(x),
@@ -232,7 +240,11 @@ impl GenericParams {
                     let ty = const_param
                         .ty()
                         .map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it));
-                    let param = ConstParamData { name, ty: Interned::new(ty) };
+                    let param = ConstParamData {
+                        name,
+                        ty: Interned::new(ty),
+                        has_default: const_param.default_val().is_some(),
+                    };
                     self.type_or_consts.alloc(param.into());
                 }
             }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 38beb90b364..96424d087ef 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -1709,7 +1709,11 @@ impl Trait {
         db.trait_data(self.id).is_unsafe
     }
 
-    pub fn type_parameters(&self, db: &dyn HirDatabase) -> Vec<TypeOrConstParamData> {
+    pub fn type_or_const_param_count(
+        &self,
+        db: &dyn HirDatabase,
+        count_required_only: bool,
+    ) -> usize {
         db.generic_params(GenericDefId::from(self.id))
             .type_or_consts
             .iter()
@@ -1721,9 +1725,9 @@ impl Trait {
                 }
                 _ => true,
             })
-            .map(|(_, ty)|ty.clone())
-            .collect()
-        }
+            .filter(|(_, ty)| !count_required_only || !ty.has_default())
+            .count()
+    }
 }
 
 impl HasVisibility for Trait {
diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs
index 0167098da1c..b2d5b6f5bde 100644
--- a/crates/ide-completion/src/completions/type.rs
+++ b/crates/ide-completion/src/completions/type.rs
@@ -1,8 +1,7 @@
 //! Completion of names from the current scope in type position.
 
 use hir::{HirDisplay, ScopeDef};
-use itertools::Itertools;
-use syntax::{ast, AstNode};
+use syntax::{ast, AstNode, SyntaxKind};
 
 use crate::{
     context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
@@ -141,34 +140,36 @@ pub(crate) fn complete_type_path(
                     return;
                 }
                 TypeLocation::GenericArgList(Some(arg_list)) => {
-                    // the current token is in which generic arg
-                    let arg_pos = if let Some((pos, _)) =
-                        arg_list.generic_args().find_position(|arg| {
-                            arg.syntax()
-                                .descendants_with_tokens()
-                                .any(|t| t.as_token() == Some(&ctx.original_token))
-                        }) {
-                        pos
-                    } else {
-                        0
-                    };
+                    let in_assoc_type_arg = ctx
+                        .original_token
+                        .parent_ancestors()
+                        .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);
 
-                    match arg_list.generic_args().next() {
-                        Some(ast::GenericArg::AssocTypeArg(_)) => {}
-                        _ => {
-                            if let Some(path_seg) =
-                                arg_list.syntax().parent().and_then(ast::PathSegment::cast)
+                    if !in_assoc_type_arg {
+                        if let Some(path_seg) =
+                            arg_list.syntax().parent().and_then(ast::PathSegment::cast)
+                        {
+                            if path_seg
+                                .syntax()
+                                .ancestors()
+                                .find_map(ast::TypeBound::cast)
+                                .is_some()
                             {
-                                if path_seg
-                                    .syntax()
-                                    .ancestors()
-                                    .find_map(ast::TypeBound::cast)
-                                    .is_some()
+                                if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
+                                    trait_,
+                                ))) = ctx.sema.resolve_path(&path_seg.parent_path())
                                 {
-                                    if let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(
-                                        trait_,
-                                    ))) = ctx.sema.resolve_path(&path_seg.parent_path())
-                                    {
+                                    let arg_idx = arg_list
+                                        .generic_args()
+                                        .filter(|arg| {
+                                            arg.syntax().text_range().end()
+                                                < ctx.original_token.text_range().start()
+                                        })
+                                        .count();
+
+                                    let n_required_params =
+                                        trait_.type_or_const_param_count(ctx.sema.db, true);
+                                    if arg_idx >= n_required_params {
                                         trait_
                                             .items_with_supertraits(ctx.sema.db)
                                             .into_iter()
@@ -180,10 +181,12 @@ pub(crate) fn complete_type_path(
                                                     acc.add_type_alias_with_eq(ctx, alias);
                                                 }
                                             });
+                                    }
 
-                                        if arg_pos >= trait_.type_parameters(ctx.sema.db).len() {
-                                            return; // only AssocTypeArgs make sense
-                                        }
+                                    let n_params =
+                                        trait_.type_or_const_param_count(ctx.sema.db, false);
+                                    if arg_idx >= n_params {
+                                        return; // only show assoc types
                                     }
                                 }
                             }
diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs
index 175acab57fe..d10fcb2ccea 100644
--- a/crates/ide-completion/src/tests/type_pos.rs
+++ b/crates/ide-completion/src/tests/type_pos.rs
@@ -399,7 +399,7 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
             ct CONST
             cp CONST_PARAM
             en Enum
-            ma makro!(…)            macro_rules! makro
+            ma makro!(…)   macro_rules! makro
             md module
             st Record
             st Tuple
@@ -407,8 +407,6 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
             tt Trait
             tt Trait1
             tt Trait2
-            ta Foo =  (as Trait2)   type Foo
-            ta Super =  (as Trait1) type Super
             tp T
             un Union
             bt u32
@@ -490,17 +488,147 @@ fn func(_: Enum::$0) {}
 }
 
 #[test]
-fn completes_associated_type_only() {
+fn completes_type_parameter_or_associated_type() {
+    check(
+        r#"
+trait MyTrait<T, U> {
+    type Item1;
+    type Item2;
+};
+
+fn f(t: impl MyTrait<u$0
+"#,
+        expect![[r#"
+            ct CONST
+            en Enum
+            ma makro!(…) macro_rules! makro
+            md module
+            st Record
+            st Tuple
+            st Unit
+            tt MyTrait
+            tt Trait
+            un Union
+            bt u32
+            kw crate::
+            kw self::
+            kw super::
+        "#]],
+    );
+
+    check(
+        r#"
+trait MyTrait<T, U> {
+    type Item1;
+    type Item2;
+};
+
+fn f(t: impl MyTrait<u8, u$0
+"#,
+        expect![[r#"
+            ct CONST
+            en Enum
+            ma makro!(…) macro_rules! makro
+            md module
+            st Record
+            st Tuple
+            st Unit
+            tt MyTrait
+            tt Trait
+            un Union
+            bt u32
+            kw crate::
+            kw self::
+            kw super::
+        "#]],
+    );
+
+    check(
+        r#"
+trait MyTrait<T, U> {
+    type Item1;
+    type Item2;
+};
+
+fn f(t: impl MyTrait<u8, u8, I$0
+"#,
+        expect![[r#"
+            ta Item1 =  (as MyTrait) type Item1
+            ta Item2 =  (as MyTrait) type Item2
+        "#]],
+    );
+}
+
+#[test]
+fn completes_type_parameter_or_associated_type_with_default_value() {
+    check(
+        r#"
+trait MyTrait<T = u8, U> {
+    type Item1;
+    type Item2;
+};
+
+fn f(t: impl MyTrait<u$0
+"#,
+        expect![[r#"
+            ct CONST
+            en Enum
+            ma makro!(…) macro_rules! makro
+            md module
+            st Record
+            st Tuple
+            st Unit
+            tt MyTrait
+            tt Trait
+            un Union
+            bt u32
+            kw crate::
+            kw self::
+            kw super::
+        "#]],
+    );
+
+    check(
+        r#"
+trait MyTrait<T = u8, U> {
+    type Item1;
+    type Item2;
+};
+
+fn f(t: impl MyTrait<u8, u$0
+"#,
+        expect![[r#"
+            ct CONST
+            en Enum
+            ma makro!(…)             macro_rules! makro
+            md module
+            st Record
+            st Tuple
+            st Unit
+            tt MyTrait
+            tt Trait
+            ta Item1 =  (as MyTrait) type Item1
+            ta Item2 =  (as MyTrait) type Item2
+            un Union
+            bt u32
+            kw crate::
+            kw self::
+            kw super::
+        "#]],
+    );
+
     check(
         r#"
-trait MyTrait<T> {
-    type Item;
+trait MyTrait<T = u8, U> {
+    type Item1;
+    type Item2;
 };
 
-fn f(t: impl MyTrait<u8,I$0
+fn f(t: impl MyTrait<u8, u8, I$0
 "#,
         expect![[r#"
-            ta Item =  (as MyTrait) type Item
+            ta Item1 =  (as MyTrait) type Item1
+            ta Item2 =  (as MyTrait) type Item2
         "#]],
     );
 }
@@ -510,10 +638,38 @@ fn completes_types_after_associated_type() {
     check(
         r#"
 trait MyTrait {
-    type Item;
+    type Item1;
+    type Item2;
+};
+
+fn f(t: impl MyTrait<Item1 = $0
+"#,
+        expect![[r#"
+            ct CONST
+            en Enum
+            ma makro!(…) macro_rules! makro
+            md module
+            st Record
+            st Tuple
+            st Unit
+            tt MyTrait
+            tt Trait
+            un Union
+            bt u32
+            kw crate::
+            kw self::
+            kw super::
+        "#]],
+    );
+
+    check(
+        r#"
+trait MyTrait {
+    type Item1;
+    type Item2;
 };
 
-fn f(t: impl MyTrait<Item = $0
+fn f(t: impl MyTrait<Item1 = u8, Item2 = $0
 "#,
         expect![[r#"
             ct CONST