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:
commit
3787595751
3 changed files with 102 additions and 29 deletions
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {}
|
||||
|
|
@ -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`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue