about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_attr_data_structures/src/attributes.rs4
-rw-r--r--compiler/rustc_attr_data_structures/src/encode_cross_crate.rs2
-rw-r--r--compiler/rustc_attr_data_structures/src/lints.rs1
-rw-r--r--compiler/rustc_attr_parsing/messages.ftl4
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs4
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/repr.rs7
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs10
-rw-r--r--compiler/rustc_attr_parsing/src/lints.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs7
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs2
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs7
-rw-r--r--compiler/rustc_passes/src/check_attr.rs96
-rw-r--r--src/librustdoc/clean/types.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/default_union_representation.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs2
-rw-r--r--tests/pretty/hir-lifetimes.pp2
-rw-r--r--tests/pretty/hir-pretty-attr.pp2
-rw-r--r--tests/ui/attributes/malformed-attrs.rs1
-rw-r--r--tests/ui/attributes/malformed-attrs.stderr148
-rw-r--r--tests/ui/empty/empty-attributes.stderr36
-rw-r--r--tests/ui/repr/repr-empty-packed.stderr23
-rw-r--r--tests/ui/unpretty/exhaustive.hir.stdout3
27 files changed, 195 insertions, 191 deletions
diff --git a/compiler/rustc_attr_data_structures/src/attributes.rs b/compiler/rustc_attr_data_structures/src/attributes.rs
index 6af15da7d08..ba62be676d4 100644
--- a/compiler/rustc_attr_data_structures/src/attributes.rs
+++ b/compiler/rustc_attr_data_structures/src/attributes.rs
@@ -67,8 +67,6 @@ pub enum ReprAttr {
     ReprSimd,
     ReprTransparent,
     ReprAlign(Align),
-    // this one is just so we can emit a lint for it
-    ReprEmpty,
 }
 pub use ReprAttr::*;
 
@@ -304,7 +302,7 @@ pub enum AttributeKind {
     PubTransparent(Span),
 
     /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
-    Repr(ThinVec<(ReprAttr, Span)>),
+    Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
 
     /// Represents `#[rustc_layout_scalar_valid_range_end]`.
     RustcLayoutScalarValidRangeEnd(Box<u128>, Span),
diff --git a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
index 8ebd38a6ba7..b109ebbf47b 100644
--- a/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
+++ b/compiler/rustc_attr_data_structures/src/encode_cross_crate.rs
@@ -41,7 +41,7 @@ impl AttributeKind {
             Optimize(..) => No,
             PassByValue(..) => Yes,
             PubTransparent(..) => Yes,
-            Repr(..) => No,
+            Repr { .. } => No,
             RustcLayoutScalarValidRangeEnd(..) => Yes,
             RustcLayoutScalarValidRangeStart(..) => Yes,
             RustcObjectLifetimeDefault => No,
diff --git a/compiler/rustc_attr_data_structures/src/lints.rs b/compiler/rustc_attr_data_structures/src/lints.rs
index e34c54c6d32..60ca4d43ce9 100644
--- a/compiler/rustc_attr_data_structures/src/lints.rs
+++ b/compiler/rustc_attr_data_structures/src/lints.rs
@@ -12,4 +12,5 @@ pub struct AttributeLint<Id> {
 pub enum AttributeLintKind {
     UnusedDuplicate { this: Span, other: Span, warning: bool },
     IllFormedAttributeInput { suggestions: Vec<String> },
+    EmptyAttribute { first_span: Span },
 }
diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl
index 9ad46a83f50..8a709ea5d20 100644
--- a/compiler/rustc_attr_parsing/messages.ftl
+++ b/compiler/rustc_attr_parsing/messages.ftl
@@ -6,6 +6,10 @@ attr_parsing_deprecated_item_suggestion =
     .help = add `#![feature(deprecated_suggestion)]` to the crate root
     .note = see #94785 for more details
 
+attr_parsing_empty_attribute =
+    unused attribute
+    .suggestion = remove this attribute
+
 attr_parsing_empty_confusables =
     expected at least one confusable name
 attr_parsing_expected_one_cfg_pattern =
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 13f560dff38..cb3956d46a0 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -298,6 +298,10 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
             cx.expected_list(cx.attr_span);
             return features;
         };
+        if list.is_empty() {
+            cx.warn_empty_attribute(cx.attr_span);
+            return features;
+        }
         for item in list.mixed() {
             let Some(name_value) = item.meta_item() else {
                 cx.expected_name_value(item.span(), Some(sym::enable));
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index 1c070dc2685..6a45832ed7f 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -23,7 +23,8 @@ pub(crate) struct ReprParser;
 impl<S: Stage> CombineAttributeParser<S> for ReprParser {
     type Item = (ReprAttr, Span);
     const PATH: &[Symbol] = &[sym::repr];
-    const CONVERT: ConvertFn<Self::Item> = |items, _| AttributeKind::Repr(items);
+    const CONVERT: ConvertFn<Self::Item> =
+        |items, first_span| AttributeKind::Repr { reprs: items, first_span };
     // FIXME(jdonszelmann): never used
     const TEMPLATE: AttributeTemplate =
         template!(List: "C | Rust | align(...) | packed(...) | <integer type> | transparent");
@@ -40,8 +41,8 @@ impl<S: Stage> CombineAttributeParser<S> for ReprParser {
         };
 
         if list.is_empty() {
-            // this is so validation can emit a lint
-            reprs.push((ReprAttr::ReprEmpty, cx.attr_span));
+            cx.warn_empty_attribute(cx.attr_span);
+            return reprs;
         }
 
         for param in list.mixed() {
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 2a01ee58493..bcd7b024a9e 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -165,6 +165,7 @@ mod private {
 #[allow(private_interfaces)]
 pub trait Stage: Sized + 'static + Sealed {
     type Id: Copy;
+    const SHOULD_EMIT_LINTS: bool;
 
     fn parsers() -> &'static group_type!(Self);
 
@@ -175,6 +176,7 @@ pub trait Stage: Sized + 'static + Sealed {
 #[allow(private_interfaces)]
 impl Stage for Early {
     type Id = NodeId;
+    const SHOULD_EMIT_LINTS: bool = false;
 
     fn parsers() -> &'static group_type!(Self) {
         &early::ATTRIBUTE_PARSERS
@@ -188,6 +190,7 @@ impl Stage for Early {
 #[allow(private_interfaces)]
 impl Stage for Late {
     type Id = HirId;
+    const SHOULD_EMIT_LINTS: bool = true;
 
     fn parsers() -> &'static group_type!(Self) {
         &late::ATTRIBUTE_PARSERS
@@ -228,6 +231,9 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
     /// must be delayed until after HIR is built. This method will take care of the details of
     /// that.
     pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
+        if !S::SHOULD_EMIT_LINTS {
+            return;
+        }
         let id = self.target_id;
         (self.emit_lint)(AttributeLint { id, span, kind: lint });
     }
@@ -409,6 +415,10 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
             },
         })
     }
+
+    pub(crate) fn warn_empty_attribute(&mut self, span: Span) {
+        self.emit_lint(AttributeLintKind::EmptyAttribute { first_span: span }, span);
+    }
 }
 
 impl<'f, 'sess, S: Stage> Deref for AcceptContext<'f, 'sess, S> {
diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs
index fee22293b47..e648ca4fdf8 100644
--- a/compiler/rustc_attr_parsing/src/lints.rs
+++ b/compiler/rustc_attr_parsing/src/lints.rs
@@ -28,5 +28,11 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
                 },
             );
         }
+        AttributeLintKind::EmptyAttribute { first_span } => lint_emitter.emit_node_span_lint(
+            rustc_session::lint::builtin::UNUSED_ATTRIBUTES,
+            *id,
+            *first_span,
+            session_diagnostics::EmptyAttributeList { attr_span: *first_span },
+        ),
     }
 }
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 6145f1e1d3e..28f6786f37f 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -473,6 +473,13 @@ pub(crate) struct EmptyConfusables {
     pub span: Span,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(attr_parsing_empty_attribute)]
