Expand target for autocompletion

This commit is contained in:
Tavo Annus 2024-01-06 15:17:16 +02:00
parent a946970e2d
commit 0b838e3e23
14 changed files with 450 additions and 466 deletions

View file

@ -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.

View file

@ -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)
}

View file

@ -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());

View file

@ -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(", ");
match func.as_assoc_item(db).map(|it| it.container(db)) {
Some(container) => {
let container_name = match container {
crate::AssocItemContainer::Trait(trait_) => {
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_))
}
crate::AssocItemContainer::Impl(imp) => {
let self_ty = imp.self_ty(db);
// Should it be guaranteed that `mod_item_path` always exists?
match self_ty
.as_adt()
.and_then(|adt| mod_item_path(sema_scope, &adt.into()))
{
Some(path) => {
path.display(sema_scope.db.upcast()).to_string()
}
None => self_ty.display(db).to_string(),
}
}
};
let fn_name = func.name(db).display(db.upcast()).to_string();
format!("{container_name}::{fn_name}({args})",)
}
None => {
let fn_name =
mod_item_path_str(sema_scope, &ModuleDef::Function(*func));
format!("{fn_name}({args})",)
}
None => {
let fn_name = mod_item_path_str(sema_scope, &ModuleDef::Function(*func));
format!("{fn_name}({args})",)
}
}
}
TypeTree::Variant { variant, generics, params } => {
Expr::Method { func, target, params, .. } => {
if target.contains_many_in_illegal_pos() {
return many_formatter(&target.ty(db));
}
let func_name = func.name(db).display(db.upcast()).to_string();
let self_param = func.self_param(db).unwrap();
let target = target.gen_source_code(sema_scope, many_formatter);
let args =
params.iter().map(|f| f.gen_source_code(sema_scope, many_formatter)).join(", ");
match func.as_assoc_item(db).and_then(|it| it.containing_trait_or_trait_impl(db)) {
Some(trait_) => {
let trait_name = mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_));
let target = match self_param.access(db) {
crate::Access::Shared => format!("&{target}"),
crate::Access::Exclusive => format!("&mut {target}"),
crate::Access::Owned => target,
};
match args.is_empty() {
true => format!("{trait_name}::{func_name}({target})",),
false => format!("{trait_name}::{func_name}({target}, {args})",),
}
}
None => format!("{target}.{func_name}({args})"),
}
}
Expr::Variant { variant, generics, params } => {
let generics = non_default_generics(db, (*variant).into(), generics);
let generics_str = match generics.is_empty() {
true => String::new(),
@ -236,7 +230,7 @@ impl TypeTree {
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Variant(*variant));
format!("{prefix}{inner}")
}
TypeTree::Struct { strukt, generics, params } => {
Expr::Struct { strukt, generics, params } => {
let generics = non_default_generics(db, (*strukt).into(), generics);
let inner = match strukt.kind(db) {
StructKind::Tuple => {
@ -274,16 +268,24 @@ impl TypeTree {
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)));
format!("{prefix}{inner}")
}
TypeTree::Field { type_tree, field } => {
let strukt = type_tree.gen_source_code(sema_scope, many_formatter);
Expr::Field { expr, field } => {
if expr.contains_many_in_illegal_pos() {
return many_formatter(&expr.ty(db));
}
let strukt = expr.gen_source_code(sema_scope, many_formatter);
let field = field.name(db).display(db.upcast()).to_string();
format!("{strukt}.{field}")
}
TypeTree::Reference(type_tree) => {
let inner = type_tree.gen_source_code(sema_scope, many_formatter);
Expr::Reference(expr) => {
if expr.contains_many_in_illegal_pos() {
return many_formatter(&expr.ty(db));
}
let inner = expr.gen_source_code(sema_scope, many_formatter);
format!("&{inner}")
}
TypeTree::Many(ty) => many_formatter(ty),
Expr::Many(ty) => many_formatter(ty),
}
}
@ -292,29 +294,27 @@ impl TypeTree {
/// Same as getting the type of root node
pub fn ty(&self, db: &dyn HirDatabase) -> Type {
match self {
TypeTree::Const(it) => it.ty(db),
TypeTree::Static(it) => it.ty(db),
TypeTree::Local(it) => it.ty(db),
TypeTree::ConstParam(it) => it.ty(db),
TypeTree::FamousType { ty, .. } => ty.clone(),
TypeTree::Function { func, generics, params } => match func.has_self_param(db) {
true => func.ret_type_with_generics(
db,
params[0].ty(db).type_arguments().chain(generics.iter().cloned()),
),
false => func.ret_type_with_generics(db, generics.iter().cloned()),
},
TypeTree::Variant { variant, generics, .. } => {
variant.parent_enum(db).ty_with_generics(db, generics.iter().cloned())
Expr::Const(it) => it.ty(db),
Expr::Static(it) => it.ty(db),
Expr::Local(it) => it.ty(db),
Expr::ConstParam(it) => it.ty(db),
Expr::FamousType { ty, .. } => ty.clone(),
Expr::Function { func, generics, .. } => {
func.ret_type_with_args(db, generics.iter().cloned())
}
TypeTree::Struct { strukt, generics, .. } => {
strukt.ty_with_generics(db, generics.iter().cloned())
Expr::Method { func, generics, target, .. } => func.ret_type_with_args(
db,
target.ty(db).type_arguments().chain(generics.iter().cloned()),
),
Expr::Variant { variant, generics, .. } => {
variant.parent_enum(db).ty_with_args(db, generics.iter().cloned())
}
TypeTree::Field { type_tree, field } => {
field.ty_with_generics(db, type_tree.ty(db).type_arguments())
Expr::Struct { strukt, generics, .. } => {
strukt.ty_with_args(db, generics.iter().cloned())
}
TypeTree::Reference(it) => it.ty(db),
TypeTree::Many(ty) => ty.clone(),
Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()),
Expr::Reference(it) => it.ty(db),
Expr::Many(ty) => ty.clone(),
}
}
@ -323,7 +323,7 @@ impl TypeTree {
let mut res = Vec::new();
match self {
TypeTree::Function { func, params, .. } => {
Expr::Method { func, params, .. } => {
res.extend(params.iter().flat_map(|it| it.traits_used(db)));
if let Some(it) = func.as_assoc_item(db) {
if let Some(it) = it.containing_trait_or_trait_impl(db) {
@ -336,4 +336,28 @@ impl TypeTree {
res
}
/// Check in the tree contains `Expr::Many` variant in illegal place to insert `todo`,
/// `unimplemented` or similar macro
///
/// Some examples are following
/// ```no_compile
/// macro!().foo
/// macro!().bar()
/// &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(_))
}
}

View file

@ -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()
}

View file

@ -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()
}

View file

@ -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;

View file

@ -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);

View file

@ -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),
}
}
}
}

View file

@ -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 []

View file

@ -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
"#]],
);
}

View file

@ -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
"#]],
);
}

View file

@ -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("_");

View file

@ -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);