about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-06-24 07:20:50 +0000
committerbors <bors@rust-lang.org>2024-06-24 07:20:50 +0000
commit4dd0ad7afd118e4ab7af0423696cf2b469207c4d (patch)
tree2bd128c6a7204c7a4b9d55b43dd94daf3d8e5968
parent70e9582f4c076a6b22ba4844f075762a1ed2296b (diff)
parentefecb90b7f4e6468e03c1a9520535a5d7bb5cd1c (diff)
Auto merge of #17478 - kilpkonn:master, r=Veykril
Simplify some term search tactics

Working on the paper `@phijor` found that "Data constructor" tactic could be simplified quite a bit by running it only in the backwards direction. With n+1 rounds it has same coverage as previous implementation in n rounds, however the tactic it self is more simple and also potentially faster as there is less to do.

In a nutshell the idea is to only work with types in the wish-list rather than with any types.

Turns out it is quite a bit faster:

Before:
```
ripgrep:
Tail Expr syntactic hits: 238/1692 (14%)
Tail Exprs found: 1223/1692 (72%)
Term search avg time: 15ms

nalgebra:
Tail Expr syntactic hits: 125/3001 (4%)
Tail Exprs found: 2143/3001 (71%)
Term search avg time: 849ms
```

After
````
ripgrep:
Tail Expr syntactic hits: 246/1692 (14%)
Tail Exprs found: 1209/1692 (71%)
Term search avg time: 8ms

nalgebra:
Tail Expr syntactic hits: 125/3001 (4%)
Tail Exprs found: 2028/3001 (67%)
Term search avg time: 305ms
````

_Also removed niche optimization of removing scope defs from the search space as this wasn't helping much anyway and made code a bit more complex._
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/term_search.rs46
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs536
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/render.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs8
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc8
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json8
8 files changed, 163 insertions, 456 deletions
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs
index aa046b02e2e..6f845137084 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs
@@ -93,12 +93,6 @@ struct LookupTable {
     data: FxHashMap<Type, AlternativeExprs>,
     /// New types reached since last query by the `NewTypesKey`
     new_types: FxHashMap<NewTypesKey, Vec<Type>>,
-    /// ScopeDefs that are not interesting any more
-    exhausted_scopedefs: FxHashSet<ScopeDef>,
-    /// ScopeDefs that were used in current round
-    round_scopedef_hits: FxHashSet<ScopeDef>,
-    /// Amount of rounds since scopedef was first used.
-    rounds_since_sopedef_hit: FxHashMap<ScopeDef, u32>,
     /// Types queried but not present
     types_wishlist: FxHashSet<Type>,
     /// Threshold to squash trees to `Many`
@@ -212,37 +206,6 @@ impl LookupTable {
         }
     }
 