+pub(crate) struct EmptyAttributeList {
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub attr_span: Span,
+}
+
 #[derive(Diagnostic)]
 #[diag(attr_parsing_invalid_alignment_value, code = E0589)]
 pub(crate) struct InvalidAlignmentValue {
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index d201ca196d6..8135f3744f8 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -485,7 +485,7 @@ impl<'a> TraitDef<'a> {
             Annotatable::Item(item) => {
                 let is_packed = matches!(
                     AttributeParser::parse_limited(cx.sess, &item.attrs, sym::repr, item.span, item.id),
-                    Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
+                    Some(Attribute::Parsed(AttributeKind::Repr { reprs, .. })) if reprs.iter().any(|(x, _)| matches!(x, ReprPacked(..)))
                 );
 
                 let newitem = match &item.kind {
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 2713ec07f97..be63bb8ac59 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -109,7 +109,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
 
         if let hir::Attribute::Parsed(p) = attr {
             match p {
-                AttributeKind::Repr(reprs) => {
+                AttributeKind::Repr { reprs, first_span: _ } => {
                     codegen_fn_attrs.alignment = reprs
                         .iter()
                         .filter_map(
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index f4fcb13b1a1..bd89d010a3c 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1395,8 +1395,7 @@ fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
 pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
     let repr = def.repr();
     if repr.packed() {
-        if let Some(reprs) =
-            attrs::find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr(r) => r)
+        if let Some(reprs) = attrs::find_attr!(tcx.get_all_attrs(def.did()), attrs::AttributeKind::Repr { reprs, .. } => reprs)
         {
             for (r, _) in reprs {
                 if let ReprPacked(pack) = r
@@ -1619,10 +1618,10 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     if def.variants().is_empty() {
         attrs::find_attr!(
             tcx.get_all_attrs(def_id),
-            attrs::AttributeKind::Repr(rs) => {
+            attrs::AttributeKind::Repr { reprs, first_span } => {
                 struct_span_code_err!(
                     tcx.dcx(),
-                    rs.first().unwrap().1,
+                    reprs.first().map(|repr| repr.1).unwrap_or(*first_span),
                     E0084,
                     "unsupported representation for zero-variant enum"
                 )
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index 97e627f2eb2..ad7686b3e5b 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -168,7 +168,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
     fn check_item(&mut self, cx: &EarlyContext<'_>, it: &ast::Item) {
         let has_repr_c = matches!(
             AttributeParser::parse_limited(cx.sess(), &it.attrs, sym::repr, it.span, it.id),
-            Some(Attribute::Parsed(AttributeKind::Repr(r))) if r.iter().any(|(r, _)| r == &ReprAttr::ReprC)
+            Some(Attribute::Parsed(AttributeKind::Repr { reprs, ..})) if reprs.iter().any(|(r, _)| r == &ReprAttr::ReprC)
         );
 
         if has_repr_c {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index f1b16ea54e6..b780b1c5776 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1525,7 +1525,8 @@ impl<'tcx> TyCtxt<'tcx> {
             field_shuffle_seed ^= user_seed;
         }
 
-        if let Some(reprs) = attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr(r) => r)
+        if let Some(reprs) =
+            attr::find_attr!(self.get_all_attrs(did), AttributeKind::Repr { reprs, .. } => reprs)
         {
             for (r, _) in reprs {
                 flags.insert(match *r {
@@ -1566,10 +1567,6 @@ impl<'tcx> TyCtxt<'tcx> {
                         max_align = max_align.max(Some(align));
                         ReprFlags::empty()
                     }
-                    attr::ReprEmpty => {
-                        /* skip these, they're just for diagnostics */
-                        ReprFlags::empty()
-                    }
                 });
             }
         }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 18b3ab12e2d..3fa5cdc36bc 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -160,7 +160,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 }
                 Attribute::Parsed(AttributeKind::DocComment { .. }) => { /* `#[doc]` is actually a lot more than just doc comments, so is checked below*/
                 }
-                Attribute::Parsed(AttributeKind::Repr(_)) => { /* handled below this loop and elsewhere */
+                Attribute::Parsed(AttributeKind::Repr { .. }) => { /* handled below this loop and elsewhere */
                 }
                 Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => {
                     self.check_object_lifetime_default(hir_id);
@@ -1948,7 +1948,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         // #[repr(foo)]
         // #[repr(bar, align(8))]
         // ```
-        let reprs = find_attr!(attrs, AttributeKind::Repr(r) => r.as_slice()).unwrap_or(&[]);
+        let (reprs, first_attr_span) = find_attr!(attrs, AttributeKind::Repr { reprs, first_span } => (reprs.as_slice(), Some(*first_span))).unwrap_or((&[], None));
 
         let mut int_reprs = 0;
         let mut is_explicit_rust = false;
@@ -2045,31 +2045,30 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                         continue;
                     }
                 }
-                // FIXME(jdonszelmann): move the diagnostic for unused repr attrs here, I think
-                // it's a better place for it.
-                ReprAttr::ReprEmpty => {
-                    // catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`)
-                    if item.is_some() {
-                        match target {
-                            Target::Struct | Target::Union | Target::Enum => continue,
-                            Target::Fn | Target::Method(_) => {
-                                self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
-                                    span: *repr_span,
-                                    item: target.name(),
-                                });
-                            }
-                            _ => {
-                                self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
-                                    hint_span: *repr_span,
-                                    span,
-                                });
-                            }
-                        }
-                    }
+            };
+        }
 
-                    return;
+        // catch `repr()` with no arguments, applied to an item (i.e. not `#![repr()]`)
+        if let Some(first_attr_span) = first_attr_span
+            && reprs.is_empty()
+            && item.is_some()
+        {
+            match target {
+                Target::Struct | Target::Union | Target::Enum => {}
+                Target::Fn | Target::Method(_) => {
+                    self.dcx().emit_err(errors::ReprAlignShouldBeAlign {
+                        span: first_attr_span,
+                        item: target.name(),
+                    });
                 }
-            };
+                _ => {
+                    self.dcx().emit_err(errors::AttrApplication::StructEnumUnion {
+                        hint_span: first_attr_span,
+                        span,
+                    });
+                }
+            }
+            return;
         }
 
         // Just point at all repr hints if there are any incompatibilities.
@@ -2324,43 +2323,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     fn check_unused_attribute(&self, hir_id: HirId, attr: &Attribute, style: Option<AttrStyle>) {
-        // FIXME(jdonszelmann): deduplicate these checks after more attrs are parsed. This is very
-        // ugly now but can 100% be removed later.
-        if let Attribute::Parsed(p) = attr {
-            match p {
-                AttributeKind::Repr(reprs) => {
-                    for (r, span) in reprs {
-                        if let ReprAttr::ReprEmpty = r {
-                            self.tcx.emit_node_span_lint(
-                                UNUSED_ATTRIBUTES,
-                                hir_id,
-                                *span,
-                                errors::Unused {
-                                    attr_span: *span,
-                                    note: errors::UnusedNote::EmptyList { name: sym::repr },
-                                },
-                            );
-                        }
-                    }
-                    return;
-                }
-                AttributeKind::TargetFeature(features, span) if features.len() == 0 => {
-                    self.tcx.emit_node_span_lint(
-                        UNUSED_ATTRIBUTES,
-                        hir_id,
-                        *span,
-                        errors::Unused {
-                            attr_span: *span,
-                            note: errors::UnusedNote::EmptyList { name: sym::target_feature },
-                        },
-                    );
-                    return;
-                }
-                _ => {}
-            };
-        }
-
         // Warn on useless empty attributes.
+        // FIXME(jdonszelmann): this lint should be moved to attribute parsing, see `AcceptContext::warn_empty_attribute`
         let note = if attr.has_any_name(&[
             sym::macro_use,
             sym::allow,
@@ -2576,7 +2540,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     }
 
     fn check_rustc_pub_transparent(&self, attr_span: Span, span: Span, attrs: &[Attribute]) {
-        if !find_attr!(attrs, AttributeKind::Repr(r) => r.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
+        if !find_attr!(attrs, AttributeKind::Repr { reprs, .. } => reprs.iter().any(|(r, _)| r == &ReprAttr::ReprTransparent))
             .unwrap_or(false)
         {
             self.dcx().emit_err(errors::RustcPubTransparent { span, attr_span });
@@ -2852,8 +2816,12 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
             ATTRS_TO_CHECK.iter().find(|attr_to_check| attr.has_name(**attr_to_check))
         {
             (attr.span(), *a)
-        } else if let Attribute::Parsed(AttributeKind::Repr(r)) = attr {
-            (r.first().unwrap().1, sym::repr)
+        } else if let Attribute::Parsed(AttributeKind::Repr {
+            reprs: _,
+            first_span: first_attr_span,
+        }) = attr
+        {
+            (*first_attr_span, sym::repr)
         } else {
             continue;
         };
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index de920469fdc..a05aab22f1e 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -784,7 +784,7 @@ impl Item {
                         // don't want it it `Item::attrs`.
                         hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => None,
                         // We have separate pretty-printing logic for `#[repr(..)]` attributes.
-                        hir::Attribute::Parsed(AttributeKind::Repr(..)) => None,
+                        hir::Attribute::Parsed(AttributeKind::Repr { .. }) => None,
                         // target_feature is special-cased because cargo-semver-checks uses it
                         hir::Attribute::Parsed(AttributeKind::TargetFeature(features, _)) => {
                             let mut output = String::new();
diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
index b9ae9afe851..8b6bfaebbe5 100644
--- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
+++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
@@ -266,7 +266,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
             .tcx
             .hir_attrs(item.hir_id())
             .iter()
-            .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr(..))))
+            .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr{ .. })))
         {
             // Do not lint items with a `#[repr]` attribute as their layout may be imposed by an external API.
             return;
diff --git a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs
index 05d8a8c26d1..3e8808cec61 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/repr_attributes.rs
@@ -9,7 +9,7 @@ use clippy_utils::msrvs::{self, Msrv};
 use super::REPR_PACKED_WITHOUT_ABI;
 
 pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute], msrv: Msrv) {
-    if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr(r) => r) {
+    if let Some(reprs) = find_attr!(attrs, AttributeKind::Repr { reprs, .. } => reprs) {
         let packed_span = reprs
             .iter()
             .find(|(r, _)| matches!(r, ReprAttr::ReprPacked(..)))
diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
index 615421f3a40..9bf2144e445 100644
--- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
@@ -99,5 +99,5 @@ fn is_zst<'tcx>(cx: &LateContext<'tcx>, field: &FieldDef, args: ty::GenericArgsR
 fn has_c_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
     let attrs = cx.tcx.hir_attrs(hir_id);
 
-    find_attr!(attrs, AttributeKind::Repr(r) if r.iter().any(|(x, _)| *x == ReprAttr::ReprC))
+    find_attr!(attrs, AttributeKind::Repr { reprs, .. } if reprs.iter().any(|(x, _)| *x == ReprAttr::ReprC))
 }
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index a8b33418c8c..c01f0ffaac9 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1761,7 +1761,7 @@ pub fn has_attr(attrs: &[hir::Attribute], symbol: Symbol) -> bool {
 }
 
 pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
-    find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr(..))
+    find_attr!(cx.tcx.hir_attrs(hir_id), AttributeKind::Repr { .. })
 }
 
 pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
diff --git a/tests/pretty/hir-lifetimes.pp b/tests/pretty/hir-lifetimes.pp
index 4d1ab9d383b..58de6d81915 100644
--- a/tests/pretty/hir-lifetimes.pp
+++ b/tests/pretty/hir-lifetimes.pp
@@ -69,7 +69,7 @@ type Q<'a> = dyn MyTrait<'a, 'a> + 'a;
 fn h<'b, F>(f: F, y: Foo<'b>) where F: for<'d> MyTrait<'d, 'b> { }
 
 // FIXME(?): attr printing is weird
-#[attr = Repr([ReprC])]
+#[attr = Repr {reprs: [ReprC]}]
 struct S<'a>(&'a u32);
 
 extern "C" {
diff --git a/tests/pretty/hir-pretty-attr.pp b/tests/pretty/hir-pretty-attr.pp
index d8cc8c424ca..db7489c1264 100644
--- a/tests/pretty/hir-pretty-attr.pp
+++ b/tests/pretty/hir-pretty-attr.pp
@@ -6,6 +6,6 @@ extern crate std;
 //@ pretty-mode:hir
 //@ pp-exact:hir-pretty-attr.pp
 
-#[attr = Repr([ReprC, ReprPacked(Align(4 bytes)), ReprTransparent])]
+#[attr = Repr {reprs: [ReprC, ReprPacked(Align(4 bytes)), ReprTransparent]}]
 struct Example {
 }
diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs
index a09fe86557d..aa52de63a60 100644
--- a/tests/ui/attributes/malformed-attrs.rs
+++ b/tests/ui/attributes/malformed-attrs.rs
@@ -47,6 +47,7 @@
 //~^ ERROR malformed
 #[repr]
 //~^ ERROR malformed
+//~| ERROR is not supported on function items
 #[rustc_as_ptr = 5]
 //~^ ERROR malformed
 #[inline = 5]
diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr
index 9fe4f45b3ef..2f7bf50ead5 100644
--- a/tests/ui/attributes/malformed-attrs.stderr
+++ b/tests/ui/attributes/malformed-attrs.stderr
@@ -1,11 +1,11 @@
 error: `cfg` is not followed by parentheses
-  --> $DIR/malformed-attrs.rs:101:1
+  --> $DIR/malformed-attrs.rs:102:1
    |
 LL | #[cfg]
    | ^^^^^^ help: expected syntax is: `cfg(/* predicate */)`
 
 error: malformed `cfg_attr` attribute input
-  --> $DIR/malformed-attrs.rs:103:1
+  --> $DIR/malformed-attrs.rs:104:1
    |
 LL | #[cfg_attr]
    | ^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL | #[cfg_attr(condition, attribute, other_attribute, ...)]
    |           ++++++++++++++++++++++++++++++++++++++++++++
 
 error[E0463]: can't find crate for `wloop`
-  --> $DIR/malformed-attrs.rs:210:1
+  --> $DIR/malformed-attrs.rs:211:1
    |
 LL | extern crate wloop;
    | ^^^^^^^^^^^^^^^^^^^ can't find crate
@@ -35,19 +35,19 @@ LL | #![windows_subsystem]
    | ^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![windows_subsystem = "windows|console"]`
 
 error: malformed `crate_name` attribute input
-  --> $DIR/malformed-attrs.rs:73:1
+  --> $DIR/malformed-attrs.rs:74:1
    |
 LL | #[crate_name]
    | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]`
 
 error: malformed `export_stable` attribute input
-  --> $DIR/malformed-attrs.rs:80:1
+  --> $DIR/malformed-attrs.rs:81:1
    |
 LL | #[export_stable = 1]
    | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_stable]`
 
 error: malformed `coverage` attribute input
-  --> $DIR/malformed-attrs.rs:89:1
+  --> $DIR/malformed-attrs.rs:90:1
    |
 LL | #[coverage]
    | ^^^^^^^^^^^
@@ -60,49 +60,49 @@ LL | #[coverage(on)]
    |           ++++
 
 error: malformed `no_sanitize` attribute input
-  --> $DIR/malformed-attrs.rs:91:1
+  --> $DIR/malformed-attrs.rs:92:1
    |
 LL | #[no_sanitize]
    | ^^^^^^^^^^^^^^ help: must be of the form: `#[no_sanitize(address, kcfi, memory, thread)]`
 
 error: malformed `proc_macro` attribute input
-  --> $DIR/malformed-attrs.rs:98:1
+  --> $DIR/malformed-attrs.rs:99:1
    |
 LL | #[proc_macro = 18]
    | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro]`
 
 error: malformed `instruction_set` attribute input
-  --> $DIR/malformed-attrs.rs:105:1
+  --> $DIR/malformed-attrs.rs:106:1
    |
 LL | #[instruction_set]
    | ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[instruction_set(set)]`
 
 error: malformed `patchable_function_entry` attribute input
-  --> $DIR/malformed-attrs.rs:107:1
+  --> $DIR/malformed-attrs.rs:108:1
    |
 LL | #[patchable_function_entry]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]`
 
 error: malformed `coroutine` attribute input
-  --> $DIR/malformed-attrs.rs:110:5
+  --> $DIR/malformed-attrs.rs:111:5
    |
 LL |     #[coroutine = 63] || {}
    |     ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[coroutine]`
 
 error: malformed `proc_macro_attribute` attribute input
-  --> $DIR/malformed-attrs.rs:115:1
+  --> $DIR/malformed-attrs.rs:116:1
    |
 LL | #[proc_macro_attribute = 19]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_attribute]`
 
 error: malformed `proc_macro_derive` attribute input
-  --> $DIR/malformed-attrs.rs:122:1
+  --> $DIR/malformed-attrs.rs:123:1
    |
 LL | #[proc_macro_derive]
    | ^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[proc_macro_derive(TraitName, /*opt*/ attributes(name1, name2, ...))]`
 
 error: malformed `must_not_suspend` attribute input
-  --> $DIR/malformed-attrs.rs:131:1
+  --> $DIR/malformed-attrs.rs:132:1
    |
 LL | #[must_not_suspend()]
    | ^^^^^^^^^^^^^^^^^^^^^
@@ -117,109 +117,109 @@ LL + #[must_not_suspend]
    |
 
 error: malformed `cfi_encoding` attribute input
-  --> $DIR/malformed-attrs.rs:133:1
+  --> $DIR/malformed-attrs.rs:134:1
    |
 LL | #[cfi_encoding]
    | ^^^^^^^^^^^^^^^ help: must be of the form: `#[cfi_encoding = "encoding"]`
 
 error: malformed `type_const` attribute input
-  --> $DIR/malformed-attrs.rs:142:5
+  --> $DIR/malformed-attrs.rs:143:5
    |
 LL |     #[type_const = 1]
    |     ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[type_const]`
 
 error: malformed `marker` attribute input
-  --> $DIR/malformed-attrs.rs:154:1
+  --> $DIR/malformed-attrs.rs:155:1
    |
 LL | #[marker = 3]
    | ^^^^^^^^^^^^^ help: must be of the form: `#[marker]`
 
 error: malformed `fundamental` attribute input
-  --> $DIR/malformed-attrs.rs:156:1
+  --> $DIR/malformed-attrs.rs:157:1
    |
 LL | #[fundamental()]
    | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[fundamental]`
 
 error: malformed `ffi_pure` attribute input
-  --> $DIR/malformed-attrs.rs:164:5
+  --> $DIR/malformed-attrs.rs:165:5
    |
 LL |     #[unsafe(ffi_pure = 1)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[ffi_pure]`
 
 error: malformed `link_ordinal` attribute input
-  --> $DIR/malformed-attrs.rs:166:5
+  --> $DIR/malformed-attrs.rs:167:5
    |
 LL |     #[link_ordinal]
    |     ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_ordinal(ordinal)]`
 
 error: malformed `ffi_const` attribute input
-  --> $DIR/malformed-attrs.rs:170:5
+  --> $DIR/malformed-attrs.rs:171:5
    |
 LL |     #[unsafe(ffi_const = 1)]
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[ffi_const]`
 
 error: malformed `linkage` attribute input
-  --> $DIR/malformed-attrs.rs:172:5
+  --> $DIR/malformed-attrs.rs:173:5
    |
 LL |     #[linkage]
    |     ^^^^^^^^^^ help: must be of the form: `#[linkage = "external|internal|..."]`
 
 error: malformed `allow` attribute input
-  --> $DIR/malformed-attrs.rs:177:1
+  --> $DIR/malformed-attrs.rs:178:1
    |
 LL | #[allow]
    | ^^^^^^^^ help: must be of the form: `#[allow(lint1, lint2, ..., /*opt*/ reason = "...")]`
 
 error: malformed `expect` attribute input
-  --> $DIR/malformed-attrs.rs:179:1
+  --> $DIR/malformed-attrs.rs:180:1
    |
 LL | #[expect]
    | ^^^^^^^^^ help: must be of the form: `#[expect(lint1, lint2, ..., /*opt*/ reason = "...")]`
 
 error: malformed `warn` attribute input
-  --> $DIR/malformed-attrs.rs:181:1
+  --> $DIR/malformed-attrs.rs:182:1
    |
 LL | #[warn]
    | ^^^^^^^ help: must be of the form: `#[warn(lint1, lint2, ..., /*opt*/ reason = "...")]`
 
 error: malformed `deny` attribute input
-  --> $DIR/malformed-attrs.rs:183:1
+  --> $DIR/malformed-attrs.rs:184:1
    |
 LL | #[deny]
    | ^^^^^^^ help: must be of the form: `#[deny(lint1, lint2, ..., /*opt*/ reason = "...")]`
 
 error: malformed `forbid` attribute input
-  --> $DIR/malformed-attrs.rs:185:1
+  --> $DIR/malformed-attrs.rs:186:1
    |
 LL | #[forbid]
    | ^^^^^^^^^ help: must be of the form: `#[forbid(lint1, lint2, ..., /*opt*/ reason = "...")]`
 
 error: malformed `debugger_visualizer` attribute input
-  --> $DIR/malformed-attrs.rs:187:1
+  --> $DIR/malformed-attrs.rs:188:1
    |
 LL | #[debugger_visualizer]
    | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[debugger_visualizer(natvis_file = "...", gdb_script_file = "...")]`
 
 error: malformed `automatically_derived` attribute input
-  --> $DIR/malformed-attrs.rs:190:1
+  --> $DIR/malformed-attrs.rs:191:1
    |
 LL | #[automatically_derived = 18]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[automatically_derived]`
 
 error: malformed `thread_local` attribute input
-  --> $DIR/malformed-attrs.rs:202:1
+  --> $DIR/malformed-attrs.rs:203:1
    |
 LL | #[thread_local()]
    | ^^^^^^^^^^^^^^^^^ help: must be of the form: `#[thread_local]`
 
 error: malformed `no_link` attribute input
-  --> $DIR/malformed-attrs.rs:206:1
+  --> $DIR/malformed-attrs.rs:207:1
    |
 LL | #[no_link()]
    | ^^^^^^^^^^^^ help: must be of the form: `#[no_link]`
 
 error: malformed `macro_use` attribute input
-  --> $DIR/malformed-attrs.rs:208:1
+  --> $DIR/malformed-attrs.rs:209:1
    |
 LL | #[macro_use = 1]
    | ^^^^^^^^^^^^^^^^
@@ -234,7 +234,7 @@ LL + #[macro_use]
    |
 
 error: malformed `macro_export` attribute input
-  --> $DIR/malformed-attrs.rs:213:1
+  --> $DIR/malformed-attrs.rs:214:1
    |
 LL | #[macro_export = 18]
    | ^^^^^^^^^^^^^^^^^^^^
@@ -249,31 +249,31 @@ LL + #[macro_export]
    |
 
 error: malformed `allow_internal_unsafe` attribute input
-  --> $DIR/malformed-attrs.rs:215:1
+  --> $DIR/malformed-attrs.rs:216:1
    |
 LL | #[allow_internal_unsafe = 1]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[allow_internal_unsafe]`
 
 error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type
-  --> $DIR/malformed-attrs.rs:98:1
+  --> $DIR/malformed-attrs.rs:99:1
    |
 LL | #[proc_macro = 18]
    | ^^^^^^^^^^^^^^^^^^
 
 error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type
-  --> $DIR/malformed-attrs.rs:115:1
+  --> $DIR/malformed-attrs.rs:116:1
    |
 LL | #[proc_macro_attribute = 19]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type
-  --> $DIR/malformed-attrs.rs:122:1
+  --> $DIR/malformed-attrs.rs:123:1
    |
 LL | #[proc_macro_derive]
    | ^^^^^^^^^^^^^^^^^^^^
 
 error[E0658]: allow_internal_unsafe side-steps the unsafe_code lint
-  --> $DIR/malformed-attrs.rs:215:1
+  --> $DIR/malformed-attrs.rs:216:1
    |
 LL | #[allow_internal_unsafe = 1]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -292,7 +292,7 @@ LL | #[doc]
    = note: `#[deny(ill_formed_attribute_input)]` on by default
 
 error: valid forms for the attribute are `#[doc(hidden|inline|...)]` and `#[doc = "string"]`
-  --> $DIR/malformed-attrs.rs:75:1
+  --> $DIR/malformed-attrs.rs:76:1
    |
 LL | #[doc]
    | ^^^^^^
@@ -301,7 +301,7 @@ LL | #[doc]
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
 
 error: attribute must be of the form `#[link(name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...", /*opt*/ import_name_type = "decorated|noprefix|undecorated")]`
-  --> $DIR/malformed-attrs.rs:82:1
+  --> $DIR/malformed-attrs.rs:83:1
    |
 LL | #[link]
    | ^^^^^^^
@@ -310,7 +310,7 @@ LL | #[link]
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
 
 error: invalid argument
-  --> $DIR/malformed-attrs.rs:187:1
+  --> $DIR/malformed-attrs.rs:188:1
    |
 LL | #[debugger_visualizer]
    | ^^^^^^^^^^^^^^^^^^^^^^
@@ -382,7 +382,7 @@ LL | #[repr]
    | help: must be of the form: `#[repr(C | Rust | align(...) | packed(...) | <integer type> | transparent)]`
 
 error[E0565]: malformed `rustc_as_ptr` attribute input
-  --> $DIR/malformed-attrs.rs:50:1
+  --> $DIR/malformed-attrs.rs:51:1
    |
 LL | #[rustc_as_ptr = 5]
    | ^^^^^^^^^^^^^^^---^
@@ -391,7 +391,7 @@ LL | #[rustc_as_ptr = 5]
    | help: must be of the form: `#[rustc_as_ptr]`
 
 error[E0539]: malformed `align` attribute input
-  --> $DIR/malformed-attrs.rs:55:1
+  --> $DIR/malformed-attrs.rs:56:1
    |
 LL | #[align]
    | ^^^^^^^^
@@ -400,7 +400,7 @@ LL | #[align]
    | help: must be of the form: `#[align(<alignment in bytes>)]`
 
 error[E0539]: malformed `optimize` attribute input
-  --> $DIR/malformed-attrs.rs:57:1
+  --> $DIR/malformed-attrs.rs:58:1
    |
 LL | #[optimize]
    | ^^^^^^^^^^^
@@ -409,7 +409,7 @@ LL | #[optimize]
    | help: must be of the form: `#[optimize(size|speed|none)]`
 
 error[E0565]: malformed `cold` attribute input
-  --> $DIR/malformed-attrs.rs:59:1
+  --> $DIR/malformed-attrs.rs:60:1
    |
 LL | #[cold = 1]
    | ^^^^^^^---^
@@ -418,13 +418,13 @@ LL | #[cold = 1]
    | help: must be of the form: `#[cold]`
 
 error: valid forms for the attribute are `#[must_use = "reason"]` and `#[must_use]`
-  --> $DIR/malformed-attrs.rs:61:1
+  --> $DIR/malformed-attrs.rs:62:1
    |
 LL | #[must_use()]
    | ^^^^^^^^^^^^^
 
 error[E0565]: malformed `no_mangle` attribute input
-  --> $DIR/malformed-attrs.rs:63:1
+  --> $DIR/malformed-attrs.rs:64:1
    |
 LL | #[no_mangle = 1]
    | ^^^^^^^^^^^^---^
@@ -433,7 +433,7 @@ LL | #[no_mangle = 1]
    | help: must be of the form: `#[no_mangle]`
 
 error[E0565]: malformed `naked` attribute input
-  --> $DIR/malformed-attrs.rs:65:1
+  --> $DIR/malformed-attrs.rs:66:1
    |
 LL | #[unsafe(naked())]
    | ^^^^^^^^^^^^^^--^^
@@ -442,7 +442,7 @@ LL | #[unsafe(naked())]
    | help: must be of the form: `#[naked]`
 
 error[E0565]: malformed `track_caller` attribute input
-  --> $DIR/malformed-attrs.rs:67:1
+  --> $DIR/malformed-attrs.rs:68:1
    |
 LL | #[track_caller()]
    | ^^^^^^^^^^^^^^--^
@@ -451,13 +451,13 @@ LL | #[track_caller()]
    | help: must be of the form: `#[track_caller]`
 
 error[E0539]: malformed `export_name` attribute input
-  --> $DIR/malformed-attrs.rs:69:1
+  --> $DIR/malformed-attrs.rs:70:1
    |
 LL | #[export_name()]
    | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_name = "name"]`
 
 error[E0805]: malformed `used` attribute input
-  --> $DIR/malformed-attrs.rs:71:1
+  --> $DIR/malformed-attrs.rs:72:1
    |
 LL | #[used()]
    | ^^^^^^--^
@@ -473,7 +473,7 @@ LL + #[used]
    |
 
 error[E0539]: malformed `target_feature` attribute input
-  --> $DIR/malformed-attrs.rs:78:1
+  --> $DIR/malformed-attrs.rs:79:1
    |
 LL | #[target_feature]
    | ^^^^^^^^^^^^^^^^^
@@ -482,19 +482,19 @@ LL | #[target_feature]
    | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]`
 
 error[E0539]: malformed `link_name` attribute input
-  --> $DIR/malformed-attrs.rs:85:1
+  --> $DIR/malformed-attrs.rs:86:1
    |
 LL | #[link_name]
    | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]`
 
 error[E0539]: malformed `link_section` attribute input
-  --> $DIR/malformed-attrs.rs:87:1
+  --> $DIR/malformed-attrs.rs:88:1
    |
 LL | #[link_section]
    | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]`
 
 error[E0565]: malformed `no_implicit_prelude` attribute input
-  --> $DIR/malformed-attrs.rs:96:1
+  --> $DIR/malformed-attrs.rs:97:1
    |
 LL | #[no_implicit_prelude = 23]
    | ^^^^^^^^^^^^^^^^^^^^^^----^
@@ -503,7 +503,7 @@ LL | #[no_implicit_prelude = 23]
    | help: must be of the form: `#[no_implicit_prelude]`
 
 error[E0539]: malformed `must_use` attribute input
-  --> $DIR/malformed-attrs.rs:118:1
+  --> $DIR/malformed-attrs.rs:119:1
    |
 LL | #[must_use = 1]
    | ^^^^^^^^^^^^^-^
@@ -520,7 +520,7 @@ LL + #[must_use]
    |
 
 error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input
-  --> $DIR/malformed-attrs.rs:127:1
+  --> $DIR/malformed-attrs.rs:128:1
    |
 LL | #[rustc_layout_scalar_valid_range_start]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -529,7 +529,7 @@ LL | #[rustc_layout_scalar_valid_range_start]
    | help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]`
 
 error[E0539]: malformed `rustc_layout_scalar_valid_range_end` attribute input
-  --> $DIR/malformed-attrs.rs:129:1
+  --> $DIR/malformed-attrs.rs:130:1
    |
 LL | #[rustc_layout_scalar_valid_range_end]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -538,7 +538,7 @@ LL | #[rustc_layout_scalar_valid_range_end]
    | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]`
 
 error[E0565]: malformed `non_exhaustive` attribute input
-  --> $DIR/malformed-attrs.rs:196:1
+  --> $DIR/malformed-attrs.rs:197:1
    |
 LL | #[non_exhaustive = 1]
    | ^^^^^^^^^^^^^^^^^---^
@@ -558,8 +558,20 @@ LL | |     #[coroutine = 63] || {}
 LL | | }
    | |_- not a `const fn`
 
