Rollup merge of #150720 - WhyNovaa:diagnostics-impl-fix, r=lcnr

Do not suggest `derive` if there is already an impl

This PR fixes an issue where the compiler would suggest adding `#[derive(Trait)]` even if the struct or enum already implements that trait manually.

Fixes [#146515](https://github.com/rust-lang/rust/issues/146515)
This commit is contained in:
Jonathan Brouwer 2026-01-28 21:10:49 +01:00 committed by GitHub
commit 3787595751
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 102 additions and 29 deletions

View file

@ -3281,6 +3281,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// Checks if we can suggest a derive macro for the unmet trait bound.
/// Returns Some(list_of_derives) if possible, or None if not.
fn consider_suggesting_derives_for_ty(
&self,
trait_pred: ty::TraitPredicate<'tcx>,
adt: ty::AdtDef<'tcx>,
) -> Option<Vec<(String, Span, Symbol)>> {
let diagnostic_name = self.tcx.get_diagnostic_name(trait_pred.def_id())?;
let can_derive = match diagnostic_name {
sym::Default
| sym::Eq
| sym::PartialEq
| sym::Ord
| sym::PartialOrd
| sym::Clone
| sym::Copy
| sym::Hash
| sym::Debug => true,
_ => false,
};
if !can_derive {
return None;
}
let trait_def_id = trait_pred.def_id();
let self_ty = trait_pred.self_ty();
// We need to check if there is already a manual implementation of the trait
// for this specific ADT to avoid suggesting `#[derive(..)]` that would conflict.
if self.tcx.non_blanket_impls_for_ty(trait_def_id, self_ty).any(|impl_def_id| {
self.tcx
.type_of(impl_def_id)
.instantiate_identity()
.ty_adt_def()
.is_some_and(|def| def.did() == adt.did())
}) {
return None;
}
let mut derives = Vec::new();
let self_name = self_ty.to_string();
let self_span = self.tcx.def_span(adt.did());
for super_trait in supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref)) {
if let Some(parent_diagnostic_name) = self.tcx.get_diagnostic_name(super_trait.def_id())
{
derives.push((self_name.clone(), self_span, parent_diagnostic_name));
}
}
derives.push((self_name, self_span, diagnostic_name));
Some(derives)
}
fn note_predicate_source_and_get_derives(
&self,
err: &mut Diag<'_>,
@ -3298,35 +3355,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Some(adt) if adt.did().is_local() => adt,
_ => continue,
};
if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) {
let can_derive = match diagnostic_name {
sym::Default
| sym::Eq
| sym::PartialEq
| sym::Ord
| sym::PartialOrd
| sym::Clone
| sym::Copy
| sym::Hash
| sym::Debug => true,
_ => false,
};
if can_derive {
let self_name = trait_pred.self_ty().to_string();
let self_span = self.tcx.def_span(adt.did());
for super_trait in
supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref))
{
if let Some(parent_diagnostic_name) =
self.tcx.get_diagnostic_name(super_trait.def_id())
{
derives.push((self_name.clone(), self_span, parent_diagnostic_name));
}
}
derives.push((self_name, self_span, diagnostic_name));
} else {
traits.push(trait_pred.def_id());
}
if let Some(new_derives) = self.consider_suggesting_derives_for_ty(trait_pred, adt) {
derives.extend(new_derives);
} else {
traits.push(trait_pred.def_id());
}

View file

@ -0,0 +1,20 @@
// issue: https://github.com/rust-lang/rust/issues/146515
use std::rc::Rc;
#[derive(Clone)]
struct ContainsRc<T> {
value: Rc<T>,
}
fn clone_me<T>(x: &ContainsRc<T>) -> ContainsRc<T> {
//~^ NOTE expected `ContainsRc<T>` because of return type
x.clone()
//~^ ERROR mismatched types
//~| NOTE expected `ContainsRc<T>`, found `&ContainsRc<T>`
//~| NOTE expected struct `ContainsRc<_>`
//~| NOTE `ContainsRc<T>` does not implement `Clone`, so `&ContainsRc<T>` was cloned instead
//~| NOTE the trait `Clone` must be implemented
}
fn main() {}

View file

@ -0,0 +1,23 @@
error[E0308]: mismatched types
--> $DIR/derive-clone-already-present-issue-146515.rs:12:5
|
LL | fn clone_me<T>(x: &ContainsRc<T>) -> ContainsRc<T> {
| ------------- expected `ContainsRc<T>` because of return type
LL |
LL | x.clone()
| ^^^^^^^^^ expected `ContainsRc<T>`, found `&ContainsRc<T>`
|
= note: expected struct `ContainsRc<_>`
found reference `&ContainsRc<_>`
note: `ContainsRc<T>` does not implement `Clone`, so `&ContainsRc<T>` was cloned instead
--> $DIR/derive-clone-already-present-issue-146515.rs:12:5
|
LL | x.clone()
| ^
= help: `Clone` is not implemented because the trait bound `T: Clone` is not satisfied
note: the trait `Clone` must be implemented
--> $SRC_DIR/core/src/clone.rs:LL:COL
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0308`.