about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-07-06 16:56:16 +0000
committerbors <bors@rust-lang.org>2025-07-06 16:56:16 +0000
commitde031bbcb161b0b7fc0eb16f77b02ce9fbdf4c9e (patch)
tree2f67f39a59825f5628e9f511f3c7e1fa6f847ab5 /compiler
parent3c95364c4afc3f15749f5a511d599af086f5456e (diff)
parentbff79a75172b5f8200b4f30264e5fc9d36f8080e (diff)
downloadrust-de031bbcb161b0b7fc0eb16f77b02ce9fbdf4c9e.tar.gz
rust-de031bbcb161b0b7fc0eb16f77b02ce9fbdf4c9e.zip
Auto merge of #143526 - matthiaskrgr:rollup-pm69g5v, r=matthiaskrgr
Rollup of 4 pull requests

Successful merges:

 - rust-lang/rust#143252 (Rewrite empty attribute lint for new attribute parser)
 - rust-lang/rust#143492 (Use `object` crate from crates.io to fix windows build error)
 - rust-lang/rust#143514 (Organize macro tests a bit more)
 - rust-lang/rust#143518 (rustc_builtin_macros: Make sure registered attributes stay sorted)

r? `@ghost`
`@rustbot` modify labels: rollup
Diffstat (limited to 'compiler')
-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_builtin_macros/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_gcc/Cargo.lock10
-rw-r--r--compiler/rustc_codegen_gcc/Cargo.toml3
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs7
-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_macros/src/print_attribute.rs3
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs7
-rw-r--r--compiler/rustc_passes/src/check_attr.rs96
20 files changed, 96 insertions, 90 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_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs
index 0594f7e86c3..6bf590df5c9 100644
--- a/compiler/rustc_builtin_macros/src/lib.rs
+++ b/compiler/rustc_builtin_macros/src/lib.rs
@@ -108,6 +108,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
     }
 
     register_attr! {
+        // tidy-alphabetical-start
         alloc_error_handler: alloc_error_handler::expand,
         autodiff_forward: autodiff::expand_forward,
         autodiff_reverse: autodiff::expand_reverse,
@@ -120,6 +121,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) {
         global_allocator: global_allocator::expand,
         test: test::expand_test,
         test_case: test::expand_test_case,
+        // tidy-alphabetical-end
     }
 
     register_derive! {
diff --git a/compiler/rustc_codegen_gcc/Cargo.lock b/compiler/rustc_codegen_gcc/Cargo.lock
index b20c181a8cb..7f35c1a80bd 100644
--- a/compiler/rustc_codegen_gcc/Cargo.lock
+++ b/compiler/rustc_codegen_gcc/Cargo.lock
@@ -144,6 +144,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "object"
+version = "0.37.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "03fd943161069e1768b4b3d050890ba48730e590f57e56d4aa04e7e090e61b4a"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "once_cell"
 version = "1.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -179,6 +188,7 @@ dependencies = [
  "boml",
  "gccjit",
  "lang_tester",
+ "object",
  "tempfile",
 ]
 
diff --git a/compiler/rustc_codegen_gcc/Cargo.toml b/compiler/rustc_codegen_gcc/Cargo.toml
index c284e3f060b..193348d1ef6 100644
--- a/compiler/rustc_codegen_gcc/Cargo.toml
+++ b/compiler/rustc_codegen_gcc/Cargo.toml
@@ -22,6 +22,8 @@ master = ["gccjit/master"]
 default = ["master"]
 
 [dependencies]
+object = { version = "0.37.0", default-features = false, features = ["std", "read"] }
+tempfile = "3.20"
 gccjit = "2.7"
 #gccjit = { git = "https://github.com/rust-lang/gccjit.rs" }
 
@@ -31,7 +33,6 @@ gccjit = "2.7"
 [dev-dependencies]
 boml = "0.3.1"
 lang_tester = "0.8.0"
-tempfile = "3.20"
 
 [profile.dev]
 # By compiling dependencies with optimizations, performing tests gets much faster.
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index a912678ef2a..1a6eec0ed0b 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -26,12 +26,9 @@
 #![deny(clippy::pattern_type_mismatch)]
 #![allow(clippy::needless_lifetimes, clippy::uninlined_format_args)]
 
-// Some "regular" crates we want to share with rustc
-extern crate object;
+// These crates are pulled from the sysroot because they are part of
+// rustc's public API, so we need to ensure version compatibility.
 extern crate smallvec;
-// FIXME(antoyo): clippy bug: remove the #[allow] when it's fixed.
-#[allow(unused_extern_crates)]
-extern crate tempfile;
 #[macro_use]
 extern crate tracing;
 
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_macros/src/print_attribute.rs b/compiler/rustc_macros/src/print_attribute.rs
index 42d94e72ee9..9023520c750 100644
--- a/compiler/rustc_macros/src/print_attribute.rs
+++ b/compiler/rustc_macros/src/print_attribute.rs
@@ -21,7 +21,8 @@ fn print_fields(name: &Ident, fields: &Fields) -> (TokenStream, TokenStream, Tok
                             __p.word_space(",");
                         }
                         __p.word(#string_name);
-                        __p.word_space(":");
+                        __p.word(":");
+                        __p.nbsp();
                         __printed_anything = true;
                     }
                     #name.print_attribute(__p);
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;
         };