+error: `#[repr(align(...))]` is not supported on function items
+  --> $DIR/malformed-attrs.rs:48:1
+   |
+LL | #[repr]
+   | ^^^^^^^
+   |
+help: use `#[align(...)]` instead
+  --> $DIR/malformed-attrs.rs:48:1
+   |
+LL | #[repr]
+   | ^^^^^^^
+
 warning: `#[diagnostic::do_not_recommend]` does not expect any arguments
-  --> $DIR/malformed-attrs.rs:148:1
+  --> $DIR/malformed-attrs.rs:149:1
    |
 LL | #[diagnostic::do_not_recommend()]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -567,7 +579,7 @@ LL | #[diagnostic::do_not_recommend()]
    = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default
 
 warning: missing options for `on_unimplemented` attribute
-  --> $DIR/malformed-attrs.rs:137:1
+  --> $DIR/malformed-attrs.rs:138:1
    |
 LL | #[diagnostic::on_unimplemented]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -575,7 +587,7 @@ LL | #[diagnostic::on_unimplemented]
    = help: at least one of the `message`, `note` and `label` options are expected
 
 warning: malformed `on_unimplemented` attribute
-  --> $DIR/malformed-attrs.rs:139:1
+  --> $DIR/malformed-attrs.rs:140:1
    |
 LL | #[diagnostic::on_unimplemented = 1]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here
