mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-10 15:14:32 +00:00
Run data_constructor
tactic only backwards
This commit is contained in:
parent
67f7eb505e
commit
957325a5fe
4 changed files with 93 additions and 208 deletions
|
@ -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,163 +151,27 @@ 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();
|
||||
}
|
||||
|
||||
// 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 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<_>>()?;
|
||||
|
||||
let enum_ty = Adt::from(parent_enum).ty_with_args(db, generics.iter().cloned());
|
||||
|
||||
// Ignore types that have something to do with lifetimes
|
||||
if config.enable_borrowcheck && enum_ty.contains_reference(db) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Early exit if some param cannot be filled from lookup
|
||||
let param_exprs: Vec<Vec<Expr>> = variant
|
||||
.fields(db)
|
||||
.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() }]
|
||||
} else {
|
||||
param_exprs
|
||||
.into_iter()
|
||||
.multi_cartesian_product()
|
||||
.map(|params| Expr::Variant { variant, 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)
|
||||
}
|
||||
ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => {
|
||||
// Ignore unstable and not visible
|
||||
if it.is_unstable(db) || !it.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(strukt);
|
||||
|
||||
// We currently do not check lifetime bounds so ignore all types that have something to do
|
||||
// with them
|
||||
|
@ -315,48 +179,73 @@ pub(super) fn data_constructor<'a, DB: HirDatabase>(
|
|||
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 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 struct_ty_shallow = Adt::from(*it).ty(db);
|
||||
let generic_params = lookup
|
||||
.types_wishlist()
|
||||
.clone()
|
||||
let generics: Vec<_> = ty.type_arguments().collect();
|
||||
|
||||
// Early exit if some param cannot be filled from lookup
|
||||
let param_exprs: Vec<Vec<Expr>> = fields
|
||||
.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()));
|
||||
.map(|field| lookup.find(db, &field.ty_with_args(db, generics.iter().cloned())))
|
||||
.collect::<Option<_>>()?;
|
||||
|
||||
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<_>>()?;
|
||||
// Note that we need special case for 0 param constructors because of multi cartesian
|
||||
// product
|
||||
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::Struct { strukt, generics: generics.clone(), params })
|
||||
.collect()
|
||||
};
|
||||
|
||||
let struct_ty = Adt::from(*it).ty_with_args(db, generics.iter().cloned());
|
||||
lookup.insert(ty.clone(), exprs.iter().cloned());
|
||||
Some((ty, exprs))
|
||||
}
|
||||
Adt::Enum(enum_) => {
|
||||
// Ignore unstable or not visible
|
||||
if enum_.is_unstable(db) || !enum_.is_visible_from(db, module) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
if ty.contains_unknown() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Ignore types that have something to do with lifetimes
|
||||
if ctx.config.enable_borrowcheck && ty.contains_reference(db) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let generics: Vec<_> = ty.type_arguments().collect();
|
||||
let exprs = enum_
|
||||
.variants(db)
|
||||
.into_iter()
|
||||
.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()
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ fn f() { let a = A { x: 1, y: true }; let b: i32 = a.x; }"#,
|
|||
term_search,
|
||||
r#"//- minicore: todo, unimplemented, option
|
||||
fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
|
||||
r#"fn f() { let a: i32 = 1; let b: Option<i32> = None; }"#,
|
||||
r#"fn f() { let a: i32 = 1; let b: Option<i32> = Some(a); }"#,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -764,6 +764,7 @@ fn main() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
st dep::test_mod_b::Struct {…} [type_could_unify]
|
||||
ex dep::test_mod_b::Struct { } [type_could_unify]
|
||||
st Struct (use dep::test_mod_b::Struct) [type_could_unify+requires_import]
|
||||
fn main() []
|
||||
fn test(…) []
|
||||
|
@ -839,6 +840,7 @@ fn main() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
ev dep::test_mod_b::Enum::variant [type_could_unify]
|
||||
ex dep::test_mod_b::Enum::variant [type_could_unify]
|
||||
en Enum (use dep::test_mod_b::Enum) [type_could_unify+requires_import]
|
||||
fn main() []
|
||||
fn test(…) []
|
||||
|
@ -876,6 +878,7 @@ fn main() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
ev dep::test_mod_b::Enum::Variant [type_could_unify]
|
||||
ex dep::test_mod_b::Enum::Variant [type_could_unify]
|
||||
fn main() []
|
||||
fn test(…) []
|
||||
md dep []
|
||||
|
@ -1839,7 +1842,6 @@ fn f() { A { bar: b$0 }; }
|
|||
fn baz() [type]
|
||||
ex baz() [type]
|
||||
ex bar() [type]
|
||||
ex A { bar: ... }.bar [type]
|
||||
st A []
|
||||
fn f() []
|
||||
"#]],
|
||||
|
@ -1978,7 +1980,6 @@ fn main() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
ex core::ops::Deref::deref(&t) (use core::ops::Deref) [type_could_unify]
|
||||
ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
|
||||
lc m [local]
|
||||
lc t [local]
|
||||
lc &t [type+local]
|
||||
|
@ -2028,7 +2029,6 @@ fn main() {
|
|||
"#,
|
||||
expect![[r#"
|
||||
ex core::ops::DerefMut::deref_mut(&mut t) (use core::ops::DerefMut) [type_could_unify]
|
||||
ex core::ops::DerefMut::deref_mut(&mut T(S)) (use core::ops::DerefMut) [type_could_unify]
|
||||
lc m [local]
|
||||
lc t [local]
|
||||
lc &mut t [type+local]
|
||||
|
@ -2132,7 +2132,6 @@ fn main() {
|
|||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
ex core::ops::Deref::deref(&T(S)) (use core::ops::Deref) [type_could_unify]
|
||||
ex core::ops::Deref::deref(&bar()) (use core::ops::Deref) [type_could_unify]
|
||||
st S []
|
||||
st &S [type]
|
||||
|
|
|
@ -276,7 +276,7 @@ impl Foo for Baz {
|
|||
}
|
||||
fn asd() -> Bar {
|
||||
let a = Baz;
|
||||
Foo::foo(_)
|
||||
Foo::foo(a)
|
||||
}
|
||||
",
|
||||
);
|
||||
|
@ -365,7 +365,7 @@ impl Foo for A {
|
|||
}
|
||||
fn main() {
|
||||
let a = A;
|
||||
let c: Bar = Foo::foo(_);
|
||||
let c: Bar = Foo::foo(&a);
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue