Add static method tactic
This commit is contained in:
parent
35eb0dbbc0
commit
627255dd5a
@ -377,27 +377,36 @@ impl AttrsWithOwner {
|
|||||||
AttrDefId::GenericParamId(it) => match it {
|
AttrDefId::GenericParamId(it) => match it {
|
||||||
GenericParamId::ConstParamId(it) => {
|
GenericParamId::ConstParamId(it) => {
|
||||||
let src = it.parent().child_source(db);
|
let src = it.parent().child_source(db);
|
||||||
RawAttrs::from_attrs_owner(
|
match src.value.get(it.local_id()) {
|
||||||
|
Some(val) => RawAttrs::from_attrs_owner(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
src.with_value(&src.value[it.local_id()]),
|
src.with_value(val),
|
||||||
db.span_map(src.file_id).as_ref(),
|
db.span_map(src.file_id).as_ref(),
|
||||||
)
|
),
|
||||||
|
None => RawAttrs::EMPTY,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GenericParamId::TypeParamId(it) => {
|
GenericParamId::TypeParamId(it) => {
|
||||||
let src = it.parent().child_source(db);
|
let src = it.parent().child_source(db);
|
||||||
RawAttrs::from_attrs_owner(
|
match src.value.get(it.local_id()) {
|
||||||
|
Some(val) => RawAttrs::from_attrs_owner(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
src.with_value(&src.value[it.local_id()]),
|
src.with_value(val),
|
||||||
db.span_map(src.file_id).as_ref(),
|
db.span_map(src.file_id).as_ref(),
|
||||||
)
|
),
|
||||||
|
None => RawAttrs::EMPTY,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
GenericParamId::LifetimeParamId(it) => {
|
GenericParamId::LifetimeParamId(it) => {
|
||||||
let src = it.parent.child_source(db);
|
let src = it.parent.child_source(db);
|
||||||
RawAttrs::from_attrs_owner(
|
match src.value.get(it.local_id) {
|
||||||
|
Some(val) => RawAttrs::from_attrs_owner(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
src.with_value(&src.value[it.local_id]),
|
src.with_value(val),
|
||||||
db.span_map(src.file_id).as_ref(),
|
db.span_map(src.file_id).as_ref(),
|
||||||
)
|
),
|
||||||
|
None => RawAttrs::EMPTY,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
|
AttrDefId::ExternBlockId(it) => attrs_from_item_tree_loc(db, it),
|
||||||
|
@ -1187,6 +1187,10 @@ impl Struct {
|
|||||||
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
|
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
|
||||||
db.struct_data(self.id).variant_data.clone()
|
db.struct_data(self.id).variant_data.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
|
||||||
|
db.attrs(self.id.into()).is_unstable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasVisibility for Struct {
|
impl HasVisibility for Struct {
|
||||||
@ -1229,6 +1233,10 @@ impl Union {
|
|||||||
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
|
fn variant_data(self, db: &dyn HirDatabase) -> Arc<VariantData> {
|
||||||
db.union_data(self.id).variant_data.clone()
|
db.union_data(self.id).variant_data.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
|
||||||
|
db.attrs(self.id.into()).is_unstable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasVisibility for Union {
|
impl HasVisibility for Union {
|
||||||
@ -1318,6 +1326,10 @@ impl Enum {
|
|||||||
pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
|
pub fn layout(self, db: &dyn HirDatabase) -> Result<Layout, LayoutError> {
|
||||||
Adt::from(self).layout(db)
|
Adt::from(self).layout(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
|
||||||
|
db.attrs(self.id.into()).is_unstable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HasVisibility for Enum {
|
impl HasVisibility for Enum {
|
||||||
@ -1393,6 +1405,10 @@ impl Variant {
|
|||||||
_ => parent_layout,
|
_ => parent_layout,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
|
||||||
|
db.attrs(self.id.into()).is_unstable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Variants inherit visibility from the parent enum.
|
/// Variants inherit visibility from the parent enum.
|
||||||
@ -2912,7 +2928,7 @@ impl GenericDef {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
|
pub fn type_or_const_params(self, db: &dyn HirDatabase) -> Vec<TypeOrConstParam> {
|
||||||
let generics = db.generic_params(self.into());
|
let generics = db.generic_params(self.into());
|
||||||
generics
|
generics
|
||||||
.type_or_consts
|
.type_or_consts
|
||||||
@ -2922,6 +2938,40 @@ impl GenericDef {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn type_params(self, db: &dyn HirDatabase) -> Vec<TypeParam> {
|
||||||
|
let generics = db.generic_params(self.into());
|
||||||
|
generics
|
||||||
|
.type_or_consts
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(local_id, data)| match data {
|
||||||
|
hir_def::generics::TypeOrConstParamData::TypeParamData(_) => Some(TypeParam {
|
||||||
|
id: TypeParamId::from_unchecked(TypeOrConstParamId {
|
||||||
|
parent: self.into(),
|
||||||
|
local_id,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
hir_def::generics::TypeOrConstParamData::ConstParamData(_) => None,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn const_params(self, db: &dyn HirDatabase) -> Vec<ConstParam> {
|
||||||
|
let generics = db.generic_params(self.into());
|
||||||
|
generics
|
||||||
|
.type_or_consts
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(local_id, data)| match data {
|
||||||
|
hir_def::generics::TypeOrConstParamData::TypeParamData(_) => None,
|
||||||
|
hir_def::generics::TypeOrConstParamData::ConstParamData(_) => Some(ConstParam {
|
||||||
|
id: ConstParamId::from_unchecked(TypeOrConstParamId {
|
||||||
|
parent: self.into(),
|
||||||
|
local_id,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single local definition.
|
/// A single local definition.
|
||||||
@ -3284,12 +3334,16 @@ impl TypeParam {
|
|||||||
let ty = generic_arg_from_param(db, self.id.into())?;
|
let ty = generic_arg_from_param(db, self.id.into())?;
|
||||||
let resolver = self.id.parent().resolver(db.upcast());
|
let resolver = self.id.parent().resolver(db.upcast());
|
||||||
match ty.data(Interner) {
|
match ty.data(Interner) {
|
||||||
GenericArgData::Ty(it) => {
|
GenericArgData::Ty(it) if *it.kind(Interner) != TyKind::Error => {
|
||||||
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
|
Some(Type::new_with_resolver_inner(db, &resolver, it.clone()))
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_unstable(self, db: &dyn HirDatabase) -> bool {
|
||||||
|
db.attrs(GenericParamId::from(self.id).into()).is_unstable()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
@ -3773,6 +3827,50 @@ impl Type {
|
|||||||
matches!(self.ty.kind(Interner), TyKind::Ref(..))
|
matches!(self.ty.kind(Interner), TyKind::Ref(..))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains_reference(&self, db: &dyn HirDatabase) -> bool {
|
||||||
|
return go(db, self.env.krate, &self.ty);
|
||||||
|
|
||||||
|
fn go(db: &dyn HirDatabase, krate: CrateId, ty: &Ty) -> bool {
|
||||||
|
match ty.kind(Interner) {
|
||||||
|
// Reference itself
|
||||||
|
TyKind::Ref(_, _, _) => true,
|
||||||
|
|
||||||
|
// For non-phantom_data adts we check variants/fields as well as generic parameters
|
||||||
|
TyKind::Adt(adt_id, substitution)
|
||||||
|
if !db.struct_datum(krate, *adt_id).flags.phantom_data =>
|
||||||
|
{
|
||||||
|
let adt_datum = &db.struct_datum(krate, *adt_id);
|
||||||
|
let adt_datum_bound =
|
||||||
|
adt_datum.binders.clone().substitute(Interner, substitution);
|
||||||
|
adt_datum_bound
|
||||||
|
.variants
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|variant| variant.fields.into_iter())
|
||||||
|
.any(|ty| go(db, krate, &ty))
|
||||||
|
|| substitution
|
||||||
|
.iter(Interner)
|
||||||
|
.filter_map(|x| x.ty(Interner))
|
||||||
|
.any(|ty| go(db, krate, ty))
|
||||||
|
}
|
||||||
|
// And for `PhantomData<T>`, we check `T`.
|
||||||
|
TyKind::Adt(_, substitution)
|
||||||
|
| TyKind::Tuple(_, substitution)
|
||||||
|
| TyKind::OpaqueType(_, substitution)
|
||||||
|
| TyKind::AssociatedType(_, substitution)
|
||||||
|
| TyKind::FnDef(_, substitution) => substitution
|
||||||
|
.iter(Interner)
|
||||||
|
.filter_map(|x| x.ty(Interner))
|
||||||
|
.any(|ty| go(db, krate, ty)),
|
||||||
|
|
||||||
|
// For `[T]` or `*T` we check `T`
|
||||||
|
TyKind::Array(ty, _) | TyKind::Slice(ty) | TyKind::Raw(_, ty) => go(db, krate, ty),
|
||||||
|
|
||||||
|
// Consider everything else as not reference
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_reference(&self) -> Option<(Type, Mutability)> {
|
pub fn as_reference(&self) -> Option<(Type, Mutability)> {
|
||||||
let (ty, _lt, m) = self.ty.as_reference()?;
|
let (ty, _lt, m) = self.ty.as_reference()?;
|
||||||
let m = Mutability::from_mutable(matches!(m, hir_ty::Mutability::Mut));
|
let m = Mutability::from_mutable(matches!(m, hir_ty::Mutability::Mut));
|
||||||
|
@ -47,6 +47,8 @@ struct LookupTable {
|
|||||||
round_scopedef_hits: FxHashSet<ScopeDef>,
|
round_scopedef_hits: FxHashSet<ScopeDef>,
|
||||||
/// Amount of rounds since scopedef was first used.
|
/// Amount of rounds since scopedef was first used.
|
||||||
rounds_since_sopedef_hit: FxHashMap<ScopeDef, u32>,
|
rounds_since_sopedef_hit: FxHashMap<ScopeDef, u32>,
|
||||||
|
/// Types queried but not present
|
||||||
|
types_wishlist: FxHashSet<Type>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LookupTable {
|
impl LookupTable {
|
||||||
@ -149,6 +151,10 @@ impl LookupTable {
|
|||||||
fn exhausted_scopedefs(&self) -> &FxHashSet<ScopeDef> {
|
fn exhausted_scopedefs(&self) -> &FxHashSet<ScopeDef> {
|
||||||
&self.exhausted_scopedefs
|
&self.exhausted_scopedefs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_types_wishlist(&mut self) -> FxHashSet<Type> {
|
||||||
|
std::mem::take(&mut self.types_wishlist)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Term search
|
/// # Term search
|
||||||
@ -205,6 +211,7 @@ pub fn term_search<DB: HirDatabase>(
|
|||||||
solutions.extend(tactics::free_function(sema.db, &module, &defs, &mut lookup, goal));
|
solutions.extend(tactics::free_function(sema.db, &module, &defs, &mut lookup, goal));
|
||||||
solutions.extend(tactics::impl_method(sema.db, &module, &defs, &mut lookup, goal));
|
solutions.extend(tactics::impl_method(sema.db, &module, &defs, &mut lookup, goal));
|
||||||
solutions.extend(tactics::struct_projection(sema.db, &module, &defs, &mut lookup, goal));
|
solutions.extend(tactics::struct_projection(sema.db, &module, &defs, &mut lookup, goal));
|
||||||
|
solutions.extend(tactics::impl_static_method(sema.db, &module, &defs, &mut lookup, goal));
|
||||||
|
|
||||||
// Break after 1 round after successful solution
|
// Break after 1 round after successful solution
|
||||||
if solution_found {
|
if solution_found {
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
//! * `goal` - Term search target type
|
//! * `goal` - Term search target type
|
||||||
//! And they return iterator that yields type trees that unify with the `goal` type.
|
//! And they return iterator that yields type trees that unify with the `goal` type.
|
||||||
|
|
||||||
use hir_def::generics::TypeOrConstParamData;
|
use std::iter;
|
||||||
|
|
||||||
use hir_ty::db::HirDatabase;
|
use hir_ty::db::HirDatabase;
|
||||||
use hir_ty::mir::BorrowKind;
|
use hir_ty::mir::BorrowKind;
|
||||||
use hir_ty::TyBuilder;
|
use hir_ty::TyBuilder;
|
||||||
@ -16,8 +17,8 @@ use itertools::Itertools;
|
|||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Adt, AssocItem, Enum, GenericParam, HasVisibility, Impl, Module, ModuleDef, ScopeDef, Type,
|
Adt, AssocItem, Enum, GenericDef, GenericParam, HasVisibility, Impl, Module, ModuleDef,
|
||||||
Variant,
|
ScopeDef, Type, Variant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::term_search::TypeTree;
|
use crate::term_search::TypeTree;
|
||||||
@ -78,7 +79,7 @@ pub(super) fn trivial<'a>(
|
|||||||
lookup.insert(ty.clone(), std::iter::once(tt.clone()));
|
lookup.insert(ty.clone(), std::iter::once(tt.clone()));
|
||||||
|
|
||||||
// Don't suggest local references as they are not valid for return
|
// Don't suggest local references as they are not valid for return
|
||||||
if matches!(tt, TypeTree::Local(_)) && ty.is_reference() {
|
if matches!(tt, TypeTree::Local(_)) && ty.contains_reference(db) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,37 +114,67 @@ pub(super) fn type_constructor<'a>(
|
|||||||
variant: Variant,
|
variant: Variant,
|
||||||
goal: &Type,
|
goal: &Type,
|
||||||
) -> Vec<(Type, Vec<TypeTree>)> {
|
) -> Vec<(Type, Vec<TypeTree>)> {
|
||||||
let generics = db.generic_params(variant.parent_enum(db).id.into());
|
let generics = GenericDef::from(variant.parent_enum(db));
|
||||||
|
|
||||||
|
// Ignore unstable variants
|
||||||
|
if variant.is_unstable(db) {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
// Ignore enums with const generics
|
// Ignore enums with const generics
|
||||||
if generics
|
if !generics.const_params(db).is_empty() {
|
||||||
.type_or_consts
|
|
||||||
.values()
|
|
||||||
.any(|it| matches!(it, TypeOrConstParamData::ConstParamData(_)))
|
|
||||||
{
|
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We currently do not check lifetime bounds so ignore all types that have something to do
|
// We currently do not check lifetime bounds so ignore all types that have something to do
|
||||||
// with them
|
// with them
|
||||||
if !generics.lifetimes.is_empty() {
|
if !generics.lifetime_params(db).is_empty() {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only account for stable type parameters for now
|
||||||
|
let type_params = generics.type_params(db);
|
||||||
|
|
||||||
|
// 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 generic_params = lookup
|
let generic_params = lookup
|
||||||
.iter_types()
|
.iter_types()
|
||||||
.collect::<Vec<_>>() // Force take ownership
|
.collect::<Vec<_>>() // Force take ownership
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.permutations(generics.type_or_consts.len());
|
.permutations(non_default_type_params_len);
|
||||||
|
|
||||||
generic_params
|
generic_params
|
||||||
.filter_map(|generics| {
|
.filter_map(|generics| {
|
||||||
|
// Insert default type params
|
||||||
|
let mut g = generics.into_iter();
|
||||||
|
let generics: Vec<_> = type_params
|
||||||
|
.iter()
|
||||||
|
.map(|it| match it.default(db) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => g.next().expect("Missing type param"),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let enum_ty = parent_enum.ty_with_generics(db, generics.iter().cloned());
|
let enum_ty = parent_enum.ty_with_generics(db, generics.iter().cloned());
|
||||||
|
|
||||||
|
// Allow types with generics only if they take us straight to goal for
|
||||||
|
// performance reasons
|
||||||
if !generics.is_empty() && !enum_ty.could_unify_with_deeply(db, goal) {
|
if !generics.is_empty() && !enum_ty.could_unify_with_deeply(db, goal) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ignore types that have something to do with lifetimes
|
||||||
|
if enum_ty.contains_reference(db) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// Early exit if some param cannot be filled from lookup
|
// Early exit if some param cannot be filled from lookup
|
||||||
let param_trees: Vec<Vec<TypeTree>> = variant
|
let param_trees: Vec<Vec<TypeTree>> = variant
|
||||||
.fields(db)
|
.fields(db)
|
||||||
@ -203,33 +234,64 @@ pub(super) fn type_constructor<'a>(
|
|||||||
Some(trees)
|
Some(trees)
|
||||||
}
|
}
|
||||||
ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => {
|
ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(it))) => {
|
||||||
let generics = db.generic_params(it.id.into());
|
// Ignore unstable
|
||||||
|
if it.is_unstable(db) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let generics = GenericDef::from(*it);
|
||||||
|
|
||||||
// Ignore enums with const generics
|
// Ignore enums with const generics
|
||||||
if generics
|
if !generics.const_params(db).is_empty() {
|
||||||
.type_or_consts
|
|
||||||
.values()
|
|
||||||
.any(|it| matches!(it, TypeOrConstParamData::ConstParamData(_)))
|
|
||||||
{
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We currently do not check lifetime bounds so ignore all types that have something to do
|
// We currently do not check lifetime bounds so ignore all types that have something to do
|
||||||
// with them
|
// with them
|
||||||
if !generics.lifetimes.is_empty() {
|
if !generics.lifetime_params(db).is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let type_params = generics.type_params(db);
|
||||||
|
|
||||||
|
// 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 None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let non_default_type_params_len =
|
||||||
|
type_params.iter().filter(|it| it.default(db).is_none()).count();
|
||||||
|
|
||||||
let generic_params = lookup
|
let generic_params = lookup
|
||||||
.iter_types()
|
.iter_types()
|
||||||
.collect::<Vec<_>>() // Force take ownership
|
.collect::<Vec<_>>() // Force take ownership
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.permutations(generics.type_or_consts.len());
|
.permutations(non_default_type_params_len);
|
||||||
|
|
||||||
let trees = generic_params
|
let trees = generic_params
|
||||||
.filter_map(|generics| {
|
.filter_map(|generics| {
|
||||||
|
// Insert default type params
|
||||||
|
let mut g = generics.into_iter();
|
||||||
|
let generics: Vec<_> = type_params
|
||||||
|
.iter()
|
||||||
|
.map(|it| match it.default(db) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
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_generics(db, generics.iter().cloned());
|
||||||
if !generics.is_empty() && !struct_ty.could_unify_with_deeply(db, goal) {
|
|
||||||
|
// Allow types with generics only if they take us straight to goal for
|
||||||
|
// performance reasons
|
||||||
|
if non_default_type_params_len != 0
|
||||||
|
&& struct_ty.could_unify_with_deeply(db, goal)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore types that have something to do with lifetimes
|
||||||
|
if struct_ty.contains_reference(db) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let fileds = it.fields(db);
|
let fileds = it.fields(db);
|
||||||
@ -301,20 +363,31 @@ pub(super) fn free_function<'a>(
|
|||||||
defs.iter()
|
defs.iter()
|
||||||
.filter_map(|def| match def {
|
.filter_map(|def| match def {
|
||||||
ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
|
ScopeDef::ModuleDef(ModuleDef::Function(it)) => {
|
||||||
let generics = db.generic_params(it.id.into());
|
let generics = GenericDef::from(*it);
|
||||||
|
|
||||||
// Skip functions that require const generics
|
// Skip functions that require const generics
|
||||||
if generics
|
if !generics.const_params(db).is_empty() {
|
||||||
.type_or_consts
|
|
||||||
.values()
|
|
||||||
.any(|it| matches!(it, TypeOrConstParamData::ConstParamData(_)))
|
|
||||||
{
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore bigger number of generics for now as they kill the performance
|
|
||||||
// Ignore lifetimes as we do not check them
|
// Ignore lifetimes as we do not check them
|
||||||
if generics.type_or_consts.len() > 0 || !generics.lifetimes.is_empty() {
|
if !generics.lifetime_params(db).is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let type_params = generics.type_params(db);
|
||||||
|
|
||||||
|
// 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 None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let non_default_type_params_len =
|
||||||
|
type_params.iter().filter(|it| it.default(db).is_none()).count();
|
||||||
|
|
||||||
|
// Ignore bigger number of generics for now as they kill the performance
|
||||||
|
if non_default_type_params_len > 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,16 +395,26 @@ pub(super) fn free_function<'a>(
|
|||||||
.iter_types()
|
.iter_types()
|
||||||
.collect::<Vec<_>>() // Force take ownership
|
.collect::<Vec<_>>() // Force take ownership
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.permutations(generics.type_or_consts.len());
|
.permutations(non_default_type_params_len);
|
||||||
|
|
||||||
let trees: Vec<_> = generic_params
|
let trees: Vec<_> = generic_params
|
||||||
.filter_map(|generics| {
|
.filter_map(|generics| {
|
||||||
|
// Insert default type params
|
||||||
|
let mut g = generics.into_iter();
|
||||||
|
let generics: Vec<_> = type_params
|
||||||
|
.iter()
|
||||||
|
.map(|it| match it.default(db) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => g.next().expect("Missing type param"),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let ret_ty = it.ret_type_with_generics(db, generics.iter().cloned());
|
let ret_ty = it.ret_type_with_generics(db, generics.iter().cloned());
|
||||||
// Filter out private and unsafe functions
|
// Filter out private and unsafe functions
|
||||||
if !it.is_visible_from(db, *module)
|
if !it.is_visible_from(db, *module)
|
||||||
|| it.is_unsafe_to_call(db)
|
|| it.is_unsafe_to_call(db)
|
||||||
|| it.is_unstable(db)
|
|| it.is_unstable(db)
|
||||||
|| ret_ty.is_reference()
|
|| ret_ty.contains_reference(db)
|
||||||
|| ret_ty.is_raw_ptr()
|
|| ret_ty.is_raw_ptr()
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
@ -417,24 +500,17 @@ pub(super) fn impl_method<'a>(
|
|||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.filter_map(|(imp, ty, it)| {
|
.filter_map(|(imp, ty, it)| {
|
||||||
let fn_generics = db.generic_params(it.id.into());
|
let fn_generics = GenericDef::from(it);
|
||||||
let imp_generics = db.generic_params(imp.id.into());
|
let imp_generics = GenericDef::from(imp);
|
||||||
|
|
||||||
// Ignore impl if it has const type arguments
|
// Ignore impl if it has const type arguments
|
||||||
if fn_generics
|
if !fn_generics.const_params(db).is_empty() || !imp_generics.const_params(db).is_empty()
|
||||||
.type_or_consts
|
|
||||||
.values()
|
|
||||||
.any(|it| matches!(it, TypeOrConstParamData::ConstParamData(_)))
|
|
||||||
|| imp_generics
|
|
||||||
.type_or_consts
|
|
||||||
.values()
|
|
||||||
.any(|it| matches!(it, TypeOrConstParamData::ConstParamData(_)))
|
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore all functions that have something to do with lifetimes as we don't check them
|
// Ignore all functions that have something to do with lifetimes as we don't check them
|
||||||
if !fn_generics.lifetimes.is_empty() {
|
if !fn_generics.lifetime_params(db).is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -448,8 +524,25 @@ pub(super) fn impl_method<'a>(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let imp_type_params = imp_generics.type_params(db);
|
||||||
|
let fn_type_params = fn_generics.type_params(db);
|
||||||
|
|
||||||
|
// Only account for stable type parameters for now, unstable params can be default
|
||||||
|
// tho, for example in `Box<T, #[unstable] A: Allocator>`
|
||||||
|
if imp_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
|
||||||
|
|| fn_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let non_default_type_params_len = imp_type_params
|
||||||
|
.iter()
|
||||||
|
.chain(fn_type_params.iter())
|
||||||
|
.filter(|it| it.default(db).is_none())
|
||||||
|
.count();
|
||||||
|
|
||||||
// Ignore bigger number of generics for now as they kill the performance
|
// Ignore bigger number of generics for now as they kill the performance
|
||||||
if imp_generics.type_or_consts.len() + fn_generics.type_or_consts.len() > 0 {
|
if non_default_type_params_len > 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,16 +550,27 @@ pub(super) fn impl_method<'a>(
|
|||||||
.iter_types()
|
.iter_types()
|
||||||
.collect::<Vec<_>>() // Force take ownership
|
.collect::<Vec<_>>() // Force take ownership
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.permutations(imp_generics.type_or_consts.len() + fn_generics.type_or_consts.len());
|
.permutations(non_default_type_params_len);
|
||||||
|
|
||||||
let trees: Vec<_> = generic_params
|
let trees: Vec<_> = generic_params
|
||||||
.filter_map(|generics| {
|
.filter_map(|generics| {
|
||||||
|
// Insert default type params
|
||||||
|
let mut g = generics.into_iter();
|
||||||
|
let generics: Vec<_> = imp_type_params
|
||||||
|
.iter()
|
||||||
|
.chain(fn_type_params.iter())
|
||||||
|
.map(|it| match it.default(db) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => g.next().expect("Missing type param"),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
let ret_ty = it.ret_type_with_generics(
|
let ret_ty = it.ret_type_with_generics(
|
||||||
db,
|
db,
|
||||||
ty.type_arguments().chain(generics.iter().cloned()),
|
ty.type_arguments().chain(generics.iter().cloned()),
|
||||||
);
|
);
|
||||||
// Filter out functions that return references
|
// Filter out functions that return references
|
||||||
if ret_ty.is_reference() || ret_ty.is_raw_ptr() {
|
if ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,3 +694,157 @@ pub(super) fn famous_types<'a>(
|
|||||||
})
|
})
|
||||||
.filter(|tt| tt.ty(db).could_unify_with_deeply(db, goal))
|
.filter(|tt| tt.ty(db).could_unify_with_deeply(db, goal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # Impl static method (without self type) tactic
|
||||||
|
///
|
||||||
|
/// Attempts different functions from impl blocks that take no self parameter.
|
||||||
|
///
|
||||||
|
/// Updates lookup by new types reached and returns iterator that yields
|
||||||
|
/// elements that unify with `goal`.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `db` - HIR database
|
||||||
|
/// * `module` - Module where the term search target location
|
||||||
|
/// * `defs` - Set of items in scope at term search target location
|
||||||
|
/// * `lookup` - Lookup table for types
|
||||||
|
/// * `goal` - Term search target type
|
||||||
|
pub(super) fn impl_static_method<'a>(
|
||||||
|
db: &'a dyn HirDatabase,
|
||||||
|
module: &'a Module,
|
||||||
|
_defs: &'a FxHashSet<ScopeDef>,
|
||||||
|
lookup: &'a mut LookupTable,
|
||||||
|
goal: &'a Type,
|
||||||
|
) -> impl Iterator<Item = TypeTree> + 'a {
|
||||||
|
lookup
|
||||||
|
.take_types_wishlist()
|
||||||
|
.into_iter()
|
||||||
|
.chain(iter::once(goal.clone()))
|
||||||
|
.flat_map(|ty| {
|
||||||
|
Impl::all_for_type(db, ty.clone()).into_iter().map(move |imp| (ty.clone(), imp))
|
||||||
|
})
|
||||||
|
.filter(|(_, imp)| !imp.is_unsafe(db))
|
||||||
|
.flat_map(|(ty, imp)| imp.items(db).into_iter().map(move |item| (imp, ty.clone(), item)))
|
||||||
|
.filter_map(|(imp, ty, it)| match it {
|
||||||
|
AssocItem::Function(f) => Some((imp, ty, f)),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.filter_map(|(imp, ty, it)| {
|
||||||
|
let fn_generics = GenericDef::from(it);
|
||||||
|
let imp_generics = GenericDef::from(imp);
|
||||||
|
|
||||||
|
// Ignore impl if it has const type arguments
|
||||||
|
if !fn_generics.const_params(db).is_empty() || !imp_generics.const_params(db).is_empty()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore all functions that have something to do with lifetimes as we don't check them
|
||||||
|
if !fn_generics.lifetime_params(db).is_empty()
|
||||||
|
|| !imp_generics.lifetime_params(db).is_empty()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore functions with self param
|
||||||
|
if it.has_self_param(db) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out private and unsafe functions
|
||||||
|
if !it.is_visible_from(db, *module) || it.is_unsafe_to_call(db) || it.is_unstable(db) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let imp_type_params = imp_generics.type_params(db);
|
||||||
|
let fn_type_params = fn_generics.type_params(db);
|
||||||
|
|
||||||
|
// Only account for stable type parameters for now, unstable params can be default
|
||||||
|
// tho, for example in `Box<T, #[unstable] A: Allocator>`
|
||||||
|
if imp_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
|
||||||
|
|| fn_type_params.iter().any(|it| it.is_unstable(db) && it.default(db).is_none())
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let non_default_type_params_len = imp_type_params
|
||||||
|
.iter()
|
||||||
|
.chain(fn_type_params.iter())
|
||||||
|
.filter(|it| it.default(db).is_none())
|
||||||
|
.count();
|
||||||
|
|
||||||
|
// Ignore bigger number of generics for now as they kill the performance
|
||||||
|
if non_default_type_params_len > 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let generic_params = lookup
|
||||||
|
.iter_types()
|
||||||
|
.collect::<Vec<_>>() // Force take ownership
|
||||||
|
.into_iter()
|
||||||
|
.permutations(non_default_type_params_len);
|
||||||
|
|
||||||
|
let trees: Vec<_> = generic_params
|
||||||
|
.filter_map(|generics| {
|
||||||
|
// Insert default type params
|
||||||
|
let mut g = generics.into_iter();
|
||||||
|
let generics: Vec<_> = imp_type_params
|
||||||
|
.iter()
|
||||||
|
.chain(fn_type_params.iter())
|
||||||
|
.map(|it| match it.default(db) {
|
||||||
|
Some(ty) => ty,
|
||||||
|
None => g.next().expect("Missing type param"),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let ret_ty = it.ret_type_with_generics(
|
||||||
|
db,
|
||||||
|
ty.type_arguments().chain(generics.iter().cloned()),
|
||||||
|
);
|
||||||
|
// Filter out functions that return references
|
||||||
|
if ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore functions that do not change the type
|
||||||
|
// if ty.could_unify_with_deeply(db, &ret_ty) {
|
||||||
|
// return None;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Early exit if some param cannot be filled from lookup
|
||||||
|
let param_trees: Vec<Vec<TypeTree>> = it
|
||||||
|
.params_without_self_with_generics(
|
||||||
|
db,
|
||||||
|
ty.type_arguments().chain(generics.iter().cloned()),
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
|
.map(|field| lookup.find_autoref(db, &field.ty()))
|
||||||
|
.collect::<Option<_>>()?;
|
||||||
|
|
||||||
|
// 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() }]
|
||||||
|
} else {
|
||||||
|
param_trees
|
||||||
|
.into_iter()
|
||||||
|
.multi_cartesian_product()
|
||||||
|
.take(MAX_VARIATIONS)
|
||||||
|
.map(|params| TypeTree::Function {
|
||||||
|
func: it,
|
||||||
|
generics: generics.clone(),
|
||||||
|
|
||||||
|
params,
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
lookup.insert(ret_ty.clone(), fn_trees.iter().cloned());
|
||||||
|
Some((ret_ty, fn_trees))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
Some(trees)
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.filter_map(|(ty, trees)| ty.could_unify_with_deeply(db, goal).then(|| trees))
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
@ -1,16 +1,18 @@
|
|||||||
//! Type tree for term search
|
//! Type tree for term search
|
||||||
|
|
||||||
use hir_def::find_path::PrefixKind;
|
use hir_def::find_path::PrefixKind;
|
||||||
|
use hir_expand::mod_path::ModPath;
|
||||||
use hir_ty::{db::HirDatabase, display::HirDisplay};
|
use hir_ty::{db::HirDatabase, display::HirDisplay};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Adt, AsAssocItem, Const, ConstParam, Field, Function, Local, ModuleDef, SemanticsScope, Static,
|
Adt, AsAssocItem, Const, ConstParam, Field, Function, GenericDef, Local, ModuleDef,
|
||||||
Struct, StructKind, Trait, Type, Variant,
|
SemanticsScope, Static, Struct, StructKind, Trait, Type, Variant,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Helper function to prefix items with modules when required
|
/// Helper function to get path to `ModuleDef`
|
||||||
fn mod_item_path(db: &dyn HirDatabase, sema_scope: &SemanticsScope<'_>, def: &ModuleDef) -> String {
|
fn mod_item_path(sema_scope: &SemanticsScope<'_>, def: &ModuleDef) -> Option<ModPath> {
|
||||||
|
let db = sema_scope.db;
|
||||||
// Account for locals shadowing items from module
|
// Account for locals shadowing items from module
|
||||||
let name_hit_count = def.name(db).map(|def_name| {
|
let name_hit_count = def.name(db).map(|def_name| {
|
||||||
let mut name_hit_count = 0;
|
let mut name_hit_count = 0;
|
||||||
@ -23,12 +25,45 @@ fn mod_item_path(db: &dyn HirDatabase, sema_scope: &SemanticsScope<'_>, def: &Mo
|
|||||||
});
|
});
|
||||||
|
|
||||||
let m = sema_scope.module();
|
let m = sema_scope.module();
|
||||||
let path = match name_hit_count {
|
match name_hit_count {
|
||||||
Some(0..=1) | None => m.find_use_path(db.upcast(), *def, false, true),
|
Some(0..=1) | None => m.find_use_path(db.upcast(), *def, false, true),
|
||||||
Some(_) => m.find_use_path_prefixed(db.upcast(), *def, PrefixKind::ByCrate, false, true),
|
Some(_) => m.find_use_path_prefixed(db.upcast(), *def, PrefixKind::ByCrate, false, true),
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
path.map(|it| it.display(db.upcast()).to_string()).expect("use path error")
|
/// Helper function to get path to `ModuleDef` as string
|
||||||
|
fn mod_item_path_str(sema_scope: &SemanticsScope<'_>, def: &ModuleDef) -> String {
|
||||||
|
let path = mod_item_path(sema_scope, def);
|
||||||
|
path.map(|it| it.display(sema_scope.db.upcast()).to_string()).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to get path to `Type`
|
||||||
|
fn type_path(sema_scope: &SemanticsScope<'_>, ty: &Type) -> String {
|
||||||
|
let db = sema_scope.db;
|
||||||
|
match ty.as_adt() {
|
||||||
|
Some(adt) => {
|
||||||
|
let ty_name = ty.display(db).to_string();
|
||||||
|
|
||||||
|
let mut path = mod_item_path(sema_scope, &ModuleDef::Adt(adt)).unwrap();
|
||||||
|
path.pop_segment();
|
||||||
|
let path = path.display(db.upcast()).to_string();
|
||||||
|
match path.is_empty() {
|
||||||
|
true => ty_name,
|
||||||
|
false => format!("{path}::{ty_name}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => ty.display(db).to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to filter out generic parameters that are default
|
||||||
|
fn non_default_generics(db: &dyn HirDatabase, def: GenericDef, generics: &[Type]) -> Vec<Type> {
|
||||||
|
def.type_params(db)
|
||||||
|
.into_iter()
|
||||||
|
.zip(generics)
|
||||||
|
.filter(|(tp, arg)| tp.default(db).as_ref() != Some(arg))
|
||||||
|
.map(|(_, arg)| arg.clone())
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type tree shows how can we get from set of types to some type.
|
/// Type tree shows how can we get from set of types to some type.
|
||||||
@ -85,8 +120,8 @@ impl TypeTree {
|
|||||||
pub fn gen_source_code(&self, sema_scope: &SemanticsScope<'_>) -> String {
|
pub fn gen_source_code(&self, sema_scope: &SemanticsScope<'_>) -> String {
|
||||||
let db = sema_scope.db;
|
let db = sema_scope.db;
|
||||||
match self {
|
match self {
|
||||||
TypeTree::Const(it) => mod_item_path(db, sema_scope, &ModuleDef::Const(*it)),
|
TypeTree::Const(it) => mod_item_path_str(sema_scope, &ModuleDef::Const(*it)),
|
||||||
TypeTree::Static(it) => mod_item_path(db, sema_scope, &ModuleDef::Static(*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::Local(it) => return it.name(db).display(db.upcast()).to_string(),
|
||||||
TypeTree::ConstParam(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::FamousType { value, .. } => return value.to_string(),
|
||||||
@ -100,7 +135,7 @@ impl TypeTree {
|
|||||||
match func.as_assoc_item(db).unwrap().containing_trait_or_trait_impl(db) {
|
match func.as_assoc_item(db).unwrap().containing_trait_or_trait_impl(db) {
|
||||||
Some(trait_) => {
|
Some(trait_) => {
|
||||||
let trait_name =
|
let trait_name =
|
||||||
mod_item_path(db, sema_scope, &ModuleDef::Trait(trait_));
|
mod_item_path_str(sema_scope, &ModuleDef::Trait(trait_));
|
||||||
let target = match self_param.access(db) {
|
let target = match self_param.access(db) {
|
||||||
crate::Access::Shared => format!("&{target}"),
|
crate::Access::Shared => format!("&{target}"),
|
||||||
crate::Access::Exclusive => format!("&mut {target}"),
|
crate::Access::Exclusive => format!("&mut {target}"),
|
||||||
@ -116,15 +151,51 @@ impl TypeTree {
|
|||||||
} else {
|
} else {
|
||||||
let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", ");
|
let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", ");
|
||||||
|
|
||||||
let fn_name = mod_item_path(db, sema_scope, &ModuleDef::Function(*func));
|
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})",)
|
format!("{fn_name}({args})",)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
TypeTree::Variant { variant, generics, params } => {
|
TypeTree::Variant { variant, generics, params } => {
|
||||||
|
let generics = non_default_generics(db, (*variant).into(), generics);
|
||||||
|
let generics_str = match generics.is_empty() {
|
||||||
|
true => String::new(),
|
||||||
|
false => {
|
||||||
|
let generics =
|
||||||
|
generics.iter().map(|it| type_path(sema_scope, it)).join(", ");
|
||||||
|
format!("::<{generics}>")
|
||||||
|
}
|
||||||
|
};
|
||||||
let inner = match variant.kind(db) {
|
let inner = match variant.kind(db) {
|
||||||
StructKind::Tuple => {
|
StructKind::Tuple => {
|
||||||
let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", ");
|
let args = params.iter().map(|f| f.gen_source_code(sema_scope)).join(", ");
|
||||||
format!("({args})")
|
format!("{generics_str}({args})")
|
||||||
}
|
}
|
||||||
StructKind::Record => {
|
StructKind::Record => {
|
||||||
let fields = variant.fields(db);
|
let fields = variant.fields(db);
|
||||||
@ -139,21 +210,16 @@ impl TypeTree {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
.join(", ");
|
.join(", ");
|
||||||
format!("{{ {args} }}")
|
format!("{generics_str}{{ {args} }}")
|
||||||
}
|
}
|
||||||
StructKind::Unit => match generics.is_empty() {
|
StructKind::Unit => generics_str,
|
||||||
true => String::new(),
|
|
||||||
false => {
|
|
||||||
let generics = generics.iter().map(|it| it.display(db)).join(", ");
|
|
||||||
format!("::<{generics}>")
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let prefix = mod_item_path(db, sema_scope, &ModuleDef::Variant(*variant));
|
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Variant(*variant));
|
||||||
format!("{prefix}{inner}")
|
format!("{prefix}{inner}")
|
||||||
}
|
}
|
||||||
TypeTree::Struct { strukt, generics, params } => {
|
TypeTree::Struct { strukt, generics, params } => {
|
||||||
|
let generics = non_default_generics(db, (*strukt).into(), generics);
|
||||||
let inner = match strukt.kind(db) {
|
let inner = match strukt.kind(db) {
|
||||||
StructKind::Tuple => {
|
StructKind::Tuple => {
|
||||||
let args = params.iter().map(|a| a.gen_source_code(sema_scope)).join(", ");
|
let args = params.iter().map(|a| a.gen_source_code(sema_scope)).join(", ");
|
||||||
@ -177,13 +243,14 @@ impl TypeTree {
|
|||||||
StructKind::Unit => match generics.is_empty() {
|
StructKind::Unit => match generics.is_empty() {
|
||||||
true => String::new(),
|
true => String::new(),
|
||||||
false => {
|
false => {
|
||||||
let generics = generics.iter().map(|it| it.display(db)).join(", ");
|
let generics =
|
||||||
|
generics.iter().map(|it| type_path(sema_scope, it)).join(", ");
|
||||||
format!("::<{generics}>")
|
format!("::<{generics}>")
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let prefix = mod_item_path(db, sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)));
|
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)));
|
||||||
format!("{prefix}{inner}")
|
format!("{prefix}{inner}")
|
||||||
}
|
}
|
||||||
TypeTree::Field { type_tree, field } => {
|
TypeTree::Field { type_tree, field } => {
|
||||||
|
@ -91,7 +91,7 @@ mod tests {
|
|||||||
fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
|
fn f() { let a: i32 = 1; let b: Option<i32> = todo$0!(); }"#,
|
||||||
r#"macro_rules! todo { () => (_) };
|
r#"macro_rules! todo { () => (_) };
|
||||||
enum Option<T> { Some(T), None }
|
enum Option<T> { Some(T), None }
|
||||||
fn f() { let a: i32 = 1; let b: Option<i32> = Option::None::<i32>; }"#,
|
fn f() { let a: i32 = 1; let b: Option<i32> = Option::None; }"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,6 +108,42 @@ mod tests {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_enum_with_generics3() {
|
||||||
|
check_assist(
|
||||||
|
term_search,
|
||||||
|
r#"macro_rules! todo { () => (_) };
|
||||||
|
enum Option<T> { None, Some(T) }
|
||||||
|
fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = todo$0!(); }"#,
|
||||||
|
r#"macro_rules! todo { () => (_) };
|
||||||
|
enum Option<T> { None, Some(T) }
|
||||||
|
fn f() { let a: Option<i32> = Option::None; let b: Option<Option<i32>> = Option::Some(a); }"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_enum_with_generics4() {
|
||||||
|
check_assist(
|
||||||
|
term_search,
|
||||||
|
r#"macro_rules! todo { () => (_) };
|
||||||
|
enum Foo<T = i32> { Foo(T) }
|
||||||
|
fn f() { let a = 0; let b: Foo = todo$0!(); }"#,
|
||||||
|
r#"macro_rules! todo { () => (_) };
|
||||||
|
enum Foo<T = i32> { Foo(T) }
|
||||||
|
fn f() { let a = 0; let b: Foo = Foo::Foo(a); }"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
term_search,
|
||||||
|
r#"macro_rules! todo { () => (_) };
|
||||||
|
enum Foo<T = i32> { Foo(T) }
|
||||||
|
fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = todo$0!(); }"#,
|
||||||
|
r#"macro_rules! todo { () => (_) };
|
||||||
|
enum Foo<T = i32> { Foo(T) }
|
||||||
|
fn f() { let a: Foo<u32> = Foo::Foo(0); let b: Foo<u32> = a; }"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_newtype() {
|
fn test_newtype() {
|
||||||
check_assist(
|
check_assist(
|
||||||
|
@ -148,7 +148,7 @@ impl<'a> PathTransform<'a> {
|
|||||||
let mut defaulted_params: Vec<DefaultedParam> = Default::default();
|
let mut defaulted_params: Vec<DefaultedParam> = Default::default();
|
||||||
self.generic_def
|
self.generic_def
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|it| it.type_params(db))
|
.flat_map(|it| it.type_or_const_params(db))
|
||||||
.skip(skip)
|
.skip(skip)
|
||||||
// The actual list of trait type parameters may be longer than the one
|
// The actual list of trait type parameters may be longer than the one
|
||||||
// used in the `impl` block due to trailing default type parameters.
|
// used in the `impl` block due to trailing default type parameters.
|
||||||
|
@ -439,17 +439,18 @@ impl flags::AnalysisStats {
|
|||||||
if let Some(mut err_idx) = err.find("error[E") {
|
if let Some(mut err_idx) = err.find("error[E") {
|
||||||
err_idx += 7;
|
err_idx += 7;
|
||||||
let err_code = &err[err_idx..err_idx + 4];
|
let err_code = &err[err_idx..err_idx + 4];
|
||||||
// if err_code == "0308" {
|
if err_code == "0282" {
|
||||||
println!("{}", err);
|
continue; // Byproduct of testing method
|
||||||
println!("{}", generated);
|
}
|
||||||
// }
|
bar.println(err);
|
||||||
|
bar.println(generated);
|
||||||
acc.error_codes
|
acc.error_codes
|
||||||
.entry(err_code.to_owned())
|
.entry(err_code.to_owned())
|
||||||
.and_modify(|n| *n += 1)
|
.and_modify(|n| *n += 1)
|
||||||
.or_insert(1);
|
.or_insert(1);
|
||||||
} else {
|
} else {
|
||||||
acc.syntax_errors += 1;
|
acc.syntax_errors += 1;
|
||||||
bar.println(format!("Syntax error here >>>>\n{}", err));
|
bar.println(format!("Syntax error: \n{}", err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user