From 8685d8fd39da30b1b26fb0e656caf5b6fe540244 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Fri, 29 May 2026 18:02:10 -0300 Subject: [PATCH 1/2] Avoid redundant note when a #[derive] is already suggested The `#[rustc_on_unimplemented]` note on `Debug` ("add `#[derive(Debug)]` to `X` or manually `impl Debug for X`") duplicates the `consider annotating X with #[derive(..)]` suggestion emitted by `suggest_derive`. Skip the note when that suggestion will be shown, and keep it otherwise so types whose derive can't be suggested (e.g. a field isn't `Debug`) still get actionable guidance. --- .../traits/fulfillment_errors.rs | 13 ++++++++ .../derives/debug/derives-span-Debug.stderr | 4 --- .../redundant-derive-note-on-unimplemented.rs | 15 +++++++++ ...undant-derive-note-on-unimplemented.stderr | 31 +++++++++++++++++++ tests/ui/fmt/format-args-argument-span.stderr | 2 -- tests/ui/fmt/non-source-literals.stderr | 2 -- .../method-help-unsatisfied-bound.stderr | 1 - tests/ui/modules/issue-107649.stderr | 1 - tests/ui/on-unimplemented/no-debug.stderr | 1 - .../dbg-macro-requires-debug.stderr | 1 - tests/ui/span/issue-71363.stderr | 1 - .../derive-macro-missing-bounds.stderr | 1 - 12 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 tests/ui/derives/redundant-derive-note-on-unimplemented.rs create mode 100644 tests/ui/derives/redundant-derive-note-on-unimplemented.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 4d051a370c065..cc0cbf7427fdd 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -448,8 +448,21 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some((msg, span)) = type_def { err.span_label(span, msg); } + // `#[rustc_on_unimplemented]` notes for derivable traits (e.g. `Debug`'s + // "add `#[derive(Debug)]` to `X` or manually `impl Debug for X`") duplicate + // the `consider annotating X with #[derive(..)]` suggestion that + // `suggest_derive` emits below, so skip them when that suggestion will be + // shown. We keep the note otherwise (e.g. when a field isn't `Debug`, so + // the derive can't be suggested) to avoid leaving the diagnostic without + // actionable guidance. + let derive_suggestion_will_be_shown = main_trait_predicate + == leaf_trait_predicate + && self.can_suggest_derive(&obligation, leaf_trait_predicate); for note in notes { // If it has a custom `#[rustc_on_unimplemented]` note, let's display it + if derive_suggestion_will_be_shown { + continue; + } err.note(note); } if let Some(s) = parent_label { diff --git a/tests/ui/derives/debug/derives-span-Debug.stderr b/tests/ui/derives/debug/derives-span-Debug.stderr index e4a2560db4512..0f322e37b05c3 100644 --- a/tests/ui/derives/debug/derives-span-Debug.stderr +++ b/tests/ui/derives/debug/derives-span-Debug.stderr @@ -7,7 +7,6 @@ LL | #[derive(Debug)] LL | x: Error, | ^^^^^^^^ the trait `Debug` is not implemented for `Error` | - = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error` help: consider annotating `Error` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -23,7 +22,6 @@ LL | #[derive(Debug)] LL | Error, | ^^^^^ the trait `Debug` is not implemented for `Error` | - = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error` help: consider annotating `Error` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -39,7 +37,6 @@ LL | struct Struct { LL | x: Error, | ^^^^^^^^ the trait `Debug` is not implemented for `Error` | - = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error` help: consider annotating `Error` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -55,7 +52,6 @@ LL | struct TupleStruct( LL | Error, | ^^^^^ the trait `Debug` is not implemented for `Error` | - = note: add `#[derive(Debug)]` to `Error` or manually `impl Debug for Error` help: consider annotating `Error` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/derives/redundant-derive-note-on-unimplemented.rs b/tests/ui/derives/redundant-derive-note-on-unimplemented.rs new file mode 100644 index 0000000000000..f708d3a48359c --- /dev/null +++ b/tests/ui/derives/redundant-derive-note-on-unimplemented.rs @@ -0,0 +1,15 @@ +// Regression test for the redundant `note: add `#[derive(Debug)]` to `X` or manually +// `impl Debug for X`` that was emitted alongside the `consider annotating X with +// `#[derive(Debug)]`` suggestion. When the derive suggestion is shown, the note is +// redundant and should be suppressed. +// +// See https://github.com/rust-lang/rust/issues/157118 + +#[derive(Debug)] +struct S(T); + +struct X; + +fn main() { + println!("{:?}", S(X)); //~ ERROR `X` doesn't implement `Debug` +} diff --git a/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr b/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr new file mode 100644 index 0000000000000..dcb1639593a58 --- /dev/null +++ b/tests/ui/derives/redundant-derive-note-on-unimplemented.stderr @@ -0,0 +1,31 @@ +error[E0277]: `X` doesn't implement `Debug` + --> $DIR/redundant-derive-note-on-unimplemented.rs:14:22 + | +LL | println!("{:?}", S(X)); + | ---- ^^^^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | | + | required by this formatting parameter + | + = help: the trait `Debug` is not implemented for `X` +help: the trait `Debug` is implemented for `S` + --> $DIR/redundant-derive-note-on-unimplemented.rs:8:10 + | +LL | #[derive(Debug)] + | ^^^^^ +note: required for `S` to implement `Debug` + --> $DIR/redundant-derive-note-on-unimplemented.rs:9:8 + | +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 +help: consider annotating `X` with `#[derive(Debug)]` + | +LL + #[derive(Debug)] +LL | struct X; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/fmt/format-args-argument-span.stderr b/tests/ui/fmt/format-args-argument-span.stderr index a486abe821b74..76fc178bb3d44 100644 --- a/tests/ui/fmt/format-args-argument-span.stderr +++ b/tests/ui/fmt/format-args-argument-span.stderr @@ -25,7 +25,6 @@ LL | println!("{x} {x:?} {x}"); | ^^^^^ `DisplayOnly` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = help: the trait `Debug` is not implemented for `DisplayOnly` - = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly` help: consider annotating `DisplayOnly` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -41,7 +40,6 @@ LL | println!("{x} {x:?} {x}", x = DisplayOnly); | required by this formatting parameter | = help: the trait `Debug` is not implemented for `DisplayOnly` - = note: add `#[derive(Debug)]` to `DisplayOnly` or manually `impl Debug for DisplayOnly` help: consider annotating `DisplayOnly` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/fmt/non-source-literals.stderr b/tests/ui/fmt/non-source-literals.stderr index 953a4a64fd8dc..39dc35653618f 100644 --- a/tests/ui/fmt/non-source-literals.stderr +++ b/tests/ui/fmt/non-source-literals.stderr @@ -31,7 +31,6 @@ LL | let _ = format!(concat!("{:", "?}"), NonDebug); | ^^^^^^^^ `NonDebug` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = help: the trait `Debug` is not implemented for `NonDebug` - = note: add `#[derive(Debug)]` to `NonDebug` or manually `impl Debug for NonDebug` help: consider annotating `NonDebug` with `#[derive(Debug)]` | LL + #[derive(Debug)] @@ -45,7 +44,6 @@ LL | let _ = format!(concat!("{", "0", ":?}"), NonDebug); | ^^^^^^^^ `NonDebug` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = help: the trait `Debug` is not implemented for `NonDebug` - = note: add `#[derive(Debug)]` to `NonDebug` or manually `impl Debug for NonDebug` help: consider annotating `NonDebug` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr b/tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr index 23bc9dc0f844e..ce63a69b927c5 100644 --- a/tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr +++ b/tests/ui/mismatched_types/method-help-unsatisfied-bound.stderr @@ -4,7 +4,6 @@ error[E0277]: `Foo` doesn't implement `Debug` LL | a.unwrap(); | ^^^^^^ the trait `Debug` is not implemented for `Foo` | - = note: add `#[derive(Debug)]` to `Foo` or manually `impl Debug for Foo` note: required by a bound in `Result::::unwrap` --> $SRC_DIR/core/src/result.rs:LL:COL help: consider annotating `Foo` with `#[derive(Debug)]` diff --git a/tests/ui/modules/issue-107649.stderr b/tests/ui/modules/issue-107649.stderr index 40dda93ad015c..d41fcced9be4a 100644 --- a/tests/ui/modules/issue-107649.stderr +++ b/tests/ui/modules/issue-107649.stderr @@ -4,7 +4,6 @@ error[E0277]: `Dummy` doesn't implement `Debug` 105 | dbg!(lib::Dummy); | ^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `Dummy` | - = note: add `#[derive(Debug)]` to `Dummy` or manually `impl Debug for Dummy` help: consider annotating `Dummy` with `#[derive(Debug)]` --> $DIR/auxiliary/dummy_lib.rs:2:1 | diff --git a/tests/ui/on-unimplemented/no-debug.stderr b/tests/ui/on-unimplemented/no-debug.stderr index 3c3b8d2e20541..c078e0fd42531 100644 --- a/tests/ui/on-unimplemented/no-debug.stderr +++ b/tests/ui/on-unimplemented/no-debug.stderr @@ -7,7 +7,6 @@ LL | println!("{:?} {:?}", Foo, Bar); | required by this formatting parameter | = help: the trait `Debug` is not implemented for `Foo` - = note: add `#[derive(Debug)]` to `Foo` or manually `impl Debug for Foo` help: consider annotating `Foo` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr index 4cdeb184bd915..f8699f9e55e24 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-requires-debug.stderr @@ -4,7 +4,6 @@ error[E0277]: `NotDebug` doesn't implement `Debug` LL | let _: NotDebug = dbg!(NotDebug); | ^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `NotDebug` | - = note: add `#[derive(Debug)]` to `NotDebug` or manually `impl Debug for NotDebug` help: consider annotating `NotDebug` with `#[derive(Debug)]` | LL + #[derive(Debug)] diff --git a/tests/ui/span/issue-71363.stderr b/tests/ui/span/issue-71363.stderr index d2f780bdbcb43..96c45d285e8d0 100644 --- a/tests/ui/span/issue-71363.stderr +++ b/tests/ui/span/issue-71363.stderr @@ -18,7 +18,6 @@ error[E0277]: `MyError` doesn't implement `Debug` 4 | impl std::error::Error for MyError {} | ^^^^^^^ the trait `Debug` is not implemented for `MyError` | - = note: add `#[derive(Debug)]` to `MyError` or manually `impl Debug for MyError` note: required by a bound in `std::error::Error` --> $SRC_DIR/core/src/error.rs:LL:COL help: consider annotating `MyError` with `#[derive(Debug)]` diff --git a/tests/ui/suggestions/derive-macro-missing-bounds.stderr b/tests/ui/suggestions/derive-macro-missing-bounds.stderr index b28f39ced542d..0d05bd9d91bbf 100644 --- a/tests/ui/suggestions/derive-macro-missing-bounds.stderr +++ b/tests/ui/suggestions/derive-macro-missing-bounds.stderr @@ -6,7 +6,6 @@ LL | #[derive(Debug)] LL | struct Outer(Inner); | ^^^^^^^^ the trait `Debug` is not implemented for `a::Inner` | - = note: add `#[derive(Debug)]` to `a::Inner` or manually `impl Debug for a::Inner` help: consider annotating `a::Inner` with `#[derive(Debug)]` | LL + #[derive(Debug)] From 96a2ded92e7d54acdd1efe80a9dacb1ed98d2548 Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Fri, 29 May 2026 21:29:22 -0300 Subject: [PATCH 2/2] Hoist redundant-note check out of the note loop The condition is loop-invariant, so iterating the notes only to skip every one wastes work. Test the derive suggestion once and skip the whole loop when it will be shown. --- .../src/error_reporting/traits/fulfillment_errors.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index cc0cbf7427fdd..28b23bd37485a 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -458,12 +458,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let derive_suggestion_will_be_shown = main_trait_predicate == leaf_trait_predicate && self.can_suggest_derive(&obligation, leaf_trait_predicate); - for note in notes { - // If it has a custom `#[rustc_on_unimplemented]` note, let's display it - if derive_suggestion_will_be_shown { - continue; + if !derive_suggestion_will_be_shown { + for note in notes { + // If it has a custom `#[rustc_on_unimplemented]` note, let's display + // it. + err.note(note); } - err.note(note); } if let Some(s) = parent_label { let body = obligation.cause.body_id;