@@ -583,7 +595,7 @@ LL | #[diagnostic::on_unimplemented = 1]
    = help: only `message`, `note` and `label` are allowed as options
 
 error: valid forms for the attribute are `#[inline(always|never)]` and `#[inline]`
-  --> $DIR/malformed-attrs.rs:52:1
+  --> $DIR/malformed-attrs.rs:53:1
    |
 LL | #[inline = 5]
    | ^^^^^^^^^^^^^
@@ -592,7 +604,7 @@ LL | #[inline = 5]
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
 
 error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]`
-  --> $DIR/malformed-attrs.rs:93:1
+  --> $DIR/malformed-attrs.rs:94:1
    |
 LL | #[ignore()]
    | ^^^^^^^^^^^
@@ -601,7 +613,7 @@ LL | #[ignore()]
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
 
 error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]`
-  --> $DIR/malformed-attrs.rs:222:1
+  --> $DIR/malformed-attrs.rs:223:1
    |
 LL | #[ignore = 1]
    | ^^^^^^^^^^^^^
@@ -610,7 +622,7 @@ LL | #[ignore = 1]
    = note: for more information, see issue #57571 <https://github.com/rust-lang/rust/issues/57571>
 
 error[E0308]: mismatched types
-  --> $DIR/malformed-attrs.rs:110:23
+  --> $DIR/malformed-attrs.rs:111:23
    |
 LL | fn test() {
    |          - help: a return type might be missing here: `-> _`
@@ -618,9 +630,9 @@ LL |     #[coroutine = 63] || {}
    |                       ^^^^^ expected `()`, found coroutine
    |
    = note: expected unit type `()`
-              found coroutine `{coroutine@$DIR/malformed-attrs.rs:110:23: 110:25}`
+              found coroutine `{coroutine@$DIR/malformed-attrs.rs:111:23: 111:25}`
 
-error: aborting due to 74 previous errors; 3 warnings emitted
+error: aborting due to 75 previous errors; 3 warnings emitted
 
 Some errors have detailed explanations: E0308, E0463, E0539, E0565, E0658, E0805.
 For more information about an error, try `rustc --explain E0308`.
diff --git a/tests/ui/empty/empty-attributes.stderr b/tests/ui/empty/empty-attributes.stderr
index e86dea10c70..f0be56ddc6a 100644
--- a/tests/ui/empty/empty-attributes.stderr
+++ b/tests/ui/empty/empty-attributes.stderr
@@ -1,10 +1,10 @@
 error: unused attribute
-  --> $DIR/empty-attributes.rs:9:1
+  --> $DIR/empty-attributes.rs:2:1
    |
-LL | #[repr()]
-   | ^^^^^^^^^ help: remove this attribute
+LL | #![allow()]
+   | ^^^^^^^^^^^ help: remove this attribute
    |
-   = note: attribute `repr` with an empty list has no effect
+   = note: attribute `allow` with an empty list has no effect
 note: the lint level is defined here
   --> $DIR/empty-attributes.rs:1:9
    |
@@ -12,22 +12,6 @@ LL | #![deny(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/empty-attributes.rs:12:1
-   |
-LL | #[target_feature()]
-   | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute
-   |
-   = note: attribute `target_feature` with an empty list has no effect
-
-error: unused attribute
-  --> $DIR/empty-attributes.rs:2:1
-   |
-LL | #![allow()]
-   | ^^^^^^^^^^^ help: remove this attribute
-   |
-   = note: attribute `allow` with an empty list has no effect
-
-error: unused attribute
   --> $DIR/empty-attributes.rs:3:1
    |
 LL | #![expect()]
@@ -67,5 +51,17 @@ LL | #![feature()]
    |
    = note: attribute `feature` with an empty list has no effect
 
+error: unused attribute
+  --> $DIR/empty-attributes.rs:9:1
+   |
+LL | #[repr()]
+   | ^^^^^^^^^ help: remove this attribute
+
+error: unused attribute
+  --> $DIR/empty-attributes.rs:12:1
+   |
+LL | #[target_feature()]
+   | ^^^^^^^^^^^^^^^^^^^ help: remove this attribute
+
 error: aborting due to 8 previous errors
 
diff --git a/tests/ui/repr/repr-empty-packed.stderr b/tests/ui/repr/repr-empty-packed.stderr
index c824c2998b4..6565b2e8c1d 100644
--- a/tests/ui/repr/repr-empty-packed.stderr
+++ b/tests/ui/repr/repr-empty-packed.stderr
@@ -1,27 +1,26 @@
+error[E0517]: attribute should be applied to a struct or union
+  --> $DIR/repr-empty-packed.rs:5:8
+   |
+LL |   #[repr(packed)]
+   |          ^^^^^^
+LL | / pub enum Foo {
+LL | |     Bar,
+LL | |     Baz(i32),
+LL | | }
+   | |_- not a struct or union
+
 error: unused attribute
   --> $DIR/repr-empty-packed.rs:4:1
    |
 LL | #[repr()]
    | ^^^^^^^^^ help: remove this attribute
    |
-   = note: attribute `repr` with an empty list has no effect
 note: the lint level is defined here
   --> $DIR/repr-empty-packed.rs:2:9
    |
 LL | #![deny(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
 
-error[E0517]: attribute should be applied to a struct or union
-  --> $DIR/repr-empty-packed.rs:5:8
-   |
-LL |   #[repr(packed)]
-   |          ^^^^^^
-LL | / pub enum Foo {
-LL | |     Bar,
-LL | |     Baz(i32),
-LL | | }
-   | |_- not a struct or union
-
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0517`.
diff --git a/tests/ui/unpretty/exhaustive.hir.stdout b/tests/ui/unpretty/exhaustive.hir.stdout
index 2b8f3b21396..0a983b41ef3 100644
--- a/tests/ui/unpretty/exhaustive.hir.stdout
+++ b/tests/ui/unpretty/exhaustive.hir.stdout
@@ -64,7 +64,8 @@ mod attributes {
     #[doc = "outer doc attribute"]
     #[doc = "macro"]
     #[allow()]
-    #[attr = Repr([ReprC])]
+    #[attr = Repr {reprs:
+    [ReprC]}]
     struct Struct;
 }