about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2024-12-20 01:07:48 +0000
committerEsteban Küber <esteban@kuber.com.ar>2024-12-20 17:18:54 +0000
commitb3cc9b9620fc99399e7991782188dea772e335da (patch)
treea7d1619b0163dc7fa308f9823861dd9b904159bf
parent9e136a30a965bf4e63f03095c57df7257bf96fd6 (diff)
downloadrust-b3cc9b9620fc99399e7991782188dea772e335da.tar.gz
rust-b3cc9b9620fc99399e7991782188dea772e335da.zip
Restrict `#[non_exaustive]` on structs with default field values
Do not allow users to apply `#[non_exaustive]` to a struct when they have also used default field values.
-rw-r--r--compiler/rustc_passes/messages.ftl4
-rw-r--r--compiler/rustc_passes/src/check_attr.rs27
-rw-r--r--compiler/rustc_passes/src/errors.rs9
-rw-r--r--tests/ui/structs/default-field-values-non_exhaustive.rs18
-rw-r--r--tests/ui/structs/default-field-values-non_exhaustive.stderr23
5 files changed, 78 insertions, 3 deletions
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 9cc94b75624..ba3101e9058 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -566,6 +566,10 @@ passes_no_sanitize =
     `#[no_sanitize({$attr_str})]` should be applied to {$accepted_kind}
     .label = not {$accepted_kind}
 
+passes_non_exaustive_with_default_field_values =
+    `#[non_exhaustive]` can't be used to annotate items with default field values
+    .label = this struct has default field values
+
 passes_non_exported_macro_invalid_attrs =
     attribute should be applied to function or closure
     .label = not a function or closure
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 2310dd9dc72..699a5a6cd62 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -124,7 +124,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 [sym::coverage, ..] => self.check_coverage(attr, span, target),
                 [sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target),
                 [sym::no_sanitize, ..] => self.check_no_sanitize(attr, span, target),
-                [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target),
+                [sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item),
                 [sym::marker, ..] => self.check_marker(hir_id, attr, span, target),
                 [sym::target_feature, ..] => {
                     self.check_target_feature(hir_id, attr, span, target, attrs)
@@ -684,9 +684,30 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
-    fn check_non_exhaustive(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
+    fn check_non_exhaustive(
+        &self,
+        hir_id: HirId,
+        attr: &Attribute,
+        span: Span,
+        target: Target,
+        item: Option<ItemLike<'_>>,
+    ) {
         match target {
-            Target::Struct | Target::Enum | Target::Variant => {}
+            Target::Struct => {
+                if let Some(ItemLike::Item(hir::Item {
+                    kind: hir::ItemKind::Struct(hir::VariantData::Struct { fields, .. }, _),
+                    ..
+                })) = item
+                    && !fields.is_empty()
+                    && fields.iter().any(|f| f.default.is_some())
+                {
+                    self.dcx().emit_err(errors::NonExhaustiveWithDefaultFieldValues {
+                        attr_span: attr.span,
+                        defn_span: span,
+                    });
+                }
+            }
+            Target::Enum | Target::Variant => {}
             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
             // `#[non_exhaustive]` attribute with just a lint, because we previously
             // erroneously allowed it and some crates used it accidentally, to be compatible
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 5e7bfa5e3bb..163325f2a3c 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -120,6 +120,15 @@ pub(crate) struct NonExhaustiveWrongLocation {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_non_exaustive_with_default_field_values)]
+pub(crate) struct NonExhaustiveWithDefaultFieldValues {
+    #[primary_span]
+    pub attr_span: Span,
+    #[label]
+    pub defn_span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_should_be_applied_to_trait)]
 pub(crate) struct AttrShouldBeAppliedToTrait {
     #[primary_span]
diff --git a/tests/ui/structs/default-field-values-non_exhaustive.rs b/tests/ui/structs/default-field-values-non_exhaustive.rs
new file mode 100644
index 00000000000..0ad2b0766a7
--- /dev/null
+++ b/tests/ui/structs/default-field-values-non_exhaustive.rs
@@ -0,0 +1,18 @@
+#![feature(default_field_values)]
+
+#[derive(Default)]
+#[non_exhaustive] //~ ERROR `#[non_exhaustive]` can't be used to annotate items with default field values
+struct Foo {
+    x: i32 = 42 + 3,
+}
+
+#[derive(Default)]
+enum Bar {
+    #[non_exhaustive]
+    #[default]
+    Baz { //~ ERROR default variant must be exhaustive
+        x: i32 = 42 + 3,
+    }
+}
+
+fn main () {}
diff --git a/tests/ui/structs/default-field-values-non_exhaustive.stderr b/tests/ui/structs/default-field-values-non_exhaustive.stderr
new file mode 100644
index 00000000000..13013bebe83
--- /dev/null
+++ b/tests/ui/structs/default-field-values-non_exhaustive.stderr
@@ -0,0 +1,23 @@
+error: default variant must be exhaustive
+  --> $DIR/default-field-values-non_exhaustive.rs:13:5
+   |
+LL |     #[non_exhaustive]
+   |     ----------------- declared `#[non_exhaustive]` here
+LL |     #[default]
+LL |     Baz {
+   |     ^^^
+   |
+   = help: consider a manual implementation of `Default`
+
+error: `#[non_exhaustive]` can't be used to annotate items with default field values
+  --> $DIR/default-field-values-non_exhaustive.rs:4:1
+   |
+LL |   #[non_exhaustive]
+   |   ^^^^^^^^^^^^^^^^^
+LL | / struct Foo {
+LL | |     x: i32 = 42 + 3,
+LL | | }
+   | |_- this struct has default field values
+
+error: aborting due to 2 previous errors
+