diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index b2803f4347e37..5673d044ad2ce 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -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> { + 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()); } diff --git a/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs b/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs new file mode 100644 index 0000000000000..083d73711a517 --- /dev/null +++ b/tests/ui/suggestions/derive-clone-already-present-issue-146515.rs @@ -0,0 +1,20 @@ +// issue: https://github.com/rust-lang/rust/issues/146515 + +use std::rc::Rc; + +#[derive(Clone)] +struct ContainsRc { + value: Rc, +} + +fn clone_me(x: &ContainsRc) -> ContainsRc { + //~^ NOTE expected `ContainsRc` because of return type + x.clone() + //~^ ERROR mismatched types + //~| NOTE expected `ContainsRc`, found `&ContainsRc` + //~| NOTE expected struct `ContainsRc<_>` + //~| NOTE `ContainsRc` does not implement `Clone`, so `&ContainsRc` was cloned instead + //~| NOTE the trait `Clone` must be implemented +} + +fn main() {} diff --git a/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr b/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr new file mode 100644 index 0000000000000..516ef38f668dc --- /dev/null +++ b/tests/ui/suggestions/derive-clone-already-present-issue-146515.stderr @@ -0,0 +1,23 @@ +error[E0308]: mismatched types + --> $DIR/derive-clone-already-present-issue-146515.rs:12:5 + | +LL | fn clone_me(x: &ContainsRc) -> ContainsRc { + | ------------- expected `ContainsRc` because of return type +LL | +LL | x.clone() + | ^^^^^^^^^ expected `ContainsRc`, found `&ContainsRc` + | + = note: expected struct `ContainsRc<_>` + found reference `&ContainsRc<_>` +note: `ContainsRc` does not implement `Clone`, so `&ContainsRc` 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`.