From b88ad5e994c539490fa8e54f971e7607b5498f43 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 30 Aug 2026 18:06:16 +0200 Subject: [PATCH 1/2] Add regression test --- tests/ui/parser/empty-adt-inner-attributes.rs | 17 ++++++++ .../parser/empty-adt-inner-attributes.stderr | 40 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 tests/ui/parser/empty-adt-inner-attributes.rs create mode 100644 tests/ui/parser/empty-adt-inner-attributes.stderr diff --git a/tests/ui/parser/empty-adt-inner-attributes.rs b/tests/ui/parser/empty-adt-inner-attributes.rs new file mode 100644 index 0000000000000..f463fbf9f4fdc --- /dev/null +++ b/tests/ui/parser/empty-adt-inner-attributes.rs @@ -0,0 +1,17 @@ +struct Test1 { + #![inline] + //~^ ERROR an inner attribute is not permitted in this context +} + +enum Test2 { + #![inline] + //~^ ERROR an inner attribute is not permitted in this context +} + +union Test3 { + //~^ ERROR unions cannot have zero fields + #![inline] + //~^ ERROR an inner attribute is not permitted in this context +} + +fn main() { } diff --git a/tests/ui/parser/empty-adt-inner-attributes.stderr b/tests/ui/parser/empty-adt-inner-attributes.stderr new file mode 100644 index 0000000000000..6678ca8c27f79 --- /dev/null +++ b/tests/ui/parser/empty-adt-inner-attributes.stderr @@ -0,0 +1,40 @@ +error: an inner attribute is not permitted in this context + --> $DIR/empty-struct-inner-attributes.rs:2:5 + | +LL | #![inline] + | ^^^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + = note: outer attributes, like `#[test]`, annotate the item following them + +error: expected identifier, found `}` + --> $DIR/empty-struct-inner-attributes.rs:4:1 + | +LL | struct Test1 { + | ----- while parsing this struct +... +LL | } + | ^ expected identifier + +error: an inner attribute is not permitted in this context + --> $DIR/empty-struct-inner-attributes.rs:7:5 + | +LL | #![inline] + | ^^^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + = note: outer attributes, like `#[test]`, annotate the item following them + +error: expected identifier, found `}` + --> $DIR/empty-struct-inner-attributes.rs:9:1 + | +LL | enum Test2 { + | ----- while parsing this enum +... +LL | } + | ^ expected identifier + | + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + +error: aborting due to 4 previous errors + From 6990400e601355e759c2978306161bc65e142223 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 30 Aug 2026 18:38:23 +0200 Subject: [PATCH 2/2] Recover inner attributes in algebraic data types --- compiler/rustc_parse/src/parser/attr.rs | 14 ++++++++ compiler/rustc_parse/src/parser/item.rs | 4 +++ .../parser/empty-adt-inner-attributes.stderr | 35 +++++++++---------- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index 4d6255aa08f8f..55e2d77ad18f0 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -545,4 +545,18 @@ impl<'a> Parser<'a> { Ok(()) } + + /// Parse inner attributes and error if they are present + /// Returns whether any inner attributes were discarded + pub fn recover_inner_attributes(&mut self) -> PResult<'a, bool> { + let attributes = self.parse_inner_attributes()?; + for attr in &attributes { + self.error_on_forbidden_inner_attr( + attr.span, + InnerAttrPolicy::Forbidden(Some(InnerAttrForbiddenReason::InCodeBlock)), + true, + ); + } + Ok(!attributes.is_empty()) + } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index b252a378722f3..7f49a786d66ad 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1936,6 +1936,9 @@ impl<'a> Parser<'a> { } fn parse_enum_variant(&mut self, span: Span) -> PResult<'a, Option> { + if self.recover_inner_attributes()? && self.check(exp![CloseBrace]) { + return Ok(None); + } self.recover_vcs_conflict_marker(); let variant_attrs = self.parse_outer_attributes()?; self.recover_vcs_conflict_marker(); @@ -2139,6 +2142,7 @@ impl<'a> Parser<'a> { let mut fields = ThinVec::new(); let mut recovered = Recovered::No; if self.eat(exp!(OpenBrace)) { + self.recover_inner_attributes()?; while self.token != token::CloseBrace { match self.parse_field_def(adt_ty, ident_span) { Ok(field) => { diff --git a/tests/ui/parser/empty-adt-inner-attributes.stderr b/tests/ui/parser/empty-adt-inner-attributes.stderr index 6678ca8c27f79..a6cb17804626c 100644 --- a/tests/ui/parser/empty-adt-inner-attributes.stderr +++ b/tests/ui/parser/empty-adt-inner-attributes.stderr @@ -1,5 +1,5 @@ error: an inner attribute is not permitted in this context - --> $DIR/empty-struct-inner-attributes.rs:2:5 + --> $DIR/empty-adt-inner-attributes.rs:2:5 | LL | #![inline] | ^^^^^^^^^^ @@ -7,17 +7,17 @@ LL | #![inline] = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files = note: outer attributes, like `#[test]`, annotate the item following them -error: expected identifier, found `}` - --> $DIR/empty-struct-inner-attributes.rs:4:1 +error: an inner attribute is not permitted in this context + --> $DIR/empty-adt-inner-attributes.rs:7:5 + | +LL | #![inline] + | ^^^^^^^^^^ | -LL | struct Test1 { - | ----- while parsing this struct -... -LL | } - | ^ expected identifier + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files + = note: outer attributes, like `#[test]`, annotate the item following them error: an inner attribute is not permitted in this context - --> $DIR/empty-struct-inner-attributes.rs:7:5 + --> $DIR/empty-adt-inner-attributes.rs:13:5 | LL | #![inline] | ^^^^^^^^^^ @@ -25,16 +25,15 @@ LL | #![inline] = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files = note: outer attributes, like `#[test]`, annotate the item following them -error: expected identifier, found `}` - --> $DIR/empty-struct-inner-attributes.rs:9:1 - | -LL | enum Test2 { - | ----- while parsing this enum -... -LL | } - | ^ expected identifier +error: unions cannot have zero fields + --> $DIR/empty-adt-inner-attributes.rs:11:1 | - = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` +LL | / union Test3 { +LL | | +LL | | #![inline] +LL | | +LL | | } + | |_^ error: aborting due to 4 previous errors