passes: `check_attr` on more targets

This commit modifies `check_attr` so that:

- Enum variants are now checked (some attributes would not have been
  prohibited on variants previously).
- `check_expr_attributes` and `check_stmt_attributes` are removed as
  `check_attributes` can perform the same checks.

Signed-off-by: David Wood <david@davidtw.co>
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index efe947d..758b463 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -94,7 +94,7 @@
             return;
         }
 
-        if matches!(target, Target::Fn | Target::Method(_) | Target::ForeignFn) {
+        if matches!(target, Target::Closure | Target::Fn | Target::Method(_) | Target::ForeignFn) {
             self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(hir_id));
         }
 
@@ -185,7 +185,7 @@
     /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
     fn check_non_exhaustive(&self, attr: &Attribute, span: &Span, target: Target) -> bool {
         match target {
-            Target::Struct | Target::Enum => true,
+            Target::Struct | Target::Enum | Target::Variant => true,
             _ => {
                 struct_span_err!(
                     self.tcx.sess,
@@ -470,6 +470,9 @@
 
         for hint in &hints {
             let (article, allowed_targets) = match hint.name_or_empty() {
+                _ if !matches!(target, Target::Struct | Target::Enum | Target::Union) => {
+                    ("a", "struct, enum, or union")
+                }
                 name @ sym::C | name @ sym::align => {
                     is_c |= name == sym::C;
                     match target {
@@ -535,12 +538,16 @@
                 }
                 _ => continue,
             };
-            self.emit_repr_error(
+
+            struct_span_err!(
+                self.tcx.sess,
                 hint.span(),
-                *span,
-                &format!("attribute should be applied to {}", allowed_targets),
-                &format!("not {} {}", article, allowed_targets),
+                E0517,
+                "{}",
+                &format!("attribute should be applied to {} {}", article, allowed_targets)
             )
+            .span_label(*span, &format!("not {} {}", article, allowed_targets))
+            .emit();
         }
 
         // Just point at all repr hints if there are any incompatibilities.
@@ -579,56 +586,6 @@
         }
     }
 
-    fn emit_repr_error(
-        &self,
-        hint_span: Span,
-        label_span: Span,
-        hint_message: &str,
-        label_message: &str,
-    ) {
-        struct_span_err!(self.tcx.sess, hint_span, E0517, "{}", hint_message)
-            .span_label(label_span, label_message)
-            .emit();
-    }
-
-    fn check_stmt_attributes(&self, stmt: &hir::Stmt<'_>) {
-        // When checking statements ignore expressions, they will be checked later
-        if let hir::StmtKind::Local(ref l) = stmt.kind {
-            self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None);
-            for attr in l.attrs.iter() {
-                if self.tcx.sess.check_name(attr, sym::repr) {
-                    self.emit_repr_error(
-                        attr.span,
-                        stmt.span,
-                        "attribute should not be applied to a statement",
-                        "not a struct, enum, or union",
-                    );
-                }
-            }
-        }
-    }
-
-    fn check_expr_attributes(&self, expr: &hir::Expr<'_>) {
-        let target = match expr.kind {
-            hir::ExprKind::Closure(..) => Target::Closure,
-            _ => Target::Expression,
-        };
-        self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None);
-        for attr in expr.attrs.iter() {
-            if self.tcx.sess.check_name(attr, sym::repr) {
-                self.emit_repr_error(
-                    attr.span,
-                    expr.span,
-                    "attribute should not be applied to an expression",
-                    "not defining a struct, enum, or union",
-                );
-            }
-        }
-        if target == Target::Closure {
-            self.tcx.ensure().codegen_fn_attrs(self.tcx.hir().local_def_id(expr.hir_id));
-        }
-    }
-
     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
         for attr in attrs {
             if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
@@ -672,14 +629,32 @@
     }
 
     fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
-        self.check_stmt_attributes(stmt);
+        // When checking statements ignore expressions, they will be checked later.
+        if let hir::StmtKind::Local(ref l) = stmt.kind {
+            self.check_attributes(l.hir_id, &l.attrs, &stmt.span, Target::Statement, None);
+        }
         intravisit::walk_stmt(self, stmt)
     }
 
     fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
-        self.check_expr_attributes(expr);
+        let target = match expr.kind {
+            hir::ExprKind::Closure(..) => Target::Closure,
+            _ => Target::Expression,
+        };
+
+        self.check_attributes(expr.hir_id, &expr.attrs, &expr.span, target, None);
         intravisit::walk_expr(self, expr)
     }
