about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2024-12-06 19:50:55 +0000
committerEsteban Küber <esteban@kuber.com.ar>2024-12-09 21:55:13 +0000
commit2d6e763cc68b2433b9df33da2a80dc4cf4953079 (patch)
tree15e2a09329bafd5af83abeccb807d61ddb60e370
parent148a77dfde03dba553b2e4f3cf817cd6d105b3e6 (diff)
downloadrust-2d6e763cc68b2433b9df33da2a80dc4cf4953079.tar.gz
rust-2d6e763cc68b2433b9df33da2a80dc4cf4953079.zip
Disallow `#[default] Variant {}` regardless of feature flag
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs16
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs1
-rw-r--r--tests/ui/structs/default-field-values-failures.rs6
-rw-r--r--tests/ui/structs/default-field-values-failures.stderr24
5 files changed, 38 insertions, 11 deletions
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index c05d44cb452..87d3d288013 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 12d5587b5db..6b1a6effad7 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 f8e65661e52..c9bd3371e55 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 d67bea18c69..0ac071d91d6 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 195ee0dbb5f..e60ec392fdd 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`.