Unnamed repository; edit this file 'description' to name the repository.
Handle generic args per arg index
Add more test cases for generic args
Hongxu Xu 2022-07-06
parent 0f2eba5 · commit 75fb3de
-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 0b2e78bdcf..eec960aa7d 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 38beb90b36..96424d087e 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 0167098da1..b2d5b6f5bd 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 175acab57f..d10fcb2cce 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