about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/hir/src/term_search.rs46
-rw-r--r--crates/hir/src/term_search/expr.rs15
-rw-r--r--crates/hir/src/term_search/tactics.rs137
-rw-r--r--crates/ide-assists/src/handlers/term_search.rs5
-rw-r--r--crates/ide-completion/src/render.rs1
6 files changed, 136 insertions, 73 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index a0237e3b90b..14066dee5f8 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -3856,6 +3856,11 @@ impl Type {
         Type { env: ty.env, ty: TyBuilder::slice(ty.ty) }
     }
 
+    pub fn new_tuple(krate: CrateId, tys: &[Type]) -> Type {
+        let tys = tys.iter().map(|it| it.ty.clone());
+        Type { env: TraitEnvironment::empty(krate), ty: TyBuilder::tuple_with(tys) }
+    }
+
     pub fn is_unit(&self) -> bool {
         matches!(self.ty.kind(Interner), TyKind::Tuple(0, ..))
     }
diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs
index 72762007dc9..68244b12721 100644
--- a/crates/hir/src/term_search.rs
+++ b/crates/hir/src/term_search.rs
@@ -72,6 +72,10 @@ impl AlternativeExprs {
             AlternativeExprs::Many => (),
         }
     }
+
+    fn is_many(&self) -> bool {
+        matches!(self, AlternativeExprs::Many)
+    }
 }
 
 /// # Lookup table for term search
@@ -103,27 +107,36 @@ struct LookupTable {
 
 impl LookupTable {
     /// Initialize lookup table
-    fn new(many_threshold: usize) -> Self {
+    fn new(many_threshold: usize, goal: Type) -> Self {
         let mut res = Self { many_threshold, ..Default::default() };
         res.new_types.insert(NewTypesKey::ImplMethod, Vec::new());
         res.new_types.insert(NewTypesKey::StructProjection, Vec::new());
+        res.types_wishlist.insert(goal);
         res
     }
 
     /// Find all `Expr`s that unify with the `ty`
-    fn find(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
-        self.data
+    fn find(&mut self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
+        let res = self
+            .data
             .iter()
             .find(|(t, _)| t.could_unify_with_deeply(db, ty))
-            .map(|(t, tts)| tts.exprs(t))
+            .map(|(t, tts)| tts.exprs(t));
+
+        if res.is_none() {
+            self.types_wishlist.insert(ty.clone());
+        }
+
+        res
     }
 
     /// Same as find but automatically creates shared reference of types in the lookup
     ///
     /// For example if we have type `i32` in data and we query for `&i32` it map all the type
     /// trees we have for `i32` with `Expr::Reference` and returns them.
-    fn find_autoref(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
-        self.data
+    fn find_autoref(&mut self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
+        let res = self
+            .data
             .iter()
             .find(|(t, _)| t.could_unify_with_deeply(db, ty))
             .map(|(t, it)| it.exprs(t))
@@ -139,7 +152,13 @@ impl LookupTable {
                             .map(|expr| Expr::Reference(Box::new(expr)))
                             .collect()
                     })
-            })
+            });
+
+        if res.is_none() {
+            self.types_wishlist.insert(ty.clone());
+        }
+
+        res
     }
 
     /// Insert new type trees for type
@@ -149,7 +168,12 @@ impl LookupTable {
     /// but they clearly do not unify themselves.
     fn insert(&mut self, ty: Type, exprs: impl Iterator<Item = Expr>) {
         match self.data.get_mut(&ty) {
-            Some(it) => it.extend_with_threshold(self.many_threshold, exprs),
+            Some(it) => {
+                it.extend_with_threshold(self.many_threshold, exprs);
+                if it.is_many() {
+                    self.types_wishlist.remove(&ty);
+                }
+            }
             None => {
                 self.data.insert(ty.clone(), AlternativeExprs::new(self.many_threshold, exprs));
                 for it in self.new_types.values_mut() {
@@ -206,8 +230,8 @@ impl LookupTable {
     }
 
     /// Types queried but not found
-    fn take_types_wishlist(&mut self) -> FxHashSet<Type> {
-        std::mem::take(&mut self.types_wishlist)
+    fn types_wishlist(&mut self) -> &FxHashSet<Type> {
+        &self.types_wishlist
     }
 }
 
@@ -272,7 +296,7 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
         defs.insert(def);
     });
 
-    let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold);
+    let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold, ctx.goal.clone());
 
     // Try trivial tactic first, also populates lookup table
     let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect();
diff --git a/crates/hir/src/term_search/expr.rs b/crates/hir/src/term_search/expr.rs
index 254fbe7e2b5..2d0c5630e10 100644
--- a/crates/hir/src/term_search/expr.rs
+++ b/crates/hir/src/term_search/expr.rs
@@ -138,6 +138,8 @@ pub enum Expr {
     Variant { variant: Variant, generics: Vec<Type>, params: Vec<Expr> },
     /// Struct construction
     Struct { strukt: Struct, generics: Vec<Type>, params: Vec<Expr> },
+    /// Tuple construction
+    Tuple { ty: Type, params: Vec<Expr> },
     /// Struct field access
     Field { expr: Box<Expr>, field: Field },
     /// Passing type as reference (with `&`)
@@ -366,6 +368,18 @@ impl Expr {
                 let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)))?;
                 Ok(format!("{prefix}{inner}"))
             }
