Unnamed repository; edit this file 'description' to name the repository.
Expand target for autocompletion
Tavo Annus 2024-02-11
parent a946970 · commit 0b838e3
-rw-r--r--Cargo.toml2
-rw-r--r--crates/hir-ty/src/infer/unify.rs4
-rw-r--r--crates/hir/src/lib.rs104
-rw-r--r--crates/hir/src/term_search/expr.rs (renamed from crates/hir/src/term_search/type_tree.rs)222
-rw-r--r--crates/hir/src/term_search/mod.rs132
-rw-r--r--crates/hir/src/term_search/tactics.rs206
-rw-r--r--crates/ide-assists/src/handlers/term_search.rs2
-rw-r--r--crates/ide-completion/src/completions.rs15
-rw-r--r--crates/ide-completion/src/completions/expr.rs35
-rw-r--r--crates/ide-completion/src/render.rs41
-rw-r--r--crates/ide-completion/src/tests/pattern.rs24
-rw-r--r--crates/ide-completion/src/tests/type_pos.rs112
-rw-r--r--crates/ide-diagnostics/src/handlers/typed_hole.rs2
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs11
14 files changed, 448 insertions, 464 deletions
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<Item = Type>,
- ) -> Type {
+ pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> 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<Item = Type>,
- ) -> Type {
+ pub fn ty_with_args(self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> 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<Item = Type>,
- ) -> Type {
+ pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> 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<Item = Type>,
+ generics: impl Iterator<Item = Type>,
) -> Type {
let resolver = self.id.resolver(db.upcast());
let parent_id: Option<GenericDefId> = 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<Item = Type>,
- ) -> Type {
+ pub fn ty_with_args(&self, db: &dyn HirDatabase, generics: impl Iterator<Item = Type>) -> 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
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<i32>` 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<Type>, params: Vec<TypeTree> },
+ /// Function call (does not take self param)
+ Function { func: Function, generics: Vec<Type>, params: Vec<Expr> },
+ /// Method call (has self param)
+ Method { func: Function, generics: Vec<Type>, target: Box<Expr>, params: Vec<Expr> },
/// Enum variant construction
- Variant { variant: Variant, generics: Vec<Type>, params: Vec<TypeTree> },
+ Variant { variant: Variant, generics: Vec<Type>, params: Vec<Expr> },
/// Struct construction
- Struct { strukt: Struct, generics: Vec<Type>, params: Vec<TypeTree> },
+ Struct { strukt: Struct, generics: Vec<Type>, params: Vec<Expr> },
/// Struct field access
- Field { type_tree: Box<TypeTree>, field: Field },
+ Field { expr: Box<Expr>, field: Field },
/// Passing type as reference (with `&`)
- Reference(Box<TypeTree>),
+ Reference(Box<Expr>),
/// 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(", ");
+ None => {
+ let fn_name = mod_item_path_str(sema_scope, &ModuleDef::Function(*func));
+ format!("{fn_name}({args})",)
+ }
+ }
+ }
+ Expr::Method { func, target, params, .. } => {
+ if target.contains_many_in_illegal_pos() {
+ return many_formatter(&target.ty(db));
+ }
- 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})",)
+ 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})"),
}
}
- TypeTree::Variant { variant, generics, params } => {
+ 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()
+ /// &macro!()
+ /// ```
+ 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<TypeTree>),
- Many(Type),
+enum AlternativeExprs {
+ /// There are few trees, so we keep track of them all
+ Few(FxHashSet<Expr>),
+ /// There are too many trees to keep track of
+ Many,
}
-impl AlternativeTrees {
- pub fn new(
- threshold: usize,
- ty: Type,
- trees: impl Iterator<Item = TypeTree>,
- ) -> 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<Item = Expr>) -> AlternativeExprs {
+ let mut it = AlternativeExprs::Few(Default::default());
+ it.extend_with_threshold(threshold, exprs);
it
}
- pub fn trees(&self) -> Vec<TypeTree> {
+ /// 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<Expr> {
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<Item = TypeTree>,
- ) {
+ /// 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<Item = Expr>) {
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<Type, AlternativeTrees>,
+ /// All the `Expr`s in "value" produce the type of "key"
+ data: FxHashMap<Type, AlternativeExprs>,
/// New types reached since last query by the `NewTypesKey`
new_types: FxHashMap<NewTypesKey, Vec<Type>>,
/// ScopeDefs that are not interesting any more
@@ -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<Vec<TypeTree>> {
+ /// Find all `Expr`s that unify with the `ty`
+ fn find(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
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<Vec<TypeTree>> {
+ /// 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
.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<i32> and FxHashSet<i32> both unify with Iterator<Item = i32>,
/// but they clearly do not unify themselves.
- fn insert(&mut self, ty: Type, trees: impl Iterator<Item = TypeTree>) {
+ 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, 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<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree> {
+pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
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<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree>
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<TypeTree> = tactics::trivial(&ctx, &defs, &mut lookup).collect();
+ let mut solutions: Vec<Expr> = 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<DB: HirDatabase>(ctx: TermSearchCtx<'_, DB>) -> Vec<TypeTree>
}
}
- 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<ScopeDef>,
lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = TypeTree> + 'a {
+) -> impl Iterator<Item = Expr> + '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<ScopeDef>,
lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = TypeTree> + 'a {
+) -> impl Iterator<Item = Expr> + '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<TypeTree>)> {
- let generics = GenericDef::from(variant.parent_enum(db));
-
- // Ignore unstable variants
+ ) -> Vec<(Type, Vec<Expr>)> {
+ // 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<Vec<TypeTree>> = variant
+ let param_exprs: Vec<Vec<Expr>> = 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::<Option<_>>()?;
// Note that we need special case for 0 param constructors because of multi cartesian
// product
- let variant_trees: Vec<TypeTree> = if param_trees.is_empty() {
- vec![TypeTree::Variant {
- variant,
- generics: generics.clone(),
- params: Vec::new(),
- }]
+ let variant_exprs: Vec<Expr> = if param_exprs.is_empty() {
+ vec![Expr::Variant { variant, generics: generics.clone(), params: Vec::new() }]
} else {
- param_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<TypeTree>)> = enum_
+ let exprs: Vec<(Type, Vec<Expr>)> = 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<Vec<TypeTree>> = fileds
+ let param_exprs: Vec<Vec<Expr>> = fileds
.into_iter()
.map(|field| lookup.find(db, &field.ty(db)))
.collect::<Option<_>>()?;
// Note that we need special case for 0 param constructors because of multi cartesian
// product
- let struct_trees: Vec<TypeTree> = if param_trees.is_empty() {
- vec![TypeTree::Struct { strukt: *it, generics, params: Vec::new() }]
+ let struct_exprs: Vec<Expr> = 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<ScopeDef>,
lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = TypeTree> + 'a {
+) -> impl Iterator<Item = Expr> + '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<Vec<TypeTree>> = it
+ let param_exprs: Vec<Vec<Expr>> = 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<TypeTree> = if param_trees.is_empty() {
- vec![TypeTree::Function { func: *it, generics, params: Vec::new() }]
+ let fn_exprs: Vec<Expr> = 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<ScopeDef>,
lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = TypeTree> + 'a {
+) -> impl Iterator<Item = Expr> + '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<Vec<TypeTree>> = it
+ let param_exprs: Vec<Vec<Expr>> = 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::<Option<_>>()?;
- let fn_trees: Vec<TypeTree> = std::iter::once(target_type_trees)
- .chain(param_trees.into_iter())
+ let fn_exprs: Vec<Expr> = 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<ScopeDef>,
lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = TypeTree> + 'a {
+) -> impl Iterator<Item = Expr> + '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<ScopeDef>,
lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = TypeTree> + 'a {
+) -> impl Iterator<Item = Expr> + '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<ScopeDef>,
lookup: &'a mut LookupTable,
-) -> impl Iterator<Item = TypeTree> + 'a {
+) -> impl Iterator<Item = Expr> + '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<Vec<TypeTree>> = it
+ let param_exprs: Vec<Vec<Expr>> = 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<TypeTree> = if param_trees.is_empty() {
- vec![TypeTree::Function { func: it, generics, params: Vec::new() }]
+ let fn_exprs: Vec<Expr> = 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<Builder> {
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<u8>) {
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<Vec<A
let ctx =
TermSearchCtx { sema, scope: &scope, goal: d.expected.clone(), config: Default::default() };
- let paths = term_search(ctx);
+ let paths = term_search(&ctx);
let mut assists = vec![];
let mut formatter = |_: &hir::Type| String::from("_");
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index ad16e360da..9d1ef728a4 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -415,7 +415,7 @@ impl flags::AnalysisStats {
..Default::default()
},
};
- let found_terms = hir::term_search::term_search(ctx);
+ let found_terms = hir::term_search::term_search(&ctx);
if found_terms.is_empty() {
acc.tail_expr_no_term += 1;
@@ -428,7 +428,8 @@ impl flags::AnalysisStats {
s.chars().into_iter().filter(|c| !c.is_whitespace()).collect()
}
- let mut formatter = |_: &hir::Type| syntax::ast::make::ext::expr_todo().to_string();
+ let todo = syntax::ast::make::ext::expr_todo().to_string();
+ let mut formatter = |_: &hir::Type| todo.clone();
let mut syntax_hit_found = false;
for term in found_terms {
let generated = term.gen_source_code(&scope, &mut formatter);
@@ -449,8 +450,10 @@ impl flags::AnalysisStats {
if let Some(mut err_idx) = err.find("error[E") {
err_idx += 7;
let err_code = &err[err_idx..err_idx + 4];
- if err_code == "0282" {
- continue; // Byproduct of testing method
+ match err_code {
+ "0282" => 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);