-    /// Mark `ScopeDef` as exhausted meaning it is not interesting for us any more
-    fn mark_exhausted(&mut self, def: ScopeDef) {
-        self.exhausted_scopedefs.insert(def);
-    }
-
-    /// Mark `ScopeDef` as used meaning we managed to produce something useful from it
-    fn mark_fulfilled(&mut self, def: ScopeDef) {
-        self.round_scopedef_hits.insert(def);
-    }
-
-    /// Start new round (meant to be called at the beginning of iteration in `term_search`)
-    ///
-    /// This functions marks some `ScopeDef`s as exhausted if there have been
-    /// `MAX_ROUNDS_AFTER_HIT` rounds after first using a `ScopeDef`.
-    fn new_round(&mut self) {
-        for def in &self.round_scopedef_hits {
-            let hits =
-                self.rounds_since_sopedef_hit.entry(*def).and_modify(|n| *n += 1).or_insert(0);
-            const MAX_ROUNDS_AFTER_HIT: u32 = 2;
-            if *hits > MAX_ROUNDS_AFTER_HIT {
-                self.exhausted_scopedefs.insert(*def);
-            }
-        }
-        self.round_scopedef_hits.clear();
-    }
-
-    /// Get exhausted `ScopeDef`s
-    fn exhausted_scopedefs(&self) -> &FxHashSet<ScopeDef> {
-        &self.exhausted_scopedefs
-    }
-
     /// Types queried but not found
     fn types_wishlist(&mut self) -> &FxHashSet<Type> {
         &self.types_wishlist
@@ -275,7 +238,7 @@ pub struct TermSearchConfig {
 
 impl Default for TermSearchConfig {
     fn default() -> Self {
-        Self { enable_borrowcheck: true, many_alternatives_threshold: 1, fuel: 400 }
+        Self { enable_borrowcheck: true, many_alternatives_threshold: 1, fuel: 1200 }
     }
 }
 
@@ -328,19 +291,12 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
     solutions.extend(tactics::assoc_const(ctx, &defs, &mut lookup));
 
     while should_continue() {
-        lookup.new_round();
-
         solutions.extend(tactics::data_constructor(ctx, &defs, &mut lookup, should_continue));
         solutions.extend(tactics::free_function(ctx, &defs, &mut lookup, should_continue));
         solutions.extend(tactics::impl_method(ctx, &defs, &mut lookup, should_continue));
         solutions.extend(tactics::struct_projection(ctx, &defs, &mut lookup, should_continue));
         solutions.extend(tactics::impl_static_method(ctx, &defs, &mut lookup, should_continue));
         solutions.extend(tactics::make_tuple(ctx, &defs, &mut lookup, should_continue));
-
-        // Discard not interesting `ScopeDef`s for speedup
-        for def in lookup.exhausted_scopedefs() {
-            defs.remove(def);
-        }
     }
 
     solutions.into_iter().filter(|it| !it.is_many()).unique().collect()
diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
index b738e6af77b..d1fc68d009b 100644
--- a/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/term_search/tactics.rs
@@ -17,11 +17,11 @@ use itertools::Itertools;
 use rustc_hash::FxHashSet;
 
 use crate::{
-    Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type,
-    TypeParam, Variant,
+    Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef,
+    Type, TypeParam,
 };
 
-use crate::term_search::{Expr, TermSearchConfig};
+use crate::term_search::Expr;
 
 use super::{LookupTable, NewTypesKey, TermSearchCtx};
 
@@ -74,8 +74,6 @@ pub(super) fn trivial<'a, DB: HirDatabase>(
             _ => None,
         }?;
 
-        lookup.mark_exhausted(*def);
-
         let ty = expr.ty(db);
         lookup.insert(ty.clone(), std::iter::once(expr.clone()));
 
@@ -124,6 +122,10 @@ pub(super) fn assoc_const<'a, DB: HirDatabase>(
         .filter(move |it| it.is_visible_from(db, module))
         .filter_map(AssocItem::as_const)
         .filter_map(|it| {
+            if it.attrs(db).is_unstable() {
+                return None;
+            }
+
             let expr = Expr::Const(it);
             let ty = it.ty(db);
 
@@ -151,212 +153,101 @@ pub(super) fn assoc_const<'a, DB: HirDatabase>(
 /// * `should_continue` - Function that indicates when to stop iterating
 pub(super) fn data_constructor<'a, DB: HirDatabase>(
     ctx: &'a TermSearchCtx<'a, DB>,
-    defs: &'a FxHashSet<ScopeDef>,
+    _defs: &'a FxHashSet<ScopeDef>,
     lookup: &'a mut LookupTable,
     should_continue: &'a dyn std::ops::Fn() -> bool,
 ) -> impl Iterator<Item = Expr> + 'a {
     let db = ctx.sema.db;
     let module = ctx.scope.module();
-    fn variant_helper(
-        db: &dyn HirDatabase,
-        lookup: &mut LookupTable,
-        should_continue: &dyn std::ops::Fn() -> bool,
-        parent_enum: Enum,
-        variant: Variant,
-        config: &TermSearchConfig,
-    ) -> Vec<(Type, Vec<Expr>)> {
-        // Ignore unstable
-        if variant.is_unstable(db) {
-            return Vec::new();
-        }
-
-        let generics = GenericDef::from(variant.parent_enum(db));
-        let Some(type_params) = generics
-            .type_or_const_params(db)
-            .into_iter()
-            .map(|it| it.as_type_param(db))
-            .collect::<Option<Vec<TypeParam>>>()
-        else {
-            // Ignore enums with const generics
-            return Vec::new();
-        };
-
-        // We currently do not check lifetime bounds so ignore all types that have something to do
-        // with them
-        if !generics.lifetime_params(db).is_empty() {
-            return Vec::new();
-        }
+    lookup
+        .types_wishlist()
+        .clone()
+        .into_iter()
+        .chain(iter::once(ctx.goal.clone()))
+        .filter_map(|ty| ty.as_adt().map(|adt| (adt, ty)))
+        .filter(|_| should_continue())
+        .filter_map(move |(adt, ty)| match adt {
+            Adt::Struct(strukt) => {
+                // Ignore unstable or not visible
+                if strukt.is_unstable(db) || !strukt.is_visible_from(db, module) {
+                    return None;
+                }
 
-        // Only account for stable type parameters for now, unstable params can be default
-        // tho, for example in `Box<T, #[unstable] A: Allocator>`
-        if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
-            return Vec::new();
-        }
+                let generics = GenericDef::from(strukt);
 
-        let non_default_type_params_len =
-            type_params.iter().filter(|it| it.default(db).is_none()).count();
-
-        let enum_ty_shallow = Adt::from(parent_enum).ty(db);
-        let generic_params = lookup
-            .types_wishlist()
-            .clone()
-            .into_iter()
-            .filter(|ty| ty.could_unify_with(db, &enum_ty_shallow))
-            .map(|it| it.type_arguments().collect::<Vec<Type>>())
-            .chain((non_default_type_params_len == 0).then_some(Vec::new()));
-
-        generic_params
-            .filter(|_| should_continue())
-            .filter_map(move |generics| {
-                // Insert default type params
-                let mut g = generics.into_iter();
-                let generics: Vec<_> = type_params
-                    .iter()
-                    .map(|it| it.default(db).or_else(|| g.next()))
-                    .collect::<Option<_>>()?;
+                // We currently do not check lifetime bounds so ignore all types that have something to do
+                // with them
+                if !generics.lifetime_params(db).is_empty() {
+                    return None;
+                }
 
-                let enum_ty = Adt::from(parent_enum).ty_with_args(db, generics.iter().cloned());
+                if ty.contains_unknown() {
+                    return None;
+                }
 
                 // Ignore types that have something to do with lifetimes
-                if config.enable_borrowcheck && enum_ty.contains_reference(db) {
+                if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
                     return None;
                 }
+                let fields = strukt.fields(db);
+                // Check if all fields are visible, otherwise we cannot fill them
+                if fields.iter().any(|it| !it.is_visible_from(db, module)) {
+                    return None;
+                }
+
+                let generics: Vec<_> = ty.type_arguments().collect();
 
                 // Early exit if some param cannot be filled from lookup
-                let param_exprs: Vec<Vec<Expr>> = variant
-                    .fields(db)
+                let param_exprs: Vec<Vec<Expr>> = fields
                     .into_iter()
                     .map(|field| lookup.find(db, &field.ty_with_args(db, generics.iter().cloned())))
                     .collect::<Option<_>>()?;
 
                 // Note that we need special case for 0 param constructors because of multi cartesian
                 // product
-                let variant_exprs: Vec<Expr> = if param_exprs.is_empty() {
-                    vec![Expr::Variant { variant, generics, params: Vec::new() }]
+                let exprs: Vec<Expr> = if param_exprs.is_empty() {
+                    vec![Expr::Struct { strukt, generics, params: Vec::new() }]
                 } else {
                     param_exprs
                         .into_iter()
                         .multi_cartesian_product()
-                        .map(|params| Expr::Variant { variant, generics: generics.clone(), params })
+                        .map(|params| Expr::Struct { strukt, generics: generics.clone(), params })
                         .collect()
                 };
-                lookup.insert(enum_ty.clone(), variant_exprs.iter().cloned());
-
-                Some((enum_ty, variant_exprs))
-            })
-            .collect()
-    }
-    defs.iter()
-        .filter_map(move |def| match def {
-            ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
-                let variant_exprs = variant_helper(
-                    db,
-                    lookup,
-                    should_continue,
-                    it.parent_enum(db),
-                    *it,
-                    &ctx.config,
-                );
-                if variant_exprs.is_empty() {
-                    return None;
-                }
-                if GenericDef::from(it.parent_enum(db))
-                    .type_or_const_params(db)
-                    .into_iter()
-                    .filter_map(|it| it.as_type_param(db))
-                    .all(|it| it.default(db).is_some())
-                {
-                    lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Variant(*it)));
-                }
-                Some(variant_exprs)
-            }
-            ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(enum_))) => {
-                let exprs: Vec<(Type, Vec<Expr>)> = enum_
-                    .variants(db)
-                    .into_iter()
-                    .flat_map(|it| {
-                        variant_helper(db, lookup, should_continue, *enum_, it, &ctx.config)
-                    })
-                    .collect();
-
-                if exprs.is_empty() {
-                    return None;
-                }
 
-                if GenericDef::from(*enum_)
-                    .type_or_const_params(db)
-                    .into_iter()
-                    .filter_map(|it| it.as_type_param(db))
-                    .all(|it| it.default(db).is_some())
-                {
-                    lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(*enum_))));
-                }
-
-                Some(exprs)
+                lookup.insert(ty.clone(), exprs.iter().cloned());
+                Some((ty, exprs))
             }
