Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restrict #[non_exaustive] on structs with default field values #134539

Merged
merged 1 commit into from
Dec 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_passes/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 24 additions & 3 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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,
});
jieyouxu marked this conversation as resolved.
Show resolved Hide resolved
}
}
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
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,15 @@ pub(crate) struct NonExhaustiveWrongLocation {
pub defn_span: Span,
}

#[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 {
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/structs/default-field-values-non_exhaustive.rs
Original file line number Diff line number Diff line change
@@ -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 () {}
23 changes: 23 additions & 0 deletions tests/ui/structs/default-field-values-non_exhaustive.stderr
Original file line number Diff line number Diff line change
@@ -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

Loading