about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTavo Annus <tavo.annus@gmail.com>2023-12-27 21:49:10 +0200
committerTavo Annus <tavo.annus@gmail.com>2024-02-11 13:33:29 +0200
commitbdbdd83ec1dcca82bb6f8651c54ed2068b767a71 (patch)
tree461e37643470c45b49c49b692e8e379762e463f9
parent627255dd5afb661819a4cd831b765c846a0c02aa (diff)
downloadrust-bdbdd83ec1dcca82bb6f8651c54ed2068b767a71.tar.gz
rust-bdbdd83ec1dcca82bb6f8651c54ed2068b767a71.zip
Initial version of term_search for autocomplete
-rw-r--r--crates/hir/src/term_search/mod.rs53
-rw-r--r--crates/hir/src/term_search/tactics.rs184
-rw-r--r--crates/ide-assists/src/handlers/term_search.rs9
-rw-r--r--crates/ide-completion/src/completions.rs12
-rw-r--r--crates/ide-completion/src/completions/expr.rs29
-rw-r--r--crates/ide-completion/src/render.rs20
-rw-r--r--crates/ide-diagnostics/src/handlers/typed_hole.rs9
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs10
8 files changed, 211 insertions, 115 deletions
diff --git a/crates/hir/src/term_search/mod.rs b/crates/hir/src/term_search/mod.rs
index 009d561e7d1..457165296ad 100644
--- a/crates/hir/src/term_search/mod.rs
+++ b/crates/hir/src/term_search/mod.rs
@@ -152,11 +152,37 @@ impl LookupTable {
         &self.exhausted_scopedefs
     }
 
+    /// Types queried but not found
     fn take_types_wishlist(&mut self) -> FxHashSet<Type> {
         std::mem::take(&mut self.types_wishlist)
     }
 }
 
+/// Context for the `term_search` function
+pub struct TermSearchCtx<'a, DB: HirDatabase> {
+    /// Semantics for the program
+    pub sema: &'a Semantics<'a, DB>,
+    /// Semantic scope, captures context for the term search
+    pub scope: &'a SemanticsScope<'a>,
+    /// Target / expected output type
+    pub goal: Type,
+    /// Configuration for term search
+    pub config: TermSearchConfig,
+}
+
+/// Configuration options for the term search
+#[derive(Debug, Clone, Copy)]
+pub struct TermSearchConfig {
+    /// Enable borrow checking, this guarantees the outputs of the `term_search` to borrow-check
+    pub enable_borrowcheck: bool,
+}
+
+impl Default for TermSearchConfig {
+    fn default() -> Self {
+        Self { enable_borrowcheck: true }
+    }
+}
+
 /// # Term search
 ///
 /// Search for terms (expressions) that unify with the `goal` type.
@@ -181,37 +207,32 @@ impl LookupTable {
 /// Note that there are usually more ways we can get to the `goal` type but some are discarded to
 /// reduce the memory consumption. It is also unlikely anyone is willing ti browse through
 /// thousands of possible responses so we currently take first 10 from every tactic.
-pub fn term_search<DB: HirDatabase>(
-    sema: &Semantics<'_, DB>,
-    scope: &SemanticsScope<'_>,
-    goal: &Type,
-) -> Vec<TypeTree> {
+pub fn term_search<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree> {
+    let module = ctx.scope.module();
     let mut defs = FxHashSet::default();
-    defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(scope.module())));
+    defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module)));
 
