Handle generic args per arg index

Add more test cases for generic args
This commit is contained in:
Hongxu Xu 2022-07-07 00:43:59 +08:00
parent 0f2eba54db
commit 75fb3de310
4 changed files with 220 additions and 45 deletions

View file

@ -47,6 +47,7 @@ pub struct LifetimeParamData {
pub struct ConstParamData { pub struct ConstParamData {
pub name: Name, pub name: Name,
pub ty: Interned<TypeRef>, pub ty: Interned<TypeRef>,
pub has_default: bool,
} }
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[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> { pub fn type_param(&self) -> Option<&TypeParamData> {
match self { match self {
TypeOrConstParamData::TypeParamData(x) => Some(x), TypeOrConstParamData::TypeParamData(x) => Some(x),
@ -232,7 +240,11 @@ impl GenericParams {
let ty = const_param let ty = const_param
.ty() .ty()
.map_or(TypeRef::Error, |it| TypeRef::from_ast(lower_ctx, it)); .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()); self.type_or_consts.alloc(param.into());
} }
} }

View file

@ -1709,7 +1709,11 @@ impl Trait {
db.trait_data(self.id).is_unsafe 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)) db.generic_params(GenericDefId::from(self.id))
.type_or_consts .type_or_consts
.iter() .iter()
@ -1721,8 +1725,8 @@ impl Trait {
} }
_ => true, _ => true,
}) })
.map(|(_, ty)|ty.clone()) .filter(|(_, ty)| !count_required_only || !ty.has_default())
.collect() .count()
} }
} }

View file

@ -1,8 +1,7 @@
//! Completion of names from the current scope in type position. //! Completion of names from the current scope in type position.
use hir::{HirDisplay, ScopeDef}; use hir::{HirDisplay, ScopeDef};
use itertools::Itertools; use syntax::{ast, AstNode, SyntaxKind};
use syntax::{ast, AstNode};
use crate::{ use crate::{
context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation}, context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation},
@ -141,21 +140,12 @@ pub(crate) fn complete_type_path(
return; return;
} }
TypeLocation::GenericArgList(Some(arg_list)) => { TypeLocation::GenericArgList(Some(arg_list)) => {
// the current token is in which generic arg let in_assoc_type_arg = ctx
let arg_pos = if let Some((pos, _)) = .original_token
arg_list.generic_args().find_position(|arg| { .parent_ancestors()
arg.syntax() .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG);
.descendants_with_tokens()
.any(|t| t.as_token() == Some(&ctx.original_token))
}) {
pos
} else {
0
};
match arg_list.generic_args().next() { if !in_assoc_type_arg {
Some(ast::GenericArg::AssocTypeArg(_)) => {}
_ => {
if let Some(path_seg) = if let Some(path_seg) =
arg_list.syntax().parent().and_then(ast::PathSegment::cast) arg_list.syntax().parent().and_then(ast::PathSegment::cast)
{ {
@ -169,6 +159,17 @@ pub(crate) fn complete_type_path(
trait_, trait_,
))) = ctx.sema.resolve_path(&path_seg.parent_path()) ))) = 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_ trait_
.items_with_supertraits(ctx.sema.db) .items_with_supertraits(ctx.sema.db)
.into_iter() .into_iter()
@ -180,10 +181,12 @@ pub(crate) fn complete_type_path(
acc.add_type_alias_with_eq(ctx, alias); 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
} }
} }
} }

View file

@ -407,8 +407,6 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {}
tt Trait tt Trait
tt Trait1 tt Trait1
tt Trait2 tt Trait2
ta Foo = (as Trait2) type Foo
ta Super = (as Trait1) type Super
tp T tp T
un Union un Union
bt u32 bt u32
@ -490,17 +488,147 @@ fn func(_: Enum::$0) {}
} }
#[test] #[test]
fn completes_associated_type_only() { fn completes_type_parameter_or_associated_type() {
check( check(
r#" r#"
trait MyTrait<T> { trait MyTrait<T, U> {
type Item; type Item1;
type Item2;
}; };
fn f(t: impl MyTrait<u8,I$0 fn f(t: impl MyTrait<u$0
"#, "#,
expect![[r#" expect![[r#"
ta Item = (as MyTrait) type Item 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 = u8, 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
"#]], "#]],
); );
} }
@ -510,10 +638,38 @@ fn completes_types_after_associated_type() {
check( check(
r#" r#"
trait MyTrait { trait MyTrait {
type Item; type Item1;
type Item2;
}; };
fn f(t: impl MyTrait<Item = $0 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<Item1 = u8, Item2 = $0
"#, "#,
expect![[r#" expect![[r#"
ct CONST ct CONST