diff --git a/Cargo.toml b/Cargo.toml index d17fde0c1563..f3f01aab8eee 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 2a6c3d4601b0..ca99359323a4 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 44542f041f6f..e147d7d6f44f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1068,20 +1068,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.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); @@ -1141,14 +1142,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); @@ -1254,16 +1256,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) } @@ -2070,10 +2074,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 { @@ -2081,22 +2085,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(); @@ -2414,11 +2414,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(), @@ -2427,16 +2423,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/mod.rs b/crates/hir/src/term_search/mod.rs index a519324cdad3..273c428085bb 100644 --- a/crates/hir/src/term_search/mod.rs +++ b/crates/hir/src/term_search/mod.rs @@ -19,48 +19,61 @@ 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 { + /// There are few trees, so we keep track of them all Few(FxHashSet), - Many(Type), + /// There are too many trees to keep track of + Many, } impl AlternativeTrees { - pub fn new( - threshold: usize, - ty: Type, - trees: impl Iterator, - ) -> AlternativeTrees { + /// Construct alternative trees + /// + /// # Arguments + /// `threshold` - threshold value for many trees (more than that is many) + /// `trees` - trees iterator + fn new(threshold: usize, trees: impl Iterator) -> AlternativeTrees { let mut it = AlternativeTrees::Few(Default::default()); - it.extend_with_threshold(threshold, ty, trees); + it.extend_with_threshold(threshold, trees); it } - pub fn trees(&self) -> Vec { + /// Get type trees stored in alternative trees (or `TypeTree::Many` in case of many) + /// + /// # Arguments + /// `ty` - Type of trees queried (this is used to give type to `TypeTree::Many`) + fn trees(&self, ty: &Type) -> Vec { match self { AlternativeTrees::Few(trees) => trees.iter().cloned().collect(), - AlternativeTrees::Many(ty) => vec![TypeTree::Many(ty.clone())], + AlternativeTrees::Many => vec![TypeTree::Many(ty.clone())], } } - pub fn extend_with_threshold( + /// Extend alternative trees + /// + /// # Arguments + /// `threshold` - threshold value for many trees (more than that is many) + /// `trees` - trees iterator + fn extend_with_threshold( &mut self, threshold: usize, - ty: Type, mut trees: impl Iterator, ) { match self { AlternativeTrees::Few(tts) => { while let Some(it) = trees.next() { if tts.len() > threshold { - *self = AlternativeTrees::Many(ty); + *self = AlternativeTrees::Many; break; } tts.insert(it); } } - AlternativeTrees::Many(_) => (), + AlternativeTrees::Many => (), } } } @@ -106,7 +119,7 @@ impl LookupTable { self.data .iter() .find(|(t, _)| t.could_unify_with_deeply(db, ty)) - .map(|(_, tts)| tts.trees()) + .map(|(t, tts)| tts.trees(t)) } /// Same as find but automatically creates shared reference of types in the lookup @@ -117,15 +130,15 @@ impl LookupTable { self.data .iter() .find(|(t, _)| t.could_unify_with_deeply(db, ty)) - .map(|(_, tts)| tts.trees()) + .map(|(t, tts)| tts.trees(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, tts)| { + tts.trees(t) .into_iter() .map(|tt| TypeTree::Reference(Box::new(tt))) .collect() @@ -140,12 +153,9 @@ impl LookupTable { /// but they clearly do not unify themselves. fn insert(&mut self, ty: Type, trees: 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, trees), None => { - self.data.insert( - ty.clone(), - AlternativeTrees::new(self.many_threshold, ty.clone(), trees), - ); + self.data.insert(ty.clone(), AlternativeTrees::new(self.many_threshold, trees)); for it in self.new_types.values_mut() { it.push(ty.clone()); } @@ -206,6 +216,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 +241,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 +250,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 +267,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))); @@ -270,27 +279,18 @@ pub fn term_search(ctx: TermSearchCtx<'_, DB>) -> Vec let mut lookup = LookupTable::new(); // 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 +298,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 dc5005750a64..32e8bf643399 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs @@ -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 @@ -177,9 +177,7 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>( let param_trees: 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 @@ -280,7 +278,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 @@ -406,7 +404,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) @@ -559,7 +557,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,7 +576,7 @@ 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) { @@ -600,7 +598,16 @@ pub(super) fn impl_method<'a, DB: HirDatabase>( let fn_trees: Vec = std::iter::once(target_type_trees) .chain(param_trees.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()); + TypeTree::Method { + func: it, + generics: generics.clone(), + target, + params: params.collect(), + } + }) .collect(); lookup.insert(ret_ty.clone(), fn_trees.iter().cloned()); @@ -784,7 +791,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()), ); @@ -821,7 +828,6 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>( .map(|params| TypeTree::Function { func: it, generics: generics.clone(), - params, }) .collect() diff --git a/crates/hir/src/term_search/type_tree.rs b/crates/hir/src/term_search/type_tree.rs index 72a1cbe90968..8e2b23acbff8 100644 --- a/crates/hir/src/term_search/type_tree.rs +++ b/crates/hir/src/term_search/type_tree.rs @@ -99,8 +99,10 @@ pub enum TypeTree { ConstParam(ConstParam), /// Well known type (such as `true` for bool) FamousType { ty: Type, value: &'static str }, - /// Function or method call + /// 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 }, /// Struct construction @@ -132,69 +134,60 @@ impl TypeTree { 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(", "); + 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})",) + } + None => { + let fn_name = mod_item_path_str(sema_scope, &ModuleDef::Function(*func)); + format!("{fn_name}({args})",) } - } else { - let args = params - .iter() - .map(|f| f.gen_source_code(sema_scope, many_formatter)) - .join(", "); + } + } + TypeTree::Method { func, target, params, .. } => { + 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() + .skip(1) + .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})",) + 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})"), } } TypeTree::Variant { variant, generics, params } => { @@ -297,21 +290,21 @@ impl TypeTree { 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::Function { func, generics, .. } => { + func.ret_type_with_args(db, generics.iter().cloned()) + } + TypeTree::Method { func, generics, target, .. } => func.ret_type_with_args( + db, + target.ty(db).type_arguments().chain(generics.iter().cloned()), + ), TypeTree::Variant { variant, generics, .. } => { - variant.parent_enum(db).ty_with_generics(db, generics.iter().cloned()) + variant.parent_enum(db).ty_with_args(db, generics.iter().cloned()) } TypeTree::Struct { strukt, generics, .. } => { - strukt.ty_with_generics(db, generics.iter().cloned()) + strukt.ty_with_args(db, generics.iter().cloned()) } TypeTree::Field { type_tree, field } => { - field.ty_with_generics(db, type_tree.ty(db).type_arguments()) + field.ty_with_args(db, type_tree.ty(db).type_arguments()) } TypeTree::Reference(it) => it.ty(db), TypeTree::Many(ty) => ty.clone(), @@ -323,7 +316,7 @@ impl TypeTree { let mut res = Vec::new(); match self { - TypeTree::Function { func, params, .. } => { + TypeTree::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 +329,9 @@ impl TypeTree { res } + + /// Helper function to check if outermost type tree is `TypeTree::Many` variant + pub fn is_many(&self) -> bool { + matches!(self, TypeTree::Many(_)) + } } diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index 3451a65493bb..89a7bb974ae3 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 7883bf17ec5e..db8459568a1c 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -695,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); @@ -703,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); @@ -759,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 4004a8c79548..7ac0df972efe 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -329,10 +329,7 @@ pub(crate) fn complete_expr_path( } } -pub(crate) fn complete_expr( - acc: &mut Completions, - ctx: &CompletionContext<'_>, -) { +pub(crate) fn complete_expr(acc: &mut Completions, ctx: &CompletionContext<'_>) { let _p = profile::span("complete_expr"); 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::TypeTree::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::TypeTree::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/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index fc21bba456b5..7fbf2e6ef362 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -383,17 +383,21 @@ fn main() { } "#, expect![[r#" - sn box Box::new(expr) - sn call function(expr) - sn dbg dbg!(expr) - sn dbgr dbg!(&expr) - sn if if expr {} - sn match match expr {} - sn not !expr - sn ref &expr - sn refm &mut expr - sn unsafe unsafe {} - sn while while expr {} + sn bar + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn false + sn foo(...) + sn if if expr {} + sn match match expr {} + sn not !expr + sn ref &expr + sn refm &mut expr + sn true + sn unsafe unsafe {} + sn while while expr {} "#]], ); } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index a64dff64929f..9cd27be73d19 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -292,29 +292,27 @@ 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() }); @@ -2242,6 +2240,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 5363e33b8553..67cf551fce84 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 8b383d499523..c7161f82ce74 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 ed644c843544..6816c641628f 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