-    scope.process_all_names(&mut |_, def| {
+    ctx.scope.process_all_names(&mut |_, def| {
         defs.insert(def);
     });
-    let module = scope.module();
 
     let mut lookup = LookupTable::new();
 
     // Try trivial tactic first, also populates lookup table
-    let mut solutions: Vec<TypeTree> =
-        tactics::trivial(sema.db, &defs, &mut lookup, goal).collect();
+    let mut solutions: Vec<TypeTree> = tactics::trivial(&ctx, &defs, &mut lookup).collect();
     // Use well known types tactic before iterations as it does not depend on other tactics
-    solutions.extend(tactics::famous_types(sema.db, &module, &defs, &mut lookup, goal));
+    solutions.extend(tactics::famous_types(&ctx, &defs, &mut lookup));
 
     let mut solution_found = !solutions.is_empty();
 
     for _ in 0..5 {
         lookup.new_round();
 
-        solutions.extend(tactics::type_constructor(sema.db, &module, &defs, &mut lookup, goal));
-        solutions.extend(tactics::free_function(sema.db, &module, &defs, &mut lookup, goal));
-        solutions.extend(tactics::impl_method(sema.db, &module, &defs, &mut lookup, goal));
-        solutions.extend(tactics::struct_projection(sema.db, &module, &defs, &mut lookup, goal));
-        solutions.extend(tactics::impl_static_method(sema.db, &module, &defs, &mut lookup, goal));
+        solutions.extend(tactics::type_constructor(&ctx, &defs, &mut lookup));
+        solutions.extend(tactics::free_function(&ctx, &defs, &mut lookup));
+        solutions.extend(tactics::impl_method(&ctx, &defs, &mut lookup));
+        solutions.extend(tactics::struct_projection(&ctx, &defs, &mut lookup));
+        solutions.extend(tactics::impl_static_method(&ctx, &defs, &mut lookup));
 
         // Break after 1 round after successful solution
         if solution_found {
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs
index 2a9d0d84518..da0ffd59def 100644
--- a/crates/hir/src/term_search/tactics.rs
+++ b/crates/hir/src/term_search/tactics.rs
@@ -1,11 +1,9 @@
 //! Tactics for term search
 //!
 //! All the tactics take following arguments
-//! * `db` - HIR database
-//! * `module` - Module where the term search target location
+//! * `ctx` - Context for the term search
 //! * `defs` - Set of items in scope at term search target location
 //! * `lookup` - Lookup table for types
-//! * `goal` - Term search target type
 //! And they return iterator that yields type trees that unify with the `goal` type.
 
 use std::iter;
@@ -17,13 +15,13 @@ use itertools::Itertools;
 use rustc_hash::FxHashSet;
 
 use crate::{
-    Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, Module, ModuleDef,
-    ScopeDef, Type, Variant,
+    Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type,
+    Variant,
 };
 
-use crate::term_search::TypeTree;
+use crate::term_search::{TermSearchConfig, TypeTree};
 
-use super::{LookupTable, NewTypesKey, MAX_VARIATIONS};
+use super::{LookupTable, NewTypesKey, TermSearchCtx, MAX_VARIATIONS};
 
 /// # Trivial tactic
 ///
@@ -31,41 +29,42 @@ use super::{LookupTable, NewTypesKey, MAX_VARIATIONS};
 /// Also works as a starting point to move all items in scope to lookup table.
 ///
 /// # Arguments
-/// * `db` - HIR database
+/// * `ctx` - Context for the term search
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
-/// * `goal` - Term search target type
 ///
 /// Returns iterator that yields elements that unify with `goal`.
 ///
 /// _Note that there is no use of calling this tactic in every iteration as the output does not
 /// depend on the current state of `lookup`_
-pub(super) fn trivial<'a>(
-    db: &'a dyn HirDatabase,
+pub(super) fn trivial<'a, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'a, DB>,
     defs: &'a FxHashSet<ScopeDef>,
     lookup: &'a mut LookupTable,
-    goal: &'a Type,
 ) -> impl Iterator<Item = TypeTree> + 'a {
+    let db = ctx.sema.db;
     defs.iter().filter_map(|def| {
         let tt = match def {
             ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(TypeTree::Const(*it)),
             ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(TypeTree::Static(*it)),
             ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(TypeTree::ConstParam(*it)),
             ScopeDef::Local(it) => {
-                let borrowck = db.borrowck(it.parent).ok()?;
-
-                let invalid = borrowck.iter().any(|b| {
-                    b.partially_moved.iter().any(|moved| {
-                        Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id)
-                    }) || b.borrow_regions.iter().any(|region| {
-                        // Shared borrows are fine
-                        Some(&region.local) == b.mir_body.binding_locals.get(it.binding_id)
-                            && region.kind != BorrowKind::Shared
-                    })
-                });
+                if ctx.config.enable_borrowcheck {
+                    let borrowck = db.borrowck(it.parent).ok()?;
+
+                    let invalid = borrowck.iter().any(|b| {
+                        b.partially_moved.iter().any(|moved| {
+                            Some(&moved.local) == b.mir_body.binding_locals.get(it.binding_id)
+                        }) || b.borrow_regions.iter().any(|region| {
+                            // Shared borrows are fine
+                            Some(&region.local) == b.mir_body.binding_locals.get(it.binding_id)
+                                && region.kind != BorrowKind::Shared
+                        })
+                    });
 
-                if invalid {
-                    return None;
+                    if invalid {
+                        return None;
+                    }
                 }
 
                 Some(TypeTree::Local(*it))
@@ -83,7 +82,7 @@ pub(super) fn trivial<'a>(
             return None;
         }
 
-        ty.could_unify_with_deeply(db, goal).then(|| tt)
+        ty.could_unify_with_deeply(db, &ctx.goal).then(|| tt)
     })
 }
 
@@ -95,24 +94,23 @@ pub(super) fn trivial<'a>(
 /// elements that unify with `goal`.
 ///
 /// # Arguments
-/// * `db` - HIR database
-/// * `module` - Module where the term search target location
+/// * `ctx` - Context for the term search
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
-/// * `goal` - Term search target type
-pub(super) fn type_constructor<'a>(
-    db: &'a dyn HirDatabase,
-    module: &'a Module,
+pub(super) fn type_constructor<'a, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'a, DB>,
     defs: &'a FxHashSet<ScopeDef>,
     lookup: &'a mut LookupTable,
-    goal: &'a Type,
 ) -> impl Iterator<Item = TypeTree> + 'a {
+    let db = ctx.sema.db;
+    let module = ctx.scope.module();
     fn variant_helper(
         db: &dyn HirDatabase,
         lookup: &mut LookupTable,
         parent_enum: Enum,
         variant: Variant,
         goal: &Type,
+        config: &TermSearchConfig,
     ) -> Vec<(Type, Vec<TypeTree>)> {
         let generics = GenericDef::from(variant.parent_enum(db));
 
@@ -151,7 +149,7 @@ pub(super) fn type_constructor<'a>(
             .permutations(non_default_type_params_len);
 
         generic_params
-            .filter_map(|generics| {
+            .filter_map(move |generics| {
                 // Insert default type params
                 let mut g = generics.into_iter();
                 let generics: Vec<_> = type_params
@@ -171,7 +169,7 @@ pub(super) fn type_constructor<'a>(
                 }
 
                 // Ignore types that have something to do with lifetimes
-                if enum_ty.contains_reference(db) {
+                if config.enable_borrowcheck && enum_ty.contains_reference(db) {
                     return None;
                 }
 
@@ -211,9 +209,10 @@ pub(super) fn type_constructor<'a>(
             .collect()
     }
     defs.iter()
-        .filter_map(|def| match def {
+        .filter_map(move |def| match def {
             ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
-                let variant_trees = variant_helper(db, lookup, it.parent_enum(db), *it, goal);
+                let variant_trees =
+                    variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.goal, &ctx.config);
                 if variant_trees.is_empty() {
                     return None;
                 }
@@ -224,7 +223,9 @@ pub(super) fn type_constructor<'a>(
                 let trees: Vec<(Type, Vec<TypeTree>)> = enum_
                     .variants(db)
                     .into_iter()
-                    .flat_map(|it| variant_helper(db, lookup, enum_.clone(), it, goal))
+                    .flat_map(|it| {
+                        variant_helper(db, lookup, enum_.clone(), it, &ctx.goal, &ctx.config)
+                    })
                     .collect();
 
                 if !trees.is_empty() {
@@ -234,8 +235,8 @@ pub(super) fn type_constructor<'a>(
                 Some(trees)
             }
             ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => {
-                // Ignore unstable
-                if it.is_unstable(db) {
+                // Ignore unstable and not visible
+                if it.is_unstable(db) || !it.is_visible_from(db, module) {
                     return None;
                 }
 
@@ -285,18 +286,18 @@ pub(super) fn type_constructor<'a>(
                         // Allow types with generics only if they take us straight to goal for
                         // performance reasons
                         if non_default_type_params_len != 0
-                            && struct_ty.could_unify_with_deeply(db, goal)
+                            && struct_ty.could_unify_with_deeply(db, &ctx.goal)
                         {
                             return None;
                         }
 
                         // Ignore types that have something to do with lifetimes
-                        if struct_ty.contains_reference(db) {
+                        if ctx.config.enable_borrowcheck && struct_ty.contains_reference(db) {
                             return None;
                         }
                         let fileds = it.fields(db);
                         // Check if all fields are visible, otherwise we cannot fill them
-                        if fileds.iter().any(|it| !it.is_visible_from(db, *module)) {
+                        if fileds.iter().any(|it| !it.is_visible_from(db, module)) {
                             return None;
                         }
 
@@ -335,7 +336,7 @@ pub(super) fn type_constructor<'a>(
             _ => None,
         })
         .flatten()
-        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees))
+        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees))
         .flatten()
 }
 
@@ -348,20 +349,18 @@ pub(super) fn type_constructor<'a>(
 /// elements that unify with `goal`.
 ///
 /// # Arguments
-/// * `db` - HIR database
-/// * `module` - Module where the term search target location
+/// * `ctx` - Context for the term search
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
-/// * `goal` - Term search target type
-pub(super) fn free_function<'a>(
-    db: &'a dyn HirDatabase,
-    module: &'a Module,
+pub(super) fn free_function<'a, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'a, DB>,
     defs: &'a FxHashSet<ScopeDef>,
     lookup: &'a mut LookupTable,
-    goal: &'a Type,
 ) -> impl Iterator<Item = TypeTree> + 'a {
+    let db = ctx.sema.db;
+    let module = ctx.scope.module();
     defs.iter()
-        .filter_map(|def| match def {
+        .filter_map(move |def| match def {
             ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
                 let generics = GenericDef::from(*it);
 
@@ -411,10 +410,10 @@ pub(super) fn free_function<'a>(
 
                         let ret_ty = it.ret_type_with_generics(db, generics.iter().cloned());
                         // Filter out private and unsafe functions
-                        if !it.is_visible_from(db, *module)
+                        if !it.is_visible_from(db, module)
                             || it.is_unsafe_to_call(db)
                             || it.is_unstable(db)
-                            || ret_ty.contains_reference(db)
+                            || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
                             || ret_ty.is_raw_ptr()
                         {
                             return None;
@@ -461,7 +460,7 @@ pub(super) fn free_function<'a>(
             _ => None,
         })
         .flatten()
-        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees))
+        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees))
         .flatten()
 }
 
@@ -476,18 +475,16 @@ pub(super) fn free_function<'a>(
 /// elements that unify with `goal`.
 ///
 /// # Arguments
-/// * `db` - HIR database
-/// * `module` - Module where the term search target location
+/// * `ctx` - Context for the term search
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
-/// * `goal` - Term search target type
-pub(super) fn impl_method<'a>(
-    db: &'a dyn HirDatabase,
-    module: &'a Module,
+pub(super) fn impl_method<'a, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'a, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
     lookup: &'a mut LookupTable,
-    goal: &'a Type,
 ) -> impl Iterator<Item = TypeTree> + 'a {
+    let db = ctx.sema.db;
+    let module = ctx.scope.module();
     lookup
         .new_types(NewTypesKey::ImplMethod)
         .into_iter()
@@ -499,7 +496,7 @@ pub(super) fn impl_method<'a>(
             AssocItem::Function(f) => Some((imp, ty, f)),
             _ => None,
         })
