diff --git a/Cargo.toml b/Cargo.toml index e6f7c10090..2b81f7b11b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ authors = ["rust-analyzer team"] [profile.dev] # Disabling debug info speeds up builds a bunch, # and we don't rely on it for debugging that much. -debug = 2 +debug = 0 [profile.dev.package] # These speed up local tests. diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index a6c2dfad55..25dde2d159 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs @@ -106,10 +106,6 @@ pub fn could_unify_deeply( let ty2_with_vars = vars.apply(tys.value.1.clone(), Interner); let ty1_with_vars = table.normalize_associated_types_in(ty1_with_vars); let ty2_with_vars = table.normalize_associated_types_in(ty2_with_vars); - // table.resolve_obligations_as_possible(); - // table.propagate_diverging_flag(); - // let ty1_with_vars = table.resolve_completely(ty1_with_vars); - // let ty2_with_vars = table.resolve_completely(ty2_with_vars); table.unify_deeply(&ty1_with_vars, &ty2_with_vars) } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 9b8c7b900a..283461f219 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1085,20 +1085,21 @@ impl Field { Type::new(db, var_id, ty) } - pub fn ty_with_generics( - &self, - db: &dyn HirDatabase, - mut generics: impl Iterator, - ) -> Type { + pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator) -> Type { let var_id = self.parent.into(); let def_id: AdtId = match self.parent { VariantDef::Struct(it) => it.id.into(), VariantDef::Union(it) => it.id.into(), VariantDef::Variant(it) => it.parent_enum(db).id.into(), }; + let mut generics = generics.map(|it| it.ty.clone()); let substs = TyBuilder::subst_for_def(db, def_id, None) - .fill(|_| { - GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone())) + .fill(|x| { + let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); + match x { + ParamKind::Type => ty.cast(Interner), + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + } }) .build(); let ty = db.field_types(var_id)[self.id].clone().substitute(Interner, &substs); @@ -1158,14 +1159,15 @@ impl Struct { Type::from_def(db, self.id) } - pub fn ty_with_generics( - self, - db: &dyn HirDatabase, - mut generics: impl Iterator, - ) -> Type { + pub fn ty_with_args(self, db: &dyn HirDatabase, generics: impl Iterator) -> Type { + let mut generics = generics.map(|it| it.ty.clone()); let substs = TyBuilder::subst_for_def(db, self.id, None) - .fill(|_| { - GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone())) + .fill(|x| { + let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); + match x { + ParamKind::Type => ty.cast(Interner), + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + } }) .build(); let ty = db.ty(self.id.into()).substitute(Interner, &substs); @@ -1271,16 +1273,18 @@ impl Enum { Type::from_def(db, self.id) } - pub fn ty_with_generics( - &self, - db: &dyn HirDatabase, - mut generics: impl Iterator, - ) -> Type { + pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator) -> Type { + let mut generics = generics.map(|it| it.ty.clone()); let substs = TyBuilder::subst_for_def(db, self.id, None) - .fill(|_| { - GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone())) + .fill(|x| { + let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); + match x { + ParamKind::Type => ty.cast(Interner), + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + } }) .build(); + let ty = db.ty(self.id.into()).substitute(Interner, &substs); Type::new(db, self.id, ty) } @@ -1854,10 +1858,10 @@ impl Function { Type::new_with_resolver_inner(db, &resolver, ty) } - pub fn ret_type_with_generics( + pub fn ret_type_with_args( self, db: &dyn HirDatabase, - mut generics: impl Iterator, + generics: impl Iterator, ) -> Type { let resolver = self.id.resolver(db.upcast()); let parent_id: Option = match self.id.lookup(db.upcast()).container { @@ -1865,22 +1869,18 @@ impl Function { ItemContainerId::TraitId(it) => Some(it.into()), ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => None, }; - let parent_substs = parent_id.map(|id| { - TyBuilder::subst_for_def(db, id, None) - .fill(|_| { - GenericArg::new( - Interner, - GenericArgData::Ty(generics.next().unwrap().ty.clone()), - ) - }) - .build() - }); + let mut generics = generics.map(|it| it.ty.clone()); + let mut filler = |x: &_| { + let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); + match x { + ParamKind::Type => ty.cast(Interner), + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + } + }; - let substs = TyBuilder::subst_for_def(db, self.id, parent_substs) - .fill(|_| { - GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone())) - }) - .build(); + let parent_substs = + parent_id.map(|id| TyBuilder::subst_for_def(db, id, None).fill(&mut filler).build()); + let substs = TyBuilder::subst_for_def(db, self.id, parent_substs).fill(&mut filler).build(); let callable_sig = db.callable_item_signature(self.id.into()).substitute(Interner, &substs); let ty = callable_sig.ret().clone(); @@ -2197,11 +2197,7 @@ impl SelfParam { Type { env: environment, ty } } - pub fn ty_with_generics( - &self, - db: &dyn HirDatabase, - mut generics: impl Iterator, - ) -> Type { + pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator) -> Type { let parent_id: GenericDefId = match self.func.lookup(db.upcast()).container { ItemContainerId::ImplId(it) => it.into(), ItemContainerId::TraitId(it) => it.into(), @@ -2210,16 +2206,18 @@ impl SelfParam { } }; - let parent_substs = TyBuilder::subst_for_def(db, parent_id, None) - .fill(|_| { - GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone())) - }) - .build(); - let substs = TyBuilder::subst_for_def(db, self.func, Some(parent_substs)) - .fill(|_| { - GenericArg::new(Interner, GenericArgData::Ty(generics.next().unwrap().ty.clone())) - }) - .build(); + let mut generics = generics.map(|it| it.ty.clone()); + let mut filler = |x: &_| { + let ty = generics.next().unwrap_or_else(|| TyKind::Error.intern(Interner)); + match x { + ParamKind::Type => ty.cast(Interner), + ParamKind::Const(ty) => unknown_const_as_generic(ty.clone()), + } + }; + + let parent_substs = TyBuilder::subst_for_def(db, parent_id, None).fill(&mut filler).build(); + let substs = + TyBuilder::subst_for_def(db, self.func, Some(parent_substs)).fill(&mut filler).build(); let callable_sig = db.callable_item_signature(self.func.into()).substitute(Interner, &substs); let environment = db.trait_environment(self.func.into()); diff --git a/crates/hir/src/term_search/type_tree.rs b/crates/hir/src/term_search/expr.rs similarity index 58% rename from crates/hir/src/term_search/type_tree.rs rename to crates/hir/src/term_search/expr.rs index 72a1cbe909..b43f10528d 100644 --- a/crates/hir/src/term_search/type_tree.rs +++ b/crates/hir/src/term_search/expr.rs @@ -88,7 +88,7 @@ fn non_default_generics(db: &dyn HirDatabase, def: GenericDef, generics: &[Type] /// So in short it pretty much gives us a way to get type `Option` using the items we have in /// scope. #[derive(Debug, Clone, Eq, Hash, PartialEq)] -pub enum TypeTree { +pub enum Expr { /// Constant Const(Const), /// Static variable @@ -99,21 +99,23 @@ pub enum TypeTree { ConstParam(ConstParam), /// Well known type (such as `true` for bool) FamousType { ty: Type, value: &'static str }, - /// Function or method call - Function { func: Function, generics: Vec, params: Vec }, + /// Function call (does not take self param) + Function { func: Function, generics: Vec, params: Vec }, + /// Method call (has self param) + Method { func: Function, generics: Vec, target: Box, params: Vec }, /// Enum variant construction - Variant { variant: Variant, generics: Vec, params: Vec }, + Variant { variant: Variant, generics: Vec, params: Vec }, /// Struct construction - Struct { strukt: Struct, generics: Vec, params: Vec }, + Struct { strukt: Struct, generics: Vec, params: Vec }, /// Struct field access - Field { type_tree: Box, field: Field }, + Field { expr: Box, field: Field }, /// Passing type as reference (with `&`) - Reference(Box), + Reference(Box), /// Indicates possibility of many different options that all evaluate to `ty` Many(Type), } -impl TypeTree { +impl Expr { /// Generate source code for type tree. /// /// Note that trait imports are not added to generated code. @@ -126,78 +128,70 @@ impl TypeTree { ) -> String { let db = sema_scope.db; match self { - TypeTree::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)), - TypeTree::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)), - TypeTree::Local(it) => return it.name(db).display(db.upcast()).to_string(), - TypeTree::ConstParam(it) => return it.name(db).display(db.upcast()).to_string(), - TypeTree::FamousType { value, .. } => return value.to_string(), - TypeTree::Function { func, params, .. } => { - if let Some(self_param) = func.self_param(db) { - let func_name = func.name(db).display(db.upcast()).to_string(); - let target = params - .first() - .expect("no self param") - .gen_source_code(sema_scope, many_formatter); - let args = params - .iter() - .skip(1) - .map(|f| f.gen_source_code(sema_scope, many_formatter)) - .join(", "); + Expr::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)), + Expr::Static(it) => mod_item_path_str(sema_scope, &ModuleDef::Static(*it)), + Expr::Local(it) => return it.name(db).display(db.upcast()).to_string(), + Expr::ConstParam(it) => return it.name(db).display(db.upcast()).to_string(), + Expr::FamousType { value, .. } => return value.to_string(), + Expr::Function { func, params, .. } => { + let args = + params.iter().map(|f| f.gen_source_code(sema_scope, many_formatter)).join(", "); - match func.as_assoc_item(db).unwrap().containing_trait_or_trait_impl(db) { - Some(trait_) => { - let trait_name = - mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_)); - let target = match self_param.access(db) { - crate::Access::Shared => format!("&{target}"), - crate::Access::Exclusive => format!("&mut {target}"), - crate::Access::Owned => target, - }; - match args.is_empty() { - true => format!("{trait_name}::{func_name}({target})",), - false => format!("{trait_name}::{func_name}({target}, {args})",), + match func.as_assoc_item(db).map(|it| it.container(db)) { + Some(container) => { + let container_name = match container { + crate::AssocItemContainer::Trait(trait_) => { + mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_)) } - } - None => format!("{target}.{func_name}({args})"), + crate::AssocItemContainer::Impl(imp) => { + let self_ty = imp.self_ty(db); + // Should it be guaranteed that `mod_item_path` always exists? + match self_ty + .as_adt() + .and_then(|adt| mod_item_path(sema_scope, &adt.into())) + { + Some(path) => path.display(sema_scope.db.upcast()).to_string(), + None => self_ty.display(db).to_string(), + } + } + }; + let fn_name = func.name(db).display(db.upcast()).to_string(); + format!("{container_name}::{fn_name}({args})",) } - } else { - let args = params - .iter() - .map(|f| f.gen_source_code(sema_scope, many_formatter)) - .join(", "); - - match func.as_assoc_item(db).map(|it| it.container(db)) { - Some(container) => { - let container_name = match container { - crate::AssocItemContainer::Trait(trait_) => { - mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_)) - } - crate::AssocItemContainer::Impl(imp) => { - let self_ty = imp.self_ty(db); - // Should it be guaranteed that `mod_item_path` always exists? - match self_ty - .as_adt() - .and_then(|adt| mod_item_path(sema_scope, &adt.into())) - { - Some(path) => { - path.display(sema_scope.db.upcast()).to_string() - } - None => self_ty.display(db).to_string(), - } - } - }; - let fn_name = func.name(db).display(db.upcast()).to_string(); - format!("{container_name}::{fn_name}({args})",) - } - None => { - let fn_name = - mod_item_path_str(sema_scope, &ModuleDef::Function(*func)); - format!("{fn_name}({args})",) - } + None => { + let fn_name = mod_item_path_str(sema_scope, &ModuleDef::Function(*func)); + format!("{fn_name}({args})",) } } } - TypeTree::Variant { variant, generics, params } => { + Expr::Method { func, target, params, .. } => { + if target.contains_many_in_illegal_pos() { + return many_formatter(&target.ty(db)); + } + + let func_name = func.name(db).display(db.upcast()).to_string(); + let self_param = func.self_param(db).unwrap(); + let target = target.gen_source_code(sema_scope, many_formatter); + let args = + params.iter().map(|f| f.gen_source_code(sema_scope, many_formatter)).join(", "); + + match func.as_assoc_item(db).and_then(|it| it.containing_trait_or_trait_impl(db)) { + Some(trait_) => { + let trait_name = mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_)); + let target = match self_param.access(db) { + crate::Access::Shared => format!("&{target}"), + crate::Access::Exclusive => format!("&mut {target}"), + crate::Access::Owned => target, + }; + match args.is_empty() { + true => format!("{trait_name}::{func_name}({target})",), + false => format!("{trait_name}::{func_name}({target}, {args})",), + } + } + None => format!("{target}.{func_name}({args})"), + } + } + Expr::Variant { variant, generics, params } => { let generics = non_default_generics(db, (*variant).into(), generics); let generics_str = match generics.is_empty() { true => String::new(), @@ -236,7 +230,7 @@ impl TypeTree { let prefix = mod_item_path_str(sema_scope, &ModuleDef::Variant(*variant)); format!("{prefix}{inner}") } - TypeTree::Struct { strukt, generics, params } => { + Expr::Struct { strukt, generics, params } => { let generics = non_default_generics(db, (*strukt).into(), generics); let inner = match strukt.kind(db) { StructKind::Tuple => { @@ -274,16 +268,24 @@ impl TypeTree { let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt))); format!("{prefix}{inner}") } - TypeTree::Field { type_tree, field } => { - let strukt = type_tree.gen_source_code(sema_scope, many_formatter); + Expr::Field { expr, field } => { + if expr.contains_many_in_illegal_pos() { + return many_formatter(&expr.ty(db)); + } + + let strukt = expr.gen_source_code(sema_scope, many_formatter); let field = field.name(db).display(db.upcast()).to_string(); format!("{strukt}.{field}") } - TypeTree::Reference(type_tree) => { - let inner = type_tree.gen_source_code(sema_scope, many_formatter); + Expr::Reference(expr) => { + if expr.contains_many_in_illegal_pos() { + return many_formatter(&expr.ty(db)); + } + + let inner = expr.gen_source_code(sema_scope, many_formatter); format!("&{inner}") } - TypeTree::Many(ty) => many_formatter(ty), + Expr::Many(ty) => many_formatter(ty), } } @@ -292,29 +294,27 @@ impl TypeTree { /// Same as getting the type of root node pub fn ty(&self, db: &dyn HirDatabase) -> Type { match self { - TypeTree::Const(it) => it.ty(db), - TypeTree::Static(it) => it.ty(db), - TypeTree::Local(it) => it.ty(db), - TypeTree::ConstParam(it) => it.ty(db), - TypeTree::FamousType { ty, .. } => ty.clone(), - TypeTree::Function { func, generics, params } => match func.has_self_param(db) { - true => func.ret_type_with_generics( - db, - params[0].ty(db).type_arguments().chain(generics.iter().cloned()), - ), - false => func.ret_type_with_generics(db, generics.iter().cloned()), - }, - TypeTree::Variant { variant, generics, .. } => { - variant.parent_enum(db).ty_with_generics(db, generics.iter().cloned()) + Expr::Const(it) => it.ty(db), + Expr::Static(it) => it.ty(db), + Expr::Local(it) => it.ty(db), + Expr::ConstParam(it) => it.ty(db), + Expr::FamousType { ty, .. } => ty.clone(), + Expr::Function { func, generics, .. } => { + func.ret_type_with_args(db, generics.iter().cloned()) } - TypeTree::Struct { strukt, generics, .. } => { - strukt.ty_with_generics(db, generics.iter().cloned()) + Expr::Method { func, generics, target, .. } => func.ret_type_with_args( + db, + target.ty(db).type_arguments().chain(generics.iter().cloned()), + ), + Expr::Variant { variant, generics, .. } => { + variant.parent_enum(db).ty_with_args(db, generics.iter().cloned()) } - TypeTree::Field { type_tree, field } => { - field.ty_with_generics(db, type_tree.ty(db).type_arguments()) + Expr::Struct { strukt, generics, .. } => { + strukt.ty_with_args(db, generics.iter().cloned()) } - TypeTree::Reference(it) => it.ty(db), - TypeTree::Many(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(), } } @@ -323,7 +323,7 @@ impl TypeTree { let mut res = Vec::new(); match self { - TypeTree::Function { func, params, .. } => { + Expr::Method { func, params, .. } => { res.extend(params.iter().flat_map(|it| it.traits_used(db))); if let Some(it) = func.as_assoc_item(db) { if let Some(it) = it.containing_trait_or_trait_impl(db) { @@ -336,4 +336,28 @@ impl TypeTree { res } + + /// Check in the tree contains `Expr::Many` variant in illegal place to insert `todo`, + /// `unimplemented` or similar macro + /// + /// Some examples are following + /// ```no_compile + /// macro!().foo + /// macro!().bar() + /// ¯o!() + /// ``` + fn contains_many_in_illegal_pos(&self) -> bool { + match self { + Expr::Method { target, .. } => target.contains_many_in_illegal_pos(), + Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(), + Expr::Reference(target) => target.is_many(), + Expr::Many(_) => true, + _ => false, + } + } + + /// Helper function to check if outermost type tree is `Expr::Many` variant + pub fn is_many(&self) -> bool { + matches!(self, Expr::Many(_)) + } } diff --git a/crates/hir/src/term_search/mod.rs b/crates/hir/src/term_search/mod.rs index a519324cda..f0c3fdb4d0 100644 --- a/crates/hir/src/term_search/mod.rs +++ b/crates/hir/src/term_search/mod.rs @@ -7,8 +7,8 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::{ModuleDef, ScopeDef, Semantics, SemanticsScope, Type}; -pub mod type_tree; -pub use type_tree::TypeTree; +mod expr; +pub use expr::Expr; mod tactics; @@ -19,48 +19,57 @@ enum NewTypesKey { StructProjection, } +/// Helper enum to squash big number of alternative trees into `Many` variant as there is too many +/// to take into account. #[derive(Debug)] -enum AlternativeTrees { - Few(FxHashSet), - Many(Type), +enum AlternativeExprs { + /// There are few trees, so we keep track of them all + Few(FxHashSet), + /// There are too many trees to keep track of + Many, } -impl AlternativeTrees { - pub fn new( - threshold: usize, - ty: Type, - trees: impl Iterator, - ) -> AlternativeTrees { - let mut it = AlternativeTrees::Few(Default::default()); - it.extend_with_threshold(threshold, ty, trees); +impl AlternativeExprs { + /// Construct alternative trees + /// + /// # Arguments + /// `threshold` - threshold value for many trees (more than that is many) + /// `exprs` - expressions iterator + fn new(threshold: usize, exprs: impl Iterator) -> AlternativeExprs { + let mut it = AlternativeExprs::Few(Default::default()); + it.extend_with_threshold(threshold, exprs); it } - pub fn trees(&self) -> Vec { + /// Get type trees stored in alternative trees (or `Expr::Many` in case of many) + /// + /// # Arguments + /// `ty` - Type of expressions queried (this is used to give type to `Expr::Many`) + fn exprs(&self, ty: &Type) -> Vec { match self { - AlternativeTrees::Few(trees) => trees.iter().cloned().collect(), - AlternativeTrees::Many(ty) => vec![TypeTree::Many(ty.clone())], + AlternativeExprs::Few(exprs) => exprs.iter().cloned().collect(), + AlternativeExprs::Many => vec![Expr::Many(ty.clone())], } } - pub fn extend_with_threshold( - &mut self, - threshold: usize, - ty: Type, - mut trees: impl Iterator, - ) { + /// Extend alternative expressions + /// + /// # Arguments + /// `threshold` - threshold value for many trees (more than that is many) + /// `exprs` - expressions iterator + fn extend_with_threshold(&mut self, threshold: usize, mut exprs: impl Iterator) { match self { - AlternativeTrees::Few(tts) => { - while let Some(it) = trees.next() { + AlternativeExprs::Few(tts) => { + while let Some(it) = exprs.next() { if tts.len() > threshold { - *self = AlternativeTrees::Many(ty); + *self = AlternativeExprs::Many; break; } tts.insert(it); } } - AlternativeTrees::Many(_) => (), + AlternativeExprs::Many => (), } } } @@ -76,8 +85,8 @@ impl AlternativeTrees { /// not produce any new results. #[derive(Default, Debug)] struct LookupTable { - /// All the `TypeTree`s in "value" produce the type of "key" - data: FxHashMap, + /// All the `Expr`s in "value" produce the type of "key" + data: FxHashMap, /// New types reached since last query by the `NewTypesKey` new_types: FxHashMap>, /// ScopeDefs that are not interesting any more @@ -94,40 +103,40 @@ struct LookupTable { impl LookupTable { /// Initialize lookup table - fn new() -> Self { - let mut res: Self = Default::default(); + fn new(many_threshold: usize) -> 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 } - /// Find all `TypeTree`s that unify with the `ty` - fn find(&self, db: &dyn HirDatabase, ty: &Type) -> Option> { + /// Find all `Expr`s that unify with the `ty` + fn find(&self, db: &dyn HirDatabase, ty: &Type) -> Option> { self.data .iter() .find(|(t, _)| t.could_unify_with_deeply(db, ty)) - .map(|(_, tts)| tts.trees()) + .map(|(t, tts)| tts.exprs(t)) } /// 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 `TypeTree::Reference` and returns them. - fn find_autoref(&self, db: &dyn HirDatabase, ty: &Type) -> Option> { + /// trees we have for `i32` with `Expr::Reference` and returns them. + fn find_autoref(&self, db: &dyn HirDatabase, ty: &Type) -> Option> { self.data .iter() .find(|(t, _)| t.could_unify_with_deeply(db, ty)) - .map(|(_, tts)| tts.trees()) + .map(|(t, it)| it.exprs(t)) .or_else(|| { self.data .iter() .find(|(t, _)| { Type::reference(t, Mutability::Shared).could_unify_with_deeply(db, &ty) }) - .map(|(_, tts)| { - tts.trees() + .map(|(t, it)| { + it.exprs(t) .into_iter() - .map(|tt| TypeTree::Reference(Box::new(tt))) + .map(|expr| Expr::Reference(Box::new(expr))) .collect() }) }) @@ -138,14 +147,11 @@ impl LookupTable { /// Note that the types have to be the same, unification is not enough as unification is not /// transitive. For example Vec and FxHashSet both unify with Iterator, /// but they clearly do not unify themselves. - fn insert(&mut self, ty: Type, trees: impl Iterator) { + fn insert(&mut self, ty: Type, exprs: impl Iterator) { match self.data.get_mut(&ty) { - Some(it) => it.extend_with_threshold(self.many_threshold, ty, trees), + Some(it) => it.extend_with_threshold(self.many_threshold, exprs), None => { - self.data.insert( - ty.clone(), - AlternativeTrees::new(self.many_threshold, ty.clone(), trees), - ); + self.data.insert(ty.clone(), AlternativeExprs::new(self.many_threshold, exprs)); for it in self.new_types.values_mut() { it.push(ty.clone()); } @@ -206,6 +212,7 @@ impl LookupTable { } /// Context for the `term_search` function +#[derive(Debug)] pub struct TermSearchCtx<'a, DB: HirDatabase> { /// Semantics for the program pub sema: &'a Semantics<'a, DB>, @@ -230,7 +237,7 @@ pub struct TermSearchConfig { impl Default for TermSearchConfig { fn default() -> Self { - Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 5 } + Self { enable_borrowcheck: true, many_alternatives_threshold: 1, depth: 6 } } } @@ -239,9 +246,7 @@ impl Default for TermSearchConfig { /// Search for terms (expressions) that unify with the `goal` type. /// /// # Arguments -/// * `sema` - Semantics for the program -/// * `scope` - Semantic scope, captures context for the term search -/// * `goal` - Target / expected output type +/// * `ctx` - Context for term search /// /// Internally this function uses Breadth First Search to find path to `goal` type. /// The general idea is following: @@ -258,7 +263,7 @@ impl Default for TermSearchConfig { /// 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(ctx: TermSearchCtx<'_, DB>) -> Vec { +pub fn term_search(ctx: &TermSearchCtx<'_, DB>) -> Vec { let module = ctx.scope.module(); let mut defs = FxHashSet::default(); defs.insert(ScopeDef::ModuleDef(ModuleDef::Module(module))); @@ -267,30 +272,21 @@ pub fn term_search(ctx: TermSearchCtx<'_, DB>) -> Vec defs.insert(def); }); - let mut lookup = LookupTable::new(); + let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold); // Try trivial tactic first, also populates lookup table - let mut solutions: Vec = tactics::trivial(&ctx, &defs, &mut lookup).collect(); + let mut solutions: Vec = 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(&ctx, &defs, &mut lookup)); - - let mut solution_found = !solutions.is_empty(); + solutions.extend(tactics::famous_types(ctx, &defs, &mut lookup)); for _ in 0..ctx.config.depth { lookup.new_round(); - 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 { - break; - } - - solution_found = !solutions.is_empty(); + 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)); // Discard not interesting `ScopeDef`s for speedup for def in lookup.exhausted_scopedefs() { @@ -298,5 +294,5 @@ pub fn term_search(ctx: TermSearchCtx<'_, DB>) -> Vec } } - solutions.into_iter().unique().collect() + solutions.into_iter().filter(|it| !it.is_many()).unique().collect() } diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index e0b6c1291e..a8282359ce 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -19,7 +19,7 @@ use crate::{ Variant, }; -use crate::term_search::{TermSearchConfig, TypeTree}; +use crate::term_search::{Expr, TermSearchConfig}; use super::{LookupTable, NewTypesKey, TermSearchCtx}; @@ -41,13 +41,13 @@ pub(super) fn trivial<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet, lookup: &'a mut LookupTable, -) -> impl Iterator + 'a { +) -> impl Iterator + '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)), + let expr = match def { + ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(Expr::Const(*it)), + ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(Expr::Static(*it)), + ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)), ScopeDef::Local(it) => { if ctx.config.enable_borrowcheck { let borrowck = db.borrowck(it.parent).ok()?; @@ -67,22 +67,22 @@ pub(super) fn trivial<'a, DB: HirDatabase>( } } - Some(TypeTree::Local(*it)) + Some(Expr::Local(*it)) } _ => None, }?; lookup.mark_exhausted(*def); - let ty = tt.ty(db); - lookup.insert(ty.clone(), std::iter::once(tt.clone())); + let ty = expr.ty(db); + lookup.insert(ty.clone(), std::iter::once(expr.clone())); // Don't suggest local references as they are not valid for return - if matches!(tt, TypeTree::Local(_)) && ty.contains_reference(db) { + if matches!(expr, Expr::Local(_)) && ty.contains_reference(db) { return None; } - ty.could_unify_with_deeply(db, &ctx.goal).then(|| tt) + ty.could_unify_with_deeply(db, &ctx.goal).then(|| expr) }) } @@ -101,7 +101,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet, lookup: &'a mut LookupTable, -) -> impl Iterator + 'a { +) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); fn variant_helper( @@ -111,14 +111,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( variant: Variant, goal: &Type, config: &TermSearchConfig, - ) -> Vec<(Type, Vec)> { - let generics = GenericDef::from(variant.parent_enum(db)); - - // Ignore unstable variants + ) -> Vec<(Type, Vec)> { + // Ignore unstable if variant.is_unstable(db) { return Vec::new(); } + let generics = GenericDef::from(variant.parent_enum(db)); + // Ignore enums with const generics if !generics.const_params(db).is_empty() { return Vec::new(); @@ -160,7 +160,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( }) .collect(); - let enum_ty = parent_enum.ty_with_generics(db, generics.iter().cloned()); + let enum_ty = parent_enum.ty_with_args(db, generics.iter().cloned()); // Allow types with generics only if they take us straight to goal for // performance reasons @@ -174,52 +174,42 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( } // Early exit if some param cannot be filled from lookup - let param_trees: Vec> = variant + let param_exprs: Vec> = variant .fields(db) .into_iter() - .map(|field| { - lookup.find(db, &field.ty_with_generics(db, generics.iter().cloned())) - }) + .map(|field| lookup.find(db, &field.ty_with_args(db, generics.iter().cloned()))) .collect::>()?; // Note that we need special case for 0 param constructors because of multi cartesian // product - let variant_trees: Vec = if param_trees.is_empty() { - vec![TypeTree::Variant { - variant, - generics: generics.clone(), - params: Vec::new(), - }] + let variant_exprs: Vec = if param_exprs.is_empty() { + vec![Expr::Variant { variant, generics: generics.clone(), params: Vec::new() }] } else { - param_trees + param_exprs .into_iter() .multi_cartesian_product() - .map(|params| TypeTree::Variant { - variant, - generics: generics.clone(), - params, - }) + .map(|params| Expr::Variant { variant, generics: generics.clone(), params }) .collect() }; - lookup.insert(enum_ty.clone(), variant_trees.iter().cloned()); + lookup.insert(enum_ty.clone(), variant_exprs.iter().cloned()); - Some((enum_ty, variant_trees)) + Some((enum_ty, variant_exprs)) }) .collect() } defs.iter() .filter_map(move |def| match def { ScopeDef::ModuleDef(ModuleDef::Variant(it)) => { - let variant_trees = + let variant_exprs = variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.goal, &ctx.config); - if variant_trees.is_empty() { + if variant_exprs.is_empty() { return None; } lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Variant(*it))); - Some(variant_trees) + Some(variant_exprs) } ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(enum_))) => { - let trees: Vec<(Type, Vec)> = enum_ + let exprs: Vec<(Type, Vec)> = enum_ .variants(db) .into_iter() .flat_map(|it| { @@ -227,11 +217,11 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( }) .collect(); - if !trees.is_empty() { + if !exprs.is_empty() { lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(*enum_)))); } - Some(trees) + Some(exprs) } ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => { // Ignore unstable and not visible @@ -269,7 +259,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( .into_iter() .permutations(non_default_type_params_len); - let trees = generic_params + let exprs = generic_params .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -280,7 +270,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( None => g.next().expect("Missing type param"), }) .collect(); - let struct_ty = it.ty_with_generics(db, generics.iter().cloned()); + let struct_ty = it.ty_with_args(db, generics.iter().cloned()); // Allow types with generics only if they take us straight to goal for // performance reasons @@ -301,20 +291,20 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( } // Early exit if some param cannot be filled from lookup - let param_trees: Vec> = fileds + let param_exprs: Vec> = fileds .into_iter() .map(|field| lookup.find(db, &field.ty(db))) .collect::>()?; // Note that we need special case for 0 param constructors because of multi cartesian // product - let struct_trees: Vec = if param_trees.is_empty() { - vec![TypeTree::Struct { strukt: *it, generics, params: Vec::new() }] + let struct_exprs: Vec = if param_exprs.is_empty() { + vec![Expr::Struct { strukt: *it, generics, params: Vec::new() }] } else { - param_trees + param_exprs .into_iter() .multi_cartesian_product() - .map(|params| TypeTree::Struct { + .map(|params| Expr::Struct { strukt: *it, generics: generics.clone(), params, @@ -324,17 +314,17 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( lookup .mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(*it)))); - lookup.insert(struct_ty.clone(), struct_trees.iter().cloned()); + lookup.insert(struct_ty.clone(), struct_exprs.iter().cloned()); - Some((struct_ty, struct_trees)) + Some((struct_ty, struct_exprs)) }) .collect(); - Some(trees) + Some(exprs) } _ => None, }) .flatten() - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) + .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .flatten() } @@ -354,7 +344,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, defs: &'a FxHashSet, lookup: &'a mut LookupTable, -) -> impl Iterator + 'a { +) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); defs.iter() @@ -394,7 +384,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( .into_iter() .permutations(non_default_type_params_len); - let trees: Vec<_> = generic_params + let exprs: Vec<_> = generic_params .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -406,7 +396,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( }) .collect(); - let ret_ty = it.ret_type_with_generics(db, generics.iter().cloned()); + let ret_ty = it.ret_type_with_args(db, generics.iter().cloned()); // Filter out private and unsafe functions if !it.is_visible_from(db, module) || it.is_unsafe_to_call(db) @@ -418,7 +408,7 @@ pub(super) fn free_function<'a, DB: HirDatabase>( } // Early exit if some param cannot be filled from lookup - let param_trees: Vec> = it + let param_exprs: Vec> = it .params_without_self_with_generics(db, generics.iter().cloned()) .into_iter() .map(|field| { @@ -432,13 +422,13 @@ pub(super) fn free_function<'a, DB: HirDatabase>( // Note that we need special case for 0 param constructors because of multi cartesian // product - let fn_trees: Vec = if param_trees.is_empty() { - vec![TypeTree::Function { func: *it, generics, params: Vec::new() }] + let fn_exprs: Vec = if param_exprs.is_empty() { + vec![Expr::Function { func: *it, generics, params: Vec::new() }] } else { - param_trees + param_exprs .into_iter() .multi_cartesian_product() - .map(|params| TypeTree::Function { + .map(|params| Expr::Function { func: *it, generics: generics.clone(), @@ -448,16 +438,16 @@ pub(super) fn free_function<'a, DB: HirDatabase>( }; lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Function(*it))); - lookup.insert(ret_ty.clone(), fn_trees.iter().cloned()); - Some((ret_ty, fn_trees)) + lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned()); + Some((ret_ty, fn_exprs)) }) .collect(); - Some(trees) + Some(exprs) } _ => None, }) .flatten() - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) + .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .flatten() } @@ -479,7 +469,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, -) -> impl Iterator + 'a { +) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); lookup @@ -546,7 +536,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( .into_iter() .permutations(non_default_type_params_len); - let trees: Vec<_> = generic_params + let exprs: Vec<_> = generic_params .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -559,7 +549,7 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( }) .collect(); - let ret_ty = it.ret_type_with_generics( + let ret_ty = it.ret_type_with_args( db, ty.type_arguments().chain(generics.iter().cloned()), ); @@ -578,17 +568,17 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( let self_ty = it .self_param(db) .expect("No self param") - .ty_with_generics(db, ty.type_arguments().chain(generics.iter().cloned())); + .ty_with_args(db, ty.type_arguments().chain(generics.iter().cloned())); // Ignore functions that have different self type if !self_ty.autoderef(db).any(|s_ty| ty == s_ty) { return None; } - let target_type_trees = lookup.find(db, &ty).expect("Type not in lookup"); + 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_trees: Vec> = it + let param_exprs: Vec> = it .params_without_self_with_generics( db, ty.type_arguments().chain(generics.iter().cloned()), @@ -597,20 +587,29 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( .map(|field| lookup.find_autoref(db, &field.ty())) .collect::>()?; - let fn_trees: Vec = std::iter::once(target_type_trees) - .chain(param_trees.into_iter()) + let fn_exprs: Vec = std::iter::once(target_type_exprs) + .chain(param_exprs.into_iter()) .multi_cartesian_product() - .map(|params| TypeTree::Function { func: it, generics: Vec::new(), params }) + .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_trees.iter().cloned()); - Some((ret_ty, fn_trees)) + lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned()); + Some((ret_ty, fn_exprs)) }) .collect(); - Some(trees) + Some(exprs) }) .flatten() - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) + .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .flatten() } @@ -629,26 +628,26 @@ pub(super) fn struct_projection<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, -) -> impl Iterator + 'a { +) -> impl Iterator + '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"))) + .map(|ty| (ty.clone(), lookup.find(db, &ty).expect("Expr not in lookup"))) .flat_map(move |(ty, targets)| { ty.fields(db).into_iter().filter_map(move |(field, filed_ty)| { if !field.is_visible_from(db, module) { return None; } - let trees = targets + let exprs = targets .clone() .into_iter() - .map(move |target| TypeTree::Field { field, type_tree: Box::new(target) }); - Some((filed_ty, trees)) + .map(move |target| Expr::Field { field, expr: Box::new(target) }); + Some((filed_ty, exprs)) }) }) - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) + .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .flatten() } @@ -669,20 +668,20 @@ pub(super) fn famous_types<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, -) -> impl Iterator + 'a { +) -> impl Iterator + '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" }, - TypeTree::FamousType { ty: Type::new(db, module.id, TyBuilder::unit()), value: "()" }, + Expr::FamousType { ty: Type::new(db, module.id, TyBuilder::bool()), value: "true" }, + Expr::FamousType { ty: Type::new(db, module.id, TyBuilder::bool()), value: "false" }, + Expr::FamousType { ty: Type::new(db, module.id, TyBuilder::unit()), value: "()" }, ] .into_iter() - .map(|tt| { - lookup.insert(tt.ty(db), std::iter::once(tt.clone())); - tt + .map(|exprs| { + lookup.insert(exprs.ty(db), std::iter::once(exprs.clone())); + exprs }) - .filter(|tt| tt.ty(db).could_unify_with_deeply(db, &ctx.goal)) + .filter(|expr| expr.ty(db).could_unify_with_deeply(db, &ctx.goal)) } /// # Impl static method (without self type) tactic @@ -700,7 +699,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( ctx: &'a TermSearchCtx<'a, DB>, _defs: &'a FxHashSet, lookup: &'a mut LookupTable, -) -> impl Iterator + 'a { +) -> impl Iterator + 'a { let db = ctx.sema.db; let module = ctx.scope.module(); lookup @@ -771,7 +770,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .into_iter() .permutations(non_default_type_params_len); - let trees: Vec<_> = generic_params + let exprs: Vec<_> = generic_params .filter_map(|generics| { // Insert default type params let mut g = generics.into_iter(); @@ -784,7 +783,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( }) .collect(); - let ret_ty = it.ret_type_with_generics( + let ret_ty = it.ret_type_with_args( db, ty.type_arguments().chain(generics.iter().cloned()), ); @@ -801,7 +800,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( // } // Early exit if some param cannot be filled from lookup - let param_trees: Vec> = it + let param_exprs: Vec> = it .params_without_self_with_generics( db, ty.type_arguments().chain(generics.iter().cloned()), @@ -812,28 +811,27 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( // Note that we need special case for 0 param constructors because of multi cartesian // product - let fn_trees: Vec = if param_trees.is_empty() { - vec![TypeTree::Function { func: it, generics, params: Vec::new() }] + let fn_exprs: Vec = if param_exprs.is_empty() { + vec![Expr::Function { func: it, generics, params: Vec::new() }] } else { - param_trees + param_exprs .into_iter() .multi_cartesian_product() - .map(|params| TypeTree::Function { + .map(|params| Expr::Function { func: it, generics: generics.clone(), - params, }) .collect() }; - lookup.insert(ret_ty.clone(), fn_trees.iter().cloned()); - Some((ret_ty, fn_trees)) + lookup.insert(ret_ty.clone(), fn_exprs.iter().cloned()); + Some((ret_ty, fn_exprs)) }) .collect(); - Some(trees) + Some(exprs) }) .flatten() - .filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| trees)) + .filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then(|| exprs)) .flatten() } diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index 3451a65493..89a7bb974a 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs @@ -30,7 +30,7 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< goal: target_ty, config: Default::default(), }; - let paths = hir::term_search::term_search(term_search_ctx); + 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 b696933c27..87016a6057 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -40,8 +40,8 @@ use crate::{ literal::{render_struct_literal, render_variant_lit}, macro_::render_macro, pattern::{render_struct_pat, render_variant_pat}, - render_field, render_path_resolution, render_pattern_resolution, render_tuple_field, - render_type_tree, + render_expr, render_field, render_path_resolution, render_pattern_resolution, + render_tuple_field, type_alias::{render_type_alias, render_type_alias_with_eq}, union_literal::render_union_literal, RenderContext, @@ -158,12 +158,8 @@ impl Completions { item.add_to(self, ctx.db); } - pub(crate) fn add_expr( - &mut self, - ctx: &CompletionContext<'_>, - expr: &hir::term_search::TypeTree, - ) { - match render_type_tree(ctx, expr) { + pub(crate) fn add_expr(&mut self, ctx: &CompletionContext<'_>, expr: &hir::term_search::Expr) { + match render_expr(ctx, expr) { Some(item) => item.add_to(self, ctx.db), None => (), } @@ -699,7 +695,6 @@ pub(super) fn complete_name_ref( ctx: &CompletionContext<'_>, NameRefContext { nameref, kind }: &NameRefContext, ) { - expr::complete_expr(acc, ctx); match kind { NameRefKind::Path(path_ctx) => { flyimport::import_on_the_fly_path(acc, ctx, path_ctx); @@ -707,6 +702,7 @@ pub(super) fn complete_name_ref( match &path_ctx.kind { PathKind::Expr { expr_ctx } => { expr::complete_expr_path(acc, ctx, path_ctx, expr_ctx); + expr::complete_expr(acc, ctx); dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx); item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx); @@ -763,6 +759,7 @@ pub(super) fn complete_name_ref( flyimport::import_on_the_fly_dot(acc, ctx, dot_access); dot::complete_dot(acc, ctx, dot_access); postfix::complete_postfix(acc, ctx, dot_access); + expr::complete_expr(acc, ctx); } NameRefKind::Keyword(item) => { keyword::complete_for_and_where(acc, ctx, item); diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 891717285d..9708f9c526 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -329,11 +329,8 @@ pub(crate) fn complete_expr_path( } } -pub(crate) fn complete_expr( - acc: &mut Completions, - ctx: &CompletionContext<'_>, -) { - let _p = profile::span("complete_expr"); +pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) { + let _p = tracing::span!(tracing::Level::INFO, "complete_expr").entered(); if !ctx.qualifier_ctx.none() { return; } @@ -351,12 +348,34 @@ pub(crate) fn complete_expr( config: hir::term_search::TermSearchConfig { enable_borrowcheck: false, many_alternatives_threshold: 1, - depth: 2, + depth: 6, }, }; - let exprs = hir::term_search::term_search(term_search_ctx); + let exprs = hir::term_search::term_search(&term_search_ctx); for expr in exprs { - acc.add_expr(ctx, &expr); + // Expand method calls + match expr { + hir::term_search::Expr::Method { func, generics, target, params } + if target.is_many() => + { + let target_ty = target.ty(ctx.db); + let term_search_ctx = + hir::term_search::TermSearchCtx { goal: target_ty, ..term_search_ctx }; + let target_exprs = hir::term_search::term_search(&term_search_ctx); + + for expr in target_exprs { + let expanded_expr = hir::term_search::Expr::Method { + func, + generics: generics.clone(), + target: Box::new(expr), + params: params.clone(), + }; + + acc.add_expr(ctx, &expanded_expr) + } + } + _ => acc.add_expr(ctx, &expr), + } } } } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 0a9ea1ac84..6b102257ed 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -17,7 +17,7 @@ use ide_db::{ imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind, }; -use syntax::{ast, AstNode, SmolStr, SyntaxKind, TextRange}; +use syntax::{ast, format_smolstr, AstNode, SmolStr, SyntaxKind, TextRange}; use text_edit::TextEdit; use crate::{ @@ -272,9 +272,9 @@ 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( +pub(crate) fn render_expr( ctx: &CompletionContext<'_>, - expr: &hir::term_search::TypeTree, + expr: &hir::term_search::Expr, ) -> Option { let mut i = 1; let mut snippet_formatter = |ty: &hir::Type| { @@ -292,31 +292,42 @@ pub(crate) fn render_type_tree( ty.as_adt() .and_then(|adt| adt.name(ctx.db).as_text()) .map(|s| stdx::to_lower_snake_case(s.as_str())) - .unwrap_or_else(|| String::from("_")) + .unwrap_or_else(|| String::from("...")) }; let label = expr.gen_source_code(&ctx.scope, &mut label_formatter); - let source_range = match &ctx.expected_name { - Some(name_or_ref) => name_or_ref.syntax().text_range(), - None => match ctx.original_token.parent() { - Some(node) => match node.ancestors().find_map(|n| ast::Path::cast(n)) { - Some(path) => path.syntax().text_range(), - None => node.text_range(), - }, - None => ctx.source_range(), + let source_range = match ctx.original_token.parent() { + Some(node) => match node.ancestors().find_map(|n| ast::Path::cast(n)) { + Some(path) => path.syntax().text_range(), + None => node.text_range(), }, + None => ctx.source_range(), }; - let mut item = CompletionItem::new(CompletionItemKind::Snippet, source_range, label); + let mut item = CompletionItem::new(CompletionItemKind::Snippet, source_range, label.clone()); let snippet = format!("{}$0", expr.gen_source_code(&ctx.scope, &mut snippet_formatter)); let edit = TextEdit::replace(source_range, snippet); item.snippet_edit(ctx.config.snippet_cap?, edit); + item.documentation(Documentation::new(String::from("Autogenerated expression by term search"))); item.set_relevance(crate::CompletionRelevance { - type_match: Some(crate::item::CompletionRelevanceTypeMatch::CouldUnify), + type_match: compute_type_match(ctx, &expr.ty(ctx.db)), ..Default::default() }); + for trait_ in expr.traits_used(ctx.db) { + let trait_item = hir::ItemInNs::from(hir::ModuleDef::from(trait_)); + let Some(path) = ctx.module.find_use_path( + ctx.db, + trait_item, + ctx.config.prefer_no_std, + ctx.config.prefer_prelude, + ) else { + continue; + }; + + item.add_import(LocatedImport::new(path, trait_item, trait_item)); + } Some(item) } @@ -2243,6 +2254,8 @@ fn main() { &[CompletionItemKind::Snippet, CompletionItemKind::Method], expect![[r#" sn not [snippet] + sn true [type] + sn false [type] me not() (use ops::Not) [type_could_unify+requires_import] sn if [] sn while [] diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 5363e33b85..67cf551fce 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -316,15 +316,6 @@ fn func() { bn RecordV {…} RecordV { field$1 }$0 bn TupleV(…) TupleV($1)$0 bn UnitV UnitV$0 - sn () - sn CONST - sn Enum::UnitV - sn STATIC - sn Unit - sn false - sn func() - sn function() - sn true "#]], ); } @@ -567,12 +558,10 @@ fn foo() { } "#, expect![[r#" - bn A A$0 - bn B {…} B { r#type$1 }$0 - bn struct {…} r#struct { r#type$1 }$0 - bn type r#type$0 - sn Enum::A - sn Enum::r#type + bn A A$0 + bn B {…} B { r#type$1 }$0 + bn struct {…} r#struct { r#type$1 }$0 + bn type r#type$0 "#]], ); } @@ -597,7 +586,6 @@ fn f(t: Ty) { "#, expect![[r#" ct ABC const ABC: Self - sn t "#]], ); @@ -620,7 +608,6 @@ fn f(e: MyEnum) { expect![[r#" ct A pub const A: i32 ct B pub const B: i32 - sn e "#]], ); @@ -646,7 +633,6 @@ fn f(u: U) { expect![[r#" ct C pub const C: i32 ct D pub const D: i32 - sn u "#]], ); @@ -666,7 +652,6 @@ fn f(v: u32) { "#, expect![[r#" ct MIN pub const MIN: Self - sn v "#]], ); } @@ -778,7 +763,6 @@ fn f(x: EnumAlias) { expect![[r#" bn Tuple(…) Tuple($1)$0 bn Unit Unit$0 - sn x "#]], ); } diff --git a/crates/ide-completion/src/tests/type_pos.rs b/crates/ide-completion/src/tests/type_pos.rs index 8b383d4995..c7161f82ce 100644 --- a/crates/ide-completion/src/tests/type_pos.rs +++ b/crates/ide-completion/src/tests/type_pos.rs @@ -70,27 +70,18 @@ fn fn_return_type() { fn x<'lt, T, const C: usize>() -> $0 "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait tp T - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 kw crate:: kw self:: - sn () - sn C - sn CONST - sn Enum::UnitV - sn STATIC - sn Unit - sn false - sn function() - sn true "#]], ); } @@ -109,27 +100,18 @@ fn foo() -> B$0 { } "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 it () kw crate:: kw self:: - sn () - sn CONST - sn Enum::UnitV - sn STATIC - sn Unit - sn false - sn foo() - sn function() - sn true "#]], ) } @@ -222,26 +204,18 @@ fn f2(x: u64) -> $0 { } "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 it u64 kw crate:: kw self:: - sn () - sn CONST - sn Enum::UnitV - sn STATIC - sn Unit - sn false - sn function() - sn true "#]], ); } @@ -345,27 +319,18 @@ fn foo<'lt, T, const C: usize>() { } "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait tp T - un Union Union - bt u32 u32 + un Union Union + bt u32 u32 kw crate:: kw self:: - sn () - sn C - sn CONST - sn Enum::UnitV - sn STATIC - sn Unit - sn false - sn function() - sn true "#]], ); check( @@ -376,23 +341,14 @@ fn foo<'lt, T, const C: usize>() { } "#, expect![[r#" - en Enum Enum - ma makro!(…) macro_rules! makro + en Enum Enum + ma makro!(…) macro_rules! makro md module - st Record Record - st Tuple Tuple - st Unit Unit + st Record Record + st Tuple Tuple + st Unit Unit tt Trait - un Union Union - sn () - sn C - sn CONST - sn Enum::UnitV - sn STATIC - sn Unit - sn false - sn function() - sn true + un Union Union "#]], ); } diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index fa4734b1a9..035bc75adb 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -46,7 +46,7 @@ fn fixes(sema: &Semantics<'_, RootDatabase>, d: &hir::TypedHole) -> Option continue, // Byproduct of testing method + "0277" if generated.contains(&todo) => continue, // See https://github.com/rust-lang/rust/issues/69882 + _ => (), } bar.println(err); bar.println(generated);