+            Expr::Tuple { params, .. } => {
+                let args = params
+                    .iter()
+                    .map(|a| {
+                        a.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude)
+                    })
+                    .collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
+                    .into_iter()
+                    .join(", ");
+                let res = format!("({args})");
+                Ok(res)
+            }
             Expr::Field { expr, field } => {
                 if expr.contains_many_in_illegal_pos() {
                     return Ok(many_formatter(&expr.ty(db)));
@@ -420,6 +434,7 @@ impl Expr {
             Expr::Struct { strukt, generics, .. } => {
                 Adt::from(*strukt).ty_with_args(db, generics.iter().cloned())
             }
+            Expr::Tuple { ty, .. } => ty.clone(),
             Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()),
             Expr::Reference(it) => it.ty(db),
             Expr::Many(ty) => ty.clone(),
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs
index edbf75affe6..93a780d4702 100644
--- a/crates/hir/src/term_search/tactics.rs
+++ b/crates/hir/src/term_search/tactics.rs
@@ -109,7 +109,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
         lookup: &mut LookupTable,
         parent_enum: Enum,
         variant: Variant,
-        goal: &Type,
         config: &TermSearchConfig,
     ) -> Vec<(Type, Vec<Expr>)> {
         // Ignore unstable
@@ -143,11 +142,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
         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
-            .iter_types()
-            .collect::<Vec<_>>() // Force take ownership
+            .types_wishlist()
+            .clone()
             .into_iter()
-            .permutations(non_default_type_params_len);
+            .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_map(move |generics| {
@@ -155,17 +157,11 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
                 let mut g = generics.into_iter();
                 let generics: Vec<_> = type_params
                     .iter()
-                    .map(|it| it.default(db).unwrap_or_else(|| g.next().expect("No generic")))
-                    .collect();
+                    .map(|it| it.default(db).or_else(|| g.next()))
+                    .collect::<Option<_>>()?;
 
                 let enum_ty = Adt::from(parent_enum).ty_with_args(db, generics.iter().cloned());
 
-                // Allow types with generics only if they take us straight to goal for
-                // performance reasons
-                if !generics.is_empty() && !enum_ty.could_unify_with_deeply(db, goal) {
-                    return None;
-                }
-
                 // Ignore types that have something to do with lifetimes
                 if config.enable_borrowcheck && enum_ty.contains_reference(db) {
                     return None;
@@ -199,21 +195,37 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
         .filter_map(move |def| match def {
             ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
                 let variant_exprs =
-                    variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.goal, &ctx.config);
+                    variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.config);
                 if variant_exprs.is_empty() {
                     return None;
                 }
-                lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Variant(*it)));
+                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, *enum_, it, &ctx.goal, &ctx.config))
+                    .flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.config))
                     .collect();
 
-                if !exprs.is_empty() {
+                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_))));
                 }
 
@@ -249,11 +261,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
                 let non_default_type_params_len =
                     type_params.iter().filter(|it| it.default(db).is_none()).count();
 
+                let struct_ty_shallow = Adt::from(*it).ty(db);
                 let generic_params = lookup
-                    .iter_types()
-                    .collect::<Vec<_>>() // Force take ownership
+                    .types_wishlist()
+                    .clone()
                     .into_iter()