-        .filter_map(|(imp, ty, it)| {
+        .filter_map(move |(imp, ty, it)| {
             let fn_generics = GenericDef::from(it);
             let imp_generics = GenericDef::from(imp);
 
@@ -520,7 +517,7 @@ pub(super) fn impl_method<'a>(
             }
 
             // Filter out private and unsafe functions
-            if !it.is_visible_from(db, *module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
+            if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
                 return None;
             }
 
@@ -570,7 +567,9 @@ pub(super) fn impl_method<'a>(
                         ty.type_arguments().chain(generics.iter().cloned()),
                     );
                     // Filter out functions that return references
-                    if ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() {
+                    if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
+                        || ret_ty.is_raw_ptr()
+                    {
                         return None;
                     }
 
@@ -615,7 +614,7 @@ pub(super) fn impl_method<'a>(
             Some(trees)
         })
         .flatten()
-        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees))
+        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees))
         .flatten()
 }
 
@@ -627,24 +626,21 @@ pub(super) fn impl_method<'a>(
 /// elements that unify with `goal`.
 ///
 /// # Arguments
-/// * `db` - HIR database
-/// * `module` - Module where the term search target location
+/// * `ctx` - Context for the term search
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
-/// * `goal` - Term search target type
-pub(super) fn struct_projection<'a>(
-    db: &'a dyn HirDatabase,
-    module: &'a Module,
+pub(super) fn struct_projection<'a, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'a, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
     lookup: &'a mut LookupTable,
