Rollup merge of #154118 - malezjaa:dont-suggest-some-traits-for-unions, r=JonathanBrouwer

don't suggest non-deriveable traits for unions

Fixes rust-lang/rust#137587

Before, the compiler suggested adding `#[derive(Debug)]` (other traits too) for unions,
which is misleading because some traits can't be automatically derived.

Only traits that are still suggested are Copy and Clone.

I noticed the error label changed after removing the suggestion. I hope this isn't a big deal, but let me know if that's an issue.

original example:
```rs
union Union {
    member: usize,
}

impl PartialEq<u8> for Union {
    fn eq(&self, rhs: &u8) -> bool {
        unsafe { self.member == (*rhs).into() }
    }
}

fn main() {
    assert_eq!(Union { member: 0 }, 0);
}
```

before:
```
error[E0277]: `Union` doesn't implement `Debug`
  --> src\main.rs:13:5
   |
13 |     assert_eq!(Union { member: 0 }, 0);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Debug` is not implemented for `Union`
   |
   = note: add `#[derive(Debug)]` to `Union` or manually `impl Debug for Union`
   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider annotating `Union` with `#[derive(Debug)]`
   |
 2 + #[derive(Debug)]
 3 | union Union {
   |
```

after (the message doesn't suggest adding #[derive(Debug)] to unions)
```
error[E0277]: `Union` doesn't implement `Debug`
  --> src\main.rs:13:5
   |
13 |     assert_eq!(Union { member: 0 }, 0);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
   |
help: the trait `Debug` is not implemented for `Union`
  --> src\main.rs:2:1
   |
 2 | union Union {
   | ^^^^^^^^^^^
   = note: manually `impl Debug for Union`
```
This commit is contained in:
Jonathan Brouwer
2026-03-21 00:42:48 +01:00
committed by GitHub
11 changed files with 310 additions and 32 deletions
@@ -470,11 +470,12 @@ fn parse_filter(input: Symbol) -> FilterFormatString {
// if the integer type has been resolved, to allow targeting all integers.
// `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet,
// from the `Display` impl of `InferTy` to be precise.
// `"{union|enum|struct}"` is used as a special selector for ADTs.
//
// Don't try to format these later!
Position::ArgumentNamed(arg @ ("integer" | "integral" | "float")) => {
LitOrArg::Lit(Symbol::intern(&format!("{{{arg}}}")))
}
Position::ArgumentNamed(
arg @ ("integer" | "integral" | "float" | "union" | "enum" | "struct"),
) => LitOrArg::Lit(Symbol::intern(&format!("{{{arg}}}"))),
Position::ArgumentNamed(arg) => LitOrArg::Arg(Symbol::intern(arg)),
Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(sym::empty_braces),
@@ -3460,13 +3460,13 @@ fn consider_suggesting_derives_for_ty(
let diagnostic_name = self.tcx.get_diagnostic_name(trait_pred.def_id())?;
let can_derive = match diagnostic_name {
sym::Copy | sym::Clone => true,
_ if adt.is_union() => false,
sym::Default
| sym::Eq
| sym::PartialEq
| sym::Ord
| sym::PartialOrd
| sym::Clone
| sym::Copy
| sym::Hash
| sym::Debug => true,
_ => false,
@@ -124,8 +124,11 @@ pub(crate) fn on_unimplemented_components(
}
}
if let Some(true) = self_ty.ty_adt_def().map(|def| def.did().is_local()) {
crate_local = true;
if let Some(adt) = self_ty.ty_adt_def() {
if adt.did().is_local() {
crate_local = true;
}
self_types.push(format!("{{{}}}", adt.descr()))
}
// Allow targeting all integers using `{integral}`, even if the exact type was resolved
@@ -4037,12 +4037,13 @@ pub fn can_suggest_derive(
_ => return false,
};
let is_derivable_trait = match diagnostic_name {
sym::Default => !adt.is_enum(),
sym::Copy | sym::Clone => true,
_ if adt.is_union() => false,
sym::PartialEq | sym::PartialOrd => {
let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
trait_pred.skip_binder().self_ty() == rhs_ty
}
sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
sym::Eq | sym::Ord | sym::Hash | sym::Debug | sym::Default => true,
_ => false,
};
is_derivable_trait &&
+2 -1
View File
@@ -1037,9 +1037,10 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_on_unimplemented(
on(
crate_local,
all(crate_local, not(Self = "{union}")),
note = "add `#[derive(Debug)]` to `{Self}` or manually `impl {This} for {Self}`"
),
on(all(crate_local, Self = "{union}"), note = "manually `impl {This} for {Self}`"),
on(
from_desugaring = "FormatLiteral",
label = "`{Self}` cannot be formatted using `{{:?}}` because it doesn't implement `{This}`"
@@ -26,12 +26,15 @@ impl Bar for i32 {}
// cannot use special rustc_on_unimplement symbols
// in the format string
#[diagnostic::on_unimplemented(
message = "{from_desugaring}{direct}{cause}{integral}{integer}",
message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
//~^WARN there is no parameter `from_desugaring` on trait `Baz`
//~|WARN there is no parameter `direct` on trait `Baz`
//~|WARN there is no parameter `cause` on trait `Baz`
//~|WARN there is no parameter `integral` on trait `Baz`
//~|WARN there is no parameter `integer` on trait `Baz`
//~|WARN there is no parameter `r#struct` on trait `Baz`
//~|WARN there is no parameter `r#enum` on trait `Baz`
//~|WARN there is no parameter `union` on trait `Baz`
label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
//~^WARN there is no parameter `float` on trait `Baz`
//~|WARN there is no parameter `_Self` on trait `Baz`
@@ -52,5 +55,5 @@ fn main() {
takes_bar(());
//~^ERROR the trait bound `(): Bar` is not satisfied
takes_baz(());
//~^ERROR {from_desugaring}{direct}{cause}{integral}{integer}
//~^ERROR {from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}
}
@@ -9,7 +9,7 @@ LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl
warning: there is no parameter `from_desugaring` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:17
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
| ^^^^^^^^^^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
@@ -18,7 +18,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
warning: there is no parameter `direct` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:34
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
| ^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
@@ -26,7 +26,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
warning: there is no parameter `cause` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:42
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
| ^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
@@ -34,7 +34,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
warning: there is no parameter `integral` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:49
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
| ^^^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
@@ -42,13 +42,37 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
warning: there is no parameter `integer` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:59
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}",
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
| ^^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `r#struct` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:68
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
| ^^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `r#enum` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:76
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
| ^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `union` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:29:82
|
LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}",
| ^^^^^
|
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `float` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:15
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:15
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
@@ -56,7 +80,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `_Self` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:22
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:22
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
@@ -64,7 +88,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `crate_local` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:29
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:29
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^^^^^^^
@@ -72,7 +96,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `Trait` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:42
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:42
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^
@@ -80,7 +104,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `ItemContext` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:49
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:49
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^^^^^^^^
@@ -88,7 +112,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
= help: expect either a generic argument name or `{Self}` as format argument
warning: there is no parameter `This` on trait `Baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:35:62
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:62
|
LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}"
| ^^^^
@@ -129,7 +153,7 @@ LL | #[diagnostic::on_unimplemented = "Message"]
= help: only `message`, `note` and `label` are allowed as options
error[E0277]: trait has `()` and `i32` as params
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:15
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:53:15
|
LL | takes_foo(());
| --------- ^^ trait has `()` and `i32` as params
@@ -144,13 +168,13 @@ help: this trait has no implementations, consider adding one
LL | trait Foo<T> {}
| ^^^^^^^^^^^^
note: required by a bound in `takes_foo`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:45:22
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:48:22
|
LL | fn takes_foo(_: impl Foo<i32>) {}
| ^^^^^^^^ required by this bound in `takes_foo`
error[E0277]: the trait bound `(): Bar` is not satisfied
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:52:15
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:55:15
|
LL | takes_bar(());
| --------- ^^ the trait `Bar` is not implemented for `()`
@@ -163,13 +187,13 @@ help: the trait `Bar` is implemented for `i32`
LL | impl Bar for i32 {}
| ^^^^^^^^^^^^^^^^
note: required by a bound in `takes_bar`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:22
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:49:22
|
LL | fn takes_bar(_: impl Bar) {}
| ^^^ required by this bound in `takes_bar`
error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer}
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:54:15
error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union}
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:57:15
|
LL | takes_baz(());
| --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}{This}
@@ -178,16 +202,16 @@ LL | takes_baz(());
|
= help: the trait `Baz` is not implemented for `()`
help: this trait has no implementations, consider adding one
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:43:1
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:1
|
LL | trait Baz {}
| ^^^^^^^^^
note: required by a bound in `takes_baz`
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:47:22
--> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:22
|
LL | fn takes_baz(_: impl Baz) {}
| ^^^ required by this bound in `takes_baz`
error: aborting due to 3 previous errors; 16 warnings emitted
error: aborting due to 3 previous errors; 19 warnings emitted
For more information about this error, try `rustc --explain E0277`.
+30
View File
@@ -0,0 +1,30 @@
#![feature(rustc_attrs)]
#[rustc_on_unimplemented(
on(Self = "{union}", message = "union self type"),
on(Self = "{enum}", message = "enum self type"),
on(Self = "{struct}", message = "struct self type"),
message = "fallback self type `{Self}`"
)]
trait Trait {}
union Union {
value: u8,
}
enum Enum {
Variant,
}
struct Struct;
fn needs_trait<T: Trait>() {}
fn main() {
needs_trait::<Union>();
//~^ ERROR union self type
needs_trait::<Enum>();
//~^ ERROR enum self type
needs_trait::<Struct>();
//~^ ERROR struct self type
}
@@ -0,0 +1,69 @@
error[E0277]: union self type
--> $DIR/self-types.rs:24:19
|
LL | needs_trait::<Union>();
| ^^^^^ unsatisfied trait bound
|
help: the trait `Trait` is not implemented for `Union`
--> $DIR/self-types.rs:11:1
|
LL | union Union {
| ^^^^^^^^^^^
help: this trait has no implementations, consider adding one
--> $DIR/self-types.rs:9:1
|
LL | trait Trait {}
| ^^^^^^^^^^^
note: required by a bound in `needs_trait`
--> $DIR/self-types.rs:21:19
|
LL | fn needs_trait<T: Trait>() {}
| ^^^^^ required by this bound in `needs_trait`
error[E0277]: enum self type
--> $DIR/self-types.rs:26:19
|
LL | needs_trait::<Enum>();
| ^^^^ unsatisfied trait bound
|
help: the trait `Trait` is not implemented for `Enum`
--> $DIR/self-types.rs:15:1
|
LL | enum Enum {
| ^^^^^^^^^
help: this trait has no implementations, consider adding one
--> $DIR/self-types.rs:9:1
|
LL | trait Trait {}
| ^^^^^^^^^^^
note: required by a bound in `needs_trait`
--> $DIR/self-types.rs:21:19
|
LL | fn needs_trait<T: Trait>() {}
| ^^^^^ required by this bound in `needs_trait`
error[E0277]: struct self type
--> $DIR/self-types.rs:28:19
|
LL | needs_trait::<Struct>();
| ^^^^^^ unsatisfied trait bound
|
help: the trait `Trait` is not implemented for `Struct`
--> $DIR/self-types.rs:19:1
|
LL | struct Struct;
| ^^^^^^^^^^^^^
help: this trait has no implementations, consider adding one
--> $DIR/self-types.rs:9:1
|
LL | trait Trait {}
| ^^^^^^^^^^^
note: required by a bound in `needs_trait`
--> $DIR/self-types.rs:21:19
|
LL | fn needs_trait<T: Trait>() {}
| ^^^^^ required by this bound in `needs_trait`
error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.
@@ -0,0 +1,37 @@
//! Check that we do not suggest using `#[derive(...)]` for unions,
//! as some traits cannot be autoderived for them.
//@ dont-require-annotations: NOTE
union U { //~ HELP consider annotating `U` with `#[derive(Clone)]`
//~| HELP consider annotating `U` with `#[derive(Copy)]`
//~| HELP the trait `Debug` is not implemented for `U`
//~| HELP the trait `Default` is not implemented for `U`
//~| HELP the trait `Hash` is not implemented for `U`
a: u8,
}
fn x<T: Clone>() {}
fn y<T: Copy>() {}
fn main() {
let u = U { a: 0 };
// Debug
println!("{u:?}"); //~ ERROR `U` doesn't implement `Debug`
//~| NOTE manually `impl Debug for U`
// PartialEq
let _ = u == U { a: 0 }; //~ ERROR binary operation `==` cannot be applied to type `U`
//~| NOTE the trait `PartialEq` must be implemented
// PartialOrd
let _ = u < U { a: 1 }; //~ ERROR binary operation `<` cannot be applied to type `U`
//~| NOTE the trait `PartialOrd` must be implemented
// Default
let _: U = Default::default(); //~ ERROR the trait bound `U: Default` is not satisfied
// Hash
let mut h = std::collections::hash_map::DefaultHasher::new();
std::hash::Hash::hash(&u, &mut h); //~ ERROR the trait bound `U: Hash` is not satisfied
// Clone
x::<U>(); //~ ERROR the trait bound `U: Clone` is not satisfied
// Copy
y::<U>(); //~ ERROR the trait bound `U: Copy` is not satisfied
}
@@ -0,0 +1,109 @@
error[E0277]: `U` doesn't implement `Debug`
--> $DIR/union-no-derive-suggestion.rs:19:15
|
LL | println!("{u:?}");
| ^^^^^ `U` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
help: the trait `Debug` is not implemented for `U`
--> $DIR/union-no-derive-suggestion.rs:5:1
|
LL | union U {
| ^^^^^^^
= note: manually `impl Debug for U`
error[E0369]: binary operation `==` cannot be applied to type `U`
--> $DIR/union-no-derive-suggestion.rs:22:15
|
LL | let _ = u == U { a: 0 };
| - ^^ ---------- U
| |
| U
|
note: an implementation of `PartialEq` might be missing for `U`
--> $DIR/union-no-derive-suggestion.rs:5:1
|
LL | union U {
| ^^^^^^^ must implement `PartialEq`
note: the trait `PartialEq` must be implemented
--> $SRC_DIR/core/src/cmp.rs:LL:COL
error[E0369]: binary operation `<` cannot be applied to type `U`
--> $DIR/union-no-derive-suggestion.rs:25:15
|
LL | let _ = u < U { a: 1 };
| - ^ ---------- U
| |
| U
|
note: an implementation of `PartialOrd` might be missing for `U`
--> $DIR/union-no-derive-suggestion.rs:5:1
|
LL | union U {
| ^^^^^^^ must implement `PartialOrd`
note: the trait `PartialOrd` must be implemented
--> $SRC_DIR/core/src/cmp.rs:LL:COL
error[E0277]: the trait bound `U: Default` is not satisfied
--> $DIR/union-no-derive-suggestion.rs:28:16
|
LL | let _: U = Default::default();
| ^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
|
help: the trait `Default` is not implemented for `U`
--> $DIR/union-no-derive-suggestion.rs:5:1
|
LL | union U {
| ^^^^^^^
error[E0277]: the trait bound `U: Hash` is not satisfied
--> $DIR/union-no-derive-suggestion.rs:31:27
|
LL | std::hash::Hash::hash(&u, &mut h);
| --------------------- ^^ unsatisfied trait bound
| |
| required by a bound introduced by this call
|
help: the trait `Hash` is not implemented for `U`
--> $DIR/union-no-derive-suggestion.rs:5:1
|
LL | union U {
| ^^^^^^^
error[E0277]: the trait bound `U: Clone` is not satisfied
--> $DIR/union-no-derive-suggestion.rs:34:9
|
LL | x::<U>();
| ^ the trait `Clone` is not implemented for `U`
|
note: required by a bound in `x`
--> $DIR/union-no-derive-suggestion.rs:13:9
|
LL | fn x<T: Clone>() {}
| ^^^^^ required by this bound in `x`
help: consider annotating `U` with `#[derive(Clone)]`
|
LL + #[derive(Clone)]
LL | union U {
|
error[E0277]: the trait bound `U: Copy` is not satisfied
--> $DIR/union-no-derive-suggestion.rs:36:9
|
LL | y::<U>();
| ^ the trait `Copy` is not implemented for `U`
|
note: required by a bound in `y`
--> $DIR/union-no-derive-suggestion.rs:14:9
|
LL | fn y<T: Copy>() {}
| ^^^^ required by this bound in `y`
help: consider annotating `U` with `#[derive(Copy)]`
|
LL + #[derive(Copy)]
LL | union U {
|
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0277, E0369.
For more information about an error, try `rustc --explain E0277`.