-                    .permutations(non_default_type_params_len);
+                    .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_map(|generics| {
@@ -261,22 +276,11 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
                         let mut g = generics.into_iter();
                         let generics: Vec<_> = type_params
                             .iter()
-                            .map(|it| {
-                                it.default(db)
-                                    .unwrap_or_else(|| g.next().expect("Missing type param"))
-                            })
-                            .collect();
+                            .map(|it| it.default(db).or_else(|| g.next()))
+                            .collect::<Option<_>>()?;
 
                         let struct_ty = Adt::from(*it).ty_with_args(db, generics.iter().cloned());
 
-                        // 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, &ctx.goal)
-                        {
-                            return None;
-                        }
-
                         // Ignore types that have something to do with lifetimes
                         if ctx.config.enable_borrowcheck && struct_ty.contains_reference(db) {
                             return None;
@@ -309,8 +313,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
                                 .collect()
                         };
 
-                        lookup
-                            .mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(*it))));
+                        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))
@@ -525,14 +533,17 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
                 return None;
             }
 
-            let non_default_type_params_len = imp_type_params
-                .iter()
-                .chain(fn_type_params.iter())
-                .filter(|it| it.default(db).is_none())
-                .count();
+            // Double check that we have fully known type
+            if ty.type_arguments().any(|it| it.contains_unknown()) {
+                return None;
+            }
 
-            // Ignore bigger number of generics for now as they kill the performance
-            if non_default_type_params_len > 0 {
+            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 {
                 return None;
             }
 
@@ -540,23 +551,23 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
                 .iter_types()
                 .collect::<Vec<_>>() // Force take ownership
                 .into_iter()
-                .permutations(non_default_type_params_len);
+                .permutations(non_default_fn_type_params_len);
 
             let exprs: Vec<_> = generic_params
                 .filter_map(|generics| {
                     // Insert default type params
                     let mut g = generics.into_iter();
-                    let generics: Vec<_> = imp_type_params
-                        .iter()
-                        .chain(fn_type_params.iter())
-                        .map(|it| match it.default(db) {
+                    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(
@@ -713,7 +724,8 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
     let db = ctx.sema.db;
     let module = ctx.scope.module();
     lookup
-        .take_types_wishlist()
+        .types_wishlist()
+        .clone()
         .into_iter()
         .chain(iter::once(ctx.goal.clone()))
         .flat_map(|ty| {
@@ -768,14 +780,17 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
                 return None;
             }
 
-            let non_default_type_params_len = imp_type_params
-                .iter()
-                .chain(fn_type_params.iter())
-                .filter(|it| it.default(db).is_none())
-                .count();
+            // Double check that we have fully known type
+            if ty.type_arguments().any(|it| it.contains_unknown()) {
+                return None;
+            }
 
-            // Ignore bigger number of generics for now as they kill the performance
-            if non_default_type_params_len > 1 {
+            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 {
                 return None;
             }
 
@@ -783,16 +798,16 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
                 .iter_types()
                 .collect::<Vec<_>>() // Force take ownership
                 .into_iter()
-                .permutations(non_default_type_params_len);
+                .permutations(non_default_fn_type_params_len);
 
             let exprs: Vec<_> = generic_params
                 .filter_map(|generics| {
                     // Insert default type params
                     let mut g = generics.into_iter();
-                    let generics: Vec<_> = imp_type_params
-                        .iter()
-                        .chain(fn_type_params.iter())
-                        .map(|it| match it.default(db) {
+                    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");
@@ -802,7 +817,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
                                 // 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(
diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs
index 51a1a406f31..fa32a3bbe75 100644
--- a/crates/ide-assists/src/handlers/term_search.rs
+++ b/crates/ide-assists/src/handlers/term_search.rs
@@ -57,11 +57,14 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
         })
         .unique();
 
+    let macro_name = macro_call.name(ctx.sema.db);
+    let macro_name = macro_name.display(ctx.sema.db);
+
     for code in paths {
         acc.add_group(
             &GroupLabel(String::from("Term search")),
             AssistId("term_search", AssistKind::Generate),
-            format!("Replace todo!() with {code}"),
+            format!("Replace {macro_name}!() with {code}"),
             goal_range,
             |builder| {
                 builder.replace(goal_range, code);
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs
index 3f374b307fb..6d1a5a0bc52 100644
--- a/crates/ide-completion/src/render.rs
+++ b/crates/ide-completion/src/render.rs
@@ -2599,6 +2599,7 @@ fn foo() {
             expect![[r#"
                 lc foo [type+local]
                 ex foo [type]
+                ex Foo::B [type]
                 ev Foo::A(…) [type_could_unify]
                 ev Foo::B [type_could_unify]
                 en Foo [type_could_unify]