-    goal: &'a Type,
 ) -> impl Iterator<Item = TypeTree> + 'a {
+    let db = ctx.sema.db;
+    let module = ctx.scope.module();
     lookup
         .new_types(NewTypesKey::StructProjection)
         .into_iter()
         .map(|ty| (ty.clone(), lookup.find(db, &ty).expect("TypeTree not in lookup")))
         .flat_map(move |(ty, targets)| {
-            let module = module.clone();
             ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| {
                 if !field.is_visible_from(db, module) {
                     return None;
@@ -656,7 +652,7 @@ pub(super) fn struct_projection<'a>(
                 Some((filed_ty, trees))
             })
         })
-        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees))
+        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees))
         .flatten()
 }
 
@@ -670,18 +666,16 @@ pub(super) fn struct_projection<'a>(
 /// _Note that there is no point of calling it iteratively as the output is always the same_
 ///
 /// # Arguments
-/// * `db` - HIR database
-/// * `module` - Module where the term search target location
+/// * `ctx` - Context for the term search
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
-/// * `goal` - Term search target type
-pub(super) fn famous_types<'a>(
-    db: &'a dyn HirDatabase,
-    module: &'a Module,
+pub(super) fn famous_types<'a, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'a, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
     lookup: &'a mut LookupTable,
