Improve generics handling in term search
This commit is contained in:
parent
1d3558bfe1
commit
8bd30e9b3f
@ -3856,6 +3856,11 @@ impl Type {
|
|||||||
Type { env: ty.env, ty: TyBuilder::slice(ty.ty) }
|
Type { env: ty.env, ty: TyBuilder::slice(ty.ty) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_tuple(krate: CrateId, tys: &[Type]) -> Type {
|
||||||
|
let tys = tys.iter().map(|it| it.ty.clone());
|
||||||
|
Type { env: TraitEnvironment::empty(krate), ty: TyBuilder::tuple_with(tys) }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_unit(&self) -> bool {
|
pub fn is_unit(&self) -> bool {
|
||||||
matches!(self.ty.kind(Interner), TyKind::Tuple(0, ..))
|
matches!(self.ty.kind(Interner), TyKind::Tuple(0, ..))
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,10 @@ impl AlternativeExprs {
|
|||||||
AlternativeExprs::Many => (),
|
AlternativeExprs::Many => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_many(&self) -> bool {
|
||||||
|
matches!(self, AlternativeExprs::Many)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Lookup table for term search
|
/// # Lookup table for term search
|
||||||
@ -103,27 +107,36 @@ struct LookupTable {
|
|||||||
|
|
||||||
impl LookupTable {
|
impl LookupTable {
|
||||||
/// Initialize lookup table
|
/// Initialize lookup table
|
||||||
fn new(many_threshold: usize) -> Self {
|
fn new(many_threshold: usize, goal: Type) -> Self {
|
||||||
let mut res = Self { many_threshold, ..Default::default() };
|
let mut res = Self { many_threshold, ..Default::default() };
|
||||||
res.new_types.insert(NewTypesKey::ImplMethod, Vec::new());
|
res.new_types.insert(NewTypesKey::ImplMethod, Vec::new());
|
||||||
res.new_types.insert(NewTypesKey::StructProjection, Vec::new());
|
res.new_types.insert(NewTypesKey::StructProjection, Vec::new());
|
||||||
|
res.types_wishlist.insert(goal);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find all `Expr`s that unify with the `ty`
|
/// Find all `Expr`s that unify with the `ty`
|
||||||
fn find(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
|
fn find(&mut self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
|
||||||
self.data
|
let res = self
|
||||||
|
.data
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
|
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
|
||||||
.map(|(t, tts)| tts.exprs(t))
|
.map(|(t, tts)| tts.exprs(t));
|
||||||
|
|
||||||
|
if res.is_none() {
|
||||||
|
self.types_wishlist.insert(ty.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as find but automatically creates shared reference of types in the lookup
|
/// 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
|
/// 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 `Expr::Reference` and returns them.
|
/// trees we have for `i32` with `Expr::Reference` and returns them.
|
||||||
fn find_autoref(&self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
|
fn find_autoref(&mut self, db: &dyn HirDatabase, ty: &Type) -> Option<Vec<Expr>> {
|
||||||
self.data
|
let res = self
|
||||||
|
.data
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
|
.find(|(t, _)| t.could_unify_with_deeply(db, ty))
|
||||||
.map(|(t, it)| it.exprs(t))
|
.map(|(t, it)| it.exprs(t))
|
||||||
@ -139,7 +152,13 @@ impl LookupTable {
|
|||||||
.map(|expr| Expr::Reference(Box::new(expr)))
|
.map(|expr| Expr::Reference(Box::new(expr)))
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
})
|
});
|
||||||
|
|
||||||
|
if res.is_none() {
|
||||||
|
self.types_wishlist.insert(ty.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert new type trees for type
|
/// Insert new type trees for type
|
||||||
@ -149,7 +168,12 @@ impl LookupTable {
|
|||||||
/// but they clearly do not unify themselves.
|
/// but they clearly do not unify themselves.
|
||||||
fn insert(&mut self, ty: Type, exprs: impl Iterator<Item = Expr>) {
|
fn insert(&mut self, ty: Type, exprs: impl Iterator<Item = Expr>) {
|
||||||
match self.data.get_mut(&ty) {
|
match self.data.get_mut(&ty) {
|
||||||
Some(it) => it.extend_with_threshold(self.many_threshold, exprs),
|
Some(it) => {
|
||||||
|
it.extend_with_threshold(self.many_threshold, exprs);
|
||||||
|
if it.is_many() {
|
||||||
|
self.types_wishlist.remove(&ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
None => {
|
None => {
|
||||||
self.data.insert(ty.clone(), AlternativeExprs::new(self.many_threshold, exprs));
|
self.data.insert(ty.clone(), AlternativeExprs::new(self.many_threshold, exprs));
|
||||||
for it in self.new_types.values_mut() {
|
for it in self.new_types.values_mut() {
|
||||||
@ -206,8 +230,8 @@ impl LookupTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Types queried but not found
|
/// Types queried but not found
|
||||||
fn take_types_wishlist(&mut self) -> FxHashSet<Type> {
|
fn types_wishlist(&mut self) -> &FxHashSet<Type> {
|
||||||
std::mem::take(&mut self.types_wishlist)
|
&self.types_wishlist
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,7 +296,7 @@ pub fn term_search<DB: HirDatabase>(ctx: &TermSearchCtx<'_, DB>) -> Vec<Expr> {
|
|||||||
defs.insert(def);
|
defs.insert(def);
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold);
|
let mut lookup = LookupTable::new(ctx.config.many_alternatives_threshold, ctx.goal.clone());
|
||||||
|
|
||||||
// Try trivial tactic first, also populates lookup table
|
// Try trivial tactic first, also populates lookup table
|
||||||
let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect();
|
let mut solutions: Vec<Expr> = tactics::trivial(ctx, &defs, &mut lookup).collect();
|
||||||
|
@ -138,6 +138,8 @@ pub enum Expr {
|
|||||||
Variant { variant: Variant, generics: Vec<Type>, params: Vec<Expr> },
|
Variant { variant: Variant, generics: Vec<Type>, params: Vec<Expr> },
|
||||||
/// Struct construction
|
/// Struct construction
|
||||||
Struct { strukt: Struct, generics: Vec<Type>, params: Vec<Expr> },
|
Struct { strukt: Struct, generics: Vec<Type>, params: Vec<Expr> },
|
||||||
|
/// Tuple construction
|
||||||
|
Tuple { ty: Type, params: Vec<Expr> },
|
||||||
/// Struct field access
|
/// Struct field access
|
||||||
Field { expr: Box<Expr>, field: Field },
|
Field { expr: Box<Expr>, field: Field },
|
||||||
/// Passing type as reference (with `&`)
|
/// Passing type as reference (with `&`)
|
||||||
@ -366,6 +368,18 @@ impl Expr {
|
|||||||
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)))?;
|
let prefix = mod_item_path_str(sema_scope, &ModuleDef::Adt(Adt::Struct(*strukt)))?;
|
||||||
Ok(format!("{prefix}{inner}"))
|
Ok(format!("{prefix}{inner}"))
|
||||||
}
|
}
|
||||||
|
Expr::Tuple { params, .. } => {
|
||||||
|
let args = params
|
||||||
|
.iter()
|
||||||
|
.map(|a| {
|
||||||
|
a.gen_source_code(sema_scope, many_formatter, prefer_no_std, prefer_prelude)
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<String>, DisplaySourceCodeError>>()?
|
||||||
|
.into_iter()
|
||||||
|
.join(", ");
|
||||||
|
let res = format!("({args})");
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
Expr::Field { expr, field } => {
|
Expr::Field { expr, field } => {
|
||||||
if expr.contains_many_in_illegal_pos() {
|
if expr.contains_many_in_illegal_pos() {
|
||||||
return Ok(many_formatter(&expr.ty(db)));
|
return Ok(many_formatter(&expr.ty(db)));
|
||||||
@ -420,6 +434,7 @@ impl Expr {
|
|||||||
Expr::Struct { strukt, generics, .. } => {
|
Expr::Struct { strukt, generics, .. } => {
|
||||||
Adt::from(*strukt).ty_with_args(db, generics.iter().cloned())
|
Adt::from(*strukt).ty_with_args(db, generics.iter().cloned())
|
||||||
}
|
}
|
||||||
|
Expr::Tuple { ty, .. } => ty.clone(),
|
||||||
Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()),
|
Expr::Field { expr, field } => field.ty_with_args(db, expr.ty(db).type_arguments()),
|
||||||
Expr::Reference(it) => it.ty(db),
|
Expr::Reference(it) => it.ty(db),
|
||||||
Expr::Many(ty) => ty.clone(),
|
Expr::Many(ty) => ty.clone(),
|
||||||
|
@ -109,7 +109,6 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
|||||||
lookup: &mut LookupTable,
|
lookup: &mut LookupTable,
|
||||||
parent_enum: Enum,
|
parent_enum: Enum,
|
||||||
variant: Variant,
|
variant: Variant,
|
||||||
goal: &Type,
|
|
||||||
config: &TermSearchConfig,
|
config: &TermSearchConfig,
|
||||||
) -> Vec<(Type, Vec<Expr>)> {
|
) -> Vec<(Type, Vec<Expr>)> {
|
||||||
// Ignore unstable
|
// Ignore unstable
|
||||||
@ -143,11 +142,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
|||||||
let non_default_type_params_len =
|
let non_default_type_params_len =
|
||||||
type_params.iter().filter(|it| it.default(db).is_none()).count();
|
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
|
let generic_params = lookup
|
||||||
.iter_types()
|
.types_wishlist()
|
||||||
.collect::<Vec<_>>() // Force take ownership
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.permutations(non_default_type_params_len);
|
.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
|
generic_params
|
||||||
.filter_map(move |generics| {
|
.filter_map(move |generics| {
|
||||||
@ -155,17 +157,11 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
|||||||
let mut g = generics.into_iter();
|
let mut g = generics.into_iter();
|
||||||
let generics: Vec<_> = type_params
|
let generics: Vec<_> = type_params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|it| it.default(db).unwrap_or_else(|| g.next().expect("No generic")))
|
.map(|it| it.default(db).or_else(|| g.next()))
|
||||||
.collect();
|
.collect::<Option<_>>()?;
|
||||||
|
|
||||||
let enum_ty = Adt::from(parent_enum).ty_with_args(db, generics.iter().cloned());
|
let enum_ty = Adt::from(parent_enum).ty_with_args(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) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore types that have something to do with lifetimes
|
// Ignore types that have something to do with lifetimes
|
||||||
if config.enable_borrowcheck && enum_ty.contains_reference(db) {
|
if config.enable_borrowcheck && enum_ty.contains_reference(db) {
|
||||||
return None;
|
return None;
|
||||||
@ -199,21 +195,37 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
|||||||
.filter_map(move |def| match def {
|
.filter_map(move |def| match def {
|
||||||
ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
|
ScopeDef::ModuleDef(ModuleDef::Variant(it)) => {
|
||||||
let variant_exprs =
|
let variant_exprs =
|
||||||
variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.goal, &ctx.config);
|
variant_helper(db, lookup, it.parent_enum(db), *it, &ctx.config);
|
||||||
if variant_exprs.is_empty() {
|
if variant_exprs.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Variant(*it)));
|
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)
|
Some(variant_exprs)
|
||||||
}
|
}
|
||||||
ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(enum_))) => {
|
ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(enum_))) => {
|
||||||
let exprs: Vec<(Type, Vec<Expr>)> = enum_
|
let exprs: Vec<(Type, Vec<Expr>)> = enum_
|
||||||
.variants(db)
|
.variants(db)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.goal, &ctx.config))
|
.flat_map(|it| variant_helper(db, lookup, *enum_, it, &ctx.config))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if !exprs.is_empty() {
|
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_))));
|
lookup.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Enum(*enum_))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,11 +261,14 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
|||||||
let non_default_type_params_len =
|
let non_default_type_params_len =
|
||||||
type_params.iter().filter(|it| it.default(db).is_none()).count();
|
type_params.iter().filter(|it| it.default(db).is_none()).count();
|
||||||
|
|
||||||
|
let struct_ty_shallow = Adt::from(*it).ty(db);
|
||||||
let generic_params = lookup
|
let generic_params = lookup
|
||||||
.iter_types()
|
.types_wishlist()
|
||||||
.collect::<Vec<_>>() // Force take ownership
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.permutations(non_default_type_params_len);
|
.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
|
let exprs = generic_params
|
||||||
.filter_map(|generics| {
|
.filter_map(|generics| {
|
||||||
@ -261,22 +276,11 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
|||||||
let mut g = generics.into_iter();
|
let mut g = generics.into_iter();
|
||||||
let generics: Vec<_> = type_params
|
let generics: Vec<_> = type_params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|it| {
|
.map(|it| it.default(db).or_else(|| g.next()))
|
||||||
it.default(db)
|
.collect::<Option<_>>()?;
|
||||||
.unwrap_or_else(|| g.next().expect("Missing type param"))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let struct_ty = Adt::from(*it).ty_with_args(db, generics.iter().cloned());
|
let struct_ty = Adt::from(*it).ty_with_args(db, generics.iter().cloned());
|
||||||
|
|
||||||
// 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, &ctx.goal)
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore types that have something to do with lifetimes
|
// Ignore types that have something to do with lifetimes
|
||||||
if ctx.config.enable_borrowcheck && struct_ty.contains_reference(db) {
|
if ctx.config.enable_borrowcheck && struct_ty.contains_reference(db) {
|
||||||
return None;
|
return None;
|
||||||
@ -309,8 +313,12 @@ pub(super) fn type_constructor<'a, DB: HirDatabase>(
|
|||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
lookup
|
if non_default_type_params_len == 0 {
|
||||||
.mark_fulfilled(ScopeDef::ModuleDef(ModuleDef::Adt(Adt::Struct(*it))));
|
// 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());
|
lookup.insert(struct_ty.clone(), struct_exprs.iter().cloned());
|
||||||
|
|
||||||
Some((struct_ty, struct_exprs))
|
Some((struct_ty, struct_exprs))
|
||||||
@ -525,14 +533,17 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let non_default_type_params_len = imp_type_params
|
// Double check that we have fully known type
|
||||||
.iter()
|
if ty.type_arguments().any(|it| it.contains_unknown()) {
|
||||||
.chain(fn_type_params.iter())
|
return None;
|
||||||
.filter(|it| it.default(db).is_none())
|
}
|
||||||
.count();
|
|
||||||
|
|
||||||
// Ignore bigger number of generics for now as they kill the performance
|
let non_default_fn_type_params_len =
|
||||||
if non_default_type_params_len > 0 {
|
fn_type_params.iter().filter(|it| it.default(db).is_none()).count();
|
||||||
|
|
||||||
|
// Ignore functions with generics for now as they kill the performance
|
||||||
|
// Also checking bounds for generics is problematic
|
||||||
|
if non_default_fn_type_params_len > 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,23 +551,23 @@ pub(super) fn impl_method<'a, DB: HirDatabase>(
|
|||||||
.iter_types()
|
.iter_types()
|
||||||
.collect::<Vec<_>>() // Force take ownership
|
.collect::<Vec<_>>() // Force take ownership
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.permutations(non_default_type_params_len);
|
.permutations(non_default_fn_type_params_len);
|
||||||
|
|
||||||
let exprs: Vec<_> = generic_params
|
let exprs: Vec<_> = generic_params
|
||||||
.filter_map(|generics| {
|
.filter_map(|generics| {
|
||||||
// Insert default type params
|
// Insert default type params
|
||||||
let mut g = generics.into_iter();
|
let mut g = generics.into_iter();
|
||||||
let generics: Vec<_> = imp_type_params
|
let generics: Vec<_> = ty
|
||||||
.iter()
|
.type_arguments()
|
||||||
.chain(fn_type_params.iter())
|
.map(Some)
|
||||||
.map(|it| match it.default(db) {
|
.chain(fn_type_params.iter().map(|it| match it.default(db) {
|
||||||
Some(ty) => Some(ty),
|
Some(ty) => Some(ty),
|
||||||
None => {
|
None => {
|
||||||
let generic = g.next().expect("Missing type param");
|
let generic = g.next().expect("Missing type param");
|
||||||
// Filter out generics that do not unify due to trait bounds
|
// Filter out generics that do not unify due to trait bounds
|
||||||
it.ty(db).could_unify_with(db, &generic).then_some(generic)
|
it.ty(db).could_unify_with(db, &generic).then_some(generic)
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
.collect::<Option<_>>()?;
|
.collect::<Option<_>>()?;
|
||||||
|
|
||||||
let ret_ty = it.ret_type_with_args(
|
let ret_ty = it.ret_type_with_args(
|
||||||
@ -713,7 +724,8 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
|
|||||||
let db = ctx.sema.db;
|
let db = ctx.sema.db;
|
||||||
let module = ctx.scope.module();
|
let module = ctx.scope.module();
|
||||||
lookup
|
lookup
|
||||||
.take_types_wishlist()
|
.types_wishlist()
|
||||||
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(iter::once(ctx.goal.clone()))
|
.chain(iter::once(ctx.goal.clone()))
|
||||||
.flat_map(|ty| {
|
.flat_map(|ty| {
|
||||||
@ -768,14 +780,17 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let non_default_type_params_len = imp_type_params
|
// Double check that we have fully known type
|
||||||
.iter()
|
if ty.type_arguments().any(|it| it.contains_unknown()) {
|
||||||
.chain(fn_type_params.iter())
|
return None;
|
||||||
.filter(|it| it.default(db).is_none())
|
}
|
||||||
.count();
|
|
||||||
|
|
||||||
// Ignore bigger number of generics for now as they kill the performance
|
let non_default_fn_type_params_len =
|
||||||
if non_default_type_params_len > 1 {
|
fn_type_params.iter().filter(|it| it.default(db).is_none()).count();
|
||||||
|
|
||||||
|
// Ignore functions with generics for now as they kill the performance
|
||||||
|
// Also checking bounds for generics is problematic
|
||||||
|
if non_default_fn_type_params_len > 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -783,16 +798,16 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
|
|||||||
.iter_types()
|
.iter_types()
|
||||||
.collect::<Vec<_>>() // Force take ownership
|
.collect::<Vec<_>>() // Force take ownership
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.permutations(non_default_type_params_len);
|
.permutations(non_default_fn_type_params_len);
|
||||||
|
|
||||||
let exprs: Vec<_> = generic_params
|
let exprs: Vec<_> = generic_params
|
||||||
.filter_map(|generics| {
|
.filter_map(|generics| {
|
||||||
// Insert default type params
|
// Insert default type params
|
||||||
let mut g = generics.into_iter();
|
let mut g = generics.into_iter();
|
||||||
let generics: Vec<_> = imp_type_params
|
let generics: Vec<_> = ty
|
||||||
.iter()
|
.type_arguments()
|
||||||
.chain(fn_type_params.iter())
|
.map(Some)
|
||||||
.map(|it| match it.default(db) {
|
.chain(fn_type_params.iter().map(|it| match it.default(db) {
|
||||||
Some(ty) => Some(ty),
|
Some(ty) => Some(ty),
|
||||||
None => {
|
None => {
|
||||||
let generic = g.next().expect("Missing type param");
|
let generic = g.next().expect("Missing type param");
|
||||||
@ -802,7 +817,7 @@ pub(super) fn impl_static_method<'a, DB: HirDatabase>(
|
|||||||
// Filter out generics that do not unify due to trait bounds
|
// Filter out generics that do not unify due to trait bounds
|
||||||
it.ty(db).could_unify_with(db, &generic).then_some(generic)
|
it.ty(db).could_unify_with(db, &generic).then_some(generic)
|
||||||
}
|
}
|
||||||
})
|
}))
|
||||||
.collect::<Option<_>>()?;
|
.collect::<Option<_>>()?;
|
||||||
|
|
||||||
let ret_ty = it.ret_type_with_args(
|
let ret_ty = it.ret_type_with_args(
|
||||||
|
@ -57,11 +57,14 @@ pub(crate) fn term_search(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
|||||||
})
|
})
|
||||||
.unique();
|
.unique();
|
||||||
|
|
||||||
|
let macro_name = macro_call.name(ctx.sema.db);
|
||||||
|
let macro_name = macro_name.display(ctx.sema.db);
|
||||||
|
|
||||||
for code in paths {
|
for code in paths {
|
||||||
acc.add_group(
|
acc.add_group(
|
||||||
&GroupLabel(String::from("Term search")),
|
&GroupLabel(String::from("Term search")),
|
||||||
AssistId("term_search", AssistKind::Generate),
|
AssistId("term_search", AssistKind::Generate),
|
||||||
format!("Replace todo!() with {code}"),
|
format!("Replace {macro_name}!() with {code}"),
|
||||||
goal_range,
|
goal_range,
|
||||||
|builder| {
|
|builder| {
|
||||||
builder.replace(goal_range, code);
|
builder.replace(goal_range, code);
|
||||||
|
@ -2599,6 +2599,7 @@ fn foo() {
|
|||||||
expect![[r#"
|
expect![[r#"
|
||||||
lc foo [type+local]
|
lc foo [type+local]
|
||||||
ex foo [type]
|
ex foo [type]
|
||||||
|
ex Foo::B [type]
|
||||||
ev Foo::A(…) [type_could_unify]
|
ev Foo::A(…) [type_could_unify]
|
||||||
ev Foo::B [type_could_unify]
|
ev Foo::B [type_could_unify]
|
||||||
en Foo [type_could_unify]
|
en Foo [type_could_unify]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user