Suggest only when all fields impl the trait

This commit is contained in:
ohno418 2022-04-04 00:08:54 +09:00
parent 0ff2f58330
commit de237823e0
4 changed files with 50 additions and 26 deletions

View file

@ -536,7 +536,7 @@ fn report_selection_error(
);
self.note_version_mismatch(&mut err, &trait_ref);
self.suggest_remove_await(&obligation, &mut err);
self.suggest_derive(&mut err, trait_predicate);
self.suggest_derive(&obligation, &mut err, trait_predicate);
if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() {
self.suggest_await_before_try(

View file

@ -190,7 +190,12 @@ fn suggest_floating_point_literal(
trait_ref: &ty::PolyTraitRef<'tcx>,
);
fn suggest_derive(&self, err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>);
fn suggest_derive(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
}
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
@ -2592,33 +2597,60 @@ fn suggest_floating_point_literal(
}
}
fn suggest_derive(&self, err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>) {
fn suggest_derive(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
return;
};
let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else {
return;
};
let adt = match self_ty.ty_adt_def() {
Some(adt) if adt.did().is_local() => adt,
let (adt, substs) = match trait_pred.skip_binder().self_ty().kind() {
ty::Adt(adt, substs) if adt.did().is_local() => (adt, substs),
_ => return,
};
let can_derive = match diagnostic_name {
sym::Default => !adt.is_enum(),
sym::PartialEq | sym::PartialOrd => {
let rhs_ty = trait_pred.skip_binder().trait_ref.substs.type_at(1);
self_ty == rhs_ty
}
sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
_ => false,
let can_derive = {
let is_derivable_trait = match diagnostic_name {
sym::Default => !adt.is_enum(),
sym::PartialEq | sym::PartialOrd => {
let rhs_ty = trait_pred.skip_binder().trait_ref.substs.type_at(1);
trait_pred.skip_binder().self_ty() == rhs_ty
}
sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
_ => false,
};
is_derivable_trait &&
// Ensure all fields impl the trait.
adt.all_fields().all(|field| {
let field_ty = field.ty(self.tcx, substs);
let trait_substs = match diagnostic_name {
sym::PartialEq | sym::PartialOrd => {
self.tcx.mk_substs_trait(field_ty, &[field_ty.into()])
}
_ => self.tcx.mk_substs_trait(field_ty, &[]),
};
let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
trait_ref: ty::TraitRef {
substs: trait_substs,
..trait_pred.skip_binder().trait_ref
},
..*tr
});
let field_obl = Obligation::new(
obligation.cause.clone(),
obligation.param_env,
trait_pred.to_predicate(self.tcx),
);
self.predicate_must_hold_modulo_regions(&field_obl)
})
};
if can_derive {
err.span_suggestion_verbose(
self.tcx.def_span(adt.did()).shrink_to_lo(),
&format!(
"consider annotating `{}` with `#[derive({})]`",
trait_pred.skip_binder().self_ty().to_string(),
trait_pred.skip_binder().self_ty(),
diagnostic_name.to_string(),
),
format!("#[derive({})]\n", diagnostic_name.to_string()),

View file

@ -7,10 +7,6 @@ LL | [Foo(String::new()); 4];
= help: the following implementations were found:
<Foo<T> as Copy>
= note: the `Copy` trait is required because the repeated element will be copied
help: consider annotating `Foo<String>` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
|
error: aborting due to previous error

View file

@ -129,10 +129,6 @@ note: required by a bound in `assert_copy`
|
LL | fn assert_copy<T:Copy>() { }
| ^^^^ required by this bound in `assert_copy`
help: consider annotating `MyNoncopyStruct` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
|
error[E0277]: the trait bound `Rc<isize>: Copy` is not satisfied
--> $DIR/kindck-copy.rs:67:19