about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml2
-rw-r--r--crates/hir/src/term_search/mod.rs81
-rw-r--r--crates/hir/src/term_search/tactics.rs7
-rw-r--r--crates/hir/src/term_search/type_tree.rs45
-rw-r--r--crates/ide-assists/src/handlers/term_search.rs3
-rw-r--r--crates/ide-completion/src/completions.rs9
-rw-r--r--crates/ide-completion/src/completions/expr.rs9
-rw-r--r--crates/ide-completion/src/render.rs51
-rw-r--r--crates/ide-completion/src/tests/expression.rs11
-rw-r--r--crates/ide-completion/src/tests/pattern.rs24
-rw-r--r--crates/ide-completion/src/tests/record.rs2
-rw-r--r--crates/ide-completion/src/tests/special.rs8
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs112
-rw-r--r--crates/ide-diagnostics/src/handlers/typed_hole.rs6
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs4
-rw-r--r--crates/syntax/src/ast/node_ext.rs20
16 files changed, 300 insertions, 94 deletions
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<TypeTree>),
+    Many(Type),
+}
+
+impl AlternativeTrees {
+    pub fn new(
+        threshold: usize,
+        ty: Type,
+        trees: impl Iterator<Item = TypeTree>,
+    ) -> AlternativeTrees {
+        let mut it = AlternativeTrees::Few(Default::default());
+        it.extend_with_threshold(threshold, ty, trees);
+        it
+    }
+
+    pub fn trees(&self) -> Vec<TypeTree> {
+        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<Item = TypeTree>,
+    ) {
+        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<Type, FxHashSet<TypeTree>>,
+    data: FxHashMap<Type, AlternativeTrees>,
     /// New types reached since last query by the `NewTypesKey`
     new_types: FxHashMap<NewTypesKey, Vec<Type>>,
     /// ScopeDefs that are not interesting any more
@@ -49,6 +88,8 @@ struct LookupTable {
     rounds_since_sopedef_hit: FxHashMap<ScopeDef, u32>,
     /// Types queried but not present
     types_wishlist: FxHashSet<Type>,
+    /// 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<Item = TypeTree>) {
         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<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree>
 
     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<TypeTree> = 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<TypeTree>, field: Field },
     /// Passing type as reference (with `&`)
     Reference(Box<TypeTree>),
+    /// 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<Builder> {
+    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<u8>) {
         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<Vec<A
         d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?;
     let scope = sema.scope(d.expr.value.to_node(&root).syntax())?;
 
-    let ctx = TermSearchCtx { sema, scope: &scope, goal: d.expected.clone(), config: Default::default() };
+    let ctx =
+        TermSearchCtx { sema, scope: &scope, goal: d.expected.clone(), config: Default::default() };
     let paths = term_search(ctx);
 
     let mut assists = vec![];
+    let mut formatter = |_: &hir::Type| String::from("_");
     for path in paths.into_iter().unique() {
-        let code = path.gen_source_code(&scope);
+        let code = path.gen_source_code(&scope, &mut formatter);
 
         assists.push(Assist {
             id: AssistId("typed-hole", AssistKind::QuickFix),
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 5f43acdf19a..ad16e360da1 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -412,6 +412,7 @@ impl flags::AnalysisStats {
                     goal: target_ty,
                     config: hir::term_search::TermSearchConfig {
                         enable_borrowcheck: true,
+                        ..Default::default()
                     },
                 };
                 let found_terms = hir::term_search::term_search(ctx);
@@ -427,9 +428,10 @@ impl flags::AnalysisStats {
                     s.chars().into_iter().filter(|c| !c.is_whitespace()).collect()
                 }
 
+                let mut formatter = |_: &hir::Type| syntax::ast::make::ext::expr_todo().to_string();
                 let mut syntax_hit_found = false;
                 for term in found_terms {
-                    let generated = term.gen_source_code(&scope);
+                    let generated = term.gen_source_code(&scope, &mut formatter);
                     syntax_hit_found |= trim(&original_text) == trim(&generated);
 
                     // Validate if type-checks
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index 6e5e4127f4d..1bc1ef8434f 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -569,6 +569,26 @@ impl fmt::Display for NameOrNameRef {
     }
 }
 
+impl ast::AstNode for NameOrNameRef {
+    fn can_cast(kind: SyntaxKind) -> bool {
+        matches!(kind, SyntaxKind::NAME | SyntaxKind::NAME_REF)
+    }
+    fn cast(syntax: SyntaxNode) -> Option<Self> {
+        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 {