-    goal: &'a Type,
 ) -> impl Iterator<Item = TypeTree> + 'a {
+    let db = ctx.sema.db;
+    let module = ctx.scope.module();
     [
         TypeTree::FamousType { ty: Type::new(db, module.id, TyBuilder::bool()), value: "true" },
         TypeTree::FamousType { ty: Type::new(db, module.id, TyBuilder::bool()), value: "false" },
@@ -692,7 +686,7 @@ pub(super) fn famous_types<'a>(
         lookup.insert(tt.ty(db), std::iter::once(tt.clone()));
         tt
     })
-    .filter(|tt| tt.ty(db).could_unify_with_deeply(db, goal))
+    .filter(|tt| tt.ty(db).could_unify_with_deeply(db, &ctx.goal))
 }
 
 /// # Impl static method (without self type) tactic
@@ -703,22 +697,20 @@ pub(super) fn famous_types<'a>(
 /// elements that unify with `goal`.
 ///
 /// # Arguments
-/// * `db` - HIR database
-/// * `module` - Module where the term search target location
+/// * `ctx` - Context for the term search
 /// * `defs` - Set of items in scope at term search target location
 /// * `lookup` - Lookup table for types
-/// * `goal` - Term search target type
-pub(super) fn impl_static_method<'a>(
-    db: &'a dyn HirDatabase,
-    module: &'a Module,
+pub(super) fn impl_static_method<'a, DB: HirDatabase>(
+    ctx: &'a TermSearchCtx<'a, DB>,
     _defs: &'a FxHashSet<ScopeDef>,
     lookup: &'a mut LookupTable,
-    goal: &'a Type,
 ) -> impl Iterator<Item = TypeTree> + 'a {
+    let db = ctx.sema.db;
+    let module = ctx.scope.module();
     lookup
         .take_types_wishlist()
         .into_iter()
-        .chain(iter::once(goal.clone()))
+        .chain(iter::once(ctx.goal.clone()))
         .flat_map(|ty| {
             Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
         })
@@ -728,7 +720,7 @@ pub(super) fn impl_static_method<'a>(
             AssocItem::Function(f) => Some((imp, ty, f)),
             _ => None,
         })
-        .filter_map(|(imp, ty, it)| {
+        .filter_map(move |(imp, ty, it)| {
             let fn_generics = GenericDef::from(it);
             let imp_generics = GenericDef::from(imp);
 
@@ -751,7 +743,7 @@ pub(super) fn impl_static_method<'a>(
             }
 
             // Filter out private and unsafe functions
-            if !it.is_visible_from(db, *module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
+            if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
                 return None;
             }
 
@@ -801,7 +793,9 @@ pub(super) fn impl_static_method<'a>(
                         ty.type_arguments().chain(generics.iter().cloned()),
                     );
                     // Filter out functions that return references
-                    if ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() {
+                    if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
+                        || ret_ty.is_raw_ptr()
+                    {
                         return None;
                     }
 
@@ -845,6 +839,6 @@ pub(super) fn impl_static_method<'a>(
             Some(trees)
         })
         .flatten()
-        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees))
+        .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees))
         .flatten()
 }
diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs
index a32e36b7127..85d7add4a0b 100644
--- a/crates/ide-assists/src/handlers/term_search.rs
+++ b/crates/ide-assists/src/handlers/term_search.rs
@@ -1,4 +1,5 @@
 //! Term search assist
