From 784379eb796deb3c799473093f59555eb8cf17f1 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Sat, 29 Jul 2023 15:23:35 -0400 Subject: [PATCH 01/15] wip --- crates/ide-completion/src/context/analysis.rs | 10 +++++ crates/ide-completion/src/tests/type_pos.rs | 37 +++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 3ea50659030..4f5266051be 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -884,6 +884,16 @@ fn classify_name_ref( }; let make_path_kind_type = |ty: ast::Type| { let location = type_location(ty.syntax()); + if let Some(p) = ty.syntax().parent() { + if ast::GenericArg::can_cast(p.kind()) || ast::GenericArgList::can_cast(p.kind()) { + if let Some(p) = p.parent().and_then(|p| p.parent()) { + if let Some(segment) = ast::PathSegment::cast(p) { + let path = segment.parent_path().top_path(); + dbg!(sema.resolve_path(&path)); + } + } + } + } PathKind::Type { location: location.unwrap_or(TypeLocation::Other) } }; diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 8cb1ff4a125..4869ac17ad9 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -719,3 +719,40 @@ struct Foo { "#]], ) } + +#[test] +fn completes_const_and_type_generics_separately() { + check( + r#" +struct Foo; +const X: usize = 0; +mod foo { + fn foo() {} +} +fn main() { + self::foo::foo::(); +} +"#, + expect![[r#" + st Foo + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" +struct Foo; +const X: usize = 0; +fn foo() {} +fn main() { + foo::(); +} +"#, + expect![[r#" + ct X + kw crate:: + kw self:: + "#]], + ); +} From c4cff802691aa1ffb3da820e89dfecc41ef13de7 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Sun, 30 Jul 2023 11:12:49 -0400 Subject: [PATCH 02/15] use nodes from actual file --- crates/ide-completion/src/context/analysis.rs | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 4f5266051be..aeec1ea6f77 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -883,18 +883,25 @@ fn classify_name_ref( } }; let make_path_kind_type = |ty: ast::Type| { - let location = type_location(ty.syntax()); - if let Some(p) = ty.syntax().parent() { - if ast::GenericArg::can_cast(p.kind()) || ast::GenericArgList::can_cast(p.kind()) { - if let Some(p) = p.parent().and_then(|p| p.parent()) { - if let Some(segment) = ast::PathSegment::cast(p) { - let path = segment.parent_path().top_path(); - dbg!(sema.resolve_path(&path)); - } + let location = type_location(ty.syntax()).unwrap_or(TypeLocation::Other); + match &location { + TypeLocation::TupleField => (), + TypeLocation::TypeAscription(_) => (), + TypeLocation::GenericArgList(args) => { + dbg!(&args); + if let Some(segment) = + args.as_ref().and_then(|args| ast::PathSegment::cast(args.syntax().parent()?)) + { + let path = dbg!(segment.parent_path().top_path()); + dbg!(sema.resolve_path(&path)); } } + TypeLocation::TypeBound => (), + TypeLocation::ImplTarget => (), + TypeLocation::ImplTrait => (), + TypeLocation::Other => (), } - PathKind::Type { location: location.unwrap_or(TypeLocation::Other) } + PathKind::Type { location } }; let mut kind_macro_call = |it: ast::MacroCall| { From b9ee4a51678ad4ed1bd93b9fc756b3d2877959d3 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Sun, 30 Jul 2023 15:36:42 -0400 Subject: [PATCH 03/15] working for path segments --- crates/ide-completion/src/completions/type.rs | 32 +++++++--- crates/ide-completion/src/context.rs | 2 +- crates/ide-completion/src/context/analysis.rs | 60 ++++++++++++------- crates/ide-completion/src/tests/type_pos.rs | 20 +++++-- 4 files changed, 76 insertions(+), 38 deletions(-) diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index e4705475638..de855242ee3 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -24,12 +24,23 @@ pub(crate) fn complete_type_path( // no values in type places ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false, // unless its a constant in a generic arg list position - ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { - matches!(location, TypeLocation::GenericArgList(_)) - } - ScopeDef::ImplSelfType(_) => { - !matches!(location, TypeLocation::ImplTarget | TypeLocation::ImplTrait) - } + ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => match location + { + TypeLocation::GenericArgList(location) => match location { + Some((_, Some(generic_param))) => { + matches!(generic_param, ast::GenericParam::ConstParam(_)) + } + _ => true, + }, + _ => false, + }, + ScopeDef::ImplSelfType(_) => match location { + TypeLocation::ImplTarget | TypeLocation::ImplTrait => false, + TypeLocation::GenericArgList(Some((_, Some(generic_param)))) => { + matches!(generic_param, ast::GenericParam::TypeParam(_)) + } + _ => true, + }, // Don't suggest attribute macros and derives. ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), // Type things are fine @@ -38,7 +49,12 @@ pub(crate) fn complete_type_path( ) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown - | ScopeDef::GenericParam(TypeParam(_)) => true, + | ScopeDef::GenericParam(TypeParam(_)) => match location { + TypeLocation::GenericArgList(Some((_, Some(generic_param)))) => { + matches!(generic_param, ast::GenericParam::TypeParam(_)) + } + _ => true, + }, } }; @@ -157,7 +173,7 @@ pub(crate) fn complete_type_path( }); return; } - TypeLocation::GenericArgList(Some(arg_list)) => { + TypeLocation::GenericArgList(Some((arg_list, generic_param))) => { let in_assoc_type_arg = ctx .original_token .parent_ancestors() diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 3cb65b2729a..8ec5c6c8bfd 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -155,7 +155,7 @@ pub(crate) struct ExprCtx { pub(crate) enum TypeLocation { TupleField, TypeAscription(TypeAscriptionTarget), - GenericArgList(Option), + GenericArgList(Option<(ast::GenericArgList, Option)>), TypeBound, ImplTarget, ImplTrait, diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index aeec1ea6f77..cc9e4581bed 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -1,11 +1,11 @@ //! Module responsible for analyzing the code surrounding the cursor for completion. use std::iter; -use hir::{Semantics, Type, TypeInfo, Variant}; +use hir::{HasSource, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use syntax::{ algo::{find_node_at_offset, non_trivia_sibling}, - ast::{self, AttrKind, HasArgList, HasLoopBody, HasName, NameOrNameRef}, + ast::{self, AttrKind, HasArgList, HasGenericParams, HasLoopBody, HasName, NameOrNameRef}, match_ast, AstNode, AstToken, Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, TextSize, T, }; @@ -774,9 +774,40 @@ fn classify_name_ref( ast::TypeBound(_) => TypeLocation::TypeBound, // is this case needed? ast::TypeBoundList(_) => TypeLocation::TypeBound, - ast::GenericArg(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast))), + ast::GenericArg(it) => { + let location = find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast)) + .map(|args| { + // Determine the index of the parameter in the `GenericArgList` + // (subtract 1 because `siblings` includes the node itself) + let param_idx = it.syntax().siblings(Direction::Prev).count() - 1; + let param = args + .syntax() + .parent() + .and_then(|p| ast::PathSegment::cast(p)) + .and_then(|segment| sema.resolve_path(&segment.parent_path().top_path())) + .and_then(|resolved| { + match resolved { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Function(func) => { + let src = func.source(sema.db)?; + let params = src.value.generic_param_list()?; + params.generic_params().nth(param_idx) + } + _ => None, + }, + _ => None, + } + }); + (args, param) + }); + TypeLocation::GenericArgList(location) + }, // is this case needed? - ast::GenericArgList(it) => TypeLocation::GenericArgList(find_opt_node_in_file_compensated(sema, original_file, Some(it))), + ast::GenericArgList(it) => { + let location = find_opt_node_in_file_compensated(sema, original_file, Some(it)) + .map(|node| (node, None)); + TypeLocation::GenericArgList(location) + }, ast::TupleField(_) => TypeLocation::TupleField, _ => return None, } @@ -883,25 +914,8 @@ fn classify_name_ref( } }; let make_path_kind_type = |ty: ast::Type| { - let location = type_location(ty.syntax()).unwrap_or(TypeLocation::Other); - match &location { - TypeLocation::TupleField => (), - TypeLocation::TypeAscription(_) => (), - TypeLocation::GenericArgList(args) => { - dbg!(&args); - if let Some(segment) = - args.as_ref().and_then(|args| ast::PathSegment::cast(args.syntax().parent()?)) - { - let path = dbg!(segment.parent_path().top_path()); - dbg!(sema.resolve_path(&path)); - } - } - TypeLocation::TypeBound => (), - TypeLocation::ImplTarget => (), - TypeLocation::ImplTrait => (), - TypeLocation::Other => (), - } - PathKind::Type { location } + let location = type_location(ty.syntax()); + PathKind::Type { location: location.unwrap_or(TypeLocation::Other) } }; let mut kind_macro_call = |it: ast::MacroCall| { diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 4869ac17ad9..2a05aef17f9 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -726,15 +726,21 @@ fn completes_const_and_type_generics_separately() { r#" struct Foo; const X: usize = 0; -mod foo { - fn foo() {} -} +fn foo() {} fn main() { - self::foo::foo::(); + foo::(); } "#, expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module st Foo + st Record + st Tuple + st Unit + tt Trait + un Union bt u32 kw crate:: kw self:: @@ -744,13 +750,15 @@ fn main() { r#" struct Foo; const X: usize = 0; -fn foo() {} +fn foo() {} fn main() { - foo::(); + foo::<_, $0>(); } "#, expect![[r#" + ct CONST ct X + ma makro!(…) macro_rules! makro kw crate:: kw self:: "#]], From f4038a6bf1927aee3c5a34d7ffe14da4e7ba0560 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Tue, 1 Aug 2023 18:15:32 -0400 Subject: [PATCH 04/15] support AssocTypeArg and MethodCalls --- crates/ide-completion/src/context/analysis.rs | 94 +++++++++++----- crates/ide-completion/src/tests/type_pos.rs | 100 ++++++++++++++++-- 2 files changed, 159 insertions(+), 35 deletions(-) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index cc9e4581bed..7fd3147a739 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -719,6 +719,70 @@ fn classify_name_ref( None }; + let generic_arg_location = |arg: ast::GenericArg| { + let location = find_opt_node_in_file_compensated( + sema, + original_file, + arg.syntax().parent().and_then(ast::GenericArgList::cast), + ) + .map(|args| { + // Determine the index of the parameter in the `GenericArgList` + // (subtract 1 because `siblings` includes the node itself) + let param_idx = arg.syntax().siblings(Direction::Prev).count() - 1; + let parent = args.syntax().parent(); + let param = parent.and_then(|parent| { + match_ast! { + match parent { + ast::PathSegment(segment) => { + match sema.resolve_path(&segment.parent_path().top_path())? { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Function(func) => { + let src = func.source(sema.db)?; + let params = src.value.generic_param_list()?; + params.generic_params().nth(param_idx) + } + _ => None, + }, + _ => None, + } + }, + ast::MethodCallExpr(call) => { + let func = sema.resolve_method_call(&call)?; + let src = func.source(sema.db)?; + let params = src.value.generic_param_list()?; + params.generic_params().nth(param_idx) + }, + ast::AssocTypeArg(arg) => { + let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?; + match sema.resolve_path(&trait_.parent_path().top_path())? { + hir::PathResolution::Def(def) => match def { + hir::ModuleDef::Trait(trait_) => { + let trait_items = trait_.items(sema.db); + let assoc_ty = trait_items.iter().find_map(|item| match item { + hir::AssocItem::TypeAlias(assoc_ty) => { + (assoc_ty.name(sema.db).as_str()? == arg.name_ref()?.text()) + .then_some(assoc_ty) + }, + _ => None, + })?; + let src = assoc_ty.source(sema.db)?; + let params = src.value.generic_param_list()?; + params.generic_params().nth(param_idx) + } + _ => None, + }, + _ => None, + } + }, + _ => None, + } + } + }); + (args, param) + }); + TypeLocation::GenericArgList(location) + }; + let type_location = |node: &SyntaxNode| { let parent = node.parent()?; let res = match_ast! { @@ -774,34 +838,8 @@ fn classify_name_ref( ast::TypeBound(_) => TypeLocation::TypeBound, // is this case needed? ast::TypeBoundList(_) => TypeLocation::TypeBound, - ast::GenericArg(it) => { - let location = find_opt_node_in_file_compensated(sema, original_file, it.syntax().parent().and_then(ast::GenericArgList::cast)) - .map(|args| { - // Determine the index of the parameter in the `GenericArgList` - // (subtract 1 because `siblings` includes the node itself) - let param_idx = it.syntax().siblings(Direction::Prev).count() - 1; - let param = args - .syntax() - .parent() - .and_then(|p| ast::PathSegment::cast(p)) - .and_then(|segment| sema.resolve_path(&segment.parent_path().top_path())) - .and_then(|resolved| { - match resolved { - hir::PathResolution::Def(def) => match def { - hir::ModuleDef::Function(func) => { - let src = func.source(sema.db)?; - let params = src.value.generic_param_list()?; - params.generic_params().nth(param_idx) - } - _ => None, - }, - _ => None, - } - }); - (args, param) - }); - TypeLocation::GenericArgList(location) - }, + ast::TypeArg(it) => generic_arg_location(ast::GenericArg::TypeArg(it)), + ast::GenericArg(it) => generic_arg_location(it), // is this case needed? ast::GenericArgList(it) => { let location = find_opt_node_in_file_compensated(sema, original_file, Some(it)) diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 2a05aef17f9..67110b3d7fb 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -724,11 +724,70 @@ struct Foo { fn completes_const_and_type_generics_separately() { check( r#" -struct Foo; + struct Foo; + const X: usize = 0; + fn foo() {} + fn main() { + foo::(); + } + "#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" + struct Foo; + const X: usize = 0; + fn foo() {} + fn main() { + foo::<_, $0>(); + } + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + check( + r#" const X: usize = 0; -fn foo() {} +struct Foo; +impl Foo { fn bar(self) {} } fn main() { - foo::(); + Foo.bar::(); +} +"#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + check( + r#" +const X: usize = 0; +struct Foo; +impl Foo { fn bar(self) {} } +fn main() { + Foo.bar::<_, $0>(); } "#, expect![[r#" @@ -746,14 +805,15 @@ fn main() { kw self:: "#]], ); + check( r#" -struct Foo; const X: usize = 0; -fn foo() {} -fn main() { - foo::<_, $0>(); +struct Foo; +trait Bar { + type Baz; } +fn foo = ()>>() {} "#, expect![[r#" ct CONST @@ -763,4 +823,30 @@ fn main() { kw self:: "#]], ); + check( + r#" +const X: usize = 0; +struct Foo; +trait Bar { + type Baz; +} +fn foo = ()>>() {} +"#, + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Bar + tt Trait + tp T + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); } From 1b3e20fe686e5544feb134a7b1ffcd406a1189e6 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Wed, 2 Aug 2023 17:59:26 -0400 Subject: [PATCH 05/15] handle more cases --- crates/ide-completion/src/completions/type.rs | 2 +- crates/ide-completion/src/context/analysis.rs | 44 ++-- crates/ide-completion/src/tests/type_pos.rs | 196 ++++++++++-------- 3 files changed, 136 insertions(+), 106 deletions(-) diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index de855242ee3..69b1e1fd11f 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -173,7 +173,7 @@ pub(crate) fn complete_type_path( }); return; } - TypeLocation::GenericArgList(Some((arg_list, generic_param))) => { + TypeLocation::GenericArgList(Some((arg_list, _))) => { let in_assoc_type_arg = ctx .original_token .parent_ancestors() diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 7fd3147a739..4bd429c5723 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -726,20 +726,30 @@ fn classify_name_ref( arg.syntax().parent().and_then(ast::GenericArgList::cast), ) .map(|args| { - // Determine the index of the parameter in the `GenericArgList` - // (subtract 1 because `siblings` includes the node itself) - let param_idx = arg.syntax().siblings(Direction::Prev).count() - 1; - let parent = args.syntax().parent(); - let param = parent.and_then(|parent| { - match_ast! { + let param = (|| { + let parent = args.syntax().parent()?; + let params = match_ast! { match parent { ast::PathSegment(segment) => { match sema.resolve_path(&segment.parent_path().top_path())? { hir::PathResolution::Def(def) => match def { hir::ModuleDef::Function(func) => { - let src = func.source(sema.db)?; - let params = src.value.generic_param_list()?; - params.generic_params().nth(param_idx) + func.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Adt(adt) => { + adt.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Variant(variant) => { + variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::Trait(trait_) => { + trait_.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::TraitAlias(trait_) => { + trait_.source(sema.db)?.value.generic_param_list() + } + hir::ModuleDef::TypeAlias(ty_) => { + ty_.source(sema.db)?.value.generic_param_list() } _ => None, }, @@ -748,9 +758,7 @@ fn classify_name_ref( }, ast::MethodCallExpr(call) => { let func = sema.resolve_method_call(&call)?; - let src = func.source(sema.db)?; - let params = src.value.generic_param_list()?; - params.generic_params().nth(param_idx) + func.source(sema.db)?.value.generic_param_list() }, ast::AssocTypeArg(arg) => { let trait_ = ast::PathSegment::cast(arg.syntax().parent()?.parent()?)?; @@ -765,9 +773,7 @@ fn classify_name_ref( }, _ => None, })?; - let src = assoc_ty.source(sema.db)?; - let params = src.value.generic_param_list()?; - params.generic_params().nth(param_idx) + assoc_ty.source(sema.db)?.value.generic_param_list() } _ => None, }, @@ -776,8 +782,12 @@ fn classify_name_ref( }, _ => None, } - } - }); + }?; + // Determine the index of the parameter in the `GenericArgList` + // (subtract 1 because `siblings` includes the node itself) + let param_idx = arg.syntax().siblings(Direction::Prev).count() - 1; + params.generic_params().nth(param_idx) + })(); (args, param) }); TypeLocation::GenericArgList(location) diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 67110b3d7fb..27bcf4c630a 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -722,16 +722,31 @@ struct Foo { #[test] fn completes_const_and_type_generics_separately() { - check( + let type_completion_cases = [ + // Function r#" - struct Foo; - const X: usize = 0; - fn foo() {} - fn main() { - foo::(); - } - "#, - expect![[r#" +struct Foo; +const X: usize = 0; +fn foo() {} +fn main() { + foo::(); +} + "#, + // Method + r#" +const X: usize = 0; +struct Foo; +impl Foo { fn bar(self) {} } +fn main() { + Foo.bar::<_, $0>(); +} + "#, + ]; + + for case in type_completion_cases { + check( + case, + expect![[r#" en Enum ma makro!(…) macro_rules! makro md module @@ -745,66 +760,8 @@ fn main() { kw crate:: kw self:: "#]], - ); - check( - r#" - struct Foo; - const X: usize = 0; - fn foo() {} - fn main() { - foo::<_, $0>(); + ) } - "#, - expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], - ); - - check( - r#" -const X: usize = 0; -struct Foo; -impl Foo { fn bar(self) {} } -fn main() { - Foo.bar::(); -} -"#, - expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], - ); - check( - r#" -const X: usize = 0; -struct Foo; -impl Foo { fn bar(self) {} } -fn main() { - Foo.bar::<_, $0>(); -} -"#, - expect![[r#" - en Enum - ma makro!(…) macro_rules! makro - md module - st Foo - st Record - st Tuple - st Unit - tt Trait - un Union - bt u32 - kw crate:: - kw self:: - "#]], - ); check( r#" @@ -813,25 +770,8 @@ fn main() { trait Bar { type Baz; } -fn foo = ()>>() {} -"#, - expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], - ); - check( - r#" -const X: usize = 0; -struct Foo; -trait Bar { - type Baz; -} -fn foo = ()>>() {} -"#, +fn foo(_: impl Bar = ()>) {} + "#, expect![[r#" en Enum ma makro!(…) macro_rules! makro @@ -842,11 +782,91 @@ fn foo = ()>>() {} st Unit tt Bar tt Trait - tp T un Union bt u32 kw crate:: kw self:: "#]], ); + + let const_completion_cases = [ + // Function params + r#" +struct Foo; +const X: usize = 0; +fn foo() {} +fn main() { + foo::<_, $0>(); +} + "#, + // Method params + r#" +const X: usize = 0; +struct Foo; +impl Foo { fn bar(self) {} } +fn main() { + Foo.bar::(); +} + "#, + // Associated type params + r#" +const X: usize = 0; +struct Foo; +trait Bar { + type Baz; +} +fn foo = ()>>() {} + "#, + // Type params + r#" +const X: usize = 0; +struct Foo(T); +fn main() { + let _: Foo::<_, $0> = todo!(); +} + "#, + // Enum variant params + r#" +const X: usize = 0; +struct Foo(T); +type Bar = Foo; +fn main() { + let _: Bar:: = todo!(); +} + "#, + r#" +const X: usize = 0; +enum Foo { A(T), B } +fn main() { + Foo::B::<(), $0>; +} + "#, + // Trait params + r#" +const X: usize = 0; +trait Foo {} +impl Foo<(), $0> for () {} + "#, + // Trait alias params + r#" +#![feature(trait_alias)] +const X: usize = 0; +trait Foo {} +trait Bar = Foo; +fn foo>() {} + "#, + ]; + + for case in const_completion_cases { + check( + case, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + } } From ee39905370c84085335276b49c04d58399cb3697 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Sat, 5 Aug 2023 08:10:00 -0400 Subject: [PATCH 06/15] update tests --- crates/ide-completion/src/tests/type_pos.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 27bcf4c630a..6369f31788c 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -384,10 +384,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 + ma makro!(…) macro_rules! makro md module st Record st Tuple @@ -437,7 +435,6 @@ trait Tr { impl Tr<$0 "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -485,7 +482,6 @@ trait MyTrait { fn f(t: impl MyTrait { fn f(t: impl MyTrait { fn f(t: impl MyTrait { fn f(t: impl MyTrait = ()>>() {} const X: usize = 0; struct Foo(T); fn main() { - let _: Foo::<_, $0> = todo!(); + let _: Foo::<_, $0> = Foo(()); } "#, // Enum variant params @@ -831,7 +824,7 @@ fn main() { struct Foo(T); type Bar = Foo; fn main() { - let _: Bar:: = todo!(); + let _: Bar:: = Bar(()); } "#, r#" From 5e6d646ccc0677f6ea68da7ef964cfee2c1e28f0 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Sat, 5 Aug 2023 08:14:44 -0400 Subject: [PATCH 07/15] remove unnecessary branch --- crates/ide-completion/src/context/analysis.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 4bd429c5723..8de4d0827f9 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -848,7 +848,6 @@ fn classify_name_ref( ast::TypeBound(_) => TypeLocation::TypeBound, // is this case needed? ast::TypeBoundList(_) => TypeLocation::TypeBound, - ast::TypeArg(it) => generic_arg_location(ast::GenericArg::TypeArg(it)), ast::GenericArg(it) => generic_arg_location(it), // is this case needed? ast::GenericArgList(it) => { From d48606fefed56931414313d4f10ac8b53756c5df Mon Sep 17 00:00:00 2001 From: Max Heller Date: Sat, 5 Aug 2023 08:22:58 -0400 Subject: [PATCH 08/15] unroll test loop --- crates/ide-completion/src/tests/type_pos.rs | 197 +++++++++++++------- 1 file changed, 134 insertions(+), 63 deletions(-) diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 6369f31788c..31ff9cf956e 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -715,8 +715,8 @@ struct Foo { #[test] fn completes_const_and_type_generics_separately() { - let type_completion_cases = [ - // Function + // Function generic params + check( r#" struct Foo; const X: usize = 0; @@ -725,7 +725,41 @@ fn main() { foo::(); } "#, - // Method + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" +struct Foo; +const X: usize = 0; +fn foo() {} +fn main() { + foo::<_, $0>(); +} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Method generic params + check( r#" const X: usize = 0; struct Foo; @@ -734,28 +768,40 @@ fn main() { Foo.bar::<_, $0>(); } "#, - ]; - - for case in type_completion_cases { - check( - case, - expect![[r#" - en Enum - ma makro!(…) macro_rules! makro - md module - st Foo - st Record - st Tuple - st Unit - tt Trait - un Union - bt u32 - kw crate:: - kw self:: - "#]], - ) - } + expect![[r#" + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], + ); + check( + r#" +const X: usize = 0; +struct Foo; +impl Foo { fn bar(self) {} } +fn main() { + Foo.bar::(); +} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + // Associated type generic params check( r#" const X: usize = 0; @@ -781,27 +827,7 @@ fn foo(_: impl Bar = ()>) {} kw self:: "#]], ); - - let const_completion_cases = [ - // Function params - r#" -struct Foo; -const X: usize = 0; -fn foo() {} -fn main() { - foo::<_, $0>(); -} - "#, - // Method params - r#" -const X: usize = 0; -struct Foo; -impl Foo { fn bar(self) {} } -fn main() { - Foo.bar::(); -} - "#, - // Associated type params + check( r#" const X: usize = 0; struct Foo; @@ -810,7 +836,17 @@ trait Bar { } fn foo = ()>>() {} "#, - // Type params + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Type generic params + check( r#" const X: usize = 0; struct Foo(T); @@ -818,7 +854,17 @@ fn main() { let _: Foo::<_, $0> = Foo(()); } "#, - // Enum variant params + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Type alias generic params + check( r#" const X: usize = 0; struct Foo(T); @@ -827,6 +873,17 @@ fn main() { let _: Bar:: = Bar(()); } "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Enum variant params + check( r#" const X: usize = 0; enum Foo { A(T), B } @@ -834,13 +891,33 @@ fn main() { Foo::B::<(), $0>; } "#, - // Trait params + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Trait params + check( r#" const X: usize = 0; trait Foo {} impl Foo<(), $0> for () {} "#, - // Trait alias params + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Trait alias params + check( r#" #![feature(trait_alias)] const X: usize = 0; @@ -848,18 +925,12 @@ trait Foo {} trait Bar = Foo; fn foo>() {} "#, - ]; - - for case in const_completion_cases { - check( - case, - expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], - ); - } + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); } From bed1114b8ba258acb243c938e7b2915424426474 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Sat, 5 Aug 2023 09:07:29 -0400 Subject: [PATCH 09/15] handle omitted lifetime params --- crates/ide-completion/src/completions/type.rs | 11 +- crates/ide-completion/src/context/analysis.rs | 24 +- crates/ide-completion/src/tests/type_pos.rs | 345 ++++++++++-------- 3 files changed, 223 insertions(+), 157 deletions(-) diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 69b1e1fd11f..f4efaecba8a 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -20,7 +20,16 @@ pub(crate) fn complete_type_path( let scope_def_applicable = |def| { use hir::{GenericParam::*, ModuleDef::*}; match def { - ScopeDef::GenericParam(LifetimeParam(_)) | ScopeDef::Label(_) => false, + ScopeDef::GenericParam(LifetimeParam(_)) => { + matches!( + location, + TypeLocation::GenericArgList(Some(( + _, + Some(ast::GenericParam::LifetimeParam(_)) + ))) + ) + } + ScopeDef::Label(_) => false, // no values in type places ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false, // unless its a constant in a generic arg list position diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 8de4d0827f9..dfceb67f209 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -783,9 +783,27 @@ fn classify_name_ref( _ => None, } }?; - // Determine the index of the parameter in the `GenericArgList` - // (subtract 1 because `siblings` includes the node itself) - let param_idx = arg.syntax().siblings(Direction::Prev).count() - 1; + // Determine the index of the argument in the `GenericArgList` and match it with + // the corresponding parameter in the `GenericParamList`. + // Since lifetime parameters are often omitted, ignore them for the purposes of + // matching the argument with its parameter unless a lifetime argument is provided + // explicitly. That is, for `struct S<'a, 'b, T>`, match `S::<$0>` to to `T` and + // `S::<'a, $0, _>` to `'b`. + let mut explicit_lifetime_arg = false; + let arg_idx = arg + .syntax() + .siblings(Direction::Prev) + // Skip the node itself + .skip(1) + .map(|arg| if ast::LifetimeArg::can_cast(arg.kind()) { explicit_lifetime_arg = true }) + .count(); + let param_idx = if explicit_lifetime_arg { + arg_idx + } else { + // Lifetimes parameters always precede type and generic parameters, + // so offset the argument index by the total number of lifetime params + arg_idx + params.lifetime_params().count() + }; params.generic_params().nth(param_idx) })(); (args, param) diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 31ff9cf956e..4b441391094 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -718,216 +718,255 @@ fn completes_const_and_type_generics_separately() { // Function generic params check( r#" -struct Foo; -const X: usize = 0; -fn foo() {} -fn main() { - foo::(); -} - "#, + struct Foo; + const X: usize = 0; + fn foo() {} + fn main() { + foo::(); + } + "#, expect![[r#" - en Enum - ma makro!(…) macro_rules! makro - md module - st Foo - st Record - st Tuple - st Unit - tt Trait - un Union - bt u32 - kw crate:: - kw self:: - "#]], + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], ); check( r#" -struct Foo; -const X: usize = 0; -fn foo() {} -fn main() { - foo::<_, $0>(); -} - "#, + struct Foo; + const X: usize = 0; + fn foo() {} + fn main() { + foo::<_, $0>(); + } + "#, expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], ); // Method generic params check( r#" -const X: usize = 0; -struct Foo; -impl Foo { fn bar(self) {} } -fn main() { - Foo.bar::<_, $0>(); -} - "#, + const X: usize = 0; + struct Foo; + impl Foo { fn bar(self) {} } + fn main() { + Foo.bar::<_, $0>(); + } + "#, expect![[r#" - en Enum - ma makro!(…) macro_rules! makro - md module - st Foo - st Record - st Tuple - st Unit - tt Trait - un Union - bt u32 - kw crate:: - kw self:: - "#]], + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], ); check( r#" -const X: usize = 0; -struct Foo; -impl Foo { fn bar(self) {} } -fn main() { - Foo.bar::(); -} - "#, + const X: usize = 0; + struct Foo; + impl Foo { fn bar(self) {} } + fn main() { + Foo.bar::(); + } + "#, expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], ); // Associated type generic params check( r#" -const X: usize = 0; -struct Foo; -trait Bar { - type Baz; -} -fn foo(_: impl Bar = ()>) {} - "#, + const X: usize = 0; + struct Foo; + trait Bar { + type Baz; + } + fn foo(_: impl Bar = ()>) {} + "#, expect![[r#" - en Enum - ma makro!(…) macro_rules! makro - md module - st Foo - st Record - st Tuple - st Unit - tt Bar - tt Trait - un Union - bt u32 - kw crate:: - kw self:: - "#]], + en Enum + ma makro!(…) macro_rules! makro + md module + st Foo + st Record + st Tuple + st Unit + tt Bar + tt Trait + un Union + bt u32 + kw crate:: + kw self:: + "#]], ); check( r#" -const X: usize = 0; -struct Foo; -trait Bar { - type Baz; -} -fn foo = ()>>() {} - "#, + const X: usize = 0; + struct Foo; + trait Bar { + type Baz; + } + fn foo = ()>>() {} + "#, expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], ); // Type generic params check( r#" -const X: usize = 0; -struct Foo(T); -fn main() { - let _: Foo::<_, $0> = Foo(()); -} - "#, + const X: usize = 0; + struct Foo(T); + fn main() { + let _: Foo::<_, $0> = Foo(()); + } + "#, expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], ); // Type alias generic params check( r#" -const X: usize = 0; -struct Foo(T); -type Bar = Foo; -fn main() { - let _: Bar:: = Bar(()); -} - "#, + const X: usize = 0; + struct Foo(T); + type Bar = Foo; + fn main() { + let _: Bar:: = Bar(()); + } + "#, expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], ); // Enum variant params check( r#" -const X: usize = 0; -enum Foo { A(T), B } -fn main() { - Foo::B::<(), $0>; -} - "#, + const X: usize = 0; + enum Foo { A(T), B } + fn main() { + Foo::B::<(), $0>; + } + "#, expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], ); // Trait params check( r#" -const X: usize = 0; -trait Foo {} -impl Foo<(), $0> for () {} - "#, + const X: usize = 0; + trait Foo {} + impl Foo<(), $0> for () {} + "#, expect![[r#" - ct CONST - ct X - ma makro!(…) macro_rules! makro - kw crate:: - kw self:: - "#]], + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], ); // Trait alias params check( r#" -#![feature(trait_alias)] -const X: usize = 0; -trait Foo {} -trait Bar = Foo; -fn foo>() {} + #![feature(trait_alias)] + const X: usize = 0; + trait Foo {} + trait Bar = Foo; + fn foo>() {} + "#, + expect![[r#" + ct CONST + ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + + // Omitted lifetime params + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::; } "#, expect![[r#" ct CONST - ct X + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + // Explicit lifetime params + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<'static, 'static, F$0, _>; } + "#, + expect![[r#" + ct CONST + ma makro!(…) macro_rules! makro + kw crate:: + kw self:: + "#]], + ); + check( + r#" +struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); +fn foo<'a>() { S::<'static, F$0, _, _>; } + "#, + expect![[r#" + lt 'a ma makro!(…) macro_rules! makro kw crate:: kw self:: From bb9d8229b87163d073bc1329bdc4f42676bead07 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Sun, 6 Aug 2023 12:04:29 -0400 Subject: [PATCH 10/15] cleanup --- crates/ide-completion/src/completions.rs | 4 +- crates/ide-completion/src/completions/type.rs | 113 +++++------------- crates/ide-completion/src/context.rs | 43 ++++++- crates/ide-completion/src/context/analysis.rs | 51 ++++++-- crates/ide-completion/src/tests/type_pos.rs | 18 +-- 5 files changed, 119 insertions(+), 110 deletions(-) diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 480cb77b4fd..125ebc98a52 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -703,7 +703,9 @@ pub(super) fn complete_name_ref( TypeLocation::TypeAscription(ascription) => { r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription); } - TypeLocation::GenericArgList(_) + TypeLocation::GenericArg(_) + | TypeLocation::AssocConstEq + | TypeLocation::AssocTypeEq | TypeLocation::TypeBound | TypeLocation::ImplTarget | TypeLocation::ImplTrait diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index f4efaecba8a..621dea0b9a5 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -1,7 +1,7 @@ //! Completion of names from the current scope in type position. use hir::{HirDisplay, ScopeDef}; -use syntax::{ast, AstNode, SyntaxKind}; +use syntax::{ast, AstNode}; use crate::{ context::{PathCompletionCtx, Qualified, TypeAscriptionTarget, TypeLocation}, @@ -20,36 +20,15 @@ pub(crate) fn complete_type_path( let scope_def_applicable = |def| { use hir::{GenericParam::*, ModuleDef::*}; match def { - ScopeDef::GenericParam(LifetimeParam(_)) => { - matches!( - location, - TypeLocation::GenericArgList(Some(( - _, - Some(ast::GenericParam::LifetimeParam(_)) - ))) - ) - } + ScopeDef::GenericParam(LifetimeParam(_)) => location.complete_lifetimes(), ScopeDef::Label(_) => false, // no values in type places ScopeDef::ModuleDef(Function(_) | Variant(_) | Static(_)) | ScopeDef::Local(_) => false, // unless its a constant in a generic arg list position - ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => match location - { - TypeLocation::GenericArgList(location) => match location { - Some((_, Some(generic_param))) => { - matches!(generic_param, ast::GenericParam::ConstParam(_)) - } - _ => true, - }, - _ => false, - }, - ScopeDef::ImplSelfType(_) => match location { - TypeLocation::ImplTarget | TypeLocation::ImplTrait => false, - TypeLocation::GenericArgList(Some((_, Some(generic_param)))) => { - matches!(generic_param, ast::GenericParam::TypeParam(_)) - } - _ => true, - }, + ScopeDef::ModuleDef(Const(_)) | ScopeDef::GenericParam(ConstParam(_)) => { + location.complete_consts() + } + ScopeDef::ImplSelfType(_) => location.complete_self_type(), // Don't suggest attribute macros and derives. ScopeDef::ModuleDef(Macro(mac)) => mac.is_fn_like(ctx.db), // Type things are fine @@ -58,17 +37,12 @@ pub(crate) fn complete_type_path( ) | ScopeDef::AdtSelfType(_) | ScopeDef::Unknown - | ScopeDef::GenericParam(TypeParam(_)) => match location { - TypeLocation::GenericArgList(Some((_, Some(generic_param)))) => { - matches!(generic_param, ast::GenericParam::TypeParam(_)) - } - _ => true, - }, + | ScopeDef::GenericParam(TypeParam(_)) => location.complete_types(), } }; let add_assoc_item = |acc: &mut Completions, item| match item { - hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArgList(_)) => { + hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg(_)) => { acc.add_const(ctx, ct) } hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (), @@ -182,55 +156,32 @@ pub(crate) fn complete_type_path( }); return; } - TypeLocation::GenericArgList(Some((arg_list, _))) => { - let in_assoc_type_arg = ctx - .original_token - .parent_ancestors() - .any(|node| node.kind() == SyntaxKind::ASSOC_TYPE_ARG); + TypeLocation::GenericArg(Some((arg_list, in_trait, _))) => { + if let Some(trait_) = in_trait { + if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() { + let arg_idx = arg_list + .generic_args() + .filter(|arg| { + arg.syntax().text_range().end() + < ctx.original_token.text_range().start() + }) + .count(); - 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 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() - .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); - } - }); - - 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_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().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); } - } + }, + ); + + 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/context.rs b/crates/ide-completion/src/context.rs index 8ec5c6c8bfd..5537673d1ee 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -155,13 +155,54 @@ pub(crate) struct ExprCtx { pub(crate) enum TypeLocation { TupleField, TypeAscription(TypeAscriptionTarget), - GenericArgList(Option<(ast::GenericArgList, Option)>), + /// Generic argument position e.g. `Foo<$0>` + GenericArg(Option<(ast::GenericArgList, Option, Option)>), + /// Associated type equality constraint e.g. `Foo` + AssocTypeEq, + /// Associated constant equality constraint e.g. `Foo` + AssocConstEq, TypeBound, ImplTarget, ImplTrait, Other, } +impl TypeLocation { + pub(crate) fn complete_lifetimes(&self) -> bool { + match self { + TypeLocation::GenericArg(Some((_, _, Some(param)))) => { + matches!(param, ast::GenericParam::LifetimeParam(_)) + } + _ => false, + } + } + + pub(crate) fn complete_consts(&self) -> bool { + match self { + TypeLocation::GenericArg(Some((_, _, Some(param)))) => { + matches!(param, ast::GenericParam::ConstParam(_)) + } + TypeLocation::AssocConstEq => true, + _ => false, + } + } + + pub(crate) fn complete_types(&self) -> bool { + match self { + TypeLocation::GenericArg(Some((_, _, Some(param)))) => { + matches!(param, ast::GenericParam::TypeParam(_)) + } + TypeLocation::AssocConstEq => false, + TypeLocation::AssocTypeEq => true, + _ => true, + } + } + + pub(crate) fn complete_self_type(&self) -> bool { + self.complete_types() && !matches!(self, TypeLocation::ImplTarget | TypeLocation::ImplTrait) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum TypeAscriptionTarget { Let(Option), diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index dfceb67f209..87380067e87 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -720,12 +720,14 @@ fn classify_name_ref( }; let generic_arg_location = |arg: ast::GenericArg| { + let mut override_location = None; let location = find_opt_node_in_file_compensated( sema, original_file, arg.syntax().parent().and_then(ast::GenericArgList::cast), ) .map(|args| { + let mut in_trait = None; let param = (|| { let parent = args.syntax().parent()?; let params = match_ast! { @@ -743,7 +745,31 @@ fn classify_name_ref( variant.parent_enum(sema.db).source(sema.db)?.value.generic_param_list() } hir::ModuleDef::Trait(trait_) => { - trait_.source(sema.db)?.value.generic_param_list() + if let ast::GenericArg::AssocTypeArg(arg) = &arg { + let arg_name = arg.name_ref()?; + let arg_name = arg_name.text(); + for item in trait_.items_with_supertraits(sema.db) { + match item { + hir::AssocItem::TypeAlias(assoc_ty) => { + if assoc_ty.name(sema.db).as_str()? == arg_name { + override_location = Some(TypeLocation::AssocTypeEq); + return None; + } + }, + hir::AssocItem::Const(const_) => { + if const_.name(sema.db)?.as_str()? == arg_name { + override_location = Some(TypeLocation::AssocConstEq); + return None; + } + }, + _ => (), + } + } + return None; + } else { + in_trait = Some(trait_); + trait_.source(sema.db)?.value.generic_param_list() + } } hir::ModuleDef::TraitAlias(trait_) => { trait_.source(sema.db)?.value.generic_param_list() @@ -765,10 +791,12 @@ fn classify_name_ref( match sema.resolve_path(&trait_.parent_path().top_path())? { hir::PathResolution::Def(def) => match def { hir::ModuleDef::Trait(trait_) => { - let trait_items = trait_.items(sema.db); + let arg_name = arg.name_ref()?; + let arg_name = arg_name.text(); + let trait_items = trait_.items_with_supertraits(sema.db); let assoc_ty = trait_items.iter().find_map(|item| match item { hir::AssocItem::TypeAlias(assoc_ty) => { - (assoc_ty.name(sema.db).as_str()? == arg.name_ref()?.text()) + (assoc_ty.name(sema.db).as_str()? == arg_name) .then_some(assoc_ty) }, _ => None, @@ -784,11 +812,10 @@ fn classify_name_ref( } }?; // Determine the index of the argument in the `GenericArgList` and match it with - // the corresponding parameter in the `GenericParamList`. - // Since lifetime parameters are often omitted, ignore them for the purposes of - // matching the argument with its parameter unless a lifetime argument is provided - // explicitly. That is, for `struct S<'a, 'b, T>`, match `S::<$0>` to to `T` and - // `S::<'a, $0, _>` to `'b`. + // the corresponding parameter in the `GenericParamList`. Since lifetime parameters + // are often omitted, ignore them for the purposes of matching the argument with + // its parameter unless a lifetime argument is provided explicitly. That is, for + // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`. let mut explicit_lifetime_arg = false; let arg_idx = arg .syntax() @@ -806,9 +833,9 @@ fn classify_name_ref( }; params.generic_params().nth(param_idx) })(); - (args, param) + (args, in_trait, param) }); - TypeLocation::GenericArgList(location) + override_location.unwrap_or(TypeLocation::GenericArg(location)) }; let type_location = |node: &SyntaxNode| { @@ -870,8 +897,8 @@ fn classify_name_ref( // is this case needed? ast::GenericArgList(it) => { let location = find_opt_node_in_file_compensated(sema, original_file, Some(it)) - .map(|node| (node, None)); - TypeLocation::GenericArgList(location) + .map(|node| (node, None, None)); + TypeLocation::GenericArg(location) }, ast::TupleField(_) => TypeLocation::TupleField, _ => return None, diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 4b441391094..f2ed7247039 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -402,14 +402,13 @@ fn foo<'lt, T: Trait2<$0>, const CONST_PARAM: usize>(_: T) {} ); check( r#" -trait Trait2 { +trait Trait2 { type Foo; } fn foo<'lt, T: Trait2, const CONST_PARAM: usize>(_: T) {} "#, expect![[r#" - ct CONST en Enum ma makro!(…) macro_rules! makro md module @@ -620,7 +619,6 @@ trait MyTrait { fn f(t: impl MyTrait Date: Sun, 6 Aug 2023 12:04:33 -0400 Subject: [PATCH 11/15] FIXME --- crates/ide-completion/src/context/analysis.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 87380067e87..2064ae34dd9 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -816,6 +816,9 @@ fn classify_name_ref( // are often omitted, ignore them for the purposes of matching the argument with // its parameter unless a lifetime argument is provided explicitly. That is, for // `struct S<'a, 'b, T>`, match `S::<$0>` to `T` and `S::<'a, $0, _>` to `'b`. + // FIXME: This operates on the syntax tree and will produce incorrect results when + // generic parameters are disabled by `#[cfg]` directives. It should operate on the + // HIR, but the functionality necessary to do so is not exposed at the moment. let mut explicit_lifetime_arg = false; let arg_idx = arg .syntax() From a1d9e453b9a273db44250baa5f2dc4f261617415 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Tue, 8 Aug 2023 11:06:54 -0400 Subject: [PATCH 12/15] Apply suggestions from code review Co-authored-by: Lukas Wirth --- crates/ide-completion/src/context.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 5537673d1ee..90ad2ecaf91 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -169,19 +169,15 @@ pub(crate) enum TypeLocation { impl TypeLocation { pub(crate) fn complete_lifetimes(&self) -> bool { - match self { - TypeLocation::GenericArg(Some((_, _, Some(param)))) => { - matches!(param, ast::GenericParam::LifetimeParam(_)) - } - _ => false, - } + matches!( + self, + TypeLocation::GenericArg(Some((_, _, Some(ast::GenericParam::LifetimeParam(_))))) + ) } pub(crate) fn complete_consts(&self) -> bool { match self { - TypeLocation::GenericArg(Some((_, _, Some(param)))) => { - matches!(param, ast::GenericParam::ConstParam(_)) - } + TypeLocation::GenericArg(Some((_, _, Some(ast::GenericParam::ConstParam(_))))) => true, TypeLocation::AssocConstEq => true, _ => false, } From 0b57fa3931b88940678dcc0413fda4b4c97b4e93 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Tue, 8 Aug 2023 20:09:50 -0400 Subject: [PATCH 13/15] test --- crates/ide-completion/src/tests/type_pos.rs | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index f2ed7247039..273716bd513 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -634,6 +634,31 @@ fn f(t: impl MyTrait Date: Tue, 8 Aug 2023 20:37:23 -0400 Subject: [PATCH 14/15] convert TypeLocation::GenericArg to struct variant --- crates/ide-completion/src/completions.rs | 2 +- crates/ide-completion/src/completions/type.rs | 49 +++++++++---------- crates/ide-completion/src/context.rs | 21 ++++++-- crates/ide-completion/src/context/analysis.rs | 15 ++++-- 4 files changed, 52 insertions(+), 35 deletions(-) diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 125ebc98a52..7e2ecdbb858 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -703,7 +703,7 @@ pub(super) fn complete_name_ref( TypeLocation::TypeAscription(ascription) => { r#type::complete_ascribed_type(acc, ctx, path_ctx, ascription); } - TypeLocation::GenericArg(_) + TypeLocation::GenericArg { .. } | TypeLocation::AssocConstEq | TypeLocation::AssocTypeEq | TypeLocation::TypeBound diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 621dea0b9a5..a30fd13b1d5 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -42,7 +42,7 @@ pub(crate) fn complete_type_path( }; let add_assoc_item = |acc: &mut Completions, item| match item { - hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg(_)) => { + hir::AssocItem::Const(ct) if matches!(location, TypeLocation::GenericArg { .. }) => { acc.add_const(ctx, ct) } hir::AssocItem::Function(_) | hir::AssocItem::Const(_) => (), @@ -156,33 +156,30 @@ pub(crate) fn complete_type_path( }); return; } - TypeLocation::GenericArg(Some((arg_list, in_trait, _))) => { - if let Some(trait_) = in_trait { - if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() { - let arg_idx = arg_list - .generic_args() - .filter(|arg| { - arg.syntax().text_range().end() - < ctx.original_token.text_range().start() - }) - .count(); + TypeLocation::GenericArg { + args: Some(arg_list), of_trait: Some(trait_), .. + } => { + if arg_list.syntax().ancestors().find_map(ast::TypeBound::cast).is_some() { + 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().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); - } - }, - ); - - 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_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().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); } + }); + + 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/context.rs b/crates/ide-completion/src/context.rs index 90ad2ecaf91..1fd635ba2e7 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -156,7 +156,14 @@ pub(crate) enum TypeLocation { TupleField, TypeAscription(TypeAscriptionTarget), /// Generic argument position e.g. `Foo<$0>` - GenericArg(Option<(ast::GenericArgList, Option, Option)>), + GenericArg { + /// The generic argument list containing the generic arg + args: Option, + /// `Some(trait_)` if `trait_` is being instantiated with `args` + of_trait: Option, + /// The generic parameter being filled in by the generic arg + corresponding_param: Option, + }, /// Associated type equality constraint e.g. `Foo` AssocTypeEq, /// Associated constant equality constraint e.g. `Foo` @@ -171,13 +178,19 @@ impl TypeLocation { pub(crate) fn complete_lifetimes(&self) -> bool { matches!( self, - TypeLocation::GenericArg(Some((_, _, Some(ast::GenericParam::LifetimeParam(_))))) + TypeLocation::GenericArg { + corresponding_param: Some(ast::GenericParam::LifetimeParam(_)), + .. + } ) } pub(crate) fn complete_consts(&self) -> bool { match self { - TypeLocation::GenericArg(Some((_, _, Some(ast::GenericParam::ConstParam(_))))) => true, + TypeLocation::GenericArg { + corresponding_param: Some(ast::GenericParam::ConstParam(_)), + .. + } => true, TypeLocation::AssocConstEq => true, _ => false, } @@ -185,7 +198,7 @@ pub(crate) fn complete_consts(&self) -> bool { pub(crate) fn complete_types(&self) -> bool { match self { - TypeLocation::GenericArg(Some((_, _, Some(param)))) => { + TypeLocation::GenericArg { corresponding_param: Some(param), .. } => { matches!(param, ast::GenericParam::TypeParam(_)) } TypeLocation::AssocConstEq => false, diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 2064ae34dd9..c66cb987fea 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -838,7 +838,15 @@ fn classify_name_ref( })(); (args, in_trait, param) }); - override_location.unwrap_or(TypeLocation::GenericArg(location)) + let (arg_list, of_trait, corresponding_param) = match location { + Some((arg_list, of_trait, param)) => (Some(arg_list), of_trait, param), + _ => (None, None, None), + }; + override_location.unwrap_or(TypeLocation::GenericArg { + args: arg_list, + of_trait, + corresponding_param, + }) }; let type_location = |node: &SyntaxNode| { @@ -899,9 +907,8 @@ fn classify_name_ref( ast::GenericArg(it) => generic_arg_location(it), // is this case needed? ast::GenericArgList(it) => { - let location = find_opt_node_in_file_compensated(sema, original_file, Some(it)) - .map(|node| (node, None, None)); - TypeLocation::GenericArg(location) + let args = find_opt_node_in_file_compensated(sema, original_file, Some(it)); + TypeLocation::GenericArg { args, of_trait: None, corresponding_param: None } }, ast::TupleField(_) => TypeLocation::TupleField, _ => return None, From fb98f522d216ee0c28b56115b130c5374c444ed9 Mon Sep 17 00:00:00 2001 From: Max Heller Date: Tue, 8 Aug 2023 20:55:35 -0400 Subject: [PATCH 15/15] fixme --- crates/ide-completion/src/tests/type_pos.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 273716bd513..d518dd76410 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -753,6 +753,9 @@ fn main() { kw self:: "#]], ); + // FIXME: This should probably also suggest completions for types, at least those that have + // associated constants usable in this position. For example, a user could be typing + // `foo::<_, { usize::MAX }>()`, but we currently don't suggest `usize` in constant position. check( r#" struct Foo;