From 75fb3de310d1777c53604a818308e7fce1925e7e Mon Sep 17 00:00:00 2001 From: Hongxu Xu Date: Thu, 7 Jul 2022 00:43:59 +0800 Subject: [PATCH] Handle generic args per arg index Add more test cases for generic args --- crates/hir-def/src/generics.rs | 14 +- crates/hir/src/lib.rs | 12 +- crates/ide-completion/src/completions/type.rs | 63 ++++--- crates/ide-completion/src/tests/type_pos.rs | 176 +++++++++++++++++- 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, + 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 { + 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 { - type Item; +trait MyTrait { + type Item1; + type Item2; }; -fn f(t: impl MyTrait { + type Item1; + type Item2; +}; + +fn f(t: impl MyTrait { + type Item1; + type Item2; +}; + +fn f(t: impl MyTrait { + type Item1; + type Item2; +}; + +fn f(t: impl MyTrait { + type Item1; + type Item2; +}; + +fn f(t: impl MyTrait { + type Item1; + type Item2; +}; + +fn f(t: impl MyTrait