+use hir::term_search::TermSearchCtx;
 use ide_db::assists::{AssistId, AssistKind, GroupLabel};
 
 use itertools::Itertools;
@@ -23,7 +24,13 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
 
     let scope = ctx.sema.scope(&parent)?;
 
-    let paths = hir::term_search::term_search(&ctx.sema, &scope, &target_ty);
+    let term_search_ctx = TermSearchCtx {
+        sema: &ctx.sema,
+        scope: &scope,
+        goal: target_ty,
+        config: Default::default(),
+    };
+    let paths = hir::term_search::term_search(term_search_ctx);
 
     if paths.is_empty() {
         return None;
diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs
index ba3c0cf3fd6..920db07e06f 100644
--- a/crates/ide-completion/src/completions.rs
+++ b/crates/ide-completion/src/completions.rs
@@ -41,6 +41,7 @@ use crate::{
         macro_::render_macro,
         pattern::{render_struct_pat, render_variant_pat},
         render_field, render_path_resolution, render_pattern_resolution, render_tuple_field,
+        render_type_tree,
         type_alias::{render_type_alias, render_type_alias_with_eq},
         union_literal::render_union_literal,
         RenderContext,
@@ -157,6 +158,16 @@ impl Completions {
         item.add_to(self, ctx.db);
     }
 
+    pub(crate) fn add_expr(
+        &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);
+    }
+
     pub(crate) fn add_crate_roots(
         &mut self,
         ctx: &CompletionContext<'_>,
@@ -690,6 +701,7 @@ pub(super) fn complete_name_ref(
     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 77fd5dd98b8..b8ed429cb24 100644
--- a/crates/ide-completion/src/completions/expr.rs
+++ b/crates/ide-completion/src/completions/expr.rs
@@ -328,3 +328,32 @@ 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() {
+        return;
+    }
+
+    if let Some(ty) = &ctx.expected_type {
+        // Ignore unit types as they are not very interesting
+        if ty.is_unit() {
+            return;
+        }
+
+        let term_search_ctx = hir::term_search::TermSearchCtx {
+            sema: &ctx.sema,
+            scope: &ctx.scope,
+            goal: ty.clone(),
+            config: hir::term_search::TermSearchConfig { enable_borrowcheck: false },
+        };
+        let exprs = hir::term_search::term_search(term_search_ctx);
+        for expr in exprs {
+            acc.add_expr(ctx, &expr, path_ctx);
+        }
+    }
+}
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 2ed080a8347..6d91b379162 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -272,6 +272,26 @@ pub(crate) fn render_resolution_with_import_pat(
     Some(render_resolution_pat(ctx, pattern_ctx, local_name, Some(import_edit), resolution))
 }
 
+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),
+    );
+    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
+}
+
 fn scope_def_to_name(
     resolution: ScopeDef,
     ctx: &RenderContext<'_>,
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs
index ff585f3d15b..62e20f48b80 100644
--- a/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -1,4 +1,8 @@
-use hir::{db::ExpandDatabase, term_search::term_search, ClosureStyle, HirDisplay, Semantics};
+use hir::{
+    db::ExpandDatabase,
+    term_search::{term_search, TermSearchCtx},
+    ClosureStyle, HirDisplay, Semantics,
+};
 use ide_db::{
     assists::{Assist, AssistId, AssistKind, GroupLabel},
     label::Label,
@@ -40,7 +44,8 @@ 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 paths = term_search(sema, &scope, &d.expected);
+    let ctx = TermSearchCtx { sema, scope: &scope, goal: d.expected.clone(), config: Default::default() };
+    let paths = term_search(ctx);
 
     let mut assists = vec![];
     for path in paths.into_iter().unique() {
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index 2ca93b5ca89..5f43acdf19a 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -406,7 +406,15 @@ impl flags::AnalysisStats {
                     None => continue,
                 };
 
-                let found_terms = hir::term_search::term_search(&sema, &scope, &target_ty);
+                let ctx = hir::term_search::TermSearchCtx {
+                    sema: &sema,
+                    scope: &scope,
+                    goal: target_ty,
+                    config: hir::term_search::TermSearchConfig {
+                        enable_borrowcheck: true,
+                    },
+                };
+                let found_terms = hir::term_search::term_search(ctx);
 
                 if found_terms.is_empty() {
                     acc.tail_expr_no_term += 1;