-            ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => {
-                // Ignore unstable and not visible
-                if it.is_unstable(db) || !it.is_visible_from(db, module) {
+            Adt::Enum(enum_) => {
+                // Ignore unstable or not visible
+                if enum_.is_unstable(db) || !enum_.is_visible_from(db, module) {
                     return None;
                 }
 
-                let generics = GenericDef::from(*it);
-
-                // Ignore const params for now
-                let type_params = generics
-                    .type_or_const_params(db)
-                    .into_iter()
-                    .map(|it| it.as_type_param(db))
-                    .collect::<Option<Vec<TypeParam>>>()?;
-
+                let generics = GenericDef::from(enum_);
                 // We currently do not check lifetime bounds so ignore all types that have something to do
                 // with them
                 if !generics.lifetime_params(db).is_empty() {
                     return None;
                 }
 
-                // Only account for stable type parameters for now, unstable params can be default
-                // tho, for example in `Box<T, #[unstable] A: Allocator>`
-                if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
+                if ty.contains_unknown() {
                     return None;
                 }
 
-                let non_default_type_params_len =
-                    type_params.iter().filter(|it| it.default(db).is_none()).count();
+                // Ignore types that have something to do with lifetimes
+                if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
+                    return None;
+                }
 
-                let struct_ty_shallow = Adt::from(*it).ty(db);
-                let generic_params = lookup
-                    .types_wishlist()
-                    .clone()
+                let generics: Vec<_> = ty.type_arguments().collect();
+                let exprs = enum_
+                    .variants(db)
                     .into_iter()
-                    .filter(|ty| ty.could_unify_with(db, &struct_ty_shallow))
-                    .map(|it| it.type_arguments().collect::<Vec<Type>>())
-                    .chain((non_default_type_params_len == 0).then_some(Vec::new()));
-
-                let exprs = generic_params
-                    .filter(|_| should_continue())
-                    .filter_map(|generics| {
-                        // Insert default type params
-                        let mut g = generics.into_iter();
-                        let generics: Vec<_> = type_params
-                            .iter()
-                            .map(|it| it.default(db).or_else(|| g.next()))
-                            .collect::<Option<_>>()?;
-
-                        let struct_ty = Adt::from(*it).ty_with_args(db, generics.iter().cloned());
-
-                        // Ignore types that have something to do with lifetimes
-                        if ctx.config.enable_borrowcheck && struct_ty.contains_reference(db) {
-                            return None;
-                        }
-                        let fields = it.fields(db);
-                        // Check if all fields are visible, otherwise we cannot fill them
-                        if fields.iter().any(|it| !it.is_visible_from(db, module)) {
-                            return None;
-                        }
-
+                    .filter_map(|variant| {
                         // Early exit if some param cannot be filled from lookup
-                        let param_exprs: Vec<Vec<Expr>> = fields
+                        let param_exprs: Vec<Vec<Expr>> = variant
+                            .fields(db)
                             .into_iter()
                             .map(|field| {
                                 lookup.find(db, &field.ty_with_args(db, generics.iter().cloned()))
@@ -365,36 +256,33 @@ pub(super) fn data_constructor<'a, DB: HirDatabase>(
 
                         // Note that we need special case for 0 param constructors because of multi cartesian
                         // product
-                        let struct_exprs: Vec<Expr> = if param_exprs.is_empty() {
-                            vec![Expr::Struct { strukt: *it, generics, params: Vec::new() }]
+                        let variant_exprs: Vec<Expr> = if param_exprs.is_empty() {
+                            vec![Expr::Variant {
+                                variant,
+                                generics: generics.clone(),
+                                params: Vec::new(),
+                            }]
                         } else {
                             param_exprs
                                 .into_iter()
                                 .multi_cartesian_product()
-                                .map(|params| Expr::Struct {
-                                    strukt: *it,
+                                .map(|params| Expr::Variant {
+                                    variant,
                                     generics: generics.clone(),
                                     params,
                                 })
                                 .collect()
                         };
-
-                        if non_default_type_params_len == 0 {
-                            // Fulfilled only if there are no generic parameters
-                            lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(
-                                Adt::Struct(*it),
-                            )));
-                        }
-                        lookup.insert(struct_ty.clone(), struct_exprs.iter().cloned());
-
-                        Some((struct_ty, struct_exprs))
+                        lookup.insert(ty.clone(), variant_exprs.iter().cloned());
+                        Some(variant_exprs)
                     })
+                    .flatten()
                     .collect();
-                Some(exprs)
+
+                Some((ty, exprs))
             }
-            _ => None,
+            Adt::Union(_) => None,
         })
-        .flatten()
         .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
         .flatten()
 }
@@ -515,7 +403,6 @@ pub(super) fn free_function<'a, DB: HirDatabase>(
                                 .collect()
                         };
 
-                        lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Function(*it)));
                         lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
                         Some((ret_ty, fn_exprs))
                     })
