mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +00:00
Expand target for autocompletion
This commit is contained in:
parent
a946970e2d
commit
0b838e3e23
14 changed files with 450 additions and 466 deletions
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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()
|
||||
/// ¯o!()
|
||||
/// ```
|
||||
fn contains_many_in_illegal_pos(&self) -> bool {
|
||||
match self {
|
||||
Expr::Method { target, .. } => target.contains_many_in_illegal_pos(),
|
||||
Expr::Field { expr, .. } => expr.contains_many_in_illegal_pos(),
|
||||
Expr::Reference(target) => target.is_many(),
|
||||
Expr::Many(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to check if outermost type tree is `Expr::Many` variant
|
||||
pub fn is_many(&self) -> bool {
|
||||
matches!(self, Expr::Many(_))
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 []
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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("_");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue