Skip to content

Commit 3787595

Browse files
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](#146515)
2 parents 8bdd8dc + 11ae531 commit 3787595

3 files changed

Lines changed: 102 additions & 29 deletions

File tree

compiler/rustc_hir_typeck/src/method/suggest.rs

Lines changed: 59 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3281,6 +3281,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
32813281
}
32823282
}
32833283

3284+
/// Checks if we can suggest a derive macro for the unmet trait bound.
3285+
/// Returns Some(list_of_derives) if possible, or None if not.
3286+
fn consider_suggesting_derives_for_ty(
3287+
&self,
3288+
trait_pred: ty::TraitPredicate<'tcx>,
3289+
adt: ty::AdtDef<'tcx>,
3290+
) -> Option<Vec<(String, Span, Symbol)>> {
3291+
let diagnostic_name = self.tcx.get_diagnostic_name(trait_pred.def_id())?;
3292+
3293+
let can_derive = match diagnostic_name {
3294+
sym::Default
3295+
| sym::Eq
3296+
| sym::PartialEq
3297+
| sym::Ord
3298+
| sym::PartialOrd
3299+
| sym::Clone
3300+
| sym::Copy
3301+
| sym::Hash
3302+
| sym::Debug => true,
3303+
_ => false,
3304+
};
3305+
3306+
if !can_derive {
3307+
return None;
3308+
}
3309+
3310+
let trait_def_id = trait_pred.def_id();
3311+
let self_ty = trait_pred.self_ty();
3312+
3313+
// We need to check if there is already a manual implementation of the trait
3314+
// for this specific ADT to avoid suggesting `#[derive(..)]` that would conflict.
3315+
if self.tcx.non_blanket_impls_for_ty(trait_def_id, self_ty).any(|impl_def_id| {
3316+
self.tcx
3317+
.type_of(impl_def_id)
3318+
.instantiate_identity()
3319+
.ty_adt_def()
3320+
.is_some_and(|def| def.did() == adt.did())
3321+
}) {
3322+
return None;
3323+
}
3324+
3325+
let mut derives = Vec::new();
3326+
let self_name = self_ty.to_string();
3327+
let self_span = self.tcx.def_span(adt.did());
3328+
3329+
for super_trait in supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref)) {
3330+
if let Some(parent_diagnostic_name) = self.tcx.get_diagnostic_name(super_trait.def_id())
3331+
{
3332+
derives.push((self_name.clone(), self_span, parent_diagnostic_name));
3333+
}
3334+
}
3335+
3336+
derives.push((self_name, self_span, diagnostic_name));
3337+
3338+
Some(derives)
3339+
}
3340+
32843341
fn note_predicate_source_and_get_derives(
32853342
&self,
32863343
err: &mut Diag<'_>,
@@ -3298,35 +3355,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
32983355
Some(adt) if adt.did().is_local() => adt,
32993356
_ => continue,
33003357
};
3301-
if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) {
3302-
let can_derive = match diagnostic_name {
3303-
sym::Default
3304-
| sym::Eq
3305-
| sym::PartialEq
3306-
| sym::Ord
3307-
| sym::PartialOrd
3308-
| sym::Clone
3309-
| sym::Copy
3310-
| sym::Hash
3311-
| sym::Debug => true,
3312-
_ => false,
3313-
};
3314-
if can_derive {
3315-
let self_name = trait_pred.self_ty().to_string();
3316-
let self_span = self.tcx.def_span(adt.did());
3317-
for super_trait in
3318-
supertraits(self.tcx, ty::Binder::dummy(trait_pred.trait_ref))
3319-
{
3320-
if let Some(parent_diagnostic_name) =
3321-
self.tcx.get_diagnostic_name(super_trait.def_id())
3322-
{
3323-
derives.push((self_name.clone(), self_span, parent_diagnostic_name));
3324-
}
3325-
}
3326-
derives.push((self_name, self_span, diagnostic_name));
3327-
} else {
3328-
traits.push(trait_pred.def_id());
3329-
}
3358+
if let Some(new_derives) = self.consider_suggesting_derives_for_ty(trait_pred, adt) {
3359+
derives.extend(new_derives);
33303360
} else {
33313361
traits.push(trait_pred.def_id());
33323362
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// issue: https://github.com/rust-lang/rust/issues/146515
2+
3+
use std::rc::Rc;
4+
5+
#[derive(Clone)]
6+
struct ContainsRc<T> {
7+
value: Rc<T>,
8+
}
9+
10+
fn clone_me<T>(x: &ContainsRc<T>) -> ContainsRc<T> {
11+
//~^ NOTE expected `ContainsRc<T>` because of return type
12+
x.clone()
13+
//~^ ERROR mismatched types
14+
//~| NOTE expected `ContainsRc<T>`, found `&ContainsRc<T>`
15+
//~| NOTE expected struct `ContainsRc<_>`
16+
//~| NOTE `ContainsRc<T>` does not implement `Clone`, so `&ContainsRc<T>` was cloned instead
17+
//~| NOTE the trait `Clone` must be implemented
18+
}
19+
20+
fn main() {}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/derive-clone-already-present-issue-146515.rs:12:5
3+
|
4+
LL | fn clone_me<T>(x: &ContainsRc<T>) -> ContainsRc<T> {
5+
| ------------- expected `ContainsRc<T>` because of return type
6+
LL |
7+
LL | x.clone()
8+
| ^^^^^^^^^ expected `ContainsRc<T>`, found `&ContainsRc<T>`
9+
|
10+
= note: expected struct `ContainsRc<_>`
11+
found reference `&ContainsRc<_>`
12+
note: `ContainsRc<T>` does not implement `Clone`, so `&ContainsRc<T>` was cloned instead
13+
--> $DIR/derive-clone-already-present-issue-146515.rs:12:5
14+
|
15+
LL | x.clone()
16+
| ^
17+
= help: `Clone` is not implemented because the trait bound `T: Clone` is not satisfied
18+
note: the trait `Clone` must be implemented
19+
--> $SRC_DIR/core/src/clone.rs:LL:COL
20+
21+
error: aborting due to 1 previous error
22+
23+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)