@@ -555,6 +442,8 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
     lookup
         .new_types(NewTypesKey::ImplMethod)
         .into_iter()
+        .filter(|ty| !ty.type_arguments().any(|it| it.contains_unknown()))
+        .filter(|_| should_continue())
         .flat_map(|ty| {
             Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
         })
@@ -563,26 +452,15 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
             AssocItem::Function(f) => Some((imp, ty, f)),
             _ => None,
         })
+        .filter(|_| should_continue())
         .filter_map(move |(imp, ty, it)| {
             let fn_generics = GenericDef::from(it);
             let imp_generics = GenericDef::from(imp);
 
-            // Ignore const params for now
-            let imp_type_params = imp_generics
-                .type_or_const_params(db)
-                .into_iter()
-                .map(|it| it.as_type_param(db))
-                .collect::<Option<Vec<TypeParam>>>()?;
-
-            // Ignore const params for now
-            let fn_type_params = fn_generics
-                .type_or_const_params(db)
-                .into_iter()
-                .map(|it| it.as_type_param(db))
-                .collect::<Option<Vec<TypeParam>>>()?;
-
             // Ignore all functions that have something to do with lifetimes as we don't check them
-            if !fn_generics.lifetime_params(db).is_empty() {
+            if !fn_generics.lifetime_params(db).is_empty()
+                || !imp_generics.lifetime_params(db).is_empty()
+            {
                 return None;
             }
 
@@ -596,112 +474,59 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
                 return None;
             }
 
-            // Only account for stable type parameters for now, unstable params can be default
-            // tho, for example in `Box<T, #[unstable] A: Allocator>`
-            if imp_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
-                || fn_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
-            {
+            // Ignore functions with generics for now as they kill the performance
+            // Also checking bounds for generics is problematic
+            if !fn_generics.type_or_const_params(db).is_empty() {
                 return None;
             }
 
-            // Double check that we have fully known type
-            if ty.type_arguments().any(|it| it.contains_unknown()) {
+            let ret_ty = it.ret_type_with_args(db, ty.type_arguments());
+            // Filter out functions that return references
+            if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
+            {
                 return None;
             }
 
-            let non_default_fn_type_params_len =
-                fn_type_params.iter().filter(|it| it.default(db).is_none()).count();
-
-            // Ignore functions with generics for now as they kill the performance
-            // Also checking bounds for generics is problematic
-            if non_default_fn_type_params_len > 0 {
+            // Ignore functions that do not change the type
+            if ty.could_unify_with_deeply(db, &ret_ty) {
                 return None;
             }
 
-            let generic_params = lookup
-                .iter_types()
-                .collect::<Vec<_>>() // Force take ownership
-                .into_iter()
-                .permutations(non_default_fn_type_params_len);
+            let self_ty =
+                it.self_param(db).expect("No self param").ty_with_args(db, ty.type_arguments());
 
-            let exprs: Vec<_> = generic_params
-                .filter(|_| should_continue())
-                .filter_map(|generics| {
-                    // Insert default type params
-                    let mut g = generics.into_iter();
-                    let generics: Vec<_> = ty
-                        .type_arguments()
-                        .map(Some)
-                        .chain(fn_type_params.iter().map(|it| match it.default(db) {
-                            Some(ty) => Some(ty),
-                            None => {
-                                let generic = g.next().expect("Missing type param");
-                                // Filter out generics that do not unify due to trait bounds
-                                it.ty(db).could_unify_with(db, &generic).then_some(generic)
-                            }
-                        }))
-                        .collect::<Option<_>>()?;
-
-                    let ret_ty = it.ret_type_with_args(
-                        db,
-                        ty.type_arguments().chain(generics.iter().cloned()),
-                    );
-                    // Filter out functions that return references
-                    if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
-                        || ret_ty.is_raw_ptr()
-                    {
-                        return None;
-                    }
+            // Ignore functions that have different self type
+            if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) {
+                return None;
+            }
 
-                    // Ignore functions that do not change the type
-                    if ty.could_unify_with_deeply(db, &ret_ty) {
-                        return None;
-                    }
+            let target_type_exprs = lookup.find(db, &ty).expect("Type not in lookup");
 
-                    let self_ty = it
-                        .self_param(db)
-                        .expect("No self param")
-                        .ty_with_args(db, ty.type_arguments().chain(generics.iter().cloned()));
+            // Early exit if some param cannot be filled from lookup
+            let param_exprs: Vec<Vec<Expr>> = it
+                .params_without_self_with_args(db, ty.type_arguments())
+                .into_iter()
+                .map(|field| lookup.find_autoref(db, field.ty()))
+                .collect::<Option<_>>()?;
 
-                    // Ignore functions that have different self type
-                    if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) {
-                        return None;
+            let generics: Vec<_> = ty.type_arguments().collect();
+            let fn_exprs: Vec<Expr> = std::iter::once(target_type_exprs)
+                .chain(param_exprs)
+                .multi_cartesian_product()
+                .map(|params| {
+                    let mut params = params.into_iter();
+                    let target = Box::new(params.next().unwrap());
+                    Expr::Method {
+                        func: it,
+                        generics: generics.clone(),
+                        target,
+                        params: params.collect(),
                     }
-
-                    let target_type_exprs = lookup.find(db, &ty).expect("Type not in lookup");
-
-                    // Early exit if some param cannot be filled from lookup
-                    let param_exprs: Vec<Vec<Expr>> = it
-                        .params_without_self_with_args(
-                            db,
-                            ty.type_arguments().chain(generics.iter().cloned()),
-                        )
-                        .into_iter()
-                        .map(|field| lookup.find_autoref(db, field.ty()))
-                        .collect::<Option<_>>()?;
-
-                    let fn_exprs: Vec<Expr> = std::iter::once(target_type_exprs)
-                        .chain(param_exprs)
-                        .multi_cartesian_product()
-                        .map(|params| {
-                            let mut params = params.into_iter();
-                            let target = Box::new(params.next().unwrap());
-                            Expr::Method {
-                                func: it,
-                                generics: generics.clone(),
-                                target,
-                                params: params.collect(),
-                            }
-                        })
-                        .collect();
-
-                    lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
-                    Some((ret_ty, fn_exprs))
                 })
                 .collect();
-            Some(exprs)
+
+            Some((ret_ty, fn_exprs))
         })
-        .flatten()
         .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
         .flatten()
 }
@@ -805,6 +630,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
         .clone()
         .into_iter()
         .chain(iter::once(ctx.goal.clone()))
+        .filter(|ty| !ty.type_arguments().any(|it| it.contains_unknown()))
         .filter(|_| should_continue())
         .flat_map(|ty| {
             Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
@@ -815,24 +641,11 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
             AssocItem::Function(f) => Some((imp, ty, f)),
             _ => None,
         })
+        .filter(|_| should_continue())
         .filter_map(move |(imp, ty, it)| {
             let fn_generics = GenericDef::from(it);
             let imp_generics = GenericDef::from(imp);
 
-            // Ignore const params for now
-            let imp_type_params = imp_generics
-                .type_or_const_params(db)
-                .into_iter()
-                .map(|it| it.as_type_param(db))
-                .collect::<Option<Vec<TypeParam>>>()?;
-
-            // Ignore const params for now
-            let fn_type_params = fn_generics
-                .type_or_const_params(db)
-                .into_iter()
-                .map(|it| it.as_type_param(db))
-                .collect::<Option<Vec<TypeParam>>>()?;
-
             // Ignore all functions that have something to do with lifetimes as we don't check them
             if !fn_generics.lifetime_params(db).is_empty()
                 || !imp_generics.lifetime_params(db).is_empty()
@@ -850,104 +663,43 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
                 return None;
             }
 
-            // Only account for stable type parameters for now, unstable params can be default
-            // tho, for example in `Box<T, #[unstable] A: Allocator>`
-            if imp_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
-                || fn_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
-            {
-                return None;
-            }
-
-            // Double check that we have fully known type
-            if ty.type_arguments().any(|it| it.contains_unknown()) {
+            // Ignore functions with generics for now as they kill the performance
+            // Also checking bounds for generics is problematic
+            if !fn_generics.type_or_const_params(db).is_empty() {
                 return None;
             }
 
-            let non_default_fn_type_params_len =
-                fn_type_params.iter().filter(|it| it.default(db).is_none()).count();
-
-            // Ignore functions with generics for now as they kill the performance
-            // Also checking bounds for generics is problematic
-            if non_default_fn_type_params_len > 0 {
+            let ret_ty = it.ret_type_with_args(db, ty.type_arguments());
+            // Filter out functions that return references
+            if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr()
+            {
                 return None;
             }
 
-            let generic_params = lookup
-                .iter_types()
-                .collect::<Vec<_>>() // Force take ownership
+            // Early exit if some param cannot be filled from lookup
+            let param_exprs: Vec<Vec<Expr>> = it
+                .params_without_self_with_args(db, ty.type_arguments())
                 .into_iter()
-                .permutations(non_default_fn_type_params_len);
-
-            let exprs: Vec<_> = generic_params
-                .filter(|_| should_continue())
-                .filter_map(|generics| {
-                    // Insert default type params
-                    let mut g = generics.into_iter();
-                    let generics: Vec<_> = ty
-                        .type_arguments()
-                        .map(Some)
-                        .chain(fn_type_params.iter().map(|it| match it.default(db) {
-                            Some(ty) => Some(ty),
-                            None => {
-                                let generic = g.next().expect("Missing type param");
-                                it.trait_bounds(db)
-                                    .into_iter()
-                                    .all(|bound| generic.impls_trait(db, bound, &[]));
-                                // Filter out generics that do not unify due to trait bounds
-                                it.ty(db).could_unify_with(db, &generic).then_some(generic)
-                            }
-                        }))
-                        .collect::<Option<_>>()?;
-
-                    let ret_ty = it.ret_type_with_args(
-                        db,
-                        ty.type_arguments().chain(generics.iter().cloned()),
-                    );
-                    // Filter out functions that return references
-                    if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
-                        || ret_ty.is_raw_ptr()
-                    {
-                        return None;
-                    }
+                .map(|field| lookup.find_autoref(db, field.ty()))
+                .collect::<Option<_>>()?;
+
+            // Note that we need special case for 0 param constructors because of multi cartesian
+            // product
+            let generics = ty.type_arguments().collect();
+            let fn_exprs: Vec<Expr> = if param_exprs.is_empty() {
+                vec![Expr::Function { func: it, generics, params: Vec::new() }]
+            } else {
+                param_exprs
+                    .into_iter()
+                    .multi_cartesian_product()
+                    .map(|params| Expr::Function { func: it, generics: generics.clone(), params })
+                    .collect()
+            };
 
-                    // Ignore functions that do not change the type
-                    // if ty.could_unify_with_deeply(db, &ret_ty) {
-                    //     return None;
-                    // }
-
-                    // Early exit if some param cannot be filled from lookup
-                    let param_exprs: Vec<Vec<Expr>> = it
-                        .params_without_self_with_args(
-                            db,
-                            ty.type_arguments().chain(generics.iter().cloned()),
-                        )
-                        .into_iter()
-                        .map(|field| lookup.find_autoref(db, field.ty()))
-                        .collect::<Option<_>>()?;
-
-                    // Note that we need special case for 0 param constructors because of multi cartesian
-                    // product
-                    let fn_exprs: Vec<Expr> = if param_exprs.is_empty() {
-                        vec![Expr::Function { func: it, generics, params: Vec::new() }]
-                    } else {
-                        param_exprs
-                            .into_iter()
-                            .multi_cartesian_product()
-                            .map(|params| Expr::Function {
-                                func: it,
-                                generics: generics.clone(),
-                                params,
-                            })
-                            .collect()
-                    };
+            lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
 
-                    lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned());
-                    Some((ret_ty, fn_exprs))
-                })
-                .collect();
-            Some(exprs)
+            Some((ret_ty, fn_exprs))
         })
-        .flatten()
         .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
         .flatten()
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
index 8a9229c549f..7f1274caded 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/term_search.rs
@@ -144,7 +144,7 @@ fn f() { let a = A { x: 1, y: true }; let b: i32 = a.x; }"#,
             term_search,
             r#"//- minicore: todo, unimplemented, option
 fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
-            r#"fn f() { let a: i32 = 1; let b: Option<i32> = None; }"#,
+            r#"fn f() { let a: i32 = 1; let b: Option<i32> = Some(a); }"#,
         )
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
index ebdc813f3d7..b98f745f178 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs
@@ -764,6 +764,7 @@ fn main() {
 "#,
             expect![[r#"
                 st dep::test_mod_b::Struct {…} [type_could_unify]
+                ex dep::test_mod_b::Struct {  } [type_could_unify]
                 st Struct (use dep::test_mod_b::Struct) [type_could_unify+requires_import]
                 fn main() []
                 fn test(…) []
@@ -839,6 +840,7 @@ fn main() {
 "#,
             expect![[r#"
                 ev dep::test_mod_b::Enum::variant [type_could_unify]
+                ex dep::test_mod_b::Enum::variant [type_could_unify]
                 en Enum (use dep::test_mod_b::Enum) [type_could_unify+requires_import]
                 fn main() []
                 fn test(…) []
@@ -876,6 +878,7 @@ fn main() {
 "#,
             expect![[r#"
                 ev dep::test_mod_b::Enum::Variant [type_could_unify]
+                ex dep::test_mod_b::Enum::Variant [type_could_unify]
                 fn main() []
                 fn test(…) []
                 md dep []
@@ -1839,7 +1842,6 @@ fn f() { A { bar: b$0 }; }
                 fn baz() [type]
                 ex baz() [type]
                 ex bar() [type]
-                ex A { bar: ... }.bar [type]
                 st A []
                 fn f() []
             "#]],
@@ -1978,7 +1980,6 @@ fn main() {
             "#,
             expect![[r#"
                 ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify]
-                ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
                 lc m [local]
                 lc t [local]
                 lc &t [type+local]
@@ -2028,7 +2029,6 @@ fn main() {
             "#,
             expect![[r#"
                 ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify]
-                ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify]
                 lc m [local]
                 lc t [local]
                 lc &mut t [type+local]
@@ -2132,7 +2132,6 @@ fn main() {
 }
 "#,
             expect![[r#"
-                ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
                 ex core::ops::Deref::deref(&bar()) (use core::ops::Deref) [type_could_unify]
                 st S []
                 st &S [type]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
index 9651ce6106b..71917e09f07 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -276,7 +276,7 @@ impl Foo for Baz {
 }
 fn asd() -> Bar {
     let a = Baz;
-    Foo::foo(_)
+    Foo::foo(a)
 }
 ",
         );
@@ -365,7 +365,7 @@ impl Foo for A {
 }
 fn main() {
     let a = A;
-    let c: Bar = Foo::foo(_);
+    let c: Bar = Foo::foo(&a);
 }"#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index e8504979bed..f152ac542aa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -341,8 +341,8 @@ config_data! {
         assist_emitMustUse: bool               = false,
         /// Placeholder expression to use for missing expressions in assists.
         assist_expressionFillDefault: ExprFillDefaultDef              = ExprFillDefaultDef::Todo,
-        /// Term search fuel in "units of work" for assists (Defaults to 400).
-        assist_termSearch_fuel: usize = 400,
+        /// Term search fuel in "units of work" for assists (Defaults to 1800).
+        assist_termSearch_fuel: usize = 1800,
 
         /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file.
         imports_granularity_enforce: bool              = false,
@@ -426,8 +426,8 @@ config_data! {
         }"#).unwrap(),
         /// Whether to enable term search based snippets like `Some(foo.bar().baz())`.
         completion_termSearch_enable: bool = false,
-        /// Term search fuel in "units of work" for autocompletion (Defaults to 200).
-        completion_termSearch_fuel: usize = 200,
+        /// Term search fuel in "units of work" for autocompletion (Defaults to 1000).
+        completion_termSearch_fuel: usize = 1000,
 
         /// Controls file watching implementation.
         files_watcher: FilesWatcherDef = FilesWatcherDef::Client,
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index 14aae91741e..81f30dc56e1 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -9,10 +9,10 @@ for enum variants.
 --
 Placeholder expression to use for missing expressions in assists.
 --
-[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `400`)::
+[[rust-analyzer.assist.termSearch.fuel]]rust-analyzer.assist.termSearch.fuel (default: `1800`)::
 +
 --
-Term search fuel in "units of work" for assists (Defaults to 400).
+Term search fuel in "units of work" for assists (Defaults to 1800).
 --
 [[rust-analyzer.cachePriming.enable]]rust-analyzer.cachePriming.enable (default: `true`)::
 +
@@ -378,10 +378,10 @@ Custom completion snippets.
 --
 Whether to enable term search based snippets like `Some(foo.bar().baz())`.
 --
-[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `200`)::
+[[rust-analyzer.completion.termSearch.fuel]]rust-analyzer.completion.termSearch.fuel (default: `1000`)::
 +
 --
-Term search fuel in "units of work" for autocompletion (Defaults to 200).
+Term search fuel in "units of work" for autocompletion (Defaults to 1000).
 --
 [[rust-analyzer.diagnostics.disabled]]rust-analyzer.diagnostics.disabled (default: `[]`)::
 +
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index db2a989106f..0da5b920a72 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -592,8 +592,8 @@
                 "title": "assist",
                 "properties": {
                     "rust-analyzer.assist.termSearch.fuel": {
-                        "markdownDescription": "Term search fuel in \"units of work\" for assists (Defaults to 400).",
-                        "default": 400,
+                        "markdownDescription": "Term search fuel in \"units of work\" for assists (Defaults to 1800).",
+                        "default": 1800,
                         "type": "integer",
                         "minimum": 0
                     }
@@ -1224,8 +1224,8 @@
                 "title": "completion",
                 "properties": {
                     "rust-analyzer.completion.termSearch.fuel": {
-                        "markdownDescription": "Term search fuel in \"units of work\" for autocompletion (Defaults to 200).",
-                        "default": 200,
+                        "markdownDescription": "Term search fuel in \"units of work\" for autocompletion (Defaults to 1000).",
+                        "default": 1000,
                         "type": "integer",
                         "minimum": 0
                     }