Skip to content

Commit 6b57145

Browse files
committed
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.
1 parent 9e136a3 commit 6b57145

File tree

5 files changed

+79
-4
lines changed

5 files changed

+79
-4
lines changed

compiler/rustc_passes/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,10 @@ passes_no_sanitize =
566566
`#[no_sanitize({$attr_str})]` should be applied to {$accepted_kind}
567567
.label = not {$accepted_kind}
568568
569+
passes_non_exaustive_with_default_field_values =
570+
`#[non_exhaustive]` can't be used to annotate items with default field values
571+
.label = this struct has default field values
572+
569573
passes_non_exported_macro_invalid_attrs =
570574
attribute should be applied to function or closure
571575
.label = not a function or closure

compiler/rustc_passes/src/check_attr.rs

+24-3
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
124124
[sym::coverage, ..] => self.check_coverage(attr, span, target),
125125
[sym::optimize, ..] => self.check_optimize(hir_id, attr, span, target),
126126
[sym::no_sanitize, ..] => self.check_no_sanitize(attr, span, target),
127-
[sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target),
127+
[sym::non_exhaustive, ..] => self.check_non_exhaustive(hir_id, attr, span, target, item),
128128
[sym::marker, ..] => self.check_marker(hir_id, attr, span, target),
129129
[sym::target_feature, ..] => {
130130
self.check_target_feature(hir_id, attr, span, target, attrs)
@@ -684,9 +684,30 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
684684
}
685685

686686
/// Checks if the `#[non_exhaustive]` attribute on an `item` is valid.
687-
fn check_non_exhaustive(&self, hir_id: HirId, attr: &Attribute, span: Span, target: Target) {
687+
fn check_non_exhaustive(
688+
&self,
689+
hir_id: HirId,
690+
attr: &Attribute,
691+
span: Span,
692+
target: Target,
693+
item: Option<ItemLike<'_>>,
694+
) {
688695
match target {
689-
Target::Struct | Target::Enum | Target::Variant => {}
696+
Target::Struct => {
697+
if let Some(ItemLike::Item(hir::Item {
698+
kind: hir::ItemKind::Struct(hir::VariantData::Struct { fields, .. }, _),
699+
..
700+
})) = item
701+
&& !fields.is_empty()
702+
&& fields.iter().any(|f| f.default.is_some())
703+
{
704+
self.dcx().emit_err(errors::NonExhaustiveWithDefaultFieldValues {
705+
attr_span: attr.span,
706+
defn_span: span,
707+
});
708+
}
709+
}
710+
Target::Enum | Target::Variant => {}
690711
// FIXME(#80564): We permit struct fields, match arms and macro defs to have an
691712
// `#[non_exhaustive]` attribute with just a lint, because we previously
692713
// erroneously allowed it and some crates used it accidentally, to be compatible

compiler/rustc_passes/src/errors.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,16 @@ pub(crate) struct NonExhaustiveWrongLocation {
120120
}
121121

122122
#[derive(Diagnostic)]
123-
#[diag(passes_should_be_applied_to_trait)]
123+
#[diag(passes_non_exaustive_with_default_field_values)]
124+
pub(crate) struct NonExhaustiveWithDefaultFieldValues {
125+
#[primary_span]
126+
pub attr_span: Span,
127+
#[label]
128+
pub defn_span: Span,
129+
}
130+
131+
#[derive(Diagnostic)]
132+
#[diag(passes_should_be_applied_to_struct_enum)]
124133
pub(crate) struct AttrShouldBeAppliedToTrait {
125134
#[primary_span]
126135
pub attr_span: Span,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#![feature(default_field_values)]
2+
3+
#[derive(Default)]
4+
#[non_exhaustive] //~ ERROR `#[non_exhaustive]` can't be used to annotate items with default field values
5+
struct Foo {
6+
x: i32 = 42 + 3,
7+
}
8+
9+
#[derive(Default)]
10+
enum Bar {
11+
#[non_exhaustive]
12+
#[default]
13+
Baz { //~ ERROR default variant must be exhaustive
14+
x: i32 = 42 + 3,
15+
}
16+
}
17+
18+
fn main () {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: default variant must be exhaustive
2+
--> $DIR/default-field-values-non_exhaustive.rs:13:5
3+
|
4+
LL | #[non_exhaustive]
5+
| ----------------- declared `#[non_exhaustive]` here
6+
LL | #[default]
7+
LL | Baz {
8+
| ^^^
9+
|
10+
= help: consider a manual implementation of `Default`
11+
12+
error: `#[non_exhaustive]` can't be used to annotate items with default field values
13+
--> $DIR/default-field-values-non_exhaustive.rs:4:1
14+
|
15+
LL | #[non_exhaustive]
16+
| ^^^^^^^^^^^^^^^^^
17+
LL | / struct Foo {
18+
LL | | x: i32 = 42 + 3,
19+
LL | | }
20+
| |_- this struct has default field values
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)