Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/term_search/tactics.rs')
-rw-r--r--crates/hir/src/term_search/tactics.rs252
1 files changed, 69 insertions, 183 deletions
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs
index b738e6af77..d64f60cb94 100644
--- a/crates/hir/src/term_search/tactics.rs
+++ b/crates/hir/src/term_search/tactics.rs
@@ -17,11 +17,11 @@ use itertools::Itertools;
use rustc_hash::FxHashSet;
use crate::{
- Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type,
- TypeParam, Variant,
+ Adt, AssocItem, GenericDef, GenericParam, HasVisibility, Impl, ModuleDef, ScopeDef, Type,
+ TypeParam,
};
-use crate::term_search::{Expr, TermSearchConfig};
+use crate::term_search::Expr;
use super::{LookupTable, NewTypesKey, TermSearchCtx};
@@ -151,212 +151,101 @@ pub(super) fn assoc_const<'a, DB: HirDatabase>(
/// * `should_continue` - Function that indicates when to stop iterating
pub(super) fn data_constructor<'a, DB: HirDatabase>(
ctx: &'a TermSearchCtx<'a, DB>,
- defs: &'a FxHashSet<ScopeDef>,
+ _defs: &'a FxHashSet<ScopeDef>,
lookup: &'a mut LookupTable,
should_continue: &'a dyn std::ops::Fn() -> bool,
) -> impl Iterator<Item = Expr> + 'a {
let db = ctx.sema.db;
let module = ctx.scope.module();
- fn variant_helper(
- db: &dyn HirDatabase,
- lookup: &mut LookupTable,
- should_continue: &dyn std::ops::Fn() -> bool,
- parent_enum: Enum,
- variant: Variant,
- config: &TermSearchConfig,
- ) -> Vec<(Type, Vec<Expr>)> {
- // Ignore unstable
- if variant.is_unstable(db) {
- return Vec::new();
- }
-
- let generics = GenericDef::from(variant.parent_enum(db));
- let Some(type_params) = generics
- .type_or_const_params(db)
- .into_iter()
- .map(|it| it.as_type_param(db))
- .collect::<Option<Vec<TypeParam>>>()
- else {
- // Ignore enums with const generics
- return Vec::new();
- };
-
- // We currently do not check lifetime bounds so ignore all types that have something to do
- // with them
- if !generics.lifetime_params(db).is_empty() {
- return Vec::new();
- }
+ lookup
+ .types_wishlist()
+ .clone()
+ .into_iter()
+ .chain(iter::once(ctx.goal.clone()))
+ .filter_map(|ty| ty.as_adt().map(|adt| (adt, ty)))
+ .filter(|_| should_continue())
+ .filter_map(move |(adt, ty)| match adt {
+ Adt::Struct(strukt) => {
+ // Ignore unstable or not visible
+ if strukt.is_unstable(db) || !strukt.is_visible_from(db, module) {
+ return None;
+ }
- // Only account for stable type parameters for now, unstable params can be default
- // tho, for example in `Box<T, #[unstable] A: Allocator>`
- if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
- return Vec::new();
- }
+ let generics = GenericDef::from(strukt);
- let non_default_type_params_len =
- type_params.iter().filter(|it| it.default(db).is_none()).count();
-
- let enum_ty_shallow = Adt::from(parent_enum).ty(db);
- let generic_params = lookup
- .types_wishlist()
- .clone()
- .into_iter()
- .filter(|ty| ty.could_unify_with(db, &enum_ty_shallow))
- .map(|it| it.type_arguments().collect::<Vec<Type>>())
- .chain((non_default_type_params_len == 0).then_some(Vec::new()));
-
- generic_params
- .filter(|_| should_continue())
- .filter_map(move |generics| {
- // Insert default type params
- let mut g = generics.into_iter();
- let generics: Vec<_> = type_params
- .iter()
- .map(|it| it.default(db).or_else(|| g.next()))
- .collect::<Option<_>>()?;
+ // We currently do not check lifetime bounds so ignore all types that have something to do
+ // with them
+ if !generics.lifetime_params(db).is_empty() {
+ return None;
+ }
- let enum_ty = Adt::from(parent_enum).ty_with_args(db, generics.iter().cloned());
+ if ty.contains_unknown() {
+ return None;
+ }
// Ignore types that have something to do with lifetimes
- if config.enable_borrowcheck && enum_ty.contains_reference(db) {
+ if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
return None;
}
+ let fields = strukt.fields(db);
+ // Check if all fields are visible, otherwise we cannot fill them
+ if fields.iter().any(|it| !it.is_visible_from(db, module)) {
+ return None;
+ }
+
+ let generics: Vec<_> = ty.type_arguments().collect();
// Early exit if some param cannot be filled from lookup
- let param_exprs: Vec<Vec<Expr>> = variant
- .fields(db)
+ let param_exprs: Vec<Vec<Expr>> = fields
.into_iter()
.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_exprs: Vec<Expr> = if param_exprs.is_empty() {
- vec![Expr::Variant { variant, generics, params: Vec::new() }]
+ let exprs: Vec<Expr> = if param_exprs.is_empty() {
+ vec![Expr::Struct { strukt, generics, params: Vec::new() }]
} else {
param_exprs
.into_iter()
.multi_cartesian_product()
- .map(|params| Expr::Variant { variant, generics: generics.clone(), params })
+ .map(|params| Expr::Struct { strukt, generics: generics.clone(), params })
.collect()
};
- lookup.insert(enum_ty.clone(), variant_exprs.iter().cloned());
-
- Some((enum_ty, variant_exprs))
- })
- .collect()
- }
- defs.iter()
- .filter_map(move |def| match def {
- ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
- let variant_exprs = variant_helper(
- db,
- lookup,
- should_continue,
- it.parent_enum(db),
- *it,
- &ctx.config,
- );
- if variant_exprs.is_empty() {
- return None;
- }
- if GenericDef::from(it.parent_enum(db))
- .type_or_const_params(db)
- .into_iter()
- .filter_map(|it| it.as_type_param(db))
- .all(|it| it.default(db).is_some())
- {
- lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Variant(*it)));
- }
- Some(variant_exprs)
- }
- ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(enum_))) => {
- let exprs: Vec<(Type, Vec<Expr>)> = enum_
- .variants(db)
- .into_iter()
- .flat_map(|it| {
- variant_helper(db, lookup, should_continue, *enum_, it, &ctx.config)
- })
- .collect();
-
- if exprs.is_empty() {
- return None;
- }
-
- if GenericDef::from(*enum_)
- .type_or_const_params(db)
- .into_iter()
- .filter_map(|it| it.as_type_param(db))
- .all(|it| it.default(db).is_some())
- {
- lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(*enum_))));
- }
- Some(exprs)
+ lookup.insert(ty.clone(), exprs.iter().cloned());
+ Some((ty, exprs))
}
- ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => {
- // Ignore unstable and not visible
- if it.is_unstable(db) || !it.is_visible_from(db, module) {
+ Adt::Enum(enum_) => {
+ // Ignore unstable or not visible
+ if enum_.is_unstable(db) || !enum_.is_visible_from(db, module) {
return None;
}
- let generics = GenericDef::from(*it);
-
- // Ignore const params for now
- let type_params = generics
- .type_or_const_params(db)
- .into_iter()
- .map(|it| it.as_type_param(db))
- .collect::<Option<Vec<TypeParam>>>()?;
-
+ let generics = GenericDef::from(enum_);
// We currently do not check lifetime bounds so ignore all types that have something to do
// with them
if !generics.lifetime_params(db).is_empty() {
return None;
}
- // Only account for stable type parameters for now, unstable params can be default
- // tho, for example in `Box<T, #[unstable] A: Allocator>`
- if type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none()) {
+ if ty.contains_unknown() {
return None;
}
- let non_default_type_params_len =
- type_params.iter().filter(|it| it.default(db).is_none()).count();
+ // Ignore types that have something to do with lifetimes
+ if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
+ return None;
+ }
- let struct_ty_shallow = Adt::from(*it).ty(db);
- let generic_params = lookup
- .types_wishlist()
- .clone()
+ let generics: Vec<_> = ty.type_arguments().collect();
+ let exprs = enum_
+ .variants(db)
.into_iter()
- .filter(|ty| ty.could_unify_with(db, &struct_ty_shallow))
- .map(|it| it.type_arguments().collect::<Vec<Type>>())
- .chain((non_default_type_params_len == 0).then_some(Vec::new()));
-
- let exprs = generic_params
- .filter(|_| should_continue())
- .filter_map(|generics| {
- // Insert default type params
- let mut g = generics.into_iter();
- let generics: Vec<_> = type_params
- .iter()
- .map(|it| it.default(db).or_else(|| g.next()))
- .collect::<Option<_>>()?;
-
- let struct_ty = Adt::from(*it).ty_with_args(db, generics.iter().cloned());
-
- // Ignore types that have something to do with lifetimes
- if ctx.config.enable_borrowcheck && struct_ty.contains_reference(db) {
- return None;
- }
- let fields = it.fields(db);
- // Check if all fields are visible, otherwise we cannot fill them
- if fields.iter().any(|it| !it.is_visible_from(db, module)) {
- return None;
- }
-
+ .filter_map(|variant| {
// Early exit if some param cannot be filled from lookup
- let param_exprs: Vec<Vec<Expr>> = fields
+ let param_exprs: Vec<Vec<Expr>> = variant
+ .fields(db)
.into_iter()
.map(|field| {
lookup.find(db, &field.ty_with_args(db, generics.iter().cloned()))
@@ -365,36 +254,33 @@ pub(super) fn data_constructor<'a, DB: HirDatabase>(
// Note that we need special case for 0 param constructors because of multi cartesian
// product
- let struct_exprs: Vec<Expr> = if param_exprs.is_empty() {
- vec![Expr::Struct { strukt: *it, generics, 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_exprs
.into_iter()
.multi_cartesian_product()
- .map(|params| Expr::Struct {
- strukt: *it,
+ .map(|params| Expr::Variant {
+ variant,
generics: generics.clone(),
params,
})
.collect()
};
-
- if non_default_type_params_len == 0 {
- // Fulfilled only if there are no generic parameters
- lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(
- Adt::Struct(*it),
- )));
- }
- lookup.insert(struct_ty.clone(), struct_exprs.iter().cloned());
-
- Some((struct_ty, struct_exprs))
+ lookup.insert(ty.clone(), variant_exprs.iter().cloned());
+ Some(variant_exprs)
})
+ .flatten()
.collect();
- Some(exprs)
+
+ Some((ty, exprs))
}
- _ => None,
+ Adt::Union(_) => None,
})
- .flatten()
.filter_map(|(ty, exprs)| ty.could_unify_with_deeply(db, &ctx.goal).then_some(exprs))
.flatten()
}