+
+    fn visit_variant(
+        &mut self,
+        variant: &'tcx hir::Variant<'tcx>,
+        generics: &'tcx hir::Generics<'tcx>,
+        item_id: HirId,
+    ) {
+        self.check_attributes(variant.id, variant.attrs, &variant.span, Target::Variant, None);
+        intravisit::walk_variant(self, variant, generics, item_id)
+    }
 }
 
 fn is_c_like_enum(item: &Item<'_>) -> bool {
diff --git a/src/test/ui/attr-usage-repr.rs b/src/test/ui/attr-usage-repr.rs
index a0b8237..8965dec 100644
--- a/src/test/ui/attr-usage-repr.rs
+++ b/src/test/ui/attr-usage-repr.rs
@@ -1,6 +1,6 @@
 #![feature(repr_simd)]
 
-#[repr(C)] //~ ERROR: attribute should be applied to struct, enum, or union
+#[repr(C)] //~ ERROR: attribute should be applied to a struct, enum, or union
 fn f() {}
 
 #[repr(C)]
@@ -12,7 +12,7 @@
 #[repr(simd)]
 struct SSimd(f64, f64);
 
-#[repr(i8)] //~ ERROR: attribute should be applied to enum
+#[repr(i8)] //~ ERROR: attribute should be applied to an enum
 struct SInt(f64, f64);
 
 #[repr(C)]
@@ -21,10 +21,10 @@
 #[repr(align(8))]
 enum EAlign { A, B }
 
-#[repr(packed)] //~ ERROR: attribute should be applied to struct
+#[repr(packed)] //~ ERROR: attribute should be applied to a struct
 enum EPacked { A, B }
 
-#[repr(simd)] //~ ERROR: attribute should be applied to struct
+#[repr(simd)] //~ ERROR: attribute should be applied to a struct
 enum ESimd { A, B }
 
 #[repr(i8)]
diff --git a/src/test/ui/attr-usage-repr.stderr b/src/test/ui/attr-usage-repr.stderr
index 82d80d8..42f6562 100644
--- a/src/test/ui/attr-usage-repr.stderr
+++ b/src/test/ui/attr-usage-repr.stderr
@@ -1,4 +1,4 @@
-error[E0517]: attribute should be applied to struct, enum, or union
+error[E0517]: attribute should be applied to a struct, enum, or union
   --> $DIR/attr-usage-repr.rs:3:8
    |
 LL | #[repr(C)]
@@ -6,7 +6,7 @@
 LL | fn f() {}
    | --------- not a struct, enum, or union
 
-error[E0517]: attribute should be applied to enum
+error[E0517]: attribute should be applied to an enum
   --> $DIR/attr-usage-repr.rs:15:8
    |
 LL | #[repr(i8)]
@@ -14,7 +14,7 @@
 LL | struct SInt(f64, f64);
    | ---------------------- not an enum
 
-error[E0517]: attribute should be applied to struct or union
+error[E0517]: attribute should be applied to a struct or union
   --> $DIR/attr-usage-repr.rs:24:8
    |
 LL | #[repr(packed)]
@@ -22,7 +22,7 @@
 LL | enum EPacked { A, B }
    | --------------------- not a struct or union
 
-error[E0517]: attribute should be applied to struct
+error[E0517]: attribute should be applied to a struct
   --> $DIR/attr-usage-repr.rs:27:8
    |
 LL | #[repr(simd)]
diff --git a/src/test/ui/error-codes/E0517.stderr b/src/test/ui/error-codes/E0517.stderr
index 2cfca17..2f90d4d 100644
--- a/src/test/ui/error-codes/E0517.stderr
+++ b/src/test/ui/error-codes/E0517.stderr
@@ -1,4 +1,4 @@
-error[E0517]: attribute should be applied to struct, enum, or union
+error[E0517]: attribute should be applied to a struct, enum, or union
   --> $DIR/E0517.rs:1:8
    |
 LL | #[repr(C)]
@@ -6,7 +6,7 @@
 LL | type Foo = u8;
    | -------------- not a struct, enum, or union
 
-error[E0517]: attribute should be applied to struct or union
+error[E0517]: attribute should be applied to a struct or union
   --> $DIR/E0517.rs:4:8
    |
 LL | #[repr(packed)]
@@ -14,7 +14,7 @@
 LL | enum Foo2 {Bar, Baz}
    | -------------------- not a struct or union
 
-error[E0517]: attribute should be applied to enum
+error[E0517]: attribute should be applied to an enum
   --> $DIR/E0517.rs:7:8
    |
 LL | #[repr(u8)]
@@ -22,7 +22,7 @@
 LL | struct Foo3 {bar: bool, baz: bool}
    | ---------------------------------- not an enum
 
-error[E0517]: attribute should be applied to struct, enum, or union
+error[E0517]: attribute should be applied to a struct, enum, or union
   --> $DIR/E0517.rs:10:8
    |
 LL |   #[repr(C)]
diff --git a/src/test/ui/inline-disallow-on-variant.rs b/src/test/ui/inline-disallow-on-variant.rs
new file mode 100644
index 0000000..d92a4e8
--- /dev/null
+++ b/src/test/ui/inline-disallow-on-variant.rs
@@ -0,0 +1,7 @@
+enum Foo {
+    #[inline]
+    //~^ ERROR attribute should be applied
+    Variant,
+}
+
+fn main() {}
diff --git a/src/test/ui/inline-disallow-on-variant.stderr b/src/test/ui/inline-disallow-on-variant.stderr
new file mode 100644
index 0000000..1b17657
--- /dev/null
+++ b/src/test/ui/inline-disallow-on-variant.stderr
@@ -0,0 +1,12 @@
+error[E0518]: attribute should be applied to function or closure
+  --> $DIR/inline-disallow-on-variant.rs:2:5
+   |
+LL |     #[inline]
+   |     ^^^^^^^^^
+LL |
+LL |     Variant,
+   |     ------- not a function or closure
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0518`.
diff --git a/src/test/ui/issues/issue-31769.rs b/src/test/ui/issues/issue-31769.rs
index 45eb5e4..f56c6ea 100644
--- a/src/test/ui/issues/issue-31769.rs
+++ b/src/test/ui/issues/issue-31769.rs
@@ -1,4 +1,4 @@
 fn main() {
     #[inline] struct Foo;  //~ ERROR attribute should be applied to function or closure
-    #[repr(C)] fn foo() {} //~ ERROR attribute should be applied to struct, enum, or union
+    #[repr(C)] fn foo() {} //~ ERROR attribute should be applied to a struct, enum, or union
 }
diff --git a/src/test/ui/issues/issue-31769.stderr b/src/test/ui/issues/issue-31769.stderr
index 20534e1..03e2f93 100644
--- a/src/test/ui/issues/issue-31769.stderr
+++ b/src/test/ui/issues/issue-31769.stderr
@@ -4,7 +4,7 @@
 LL |     #[inline] struct Foo;
    |     ^^^^^^^^^ ----------- not a function or closure
 
-error[E0517]: attribute should be applied to struct, enum, or union
+error[E0517]: attribute should be applied to a struct, enum, or union
   --> $DIR/issue-31769.rs:3:12
    |
 LL |     #[repr(C)] fn foo() {}
diff --git a/src/test/ui/issues/issue-43988.rs b/src/test/ui/issues/issue-43988.rs
index b809075..4b3a026 100644
--- a/src/test/ui/issues/issue-43988.rs
+++ b/src/test/ui/issues/issue-43988.rs
@@ -13,18 +13,17 @@
 
     #[repr(nothing)]
     let _x = 0;
-    //~^^ ERROR attribute should not be applied to a statement
+    //~^^ ERROR attribute should be applied to a struct, enum, or union
 
     #[repr(something_not_real)]
     loop {
         ()
     };
-    //~^^^^ ERROR attribute should not be applied to an expression
+    //~^^^^ ERROR attribute should be applied to a struct, enum, or union
 
     #[repr]
     let _y = "123";
-    //~^^ ERROR attribute should not be applied to a statement
-    //~| ERROR malformed `repr` attribute
+    //~^^ ERROR malformed `repr` attribute
 
     fn foo() {}
 
@@ -33,6 +32,5 @@
     //~^^ ERROR attribute should be applied to function or closure
 
     let _z = #[repr] 1;
-    //~^ ERROR attribute should not be applied to an expression
-    //~| ERROR malformed `repr` attribute
+    //~^ ERROR malformed `repr` attribute
 }
diff --git a/src/test/ui/issues/issue-43988.stderr b/src/test/ui/issues/issue-43988.stderr
index 37e5616..f1205d4 100644
--- a/src/test/ui/issues/issue-43988.stderr
+++ b/src/test/ui/issues/issue-43988.stderr
@@ -5,7 +5,7 @@
    |     ^^^^^^^ help: must be of the form: `#[repr(C)]`
 
 error: malformed `repr` attribute input
-  --> $DIR/issue-43988.rs:35:14
+  --> $DIR/issue-43988.rs:34:14
    |
 LL |     let _z = #[repr] 1;
    |              ^^^^^^^ help: must be of the form: `#[repr(C)]`
@@ -26,47 +26,33 @@
 LL |     let _b = 4;
    |     ----------- not a function or closure
 
-error[E0517]: attribute should not be applied to a statement
-  --> $DIR/issue-43988.rs:14:5
+error[E0517]: attribute should be applied to a struct, enum, or union
+  --> $DIR/issue-43988.rs:14:12
    |
 LL |     #[repr(nothing)]
-   |     ^^^^^^^^^^^^^^^^
+   |            ^^^^^^^
 LL |     let _x = 0;
    |     ----------- not a struct, enum, or union
 
-error[E0517]: attribute should not be applied to an expression
-  --> $DIR/issue-43988.rs:18:5
+error[E0517]: attribute should be applied to a struct, enum, or union
+  --> $DIR/issue-43988.rs:18:12
    |
 LL |       #[repr(something_not_real)]
-   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |              ^^^^^^^^^^^^^^^^^^
 LL | /     loop {
 LL | |         ()
 LL | |     };
-   | |_____- not defining a struct, enum, or union
-
-error[E0517]: attribute should not be applied to a statement
-  --> $DIR/issue-43988.rs:24:5
-   |
-LL |     #[repr]
-   |     ^^^^^^^
-LL |     let _y = "123";
-   |     --------------- not a struct, enum, or union
+   | |_____- not a struct, enum, or union
 
 error[E0518]: attribute should be applied to function or closure
-  --> $DIR/issue-43988.rs:31:5
+  --> $DIR/issue-43988.rs:30:5
    |
 LL |     #[inline(ABC)]
    |     ^^^^^^^^^^^^^^
 LL |     foo();
    |     ----- not a function or closure
 
-error[E0517]: attribute should not be applied to an expression
-  --> $DIR/issue-43988.rs:35:14
-   |
-LL |     let _z = #[repr] 1;
-   |              ^^^^^^^ - not defining a struct, enum, or union
-
-error: aborting due to 9 previous errors
+error: aborting due to 7 previous errors
 
 Some errors have detailed explanations: E0517, E0518.
 For more information about an error, try `rustc --explain E0517`.
diff --git a/src/test/ui/issues/issue-74082.rs b/src/test/ui/issues/issue-74082.rs
index 982f8ef..e3e400c 100644
--- a/src/test/ui/issues/issue-74082.rs
+++ b/src/test/ui/issues/issue-74082.rs
@@ -1,9 +1,9 @@
 #![allow(dead_code)]
 
-#[repr(i128)] //~ ERROR: attribute should be applied to enum
+#[repr(i128)] //~ ERROR: attribute should be applied to an enum
 struct Foo;
 
-#[repr(u128)] //~ ERROR: attribute should be applied to enum
+#[repr(u128)] //~ ERROR: attribute should be applied to an enum
 struct Bar;
 
 fn main() {}
diff --git a/src/test/ui/issues/issue-74082.stderr b/src/test/ui/issues/issue-74082.stderr
index 08fe415..12f5a3b 100644
--- a/src/test/ui/issues/issue-74082.stderr
+++ b/src/test/ui/issues/issue-74082.stderr
@@ -1,4 +1,4 @@
-error[E0517]: attribute should be applied to enum
+error[E0517]: attribute should be applied to an enum
   --> $DIR/issue-74082.rs:3:8
    |
 LL | #[repr(i128)]
@@ -6,7 +6,7 @@
 LL | struct Foo;
    | ----------- not an enum
 
-error[E0517]: attribute should be applied to enum
+error[E0517]: attribute should be applied to an enum
   --> $DIR/issue-74082.rs:6:8
    |
 LL | #[repr(u128)]
diff --git a/src/test/ui/macros/issue-68060.rs b/src/test/ui/macros/issue-68060.rs
index f82eb33..aa8f578 100644
--- a/src/test/ui/macros/issue-68060.rs
+++ b/src/test/ui/macros/issue-68060.rs
@@ -3,10 +3,7 @@
         .map(
             #[target_feature(enable = "")]
             //~^ ERROR: attribute should be applied to a function
-            //~| ERROR: the feature named `` is not valid for this target
-            //~| NOTE: `` is not valid for this target
             #[track_caller]
-            //~^ ERROR: `#[track_caller]` requires Rust ABI [E0737]
             |_| (),
             //~^ NOTE: not a function
         )
diff --git a/src/test/ui/macros/issue-68060.stderr b/src/test/ui/macros/issue-68060.stderr
index a01c382..1b58cf9 100644
--- a/src/test/ui/macros/issue-68060.stderr
+++ b/src/test/ui/macros/issue-68060.stderr
@@ -7,18 +7,5 @@
 LL |             |_| (),
    |             ------ not a function
 
-error: the feature named `` is not valid for this target
-  --> $DIR/issue-68060.rs:4:30
-   |
-LL |             #[target_feature(enable = "")]
-   |                              ^^^^^^^^^^^ `` is not valid for this target
+error: aborting due to previous error
 
-error[E0737]: `#[track_caller]` requires Rust ABI
-  --> $DIR/issue-68060.rs:8:13
-   |
-LL |             #[track_caller]
-   |             ^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0737`.
diff --git a/src/test/ui/repr/repr-disallow-on-variant.rs b/src/test/ui/repr/repr-disallow-on-variant.rs
new file mode 100644
index 0000000..90cad7e
--- /dev/null
+++ b/src/test/ui/repr/repr-disallow-on-variant.rs
@@ -0,0 +1,9 @@
+struct Test;
+
+enum Foo {
+    #[repr(u8)]
+    //~^ ERROR attribute should be applied to a struct, enum, or union
+    Variant,
+}
+
+fn main() {}
diff --git a/src/test/ui/repr/repr-disallow-on-variant.stderr b/src/test/ui/repr/repr-disallow-on-variant.stderr
new file mode 100644
index 0000000..70b45e3
--- /dev/null
+++ b/src/test/ui/repr/repr-disallow-on-variant.stderr
@@ -0,0 +1,12 @@
+error[E0517]: attribute should be applied to a struct, enum, or union
+  --> $DIR/repr-disallow-on-variant.rs:4:12
+   |
+LL |     #[repr(u8)]
+   |            ^^
+LL |
+LL |     Variant,
+   |     ------- not a struct, enum, or union
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0517`.
diff --git a/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.rs b/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.rs
index 3086346..870eda8 100644
--- a/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.rs
+++ b/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.rs
@@ -5,10 +5,10 @@
 
 #[repr(no_niche)]
 pub union Cloaked1 { _A: N16 }
-//~^^ ERROR attribute should be applied to struct or enum [E0517]
+//~^^ ERROR attribute should be applied to a struct or enum [E0517]
 
 #[repr(no_niche)]
 pub union Cloaked2 { _A: N16, _B: (u8, N8) }
-//~^^ ERROR attribute should be applied to struct or enum [E0517]
+//~^^ ERROR attribute should be applied to a struct or enum [E0517]
 
 fn main() { }
diff --git a/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.stderr b/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.stderr
index 4c542c5..9af929d 100644
--- a/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.stderr
+++ b/src/test/ui/repr/repr-no-niche-inapplicable-to-unions.stderr
@@ -1,4 +1,4 @@
-error[E0517]: attribute should be applied to struct or enum
+error[E0517]: attribute should be applied to a struct or enum
   --> $DIR/repr-no-niche-inapplicable-to-unions.rs:6:8
    |
 LL | #[repr(no_niche)]
@@ -6,7 +6,7 @@
 LL | pub union Cloaked1 { _A: N16 }
    | ------------------------------ not a struct or enum
 
-error[E0517]: attribute should be applied to struct or enum
+error[E0517]: attribute should be applied to a struct or enum
   --> $DIR/repr-no-niche-inapplicable-to-unions.rs:10:8
    |
 LL | #[repr(no_niche)]
diff --git a/src/test/ui/repr/repr-transparent-other-items.rs b/src/test/ui/repr/repr-transparent-other-items.rs
index c3d772f..e537e3e 100644
--- a/src/test/ui/repr/repr-transparent-other-items.rs
+++ b/src/test/ui/repr/repr-transparent-other-items.rs
@@ -1,9 +1,9 @@
 // See also repr-transparent.rs
 
-#[repr(transparent)] //~ ERROR should be applied to struct
+#[repr(transparent)] //~ ERROR should be applied to a struct
 fn cant_repr_this() {}
 
-#[repr(transparent)] //~ ERROR should be applied to struct
+#[repr(transparent)] //~ ERROR should be applied to a struct
 static CANT_REPR_THIS: u32 = 0;
 
 fn main() {}
diff --git a/src/test/ui/repr/repr-transparent-other-items.stderr b/src/test/ui/repr/repr-transparent-other-items.stderr
index 03df356..14e6f13 100644
--- a/src/test/ui/repr/repr-transparent-other-items.stderr
+++ b/src/test/ui/repr/repr-transparent-other-items.stderr
@@ -1,4 +1,4 @@
-error[E0517]: attribute should be applied to struct, enum, or union
+error[E0517]: attribute should be applied to a struct, enum, or union
   --> $DIR/repr-transparent-other-items.rs:3:8
    |
 LL | #[repr(transparent)]
@@ -6,7 +6,7 @@
 LL | fn cant_repr_this() {}
    | ---------------------- not a struct, enum, or union
 
-error[E0517]: attribute should be applied to struct, enum, or union
+error[E0517]: attribute should be applied to a struct, enum, or union
   --> $DIR/repr-transparent-other-items.rs:6:8
    |
 LL | #[repr(transparent)]