From 441e659aa15b98ca6adc428b8c48343202e58a28 Mon Sep 17 00:00:00 2001 From: Hongxu Xu Date: Tue, 5 Jul 2022 21:48:28 +0800 Subject: [PATCH 1/5] Complete associated type only in trait generic arg Fix tidy check does not work for marks in multiline --- crates/ide-completion/src/completions/type.rs | 83 ++++++++++++------- crates/ide-completion/src/tests/type_pos.rs | 62 ++++++++++---- crates/rust-analyzer/tests/slow-tests/tidy.rs | 37 +++++---- 3 files changed, 119 insertions(+), 63 deletions(-) diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 616d862154..352708df73 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -120,39 +120,64 @@ pub(crate) fn complete_type_path( } Qualified::Absolute => acc.add_crate_roots(ctx, path_ctx), Qualified::No => { - acc.add_nameref_keywords_with_colon(ctx); - if let TypeLocation::TypeBound = location { - ctx.process_all_names(&mut |name, res| { - let add_resolution = match res { - ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => mac.is_fn_like(ctx.db), - ScopeDef::ModuleDef( - hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_), - ) => true, - _ => false, - }; - if add_resolution { - acc.add_path_resolution(ctx, path_ctx, name, res); - } - }); - return; - } - if let TypeLocation::GenericArgList(Some(arg_list)) = location { - 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 let Some(hir::PathResolution::Def(hir::ModuleDef::Trait(trait_))) = - ctx.sema.resolve_path(&path_seg.parent_path()) - { - trait_.items_with_supertraits(ctx.sema.db).into_iter().for_each(|it| { - if let hir::AssocItem::TypeAlias(alias) = it { - cov_mark::hit!(complete_assoc_type_in_generics_list); - acc.add_type_alias_with_eq(ctx, alias) + match location { + TypeLocation::TypeBound => { + acc.add_nameref_keywords_with_colon(ctx); + ctx.process_all_names(&mut |name, res| { + let add_resolution = match res { + ScopeDef::ModuleDef(hir::ModuleDef::Macro(mac)) => { + mac.is_fn_like(ctx.db) + } + ScopeDef::ModuleDef( + hir::ModuleDef::Trait(_) | hir::ModuleDef::Module(_), + ) => true, + _ => false, + }; + if add_resolution { + acc.add_path_resolution(ctx, path_ctx, name, res); + } + }); + return; + } + TypeLocation::GenericArgList(Some(arg_list)) => { + 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 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()) + { + trait_ + .items_with_supertraits(ctx.sema.db) + .into_iter() + .for_each(|it| { + if let hir::AssocItem::TypeAlias(alias) = it { + cov_mark::hit!( + complete_assoc_type_in_generics_list + ); + acc.add_type_alias_with_eq(ctx, alias); + } + }); + return; // only AssocTypeArgs make sense + } } - }); + } } } } - } + _ => {} + }; + + acc.add_nameref_keywords_with_colon(ctx); ctx.process_all_names(&mut |name, def| { if scope_def_applicable(def) { acc.add_path_resolution(ctx, path_ctx, name, def); diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 8943d303b6..b30d4cceb8 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -380,25 +380,8 @@ trait Trait2: Trait1 { fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} "#, expect![[r#" - ct CONST - cp CONST_PARAM - en Enum - ma makro!(…) macro_rules! makro - md module - st Record - st Tuple - st Unit - tt Trait - tt Trait1 - tt Trait2 ta Foo = (as Trait2) type Foo ta Super = (as Trait1) type Super - tp T - un Union - bt u32 - kw crate:: - kw self:: - kw super:: "#]], ); check( @@ -472,3 +455,48 @@ fn func(_: Enum::$0) {} "#]], ); } + +#[test] +fn completes_associated_type_only() { + check( + r#" +trait MyTrait { + type Item; +}; + +fn f(t: impl MyTrait u64 { hasher.finish() } -fn find_mark<'a>(text: &'a str, mark: &'static str) -> Option<&'a str> { - let idx = text.find(mark)?; - let text = text[idx + mark.len()..].strip_prefix("!(")?; - let idx = text.find(|c: char| !(c.is_alphanumeric() || c == '_'))?; - let text = &text[..idx]; - Some(text) +fn find_marks(set: &mut HashSet, text: &str, mark: &str) { + let mut text = text; + let mut prev_text = ""; + while text != prev_text { + prev_text = text; + if let Some(idx) = text.find(mark) { + text = &text[idx + mark.len()..]; + if let Some(stripped_text) = text.strip_prefix("!(") { + text = stripped_text.trim_start(); + if let Some(idx2) = text.find(|c: char| !(c.is_alphanumeric() || c == '_')) { + let mark_text = &text[..idx2]; + set.insert(mark_text.to_string()); + text = &text[idx2..]; + } + } + } + } } From 0f2eba54db7a16d2790f621adec54daa3e829973 Mon Sep 17 00:00:00 2001 From: Hongxu Xu Date: Wed, 6 Jul 2022 22:58:27 +0800 Subject: [PATCH 2/5] Show only assoc type args in the correct arg pos --- crates/hir/src/lib.rs | 17 +++++++++ crates/ide-completion/src/completions/type.rs | 18 ++++++++- crates/ide-completion/src/tests/type_pos.rs | 37 ++++++++++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index ef17f2a75e..38beb90b36 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -41,6 +41,7 @@ use hir_def::{ adt::{ReprKind, VariantData}, body::{BodyDiagnostic, SyntheticSyntax}, expr::{BindingAnnotation, LabelId, Pat, PatId}, + generics::{TypeOrConstParamData, TypeParamProvenance}, item_tree::ItemTreeNode, lang_item::LangItemTarget, nameres::{self, diagnostics::DefDiagnostic}, @@ -1707,6 +1708,22 @@ impl Trait { pub fn is_unsafe(&self, db: &dyn HirDatabase) -> bool { db.trait_data(self.id).is_unsafe } + + pub fn type_parameters(&self, db: &dyn HirDatabase) -> Vec { + db.generic_params(GenericDefId::from(self.id)) + .type_or_consts + .iter() + .filter(|(_, ty)| match ty { + TypeOrConstParamData::TypeParamData(ty) + if ty.provenance != TypeParamProvenance::TypeParamList => + { + false + } + _ => true, + }) + .map(|(_, ty)|ty.clone()) + .collect() + } } impl HasVisibility for Trait { diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 352708df73..0167098da1 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -1,6 +1,7 @@ //! Completion of names from the current scope in type position. use hir::{HirDisplay, ScopeDef}; +use itertools::Itertools; use syntax::{ast, AstNode}; use crate::{ @@ -140,6 +141,18 @@ 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 + }; + match arg_list.generic_args().next() { Some(ast::GenericArg::AssocTypeArg(_)) => {} _ => { @@ -167,7 +180,10 @@ pub(crate) fn complete_type_path( acc.add_type_alias_with_eq(ctx, alias); } }); - return; // only AssocTypeArgs make sense + + if arg_pos >= trait_.type_parameters(ctx.sema.db).len() { + return; // only AssocTypeArgs make sense + } } } } diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index b30d4cceb8..175acab57f 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -386,6 +386,39 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} ); check( r#" +trait Trait1 { + type Super; +} +trait Trait2: Trait1 { + type Foo; +} + +fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} +"#, + expect![[r#" + ct CONST + cp CONST_PARAM + en Enum + ma makro!(…) macro_rules! makro + md module + st Record + st Tuple + st Unit + tt Trait + tt Trait1 + tt Trait2 + ta Foo = (as Trait2) type Foo + ta Super = (as Trait1) type Super + tp T + un Union + bt u32 + kw crate:: + kw self:: + kw super:: + "#]], + ); + check( + r#" trait Trait2 { type Foo; } @@ -460,11 +493,11 @@ fn func(_: Enum::$0) {} fn completes_associated_type_only() { check( r#" -trait MyTrait { +trait MyTrait { type Item; }; -fn f(t: impl MyTrait Date: Thu, 7 Jul 2022 00:43:59 +0800 Subject: [PATCH 3/5] 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 Date: Thu, 7 Jul 2022 00:54:46 +0800 Subject: [PATCH 4/5] check arg_idx >= n_params only if arg_idx >= n_required_params --- crates/ide-completion/src/completions/type.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index b2d5b6f5bd..0469d34951 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -181,12 +181,12 @@ pub(crate) fn complete_type_path( acc.add_type_alias_with_eq(ctx, alias); } }); - } - let n_params = - trait_.type_or_const_param_count(ctx.sema.db, false); - if arg_idx >= n_params { - return; // only show assoc types + let n_params = + trait_.type_or_const_param_count(ctx.sema.db, false); + if arg_idx >= n_params { + return; // only show assoc types + } } } } From 3248601a03233778fa6a64516a783c2a49c265b7 Mon Sep 17 00:00:00 2001 From: Hongxu Xu Date: Thu, 7 Jul 2022 07:28:55 +0800 Subject: [PATCH 5/5] fix default type param value position --- crates/ide-completion/src/tests/type_pos.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index d10fcb2cce..fcd4743f74 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -563,7 +563,7 @@ fn f(t: impl MyTrait { +trait MyTrait { type Item1; type Item2; }; @@ -590,7 +590,7 @@ fn f(t: impl MyTrait { +trait MyTrait { type Item1; type Item2; }; @@ -619,7 +619,7 @@ fn f(t: impl MyTrait { +trait MyTrait { type Item1; type Item2; };