Cleanup checking for existing impls in impl From assist

Use the trait solver to check if there's an existing implementation of
From<type_in_enum_variant> for the enum.
This commit is contained in:
Matthew Hall 2020-04-02 18:42:30 +01:00
parent 1fee60181f
commit 6a2127be28
2 changed files with 24 additions and 48 deletions

View file

@ -1,4 +1,3 @@
use hir::ImplDef;
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode, NameOwner}, ast::{self, AstNode, NameOwner},
TextUnit, TextUnit,
@ -99,18 +98,7 @@ fn already_has_from_impl(
}; };
let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db); let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db);
let krate = match scope.module() { e_ty.impls_trait(sema.db, from_trait, &[var_ty.clone()])
Some(s) => s.krate(),
_ => return false,
};
let impls = ImplDef::for_trait(sema.db, krate, from_trait);
let imp = impls.iter().find(|imp| {
let targets_enum = imp.target_ty(sema.db) == e_ty;
let param_matches = imp.target_trait_substs_matches(sema.db, &[var_ty.clone()]);
targets_enum && param_matches
});
imp.is_some()
} }
#[cfg(test)] #[cfg(test)]
@ -192,7 +180,7 @@ impl From<String> for A {
A::Two(v) A::Two(v)
} }
} }
pub trait From<T> { pub trait From<T> {
fn from(T) -> Self; fn from(T) -> Self;
}"#, }"#,
@ -209,7 +197,7 @@ impl From<String> for A {
A::Two(v) A::Two(v)
} }
} }
pub trait From<T> { pub trait From<T> {
fn from(T) -> Self; fn from(T) -> Self;
}"#, }"#,

View file

@ -23,7 +23,7 @@ use hir_expand::{
}; };
use hir_ty::{ use hir_ty::{
autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy,
Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, TypeWalk, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
}; };
use ra_db::{CrateId, Edition, FileId}; use ra_db::{CrateId, Edition, FileId};
use ra_prof::profile; use ra_prof::profile;
@ -960,38 +960,6 @@ impl ImplDef {
db.impl_data(self.id).target_trait.clone() db.impl_data(self.id).target_trait.clone()
} }
pub fn target_trait_substs_matches(&self, db: &dyn HirDatabase, typs: &[Type]) -> bool {
let type_ref = match self.target_trait(db) {
Some(typ_ref) => typ_ref,
None => return false,
};
let resolver = self.id.resolver(db.upcast());
let ctx = hir_ty::TyLoweringContext::new(db, &resolver);
let ty = Ty::from_hir(&ctx, &type_ref);
let d = match ty.dyn_trait_ref() {
Some(d) => d,
None => return false,
};
let mut matches = true;
let mut i = 0;
d.substs.walk(&mut |t| {
if matches {
if i >= typs.len() {
matches = false;
return;
}
match t {
Ty::Bound(_) => matches = i == 0,
_ => {
matches = *t == typs[i].ty.value;
i += 1;
}
}
}
});
matches
}
pub fn target_type(&self, db: &dyn HirDatabase) -> TypeRef { pub fn target_type(&self, db: &dyn HirDatabase) -> TypeRef {
db.impl_data(self.id).target_type.clone() db.impl_data(self.id).target_type.clone()
} }
@ -1116,6 +1084,26 @@ impl Type {
) )
} }
pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
let trait_ref = hir_ty::TraitRef {
trait_: trait_.id,
substs: Substs::build_for_def(db, trait_.id)
.push(self.ty.value.clone())
.fill(args.iter().map(|t| t.ty.value.clone()))
.build(),
};
let goal = Canonical {
value: hir_ty::InEnvironment::new(
self.ty.environment.clone(),
hir_ty::Obligation::Trait(trait_ref),
),
num_vars: 0,
};
db.trait_solve(self.krate, goal).is_some()
}
// FIXME: this method is broken, as it doesn't take closures into account. // FIXME: this method is broken, as it doesn't take closures into account.
pub fn as_callable(&self) -> Option<CallableDef> { pub fn as_callable(&self) -> Option<CallableDef> {
Some(self.ty.value.as_callable()?.0) Some(self.ty.value.as_callable()?.0)