diff --git a/Cargo.toml b/Cargo.toml index 2b81f7b11b2..e6f7c100904 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ authors = ["rust-analyzer team"] [profile.dev] # Disabling debug info speeds up builds a bunch, # and we don't rely on it for debugging that much. -debug = 0 +debug = 2 [profile.dev.package] # These speed up local tests. diff --git a/crates/hir/src/term_search/mod.rs b/crates/hir/src/term_search/mod.rs index 457165296ad..a519324cdad 100644 --- a/crates/hir/src/term_search/mod.rs +++ b/crates/hir/src/term_search/mod.rs @@ -12,13 +12,6 @@ pub use type_tree::TypeTree; mod tactics; -/// # Maximum amount of variations to take per type -/// -/// This is to speed up term search as there may be huge amount of variations of arguments for -/// function, even when the return type is always the same. The idea is to take first n and call it -/// a day. -const MAX_VARIATIONS: usize = 10; - /// Key for lookup table to query new types reached. #[derive(Debug, Hash, PartialEq, Eq)] enum NewTypesKey { @@ -26,6 +19,52 @@ enum NewTypesKey { StructProjection, } +#[derive(Debug)] +enum AlternativeTrees { + Few(FxHashSet), + Many(Type), +} + +impl AlternativeTrees { + pub fn new( + threshold: usize, + ty: Type, + trees: impl Iterator, + ) -> AlternativeTrees { + let mut it = AlternativeTrees::Few(Default::default()); + it.extend_with_threshold(threshold, ty, trees); + it + } + + pub fn trees(&self) -> Vec { + match self { + AlternativeTrees::Few(trees) => trees.iter().cloned().collect(), + AlternativeTrees::Many(ty) => vec![TypeTree::Many(ty.clone())], + } + } + + pub fn extend_with_threshold( + &mut self, + threshold: usize, + ty: Type, + mut trees: impl Iterator, + ) { + match self { + AlternativeTrees::Few(tts) => { + while let Some(it) = trees.next() { + if tts.len() > threshold { + *self = AlternativeTrees::Many(ty); + break; + } + + tts.insert(it); + } + } + AlternativeTrees::Many(_) => (), + } + } +} + /// # Lookup table for term search /// /// Lookup table keeps all the state during term search. @@ -38,7 +77,7 @@ enum NewTypesKey { #[derive(Default, Debug)] struct LookupTable { /// All the `TypeTree`s in "value" produce the type of "key" - data: FxHashMap>, + data: FxHashMap, /// New types reached since last query by the `NewTypesKey` new_types: FxHashMap>, /// ScopeDefs that are not interesting any more @@ -49,6 +88,8 @@ struct LookupTable { rounds_since_sopedef_hit: FxHashMap, /// Types queried but not present types_wishlist: FxHashSet, + /// Threshold to squash trees to `Many` + many_threshold: usize, } impl LookupTable { @@ -65,7 +106,7 @@ impl LookupTable { self.data .iter() .find(|(t, _)| t.could_unify_with_deeply(db, ty)) - .map(|(_, tts)| tts.iter().cloned().collect()) + .map(|(_, tts)| tts.trees()) } /// Same as find but automatically creates shared reference of types in the lookup @@ -76,7 +117,7 @@ impl LookupTable { self.data .iter() .find(|(t, _)| t.could_unify_with_deeply(db, ty)) - .map(|(_, tts)| tts.iter().cloned().collect()) + .map(|(_, tts)| tts.trees()) .or_else(|| { self.data .iter() @@ -84,7 +125,10 @@ impl LookupTable { Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, &ty) }) .map(|(_, tts)| { - tts.iter().map(|tt| TypeTree::Reference(Box::new(tt.clone()))).collect() + tts.trees() + .into_iter() + .map(|tt| TypeTree::Reference(Box::new(tt))) + .collect() }) }) } @@ -96,9 +140,12 @@ impl LookupTable { /// but they clearly do not unify themselves. fn insert(&mut self, ty: Type, trees: impl Iterator) { match self.data.get_mut(&ty) { - Some(it) => it.extend(trees.take(MAX_VARIATIONS)), + Some(it) => it.extend_with_threshold(self.many_threshold, ty, trees), None => { - self.data.insert(ty.clone(), trees.take(MAX_VARIATIONS).collect()); + self.data.insert( + ty.clone(), + AlternativeTrees::new(self.many_threshold, ty.clone(), trees), + ); for it in self.new_types.values_mut() { it.push(ty.clone()); } @@ -175,11 +222,15 @@ pub struct TermSearchCtx<'a, DB: HirDatabase> { pub struct TermSearchConfig { /// Enable borrow checking, this guarantees the outputs of the `term_search` to borrow-check pub enable_borrowcheck: bool, + /// Indicate when to squash multiple trees to `Many` as there are too many to keep track + pub many_alternatives_threshold: usize, + /// Depth of the search eg. number of cycles to run + pub depth: usize, } impl Default for TermSearchConfig { fn default() -> Self { - Self { enable_borrowcheck: true } + Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 5 } } } @@ -225,7 +276,7 @@ pub fn term_search(ctx: TermSearchCtx<'_, DB>) -> Vec let mut solution_found = !solutions.is_empty(); - for _ in 0..5 { + for _ in 0..ctx.config.depth { lookup.new_round(); solutions.extend(tactics::type_constructor(&ctx, &defs, &mut lookup)); diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index da0ffd59def..e0b6c1291e4 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -21,7 +21,7 @@ use crate::{ use crate::term_search::{TermSearchConfig, TypeTree}; -use super::{LookupTable, NewTypesKey, TermSearchCtx, MAX_VARIATIONS}; +use super::{LookupTable, NewTypesKey, TermSearchCtx}; /// # Trivial tactic /// @@ -194,7 +194,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( param_trees .into_iter() .multi_cartesian_product() - .take(MAX_VARIATIONS) .map(|params| TypeTree::Variant { variant, generics: generics.clone(), @@ -315,7 +314,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( param_trees .into_iter() .multi_cartesian_product() - .take(MAX_VARIATIONS) .map(|params| TypeTree::Struct { strukt: *it, generics: generics.clone(), @@ -440,7 +438,6 @@ pub(super) fn free_function<'a, DB: HirDatabase>( param_trees .into_iter() .multi_cartesian_product() - .take(MAX_VARIATIONS) .map(|params| TypeTree::Function { func: *it, generics: generics.clone(), @@ -603,7 +600,6 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( let fn_trees: Vec = std::iter::once(target_type_trees) .chain(param_trees.into_iter()) .multi_cartesian_product() - .take(MAX_VARIATIONS) .map(|params| TypeTree::Function { func: it, generics: Vec::new(), params }) .collect(); @@ -822,7 +818,6 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( param_trees .into_iter() .multi_cartesian_product() - .take(MAX_VARIATIONS) .map(|params| TypeTree::Function { func: it, generics: generics.clone(), diff --git a/crates/hir/src/term_search/type_tree.rs b/crates/hir/src/term_search/type_tree.rs index 3cb2fcdd64b..72a1cbe9096 100644 --- a/crates/hir/src/term_search/type_tree.rs +++ b/crates/hir/src/term_search/type_tree.rs @@ -109,6 +109,8 @@ pub enum TypeTree { Field { type_tree: Box, field: Field }, /// Passing type as reference (with `&`) Reference(Box), + /// Indicates possibility of many different options that all evaluate to `ty` + Many(Type), } impl TypeTree { @@ -117,7 +119,11 @@ impl TypeTree { /// Note that trait imports are not added to generated code. /// To make sure that the code is valid, callee has to also ensure that all the traits listed /// by `traits_used` method are also imported. - pub fn gen_source_code(&self, sema_scope: &SemanticsScope<'_>) -> String { + pub fn gen_source_code( + &self, + sema_scope: &SemanticsScope<'_>, + many_formatter: &mut dyn FnMut(&Type) -> String, + ) -> String { let db = sema_scope.db; match self { TypeTree::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)), @@ -128,9 +134,15 @@ impl TypeTree { TypeTree::Function { func, params, .. } => { if let Some(self_param) = func.self_param(db) { let func_name = func.name(db).display(db.upcast()).to_string(); - let target = params.first().expect("no self param").gen_source_code(sema_scope); - let args = - params.iter().skip(1).map(|f| f.gen_source_code(sema_scope)).join(", "); + let target = params + .first() + .expect("no self param") + .gen_source_code(sema_scope, many_formatter); + let args = params + .iter() + .skip(1) + .map(|f| f.gen_source_code(sema_scope, many_formatter)) + .join(", "); match func.as_assoc_item(db).unwrap().containing_trait_or_trait_impl(db) { Some(trait_) => { @@ -149,7 +161,10 @@ impl TypeTree { None => format!("{target}.{func_name}({args})"), } } else { - let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", "); + let args = params + .iter() + .map(|f| f.gen_source_code(sema_scope, many_formatter)) + .join(", "); match func.as_assoc_item(db).map(|it| it.container(db)) { Some(container) => { @@ -194,7 +209,10 @@ impl TypeTree { }; let inner = match variant.kind(db) { StructKind::Tuple => { - let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", "); + let args = params + .iter() + .map(|f| f.gen_source_code(sema_scope, many_formatter)) + .join(", "); format!("{generics_str}({args})") } StructKind::Record => { @@ -206,7 +224,7 @@ impl TypeTree { format!( "{}: {}", f.name(db).display(db.upcast()).to_string(), - a.gen_source_code(sema_scope) + a.gen_source_code(sema_scope, many_formatter) ) }) .join(", "); @@ -222,7 +240,10 @@ impl TypeTree { let generics = non_default_generics(db, (*strukt).into(), generics); let inner = match strukt.kind(db) { StructKind::Tuple => { - let args = params.iter().map(|a| a.gen_source_code(sema_scope)).join(", "); + let args = params + .iter() + .map(|a| a.gen_source_code(sema_scope, many_formatter)) + .join(", "); format!("({args})") } StructKind::Record => { @@ -234,7 +255,7 @@ impl TypeTree { format!( "{}: {}", f.name(db).display(db.upcast()).to_string(), - a.gen_source_code(sema_scope) + a.gen_source_code(sema_scope, many_formatter) ) }) .join(", "); @@ -254,14 +275,15 @@ impl TypeTree { format!("{prefix}{inner}") } TypeTree::Field { type_tree, field } => { - let strukt = type_tree.gen_source_code(sema_scope); + let strukt = type_tree.gen_source_code(sema_scope, many_formatter); let field = field.name(db).display(db.upcast()).to_string(); format!("{strukt}.{field}") } TypeTree::Reference(type_tree) => { - let inner = type_tree.gen_source_code(sema_scope); + let inner = type_tree.gen_source_code(sema_scope, many_formatter); format!("&{inner}") } + TypeTree::Many(ty) => many_formatter(ty), } } @@ -292,6 +314,7 @@ impl TypeTree { field.ty_with_generics(db, type_tree.ty(db).type_arguments()) } TypeTree::Reference(it) => it.ty(db), + TypeTree::Many(ty) => ty.clone(), } } diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index 85d7add4a0b..3451a65493b 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs @@ -36,8 +36,9 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< return None; } + let mut formatter = |_: &hir::Type| String::from("todo!()"); for path in paths.iter().unique() { - let code = path.gen_source_code(&scope); + let code = path.gen_source_code(&scope, &mut formatter); acc.add_group( &GroupLabel(String::from("Term search")), AssistId("term_search", AssistKind::Generate), diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 920db07e06f..b696933c272 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -162,10 +162,11 @@ impl Completions { &mut self, ctx: &CompletionContext<'_>, expr: &hir::term_search::TypeTree, - path_ctx: &PathCompletionCtx, ) { - let item = render_type_tree(ctx, expr, path_ctx); - item.add_to(self, ctx.db); + match render_type_tree(ctx, expr) { + Some(item) => item.add_to(self, ctx.db), + None => (), + } } pub(crate) fn add_crate_roots( @@ -698,10 +699,10 @@ pub(super) fn complete_name_ref( ctx: &CompletionContext<'_>, NameRefContext { nameref, kind }: &NameRefContext, ) { + expr::complete_expr(acc, ctx); match kind { NameRefKind::Path(path_ctx) => { flyimport::import_on_the_fly_path(acc, ctx, path_ctx); - expr::complete_expr(acc, ctx, path_ctx); match &path_ctx.kind { PathKind::Expr { expr_ctx } => { diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index b8ed429cb24..891717285da 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -332,7 +332,6 @@ pub(crate) fn complete_expr_path( pub(crate) fn complete_expr( acc: &mut Completions, ctx: &CompletionContext<'_>, - path_ctx: &PathCompletionCtx, ) { let _p = profile::span("complete_expr"); if !ctx.qualifier_ctx.none() { @@ -349,11 +348,15 @@ pub(crate) fn complete_expr( sema: &ctx.sema, scope: &ctx.scope, goal: ty.clone(), - config: hir::term_search::TermSearchConfig { enable_borrowcheck: false }, + config: hir::term_search::TermSearchConfig { + enable_borrowcheck: false, + many_alternatives_threshold: 1, + depth: 2, + }, }; let exprs = hir::term_search::term_search(term_search_ctx); for expr in exprs { - acc.add_expr(ctx, &expr, path_ctx); + acc.add_expr(ctx, &expr); } } } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 6d91b379162..0a9ea1ac849 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -17,7 +17,7 @@ use ide_db::{ imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind, }; -use syntax::{format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange}; +use syntax::{ast, AstNode, SmolStr, SyntaxKind, TextRange}; use text_edit::TextEdit; use crate::{ @@ -275,21 +275,50 @@ pub(crate) fn render_resolution_with_import_pat( pub(crate) fn render_type_tree( ctx: &CompletionContext<'_>, expr: &hir::term_search::TypeTree, - path_ctx: &PathCompletionCtx, -) -> Builder { - let mut item = CompletionItem::new( - CompletionItemKind::Snippet, - ctx.source_range(), - expr.gen_source_code(&ctx.scope), - ); +) -> Option { + let mut i = 1; + let mut snippet_formatter = |ty: &hir::Type| { + let arg_name = ty + .as_adt() + .and_then(|adt| adt.name(ctx.db).as_text()) + .map(|s| stdx::to_lower_snake_case(s.as_str())) + .unwrap_or_else(|| String::from("_")); + let res = format!("${{{i}:{arg_name}}}"); + i += 1; + res + }; + + let mut label_formatter = |ty: &hir::Type| { + ty.as_adt() + .and_then(|adt| adt.name(ctx.db).as_text()) + .map(|s| stdx::to_lower_snake_case(s.as_str())) + .unwrap_or_else(|| String::from("_")) + }; + + let label = expr.gen_source_code(&ctx.scope, &mut label_formatter); + + let source_range = match &ctx.expected_name { + Some(name_or_ref) => name_or_ref.syntax().text_range(), + None => match ctx.original_token.parent() { + Some(node) => match node.ancestors().find_map(|n| ast::Path::cast(n)) { + Some(path) => path.syntax().text_range(), + None => node.text_range(), + }, + None => ctx.source_range(), + }, + }; + + let mut item = CompletionItem::new(CompletionItemKind::Snippet, source_range, label); + + let snippet = format!("{}$0", expr.gen_source_code(&ctx.scope, &mut snippet_formatter)); + let edit = TextEdit::replace(source_range, snippet); + item.snippet_edit(ctx.config.snippet_cap?, edit); item.set_relevance(crate::CompletionRelevance { type_match: Some(crate::item::CompletionRelevanceTypeMatch::CouldUnify), ..Default::default() }); - path_ref_match(ctx, path_ctx, &expr.ty(ctx.sema.db), &mut item); - - item + Some(item) } fn scope_def_to_name( diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index 78907a2896c..b835820260a 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -97,6 +97,11 @@ fn func(param0 @ (param1, param2): (i32, i32)) { kw unsafe kw while kw while let + sn ifletlocal + sn letlocal + sn matcharm + sn param1 + sn param2 "#]], ); } @@ -238,9 +243,11 @@ fn complete_in_block() { kw use kw while kw while let + sn false sn macro_rules sn pd sn ppd + sn true "#]], ) } @@ -682,7 +689,9 @@ fn main() { } "#, expect![[r#" - fn test() fn() -> Zulu + fn test() fn() -> Zulu + sn Zulu + sn Zulu::test() "#]], ); } diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 67cf551fce8..5363e33b855 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -316,6 +316,15 @@ fn func() { bn RecordV {…} RecordV { field$1 }$0 bn TupleV(…) TupleV($1)$0 bn UnitV UnitV$0 + sn () + sn CONST + sn Enum::UnitV + sn STATIC + sn Unit + sn false + sn func() + sn function() + sn true "#]], ); } @@ -558,10 +567,12 @@ fn foo() { } "#, expect![[r#" - bn A A$0 - bn B {…} B { r#type$1 }$0 - bn struct {…} r#struct { r#type$1 }$0 - bn type r#type$0 + bn A A$0 + bn B {…} B { r#type$1 }$0 + bn struct {…} r#struct { r#type$1 }$0 + bn type r#type$0 + sn Enum::A + sn Enum::r#type "#]], ); } @@ -586,6 +597,7 @@ fn f(t: Ty) { "#, expect![[r#" ct ABC const ABC: Self + sn t "#]], ); @@ -608,6 +620,7 @@ fn f(e: MyEnum) { expect![[r#" ct A pub const A: i32 ct B pub const B: i32 + sn e "#]], ); @@ -633,6 +646,7 @@ fn f(u: U) { expect![[r#" ct C pub const C: i32 ct D pub const D: i32 + sn u "#]], ); @@ -652,6 +666,7 @@ fn f(v: u32) { "#, expect![[r#" ct MIN pub const MIN: Self + sn v "#]], ); } @@ -763,6 +778,7 @@ fn f(x: EnumAlias) { expect![[r#" bn Tuple(…) Tuple($1)$0 bn Unit Unit$0 + sn x "#]], ); } diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index 18afde1b7ce..c137de3e52d 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -192,6 +192,8 @@ fn main() { bt u32 u32 kw crate:: kw self:: + sn Foo::default() + sn foo "#]], ); check( diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index a87d16c789f..68a6c678501 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -1230,6 +1230,10 @@ fn here_we_go() { "#, expect![[r#" st Bar (alias Qux) Bar + sn () + sn false + sn here_we_go() + sn true "#]], ); } @@ -1284,6 +1288,10 @@ fn here_we_go() { kw unsafe kw while kw while let + sn () + sn false + sn here_we_go() + sn true "#]], ); } diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index c7161f82ce7..8b383d49952 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -70,18 +70,27 @@ fn fn_return_type() { fn x<'lt, T, const C: usize>() -> $0 "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait tp T - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 kw crate:: kw self:: + sn () + sn C + sn CONST + sn Enum::UnitV + sn STATIC + sn Unit + sn false + sn function() + sn true "#]], ); } @@ -100,18 +109,27 @@ fn foo() -> B$0 { } "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 it () kw crate:: kw self:: + sn () + sn CONST + sn Enum::UnitV + sn STATIC + sn Unit + sn false + sn foo() + sn function() + sn true "#]], ) } @@ -204,18 +222,26 @@ fn f2(x: u64) -> $0 { } "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 it u64 kw crate:: kw self:: + sn () + sn CONST + sn Enum::UnitV + sn STATIC + sn Unit + sn false + sn function() + sn true "#]], ); } @@ -319,18 +345,27 @@ fn foo<'lt, T, const C: usize>() { } "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait tp T - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 kw crate:: kw self:: + sn () + sn C + sn CONST + sn Enum::UnitV + sn STATIC + sn Unit + sn false + sn function() + sn true "#]], ); check( @@ -341,14 +376,23 @@ fn foo<'lt, T, const C: usize>() { } "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait - un Union Union + un Union Union + sn () + sn C + sn CONST + sn Enum::UnitV + sn STATIC + sn Unit + sn false + sn function() + sn true "#]], ); } diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index 62e20f48b80..fa4734b1a9c 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -44,12 +44,14 @@ fn fixes(sema: &Semantics<'_, RootDatabase>, d: &hir::TypedHole) -> Option bool { + matches!(kind, SyntaxKind::NAME | SyntaxKind::NAME_REF) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + SyntaxKind::NAME => NameOrNameRef::Name(ast::Name { syntax }), + SyntaxKind::NAME_REF => NameOrNameRef::NameRef(ast::NameRef { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + NameOrNameRef::NameRef(it) => it.syntax(), + NameOrNameRef::Name(it) => it.syntax(), + } + } +} + impl NameOrNameRef { pub fn text(&self) -> TokenText<'_> { match self {