From 3d83340a53695430a34779f7d5cca244dc703b1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 6 Dec 2024 19:50:55 +0000 Subject: [PATCH] Disallow `#[default] Variant {}` regardless of feature flag --- compiler/rustc_builtin_macros/messages.ftl | 2 +- .../src/deriving/default.rs | 16 +++++++++++-- compiler/rustc_builtin_macros/src/errors.rs | 1 + .../structs/default-field-values-failures.rs | 6 +++++ .../default-field-values-failures.stderr | 24 ++++++++++++------- 5 files changed, 38 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index c05d44cb45251..87d3d288013a3 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -261,7 +261,7 @@ builtin_macros_non_exhaustive_default = default variant must be exhaustive builtin_macros_non_generic_pointee = the `#[pointee]` attribute may only be used on generic parameters -builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants +builtin_macros_non_unit_default = the `#[default]` attribute may only be used on unit enum variants{$post} .help = consider a manual implementation of `Default` builtin_macros_only_one_argument = {$name} takes 1 argument diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs index 12d5587b5db84..6b1a6effad758 100644 --- a/compiler/rustc_builtin_macros/src/deriving/default.rs +++ b/compiler/rustc_builtin_macros/src/deriving/default.rs @@ -199,10 +199,17 @@ fn extract_default_variant<'a>( if cx.ecfg.features.default_field_values() && let VariantData::Struct { fields, .. } = &variant.data && fields.iter().all(|f| f.default.is_some()) + // Disallow `#[default] Variant {}` + && !fields.is_empty() { // Allowed } else if !matches!(variant.data, VariantData::Unit(..)) { - let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span }); + let post = if cx.ecfg.features.default_field_values() { + " or variants where every field has a default value" + } else { + "" + }; + let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post }); return Err(guar); } @@ -261,7 +268,12 @@ struct DetectNonVariantDefaultAttr<'a, 'b> { impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> { fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) { if attr.has_name(kw::Default) { - self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span }); + let post = if self.cx.ecfg.features.default_field_values() { + " or variants where every field has a default value" + } else { + "" + }; + self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post }); } rustc_ast::visit::walk_attribute(self, attr); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index f8e65661e52e2..c9bd3371e559d 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -424,6 +424,7 @@ pub(crate) struct MultipleDefaultsSugg { pub(crate) struct NonUnitDefault { #[primary_span] pub(crate) span: Span, + pub(crate) post: &'static str, } #[derive(Diagnostic)] diff --git a/tests/ui/structs/default-field-values-failures.rs b/tests/ui/structs/default-field-values-failures.rs index d67bea18c6994..0ac071d91d65e 100644 --- a/tests/ui/structs/default-field-values-failures.rs +++ b/tests/ui/structs/default-field-values-failures.rs @@ -41,6 +41,12 @@ const fn foo() -> i32 { 42 } +#[derive(Debug, Default)] +enum E { + #[default] + Variant {} //~ ERROR the `#[default]` attribute may only be used on unit enum variants +} + fn main () { let _ = Foo { .. }; // ok let _ = Foo::default(); // ok diff --git a/tests/ui/structs/default-field-values-failures.stderr b/tests/ui/structs/default-field-values-failures.stderr index 195ee0dbb5f1a..e60ec392fdd6d 100644 --- a/tests/ui/structs/default-field-values-failures.stderr +++ b/tests/ui/structs/default-field-values-failures.stderr @@ -1,3 +1,11 @@ +error: the `#[default]` attribute may only be used on unit enum variants or variants where every field has a default value + --> $DIR/default-field-values-failures.rs:47:5 + | +LL | Variant {} + | ^^^^^^^ + | + = help: consider a manual implementation of `Default` + error: generic parameters may not be used in const operations --> $DIR/default-field-values-failures.rs:22:23 | @@ -30,13 +38,13 @@ LL | pub struct S; | error: missing mandatory field `bar` - --> $DIR/default-field-values-failures.rs:47:21 + --> $DIR/default-field-values-failures.rs:53:21 | LL | let _ = Bar { .. }; | ^ error[E0308]: mismatched types - --> $DIR/default-field-values-failures.rs:51:17 + --> $DIR/default-field-values-failures.rs:57:17 | LL | let _ = Rak(..); | --- ^^ expected `i32`, found `RangeFull` @@ -49,19 +57,19 @@ note: tuple struct defined here LL | pub struct Rak(i32 = 42); | ^^^ help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/default-field-values-failures.rs:51:17 + --> $DIR/default-field-values-failures.rs:57:17 | LL | let _ = Rak(..); | ^^ error[E0061]: this struct takes 1 argument but 2 arguments were supplied - --> $DIR/default-field-values-failures.rs:53:13 + --> $DIR/default-field-values-failures.rs:59:13 | LL | let _ = Rak(0, ..); | ^^^ -- unexpected argument #2 of type `RangeFull` | help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/default-field-values-failures.rs:53:20 + --> $DIR/default-field-values-failures.rs:59:20 | LL | let _ = Rak(0, ..); | ^^ @@ -77,13 +85,13 @@ LL + let _ = Rak(0); | error[E0061]: this struct takes 1 argument but 2 arguments were supplied - --> $DIR/default-field-values-failures.rs:55:13 + --> $DIR/default-field-values-failures.rs:61:13 | LL | let _ = Rak(.., 0); | ^^^ -- unexpected argument #1 of type `RangeFull` | help: you might have meant to use `..` to skip providing a value for expected fields, but this is only supported on non-tuple struct literals; it is instead interpreted as a `std::ops::RangeFull` literal - --> $DIR/default-field-values-failures.rs:55:17 + --> $DIR/default-field-values-failures.rs:61:17 | LL | let _ = Rak(.., 0); | ^^ @@ -104,7 +112,7 @@ error: generic `Self` types are currently not permitted in anonymous constants LL | bar: S = Self::S, | ^^^^ -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0061, E0277, E0308. For more information about an error, try `rustc --explain E0061`.