From 8575e8bd8cecf890f9454f317b66341402758e38 Mon Sep 17 00:00:00 2001 From: Raushan kumar Date: Thu, 16 Jul 2026 06:42:55 +0000 Subject: [PATCH] Fix accurate targeting for imperfect derives diagnostic Filters out false positives for the 'imperfect derives' E0277 note by checking if ADT fields satisfy the trait independently of the failing generic parameter. --- .../src/error_reporting/traits/suggestions.rs | 93 ++++++++++++++++++- tests/ui/associated-types/issue-38821.stderr | 4 - .../unsizing-wfcheck-issue-126272.stderr | 4 - .../clone-copy/deriving-copyclone.stderr | 2 - tests/ui/derives/imperfect-derive-advanced.rs | 42 +++++++++ .../derives/imperfect-derive-advanced.stderr | 81 ++++++++++++++++ tests/ui/derives/imperfect-derive-phantom.rs | 16 ++++ .../derives/imperfect-derive-phantom.stderr | 30 ++++++ ...undant-derive-note-on-unimplemented.stderr | 2 - .../issue-104884-trait-impl-sugg-err.stderr | 2 - ...missing-bound-in-derive-copy-impl-2.stderr | 4 - .../missing-bound-in-derive-copy-impl.stderr | 4 - 12 files changed, 261 insertions(+), 23 deletions(-) create mode 100644 tests/ui/derives/imperfect-derive-advanced.rs create mode 100644 tests/ui/derives/imperfect-derive-advanced.stderr create mode 100644 tests/ui/derives/imperfect-derive-phantom.rs create mode 100644 tests/ui/derives/imperfect-derive-phantom.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index d2e8f136d7491..1fdc924abc238 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -4374,7 +4374,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } err.span_note(spans, msg); - if derived && trait_name != "Copy" { + if derived + && trait_name != "Copy" + && self.is_truly_imperfect_derive( + parent_trait_pred, + predicate, + param_env, + ) + { err.help(format!( "consider manually implementing `{trait_name}` to avoid undesired bounds caused by \"imperfect derives\"", )); @@ -6338,6 +6345,90 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { _ => {} } } + + /// Checks whether the field independently satisfies the trait bound, ignoring + /// the specific generic parameter that caused the original E0277 error. + fn is_truly_imperfect_derive( + &self, + parent_trait_pred: ty::PolyTraitPredicate<'tcx>, + predicate: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> bool { + let tcx = self.tcx; + let ty::Adt(adt_def, parent_args) = parent_trait_pred.skip_binder().self_ty().kind() else { + return false; + }; + let Some(trait_clause) = predicate.as_trait_clause() else { + return false; + }; + let failing_ty = trait_clause.skip_binder().self_ty(); + let trait_def_id = parent_trait_pred.def_id(); + + let failing_adt_param_indices: FxHashSet = parent_args + .iter() + .enumerate() + .filter_map(|(idx, arg)| { + if let Some(t) = arg.as_type() + && t == failing_ty + { + Some(idx as u32) + } else { + None + } + }) + .collect(); + + if failing_adt_param_indices.is_empty() { + return false; + } + + adt_def.all_fields().all(|field| { + let raw_field_ty = tcx.type_of(field.did).skip_binder(); + // If the field type is exactly one of the failing parameters, it is not an imperfect derive. + if let ty::Param(p) = raw_field_ty.kind() + && failing_adt_param_indices.contains(&p.index) + { + return false; + } + // If the field type doesn't mention the parameter at all, it's independent. + if !raw_field_ty.walk().any(|arg| { + matches!(arg.as_type().map(|t| t.kind()), Some(ty::Param(p)) if failing_adt_param_indices.contains(&p.index)) + }) { + return true; + } + + self.probe(|_| { + // We keep the generic parameters that didn't fail as-is from the parent args, + // but replace the ones that did fail with fresh inference variable placeholders. + let fresh_args = ty::GenericArgs::for_item(tcx, adt_def.did(), |param, _| { + if failing_adt_param_indices.contains(¶m.index) { + self.var_for_def(DUMMY_SP, param) + } else { + parent_args[param.index as usize] + } + }); + + let field_ty = field.ty(tcx, fresh_args).skip_norm_wip(); + // Substitute the field's type for the bound's `Self` type to check whether + // the field alone would satisfy the trait, independent of other generics. + let parent_trait_args = parent_trait_pred.skip_binder().trait_ref.args; + let trait_args = parent_trait_args.iter().map(|arg| { + if arg.as_type() == Some(parent_trait_pred.skip_binder().self_ty()) { + field_ty.into() + } else { + arg + } + }); + let obligation = Obligation::new( + tcx, + ObligationCause::dummy(), + param_env, + ty::TraitRef::new(tcx, trait_def_id, trait_args), + ); + self.predicate_may_hold(&obligation) + }) + }) + } } /// Add a hint to add a missing borrow or remove an unnecessary one. diff --git a/tests/ui/associated-types/issue-38821.stderr b/tests/ui/associated-types/issue-38821.stderr index d7fcec380a3de..050054ad704bc 100644 --- a/tests/ui/associated-types/issue-38821.stderr +++ b/tests/ui/associated-types/issue-38821.stderr @@ -108,8 +108,6 @@ LL | pub enum ColumnInsertValue where ... LL | Expr: Expression::Nullable>, | ------------------------------------------------ unsatisfied trait bound - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, @@ -239,8 +237,6 @@ LL | pub enum ColumnInsertValue where ... LL | Expr: Expression::Nullable>, | ------------------------------------------------ unsatisfied trait bound - = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider further restricting the associated type | LL | Expr: Expression::Nullable>, ::SqlType: NotNull, diff --git a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr index 029c41b46ff40..ffbd0acd0f1e9 100644 --- a/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr +++ b/tests/ui/const-generics/adt_const_params/unsizing-wfcheck-issue-126272.stderr @@ -67,8 +67,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | ----- in this derive macro expansion LL | struct Bar(T); | ^^^ - unsatisfied trait bound - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit = note: 2 redundant requirements hidden = note: required for `&&'static Bar<(dyn Debug + 'static)>` to implement `Debug` = note: required for the cast from `&&&'static Bar<(dyn Debug + 'static)>` to `&dyn Debug` @@ -103,8 +101,6 @@ LL | #[derive(Debug, PartialEq, Eq, ConstParamTy)] | -- in this derive macro expansion LL | struct Bar(T); | ^^^ - type parameter would need to implement `Eq` - = help: consider manually implementing `Eq` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit = note: 1 redundant requirement hidden = note: required for `&'static Bar` to implement `Eq` note: required by a bound in `std::cmp::AssertParamIsEq` diff --git a/tests/ui/derives/clone-copy/deriving-copyclone.stderr b/tests/ui/derives/clone-copy/deriving-copyclone.stderr index 356678ada17bb..285ff40f4a877 100644 --- a/tests/ui/derives/clone-copy/deriving-copyclone.stderr +++ b/tests/ui/derives/clone-copy/deriving-copyclone.stderr @@ -38,8 +38,6 @@ LL | #[derive(Copy, Clone)] | ----- in this derive macro expansion LL | struct B { | ^ - type parameter would need to implement `Clone` - = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit note: required by a bound in `is_clone` --> $DIR/deriving-copyclone.rs:19:16 | diff --git a/tests/ui/derives/imperfect-derive-advanced.rs b/tests/ui/derives/imperfect-derive-advanced.rs new file mode 100644 index 0000000000000..52dcb03b71caa --- /dev/null +++ b/tests/ui/derives/imperfect-derive-advanced.rs @@ -0,0 +1,42 @@ +// Test that the "imperfect derives" note is emitted for associated types, +// `Rc`, and coinductive types. + +use std::rc::Rc; + +trait Trait { + type Assoc: Clone; +} + +#[derive(Clone)] +struct AssocStruct { + field: T::Assoc, +} + +struct NonClone; +impl Trait for NonClone { + type Assoc = u32; +} + +#[derive(Clone)] +struct RcStruct { + field: Rc, +} + +#[derive(Clone)] +struct List { + value: Rc, + next: Option>>, +} + +fn require_clone() {} + +fn main() { + require_clone::>(); + //~^ ERROR the trait bound `NonClone: Clone` is not satisfied + + require_clone::>(); + //~^ ERROR the trait bound `NonClone: Clone` is not satisfied + + require_clone::>(); + //~^ ERROR the trait bound `NonClone: Clone` is not satisfied +} diff --git a/tests/ui/derives/imperfect-derive-advanced.stderr b/tests/ui/derives/imperfect-derive-advanced.stderr new file mode 100644 index 0000000000000..d97d96e6dd4f8 --- /dev/null +++ b/tests/ui/derives/imperfect-derive-advanced.stderr @@ -0,0 +1,81 @@ +error[E0277]: the trait bound `NonClone: Clone` is not satisfied + --> $DIR/imperfect-derive-advanced.rs:34:21 + | +LL | require_clone::>(); + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone` + | +note: required for `AssocStruct` to implement `Clone` + --> $DIR/imperfect-derive-advanced.rs:11:8 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct AssocStruct { + | ^^^^^^^^^^^ - type parameter would need to implement `Clone` + = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" + = note: to learn more, visit +note: required by a bound in `require_clone` + --> $DIR/imperfect-derive-advanced.rs:31:21 + | +LL | fn require_clone() {} + | ^^^^^ required by this bound in `require_clone` +help: consider annotating `NonClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NonClone; + | + +error[E0277]: the trait bound `NonClone: Clone` is not satisfied + --> $DIR/imperfect-derive-advanced.rs:37:21 + | +LL | require_clone::>(); + | ^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone` + | +note: required for `RcStruct` to implement `Clone` + --> $DIR/imperfect-derive-advanced.rs:21:8 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct RcStruct { + | ^^^^^^^^ - type parameter would need to implement `Clone` + = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" + = note: to learn more, visit +note: required by a bound in `require_clone` + --> $DIR/imperfect-derive-advanced.rs:31:21 + | +LL | fn require_clone() {} + | ^^^^^ required by this bound in `require_clone` +help: consider annotating `NonClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NonClone; + | + +error[E0277]: the trait bound `NonClone: Clone` is not satisfied + --> $DIR/imperfect-derive-advanced.rs:40:21 + | +LL | require_clone::>(); + | ^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonClone` + | +note: required for `List` to implement `Clone` + --> $DIR/imperfect-derive-advanced.rs:26:8 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct List { + | ^^^^ - type parameter would need to implement `Clone` + = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" + = note: to learn more, visit +note: required by a bound in `require_clone` + --> $DIR/imperfect-derive-advanced.rs:31:21 + | +LL | fn require_clone() {} + | ^^^^^ required by this bound in `require_clone` +help: consider annotating `NonClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NonClone; + | + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/derives/imperfect-derive-phantom.rs b/tests/ui/derives/imperfect-derive-phantom.rs new file mode 100644 index 0000000000000..23cc05626fb08 --- /dev/null +++ b/tests/ui/derives/imperfect-derive-phantom.rs @@ -0,0 +1,16 @@ +// Test that the "imperfect derives" note is emitted when the derive bound +// is genuinely unnecessary (e.g., `PhantomData`). + +use std::marker::PhantomData; + +#[derive(Clone)] +struct S(PhantomData); + +struct X; + +fn require_clone(_t: T) {} + +fn main() { + require_clone(S::(PhantomData)); + //~^ ERROR the trait bound `S: Clone` is not satisfied +} diff --git a/tests/ui/derives/imperfect-derive-phantom.stderr b/tests/ui/derives/imperfect-derive-phantom.stderr new file mode 100644 index 0000000000000..ff0d7e09f286f --- /dev/null +++ b/tests/ui/derives/imperfect-derive-phantom.stderr @@ -0,0 +1,30 @@ +error[E0277]: the trait bound `S: Clone` is not satisfied + --> $DIR/imperfect-derive-phantom.rs:14:26 + | +LL | require_clone(S::(PhantomData)); + | ------------- ^^^^^^^^^^^ the trait `Clone` is not implemented for `S` + | | + | required by a bound introduced by this call + | +note: required for `S` to implement `Clone` + --> $DIR/imperfect-derive-phantom.rs:7:8 + | +LL | #[derive(Clone)] + | ----- in this derive macro expansion +LL | struct S(PhantomData); + | ^ - type parameter would need to implement `Clone` + = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" + = note: to learn more, visit +note: required by a bound in `require_clone` + --> $DIR/imperfect-derive-phantom.rs:11:21 + | +LL | fn require_clone(_t: T) {} + | ^^^^^ required by this bound in `require_clone` +help: consider borrowing here + | +LL | require_clone(S::(&PhantomData)); + | + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr b/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr index ef3c94b4f07f7..69e6e53a19d28 100644 --- a/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr +++ b/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr @@ -21,8 +21,6 @@ LL | #[derive(Debug)] | ----- in this derive macro expansion LL | struct S(T); | ^ - type parameter would need to implement `Debug` - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider annotating `X` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr index 179f9fc51a4ba..0390295a66a42 100644 --- a/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr +++ b/tests/ui/proc-macro/issue-104884-trait-impl-sugg-err.stderr @@ -44,8 +44,6 @@ LL | #[derive(PartialOrd, AddImpl)] ... LL | struct PriorityQueue(BinaryHeap>); | ^^^^^^^^^^^^^ - type parameter would need to implement `PartialOrd` - = help: consider manually implementing `PartialOrd` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit note: required by a bound in `Ord` --> $SRC_DIR/core/src/cmp.rs:LL:COL diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr index 779339232e692..ad359b538cef9 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl-2.stderr @@ -30,8 +30,6 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct Vector2 { | ^^^^^^^ ---- unsatisfied trait bound - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit = note: required for the cast from `&Vector2` to `&dyn Debug` help: consider further restricting type parameter `K` with trait `Copy` | @@ -73,8 +71,6 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct Vector2 { | ^^^^^^^ ---- unsatisfied trait bound - = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider further restricting type parameter `K` with trait `Copy` | LL | pub struct AABB { diff --git a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr index 225ab3cd5838d..92e4188e2039f 100644 --- a/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr +++ b/tests/ui/suggestions/missing-bound-in-derive-copy-impl.stderr @@ -66,8 +66,6 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct Vector2 { | ^^^^^^^ ---- unsatisfied trait bound - = help: consider manually implementing `Debug` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit = note: required for the cast from `&Vector2` to `&dyn Debug` help: consider restricting type parameter `K` with trait `Copy` | @@ -137,8 +135,6 @@ LL | #[derive(Debug, Copy, Clone)] | ----- in this derive macro expansion LL | pub struct Vector2 { | ^^^^^^^ ---- unsatisfied trait bound - = help: consider manually implementing `Clone` to avoid undesired bounds caused by "imperfect derives" - = note: to learn more, visit help: consider restricting type parameter `K` with trait `Copy` | LL | pub struct AABB {