about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2021-07-29 12:00:41 -0500
committerAaron Hill <aa1ronham@gmail.com>2021-08-21 13:27:27 -0500
commitaf46699f8104ba5257d0da56d3d817bf8fc751cf (patch)
tree44efd77918662bdbd638ae9f3d26479e52318cfd
parentb6e334d87349502766be70d649e6fe4a73573482 (diff)
downloadrust-af46699f8104ba5257d0da56d3d817bf8fc751cf.tar.gz
rust-af46699f8104ba5257d0da56d3d817bf8fc751cf.zip
Remove `Session.used_attrs` and move logic to `CheckAttrVisitor`
Instead of updating global state to mark attributes as used,
we now explicitly emit a warning when an attribute is used in
an unsupported position. As a side effect, we are to emit more
detailed warning messages (instead of just a generic "unused" message).

`Session.check_name` is removed, since its only purpose was to mark
the attribute as used. All of the callers are modified to use
`Attribute.has_name`

Additionally, `AttributeType::AssumedUsed` is removed - an 'assumed
used' attribute is implemented by simply not performing any checks
in `CheckAttrVisitor` for a particular attribute.

We no longer emit unused attribute warnings for the `#[rustc_dummy]`
attribute - it's an internal attribute used for tests, so it doesn't
mark sense to treat it as 'unused'.

With this commit, a large source of global untracked state is removed.
-rw-r--r--Cargo.lock2
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs2
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs6
-rw-r--r--compiler/rustc_attr/src/builtin.rs25
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/generic/mod.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs3
-rw-r--r--compiler/rustc_expand/src/config.rs69
-rw-r--r--compiler/rustc_expand/src/expand.rs5
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs2
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs163
-rw-r--r--compiler/rustc_incremental/src/assert_dep_graph.rs4
-rw-r--r--compiler/rustc_incremental/src/assert_module_sources.rs39
-rw-r--r--compiler/rustc_incremental/src/persist/dirty_clean.rs4
-rw-r--r--compiler/rustc_interface/src/queries.rs2
-rw-r--r--compiler/rustc_interface/src/util.rs6
-rw-r--r--compiler/rustc_lint/src/builtin.rs23
-rw-r--r--compiler/rustc_lint/src/levels.rs2
-rw-r--r--compiler/rustc_lint/src/lib.rs2
-rw-r--r--compiler/rustc_lint/src/types.rs4
-rw-r--r--compiler/rustc_lint/src/unused.rs63
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs1
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs3
-rw-r--r--compiler/rustc_middle/src/middle/limits.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_mir/src/dataflow/framework/engine.rs2
-rw-r--r--compiler/rustc_mir/src/dataflow/mod.rs4
-rw-r--r--compiler/rustc_mir/src/monomorphize/polymorphize.rs6
-rw-r--r--compiler/rustc_parse/src/lib.rs44
-rw-r--r--compiler/rustc_passes/Cargo.toml2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs161
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs9
-rw-r--r--compiler/rustc_passes/src/lang_items.rs3
-rw-r--r--compiler/rustc_passes/src/layout_test.rs2
-rw-r--r--compiler/rustc_passes/src/lib.rs1
-rw-r--r--compiler/rustc_passes/src/lib_features.rs4
-rw-r--r--compiler/rustc_passes/src/stability.rs3
-rw-r--r--compiler/rustc_passes/src/weak_lang_items.rs3
-rw-r--r--compiler/rustc_plugin_impl/src/load.rs2
-rw-r--r--compiler/rustc_privacy/src/lib.rs4
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs6
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs10
-rw-r--r--compiler/rustc_resolve/src/lib.rs5
-rw-r--r--compiler/rustc_session/src/session.rs38
-rw-r--r--compiler/rustc_symbol_mangling/src/test.rs4
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs2
-rw-r--r--compiler/rustc_typeck/src/collect.rs58
-rw-r--r--compiler/rustc_typeck/src/lib.rs4
-rw-r--r--src/test/ui/attributes/register-attr-tool-unused.rs2
-rw-r--r--src/test/ui/attributes/register-attr-tool-unused.stderr18
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.rs4
-rw-r--r--src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr4
-rw-r--r--src/test/ui/const-generics/defaults/default-annotation.rs1
-rw-r--r--src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs223
-rw-r--r--src/test/ui/invalid/invalid-plugin-attr.rs3
-rw-r--r--src/test/ui/invalid/invalid-plugin-attr.stderr10
-rw-r--r--src/test/ui/lint/lint-misplaced-attr.rs6
-rw-r--r--src/test/ui/lint/lint-misplaced-attr.stderr18
-rw-r--r--src/test/ui/lint/unused/unused-attr-macro-rules.rs7
-rw-r--r--src/test/ui/lint/unused/unused-attr-macro-rules.stderr12
-rw-r--r--src/test/ui/lint/unused/unused-attr.rs49
-rw-r--r--src/test/ui/lint/unused/unused-attr.stderr98
62 files changed, 535 insertions, 739 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9e0624c57ae..e4612d8a954 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4115,10 +4115,12 @@ dependencies = [
  "rustc_attr",
  "rustc_data_structures",
  "rustc_errors",
+ "rustc_feature",
  "rustc_hir",
  "rustc_index",
  "rustc_lexer",
  "rustc_middle",
+ "rustc_parse",
  "rustc_serialize",
  "rustc_session",
  "rustc_span",
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index d41b3b81466..b6aef1543ff 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -2281,7 +2281,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     synthetic: param
                         .attrs
                         .iter()
-                        .filter(|attr| self.sess.check_name(attr, sym::rustc_synthetic))
+                        .filter(|attr| attr.has_name(sym::rustc_synthetic))
                         .map(|_| hir::SyntheticTyParamKind::FromAttr)
                         .next(),
                 };
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index feece517ceb..0be26b6f8e9 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -268,7 +268,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             gate_feature_fn!(self, has_feature, attr.span, name, descr);
         }
         // Check unstable flavors of the `#[doc]` attribute.
-        if self.sess.check_name(attr, sym::doc) {
+        if attr.has_name(sym::doc) {
             for nested_meta in attr.meta_item_list().unwrap_or_default() {
                 macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
                     $(if nested_meta.has_name(sym::$name) {
@@ -287,7 +287,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
         }
 
         // Check for unstable modifiers on `#[link(..)]` attribute
-        if self.sess.check_name(attr, sym::link) {
+        if attr.has_name(sym::link) {
             for nested_meta in attr.meta_item_list().unwrap_or_default() {
                 if nested_meta.has_name(sym::modifiers) {
                     gate_feature_post!(
@@ -709,7 +709,7 @@ fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
 
     if !sess.opts.unstable_features.is_nightly_build() {
         let lang_features = &sess.features_untracked().declared_lang_features;
-        for attr in krate.attrs.iter().filter(|attr| sess.check_name(attr, sym::feature)) {
+        for attr in krate.attrs.iter().filter(|attr| attr.has_name(sym::feature)) {
             let mut err = struct_span_err!(
                 sess.parse_sess.span_diagnostic,
                 attr.span,
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index a50fc698850..0ab452fb42d 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -166,8 +166,6 @@ where
             continue; // not a stability level
         }
 
-        sess.mark_attr_used(attr);
-
         let meta = attr.meta();
 
         if attr.has_name(sym::rustc_promotable) {
@@ -636,8 +634,7 @@ where
     let diagnostic = &sess.parse_sess.span_diagnostic;
 
     'outer: for attr in attrs_iter {
-        if !(sess.check_name(attr, sym::deprecated) || sess.check_name(attr, sym::rustc_deprecated))
-        {
+        if !(attr.has_name(sym::deprecated) || attr.has_name(sym::rustc_deprecated)) {
             continue;
         }
 
@@ -700,17 +697,17 @@ where
                                     continue 'outer;
                                 }
                             }
-                            sym::note if sess.check_name(attr, sym::deprecated) => {
+                            sym::note if attr.has_name(sym::deprecated) => {
                                 if !get(mi, &mut note) {
                                     continue 'outer;
                                 }
                             }
-                            sym::reason if sess.check_name(attr, sym::rustc_deprecated) => {
+                            sym::reason if attr.has_name(sym::rustc_deprecated) => {
                                 if !get(mi, &mut note) {
                                     continue 'outer;
                                 }
                             }
-                            sym::suggestion if sess.check_name(attr, sym::rustc_deprecated) => {
+                            sym::suggestion if attr.has_name(sym::rustc_deprecated) => {
                                 if !get(mi, &mut suggestion) {
                                     continue 'outer;
                                 }
@@ -721,7 +718,7 @@ where
                                     meta.span(),
                                     AttrError::UnknownMetaItem(
                                         pprust::path_to_string(&mi.path),
-                                        if sess.check_name(attr, sym::deprecated) {
+                                        if attr.has_name(sym::deprecated) {
                                             &["since", "note"]
                                         } else {
                                             &["since", "reason", "suggestion"]
@@ -747,11 +744,11 @@ where
             }
         }
 
-        if suggestion.is_some() && sess.check_name(attr, sym::deprecated) {
+        if suggestion.is_some() && attr.has_name(sym::deprecated) {
             unreachable!("only allowed on rustc_deprecated")
         }
 
-        if sess.check_name(attr, sym::rustc_deprecated) {
+        if attr.has_name(sym::rustc_deprecated) {
             if since.is_none() {
                 handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
                 continue;
@@ -763,9 +760,7 @@ where
             }
         }
 
-        sess.mark_attr_used(&attr);
-
-        let is_since_rustc_version = sess.check_name(attr, sym::rustc_deprecated);
+        let is_since_rustc_version = attr.has_name(sym::rustc_deprecated);
         depr = Some((Deprecation { since, note, suggestion, is_since_rustc_version }, attr.span));
     }
 
@@ -816,7 +811,6 @@ pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
     let diagnostic = &sess.parse_sess.span_diagnostic;
     if attr.has_name(sym::repr) {
         if let Some(items) = attr.meta_item_list() {
-            sess.mark_attr_used(attr);
             for item in items {
                 let mut recognised = false;
                 if item.is_word() {
@@ -1015,14 +1009,13 @@ pub enum TransparencyError {
 }
 
 pub fn find_transparency(
-    sess: &Session,
     attrs: &[Attribute],
     macro_rules: bool,
 ) -> (Transparency, Option<TransparencyError>) {
     let mut transparency = None;
     let mut error = None;
     for attr in attrs {
-        if sess.check_name(attr, sym::rustc_macro_transparency) {
+        if attr.has_name(sym::rustc_macro_transparency) {
             if let Some((_, old_span)) = transparency {
                 error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
                 break;
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 61af4979e70..85499623f41 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -677,8 +677,6 @@ impl<'a> TraitDef<'a> {
         let self_type = cx.ty_path(path);
 
         let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived));
-        // Just mark it now since we know that it'll end up used downstream
-        cx.sess.mark_attr_used(&attr);
         let opt_trait_ref = Some(trait_ref);
         let unused_qual = {
             let word = rustc_ast::attr::mk_nested_word_item(Ident::new(
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index f83329ecba8..7971c1fff42 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -260,11 +260,11 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
             return;
         }
 
-        if self.sess.check_name(attr, sym::proc_macro_derive) {
+        if attr.has_name(sym::proc_macro_derive) {
             self.collect_custom_derive(item, attr);
-        } else if self.sess.check_name(attr, sym::proc_macro_attribute) {
+        } else if attr.has_name(sym::proc_macro_attribute) {
             self.collect_attr_proc_macro(item);
-        } else if self.sess.check_name(attr, sym::proc_macro) {
+        } else if attr.has_name(sym::proc_macro) {
             self.collect_bang_proc_macro(item);
         };
 
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index be24b60294b..d791677cb8e 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -188,8 +188,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
                     let attrs = attrs
                         .into_iter()
                         .filter(|attr| {
-                            !self.sess.check_name(attr, sym::rustc_main)
-                                && !self.sess.check_name(attr, sym::start)
+                            !attr.has_name(sym::rustc_main) && !attr.has_name(sym::start)
                         })
                         .chain(iter::once(allow_dead_code))
                         .collect();
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index f9140609c0f..e09893f7f38 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -5,7 +5,7 @@ use rustc_ast::token::{DelimToken, Token, TokenKind};
 use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
 use rustc_ast::tokenstream::{DelimSpan, Spacing};
 use rustc_ast::tokenstream::{LazyTokenStream, TokenTree};
-use rustc_ast::{self as ast, AstLike, AttrItem, AttrStyle, Attribute, MetaItem};
+use rustc_ast::{self as ast, AstLike, AttrStyle, Attribute, MetaItem};
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::map_in_place::MapInPlace;
@@ -14,7 +14,7 @@ use rustc_feature::{Feature, Features, State as FeatureState};
 use rustc_feature::{
     ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
 };
-use rustc_parse::{parse_in, validate_attr};
+use rustc_parse::validate_attr;
 use rustc_session::parse::feature_err;
 use rustc_session::Session;
 use rustc_span::edition::{Edition, ALL_EDITIONS};
@@ -75,7 +75,7 @@ fn get_features(
     // Process the edition umbrella feature-gates first, to ensure
     // `edition_enabled_features` is completed before it's queried.
     for attr in krate_attrs {
-        if !sess.check_name(attr, sym::feature) {
+        if !attr.has_name(sym::feature) {
             continue;
         }
 
@@ -108,7 +108,7 @@ fn get_features(
     }
 
     for attr in krate_attrs {
-        if !sess.check_name(attr, sym::feature) {
+        if !attr.has_name(sym::feature) {
             continue;
         }
 
@@ -237,11 +237,6 @@ macro_rules! configure {
     };
 }
 
-const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
-const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
-    <https://doc.rust-lang.org/reference/conditional-compilation.html\
-    #the-cfg_attr-attribute>";
-
 impl<'a> StripUnconfigured<'a> {
     pub fn configure<T: AstLike>(&mut self, mut node: T) -> Option<T> {
         self.process_cfg_attrs(&mut node);
@@ -349,19 +344,17 @@ impl<'a> StripUnconfigured<'a> {
             return vec![attr];
         }
 
-        let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
-            None => return vec![],
-            Some(r) => r,
-        };
+        let (cfg_predicate, expanded_attrs) =
+            match rustc_parse::parse_cfg_attr(&attr, &self.sess.parse_sess) {
+                None => return vec![],
+                Some(r) => r,
+            };
 
         // Lint on zero attributes in source.
         if expanded_attrs.is_empty() {
             return vec![attr];
         }
 
-        // At this point we know the attribute is considered used.
-        self.sess.mark_attr_used(&attr);
-
         if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
             return vec![];
         }
@@ -415,46 +408,10 @@ impl<'a> StripUnconfigured<'a> {
             .collect()
     }
 
-    fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
-        match attr.get_normal_item().args {
-            ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
-                let msg = "wrong `cfg_attr` delimiters";
-                validate_attr::check_meta_bad_delim(&self.sess.parse_sess, dspan, delim, msg);
-                match parse_in(&self.sess.parse_sess, tts.clone(), "`cfg_attr` input", |p| {
-                    p.parse_cfg_attr()
-                }) {
-                    Ok(r) => return Some(r),
-                    Err(mut e) => {
-                        e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
-                            .note(CFG_ATTR_NOTE_REF)
-                            .emit();
-                    }
-                }
-            }
-            _ => self.error_malformed_cfg_attr_missing(attr.span),
-        }
-        None
-    }
-
-    fn error_malformed_cfg_attr_missing(&self, span: Span) {
-        self.sess
-            .parse_sess
-            .span_diagnostic
-            .struct_span_err(span, "malformed `cfg_attr` attribute input")
-            .span_suggestion(
-                span,
-                "missing condition and attribute",
-                CFG_ATTR_GRAMMAR_HELP.to_string(),
-                Applicability::HasPlaceholders,
-            )
-            .note(CFG_ATTR_NOTE_REF)
-            .emit();
-    }
-
     /// Determines if a node with the given attributes should be included in this configuration.
     fn in_cfg(&self, attrs: &[Attribute]) -> bool {
         attrs.iter().all(|attr| {
-            if !is_cfg(self.sess, attr) {
+            if !is_cfg(attr) {
                 return true;
             }
             let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
@@ -500,7 +457,7 @@ impl<'a> StripUnconfigured<'a> {
         //
         // N.B., this is intentionally not part of the visit_expr() function
         //     in order for filter_map_expr() to be able to avoid this check
-        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(self.sess, a)) {
+        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(*a)) {
             let msg = "removing an expression is not supported in this position";
             self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg);
         }
@@ -536,6 +493,6 @@ pub fn parse_cfg<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a Meta
     }
 }
 
-fn is_cfg(sess: &Session, attr: &Attribute) -> bool {
-    sess.check_name(attr, sym::cfg)
+fn is_cfg(attr: &Attribute) -> bool {
+    attr.has_name(sym::cfg)
 }
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 09beda33483..abe81ec978f 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -753,11 +753,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         }
                     }
                 }
-                SyntaxExtensionKind::NonMacroAttr { mark_used } => {
+                SyntaxExtensionKind::NonMacroAttr { mark_used: _ } => {
                     self.cx.expanded_inert_attrs.mark(&attr);
-                    if *mark_used {
-                        self.cx.sess.mark_attr_used(&attr);
-                    }
                     item.visit_attrs(|attrs| attrs.insert(pos, attr));
                     fragment_kind.expect_from_annotatables(iter::once(item))
                 }
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 9aee86c9e57..e0c62388fe0 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -535,7 +535,7 @@ pub fn compile_declarative_macro(
 
     valid &= macro_check::check_meta_variables(&sess.parse_sess, def.id, def.span, &lhses, &rhses);
 
-    let (transparency, transparency_error) = attr::find_transparency(sess, &def.attrs, macro_rules);
+    let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules);
     match transparency_error {
         Some(TransparencyError::UnknownTransparency(value, span)) => {
             diag.span_err(span, &format!("unknown macro transparency: `{}`", value))
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 5729384c0da..45db4c7f118 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -52,11 +52,6 @@ pub enum AttributeType {
     /// by the compiler before the unused_attribute check
     Normal,
 
-    /// Builtin attribute that may not be consumed by the compiler
-    /// before the unused_attribute check. These attributes
-    /// will be ignored by the unused_attribute lint
-    AssumedUsed,
-
     /// Builtin attribute that is only allowed at the crate level
     CrateLevel,
 }
@@ -186,7 +181,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(Word, List: r#"expected = "reason"#, NameValueStr: "reason"),
     ),
     // FIXME(Centril): This can be used on stable but shouldn't.
-    ungated!(reexport_test_harness_main, Normal, template!(NameValueStr: "name")),
+    ungated!(reexport_test_harness_main, CrateLevel, template!(NameValueStr: "name")),
 
     // Macros:
     ungated!(automatically_derived, Normal, template!(Word)),
@@ -206,7 +201,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ungated!(allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
     ungated!(forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
     ungated!(deny, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#)),
-    ungated!(must_use, AssumedUsed, template!(Word, NameValueStr: "reason")),
+    ungated!(must_use, Normal, template!(Word, NameValueStr: "reason")),
     // FIXME(#14407)
     ungated!(
         deprecated, Normal,
@@ -224,16 +219,16 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
 
     // ABI, linking, symbols, and FFI
     ungated!(
-        link, AssumedUsed,
+        link, Normal,
         template!(List: r#"name = "...", /*opt*/ kind = "dylib|static|...", /*opt*/ wasm_import_module = "...""#),
     ),
-    ungated!(link_name, AssumedUsed, template!(NameValueStr: "name")),
-    ungated!(no_link, AssumedUsed, template!(Word)),
-    ungated!(repr, AssumedUsed, template!(List: "C")),
-    ungated!(export_name, AssumedUsed, template!(NameValueStr: "name")),
-    ungated!(link_section, AssumedUsed, template!(NameValueStr: "name")),
-    ungated!(no_mangle, AssumedUsed, template!(Word)),
-    ungated!(used, AssumedUsed, template!(Word)),
+    ungated!(link_name, Normal, template!(NameValueStr: "name")),
+    ungated!(no_link, Normal, template!(Word)),
+    ungated!(repr, Normal, template!(List: "C")),
+    ungated!(export_name, Normal, template!(NameValueStr: "name")),
+    ungated!(link_section, Normal, template!(NameValueStr: "name")),
+    ungated!(no_mangle, Normal, template!(Word)),
+    ungated!(used, Normal, template!(Word)),
 
     // Limits:
     ungated!(recursion_limit, CrateLevel, template!(NameValueStr: "N")),
@@ -256,37 +251,37 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Modules, prelude, and resolution:
     ungated!(path, Normal, template!(NameValueStr: "file")),
     ungated!(no_std, CrateLevel, template!(Word)),
-    ungated!(no_implicit_prelude, Normal, template!(Word)),
-    ungated!(non_exhaustive, AssumedUsed, template!(Word)),
+    ungated!(no_implicit_prelude, CrateLevel, template!(Word)),
+    ungated!(non_exhaustive, Normal, template!(Word)),
 
     // Runtime
-    ungated!(windows_subsystem, AssumedUsed, template!(NameValueStr: "windows|console")),
+    ungated!(windows_subsystem, Normal, template!(NameValueStr: "windows|console")),
     ungated!(panic_handler, Normal, template!(Word)), // RFC 2070
 
     // Code generation:
-    ungated!(inline, AssumedUsed, template!(Word, List: "always|never")),
-    ungated!(cold, AssumedUsed, template!(Word)),
-    ungated!(no_builtins, AssumedUsed, template!(Word)),
-    ungated!(target_feature, AssumedUsed, template!(List: r#"enable = "name""#)),
-    ungated!(track_caller, AssumedUsed, template!(Word)),
+    ungated!(inline, Normal, template!(Word, List: "always|never")),
+    ungated!(cold, Normal, template!(Word)),
+    ungated!(no_builtins, Normal, template!(Word)),
+    ungated!(target_feature, Normal, template!(List: r#"enable = "name""#)),
+    ungated!(track_caller, Normal, template!(Word)),
     gated!(
-        no_sanitize, AssumedUsed,
+        no_sanitize, Normal,
         template!(List: "address, memory, thread"),
         experimental!(no_sanitize)
     ),
-    gated!(no_coverage, AssumedUsed, template!(Word), experimental!(no_coverage)),
+    gated!(no_coverage, Normal, template!(Word), experimental!(no_coverage)),
 
     // FIXME: #14408 assume docs are used since rustdoc looks at them.
-    ungated!(doc, AssumedUsed, template!(List: "hidden|inline|...", NameValueStr: "string")),
+    ungated!(doc, Normal, template!(List: "hidden|inline|...", NameValueStr: "string")),
 
     // ==========================================================================
     // Unstable attributes:
     // ==========================================================================
 
     // Linking:
-    gated!(naked, AssumedUsed, template!(Word), naked_functions, experimental!(naked)),
+    gated!(naked, Normal, template!(Word), naked_functions, experimental!(naked)),
     gated!(
-        link_ordinal, AssumedUsed, template!(List: "ordinal"), raw_dylib,
+        link_ordinal, Normal, template!(List: "ordinal"), raw_dylib,
         experimental!(link_ordinal)
     ),
 
@@ -311,23 +306,23 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "custom test frameworks are an unstable feature",
     ),
     // RFC #1268
-    gated!(marker, AssumedUsed, template!(Word), marker_trait_attr, experimental!(marker)),
+    gated!(marker, Normal, template!(Word), marker_trait_attr, experimental!(marker)),
     gated!(
-        thread_local, AssumedUsed, template!(Word),
+        thread_local, Normal, template!(Word),
         "`#[thread_local]` is an experimental feature, and does not currently handle destructors",
     ),
     gated!(no_core, CrateLevel, template!(Word), experimental!(no_core)),
     // RFC 2412
     gated!(
-        optimize, AssumedUsed, template!(List: "size|speed"), optimize_attribute,
+        optimize, Normal, template!(List: "size|speed"), optimize_attribute,
         experimental!(optimize),
     ),
     // RFC 2867
-    gated!(instruction_set, AssumedUsed, template!(List: "set"), isa_attribute, experimental!(instruction_set)),
+    gated!(instruction_set, Normal, template!(List: "set"), isa_attribute, experimental!(instruction_set)),
 
-    gated!(ffi_returns_twice, AssumedUsed, template!(Word), experimental!(ffi_returns_twice)),
-    gated!(ffi_pure, AssumedUsed, template!(Word), experimental!(ffi_pure)),
-    gated!(ffi_const, AssumedUsed, template!(Word), experimental!(ffi_const)),
+    gated!(ffi_returns_twice, Normal, template!(Word), experimental!(ffi_returns_twice)),
+    gated!(ffi_pure, Normal, template!(Word), experimental!(ffi_pure)),
+    gated!(ffi_const, Normal, template!(Word), experimental!(ffi_const)),
     gated!(
         register_attr, CrateLevel, template!(List: "attr1, attr2, ..."),
         experimental!(register_attr),
@@ -337,10 +332,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         experimental!(register_tool),
     ),
 
-    gated!(cmse_nonsecure_entry, AssumedUsed, template!(Word), experimental!(cmse_nonsecure_entry)),
+    gated!(cmse_nonsecure_entry, Normal, template!(Word), experimental!(cmse_nonsecure_entry)),
     // RFC 2632
     gated!(
-        default_method_body_is_const, AssumedUsed, template!(Word), const_trait_impl,
+        default_method_body_is_const, Normal, template!(Word), const_trait_impl,
         "`default_method_body_is_const` is a temporary placeholder for declaring default bodies \
         as `const`, which may be removed or renamed in the future."
     ),
@@ -353,26 +348,26 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // FIXME(#14407) -- only looked at on-demand so we can't
     // guarantee they'll have already been checked.
     ungated!(
-        rustc_deprecated, AssumedUsed,
+        rustc_deprecated, Normal,
         template!(List: r#"since = "version", reason = "...""#)
     ),
     // FIXME(#14407)
-    ungated!(stable, AssumedUsed, template!(List: r#"feature = "name", since = "version""#)),
+    ungated!(stable, Normal, template!(List: r#"feature = "name", since = "version""#)),
     // FIXME(#14407)
     ungated!(
-        unstable, AssumedUsed,
+        unstable, Normal,
         template!(List: r#"feature = "name", reason = "...", issue = "N""#),
     ),
     // FIXME(#14407)
-    ungated!(rustc_const_unstable, AssumedUsed, template!(List: r#"feature = "name""#)),
+    ungated!(rustc_const_unstable, Normal, template!(List: r#"feature = "name""#)),
     // FIXME(#14407)
-    ungated!(rustc_const_stable, AssumedUsed, template!(List: r#"feature = "name""#)),
+    ungated!(rustc_const_stable, Normal, template!(List: r#"feature = "name""#)),
     gated!(
-        allow_internal_unstable, AssumedUsed, template!(Word, List: "feat1, feat2, ..."),
+        allow_internal_unstable, Normal, template!(Word, List: "feat1, feat2, ..."),
         "allow_internal_unstable side-steps feature gating and stability checks",
     ),
     gated!(
-        rustc_allow_const_fn_unstable, AssumedUsed, template!(Word, List: "feat1, feat2, ..."),
+        rustc_allow_const_fn_unstable, Normal, template!(Word, List: "feat1, feat2, ..."),
         "rustc_allow_const_fn_unstable side-steps feature gating and stability checks"
     ),
     gated!(
@@ -384,7 +379,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Internal attributes: Type system related:
     // ==========================================================================
 
-    gated!(fundamental, AssumedUsed, template!(Word), experimental!(fundamental)),
+    gated!(fundamental, Normal, template!(Word), experimental!(fundamental)),
     gated!(
         may_dangle, Normal, template!(Word), dropck_eyepatch,
         "`may_dangle` has unstable semantics and may be removed in the future",
@@ -394,26 +389,26 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // Internal attributes: Runtime related:
     // ==========================================================================
 
-    rustc_attr!(rustc_allocator, AssumedUsed, template!(Word), IMPL_DETAIL),
-    rustc_attr!(rustc_allocator_nounwind, AssumedUsed, template!(Word), IMPL_DETAIL),
+    rustc_attr!(rustc_allocator, Normal, template!(Word), IMPL_DETAIL),
+    rustc_attr!(rustc_allocator_nounwind, Normal, template!(Word), IMPL_DETAIL),
     gated!(alloc_error_handler, Normal, template!(Word), experimental!(alloc_error_handler)),
     gated!(
-        default_lib_allocator, AssumedUsed, template!(Word), allocator_internals,
+        default_lib_allocator, Normal, template!(Word), allocator_internals,
         experimental!(default_lib_allocator),
     ),
     gated!(
         needs_allocator, Normal, template!(Word), allocator_internals,
         experimental!(needs_allocator),
     ),
-    gated!(panic_runtime, AssumedUsed, template!(Word), experimental!(panic_runtime)),
-    gated!(needs_panic_runtime, AssumedUsed, template!(Word), experimental!(needs_panic_runtime)),
+    gated!(panic_runtime, Normal, template!(Word), experimental!(panic_runtime)),
+    gated!(needs_panic_runtime, Normal, template!(Word), experimental!(needs_panic_runtime)),
     gated!(
-        compiler_builtins, AssumedUsed, template!(Word),
+        compiler_builtins, Normal, template!(Word),
         "the `#[compiler_builtins]` attribute is used to identify the `compiler_builtins` crate \
         which contains compiler-rt intrinsics and will never be stable",
     ),
     gated!(
-        profiler_runtime, AssumedUsed, template!(Word),
+        profiler_runtime, Normal, template!(Word),
         "the `#[profiler_runtime]` attribute is used to identify the `profiler_builtins` crate \
         which contains the profiler runtime and will never be stable",
     ),
@@ -423,23 +418,23 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // ==========================================================================
 
     gated!(
-        linkage, AssumedUsed, template!(NameValueStr: "external|internal|..."),
+        linkage, Normal, template!(NameValueStr: "external|internal|..."),
         "the `linkage` attribute is experimental and not portable across platforms",
     ),
-    rustc_attr!(rustc_std_internal_symbol, AssumedUsed, template!(Word), INTERNAL_UNSTABLE),
+    rustc_attr!(rustc_std_internal_symbol, Normal, template!(Word), INTERNAL_UNSTABLE),
 
     // ==========================================================================
     // Internal attributes, Macro related:
     // ==========================================================================
 
     rustc_attr!(
-        rustc_builtin_macro, AssumedUsed,
+        rustc_builtin_macro, Normal,
         template!(Word, List: "name, /*opt*/ attributes(name1, name2, ...)"),
         IMPL_DETAIL,
     ),
     rustc_attr!(rustc_proc_macro_decls, Normal, template!(Word), INTERNAL_UNSTABLE),
     rustc_attr!(
-        rustc_macro_transparency, AssumedUsed,
+        rustc_macro_transparency, Normal,
         template!(NameValueStr: "transparent|semitransparent|opaque"),
         "used internally for testing macro hygiene",
     ),
@@ -449,7 +444,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     // ==========================================================================
 
     rustc_attr!(
-        rustc_on_unimplemented, AssumedUsed,
+        rustc_on_unimplemented, Normal,
         template!(
             List: r#"/*opt*/ message = "...", /*opt*/ label = "...", /*opt*/ note = "...""#,
             NameValueStr: "message"
@@ -457,31 +452,31 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         INTERNAL_UNSTABLE
     ),
     // Enumerates "identity-like" conversion methods to suggest on type mismatch.
-    rustc_attr!(rustc_conversion_suggestion, AssumedUsed, template!(Word), INTERNAL_UNSTABLE),
+    rustc_attr!(rustc_conversion_suggestion, Normal, template!(Word), INTERNAL_UNSTABLE),
 
     // ==========================================================================
     // Internal attributes, Const related:
     // ==========================================================================
 
-    rustc_attr!(rustc_promotable, AssumedUsed, template!(Word), IMPL_DETAIL),
-    rustc_attr!(rustc_legacy_const_generics, AssumedUsed, template!(List: "N"), INTERNAL_UNSTABLE),
+    rustc_attr!(rustc_promotable, Normal, template!(Word), IMPL_DETAIL),
+    rustc_attr!(rustc_legacy_const_generics, Normal, template!(List: "N"), INTERNAL_UNSTABLE),
 
     // ==========================================================================
     // Internal attributes, Layout related:
     // ==========================================================================
 
     rustc_attr!(
-        rustc_layout_scalar_valid_range_start, AssumedUsed, template!(List: "value"),
+        rustc_layout_scalar_valid_range_start, Normal, template!(List: "value"),
         "the `#[rustc_layout_scalar_valid_range_start]` attribute is just used to enable \
         niche optimizations in libcore and will never be stable",
     ),
     rustc_attr!(
-        rustc_layout_scalar_valid_range_end, AssumedUsed, template!(List: "value"),
+        rustc_layout_scalar_valid_range_end, Normal, template!(List: "value"),
         "the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
         niche optimizations in libcore and will never be stable",
     ),
     rustc_attr!(
-        rustc_nonnull_optimization_guaranteed, AssumedUsed, template!(Word),
+        rustc_nonnull_optimization_guaranteed, Normal, template!(Word),
         "the `#[rustc_nonnull_optimization_guaranteed]` attribute is just used to enable \
         niche optimizations in libcore and will never be stable",
     ),
@@ -506,7 +501,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     gated!(
         // Used in resolve:
-        prelude_import, AssumedUsed, template!(Word),
+        prelude_import, Normal, template!(Word),
         "`#[prelude_import]` is for use by rustc only",
     ),
     gated!(
@@ -514,7 +509,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         "unboxed_closures are still evolving",
     ),
     rustc_attr!(
-        rustc_inherit_overflow_checks, AssumedUsed, template!(Word),
+        rustc_inherit_overflow_checks, Normal, template!(Word),
         "the `#[rustc_inherit_overflow_checks]` attribute is just used to control \
         overflow checking behavior of several libcore functions that are inlined \
         across crates and will never be stable",
@@ -556,41 +551,41 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     rustc_attr!(TEST, rustc_layout, Normal, template!(List: "field1, field2, ...")),
     rustc_attr!(TEST, rustc_regions, Normal, template!(Word)),
     rustc_attr!(
-        TEST, rustc_error, AssumedUsed,
+        TEST, rustc_error, Normal,
         template!(Word, List: "delay_span_bug_from_inside_query")
     ),
-    rustc_attr!(TEST, rustc_dump_user_substs, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_evaluate_where_clauses, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_if_this_changed, AssumedUsed, template!(Word, List: "DepNode")),
-    rustc_attr!(TEST, rustc_then_this_would_need, AssumedUsed, template!(List: "DepNode")),
+    rustc_attr!(TEST, rustc_dump_user_substs, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_evaluate_where_clauses, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_if_this_changed, Normal, template!(Word, List: "DepNode")),
+    rustc_attr!(TEST, rustc_then_this_would_need, Normal, template!(List: "DepNode")),
     rustc_attr!(
-        TEST, rustc_clean, AssumedUsed,
+        TEST, rustc_clean, Normal,
         template!(List: r#"cfg = "...", /*opt*/ label = "...", /*opt*/ except = "...""#),
     ),
     rustc_attr!(
-        TEST, rustc_partition_reused, AssumedUsed,
+        TEST, rustc_partition_reused, Normal,
         template!(List: r#"cfg = "...", module = "...""#),
     ),
     rustc_attr!(
-        TEST, rustc_partition_codegened, AssumedUsed,
+        TEST, rustc_partition_codegened, Normal,
         template!(List: r#"cfg = "...", module = "...""#),
     ),
     rustc_attr!(
-        TEST, rustc_expected_cgu_reuse, AssumedUsed,
+        TEST, rustc_expected_cgu_reuse, Normal,
         template!(List: r#"cfg = "...", module = "...", kind = "...""#),
     ),
-    rustc_attr!(TEST, rustc_synthetic, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_symbol_name, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_polymorphize_error, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_def_path, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_mir, AssumedUsed, template!(List: "arg1, arg2, ...")),
-    rustc_attr!(TEST, rustc_dump_program_clauses, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_dump_env_program_clauses, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_object_lifetime_default, AssumedUsed, template!(Word)),
-    rustc_attr!(TEST, rustc_dump_vtable, AssumedUsed, template!(Word)),
+    rustc_attr!(TEST, rustc_synthetic, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_symbol_name, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_def_path, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ...")),
+    rustc_attr!(TEST, rustc_dump_program_clauses, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_dump_env_program_clauses, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_object_lifetime_default, Normal, template!(Word)),
+    rustc_attr!(TEST, rustc_dump_vtable, Normal, template!(Word)),
     rustc_attr!(TEST, rustc_dummy, Normal, template!(Word /* doesn't matter*/)),
     gated!(
-        omit_gdb_pretty_printer_section, AssumedUsed, template!(Word),
+        omit_gdb_pretty_printer_section, Normal, template!(Word),
         "the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite",
     ),
 ];
diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs
index b5680beae14..0a558eb0555 100644
--- a/compiler/rustc_incremental/src/assert_dep_graph.rs
+++ b/compiler/rustc_incremental/src/assert_dep_graph.rs
@@ -123,7 +123,7 @@ impl IfThisChanged<'tcx> {
         let def_path_hash = self.tcx.def_path_hash(def_id.to_def_id());
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
-            if self.tcx.sess.check_name(attr, sym::rustc_if_this_changed) {
+            if attr.has_name(sym::rustc_if_this_changed) {
                 let dep_node_interned = self.argument(attr);
                 let dep_node = match dep_node_interned {
                     None => DepNode::from_def_path_hash(def_path_hash, DepKind::hir_owner),
@@ -138,7 +138,7 @@ impl IfThisChanged<'tcx> {
                     },
                 };
                 self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node));
-            } else if self.tcx.sess.check_name(attr, sym::rustc_then_this_would_need) {
+            } else if attr.has_name(sym::rustc_then_this_would_need) {
                 let dep_node_interned = self.argument(attr);
                 let dep_node = match dep_node_interned {
                     Some(n) => match DepNode::from_label_string(&n.as_str(), def_path_hash) {
diff --git a/compiler/rustc_incremental/src/assert_module_sources.rs b/compiler/rustc_incremental/src/assert_module_sources.rs
index 8220eb6c657..a5f3e4553ce 100644
--- a/compiler/rustc_incremental/src/assert_module_sources.rs
+++ b/compiler/rustc_incremental/src/assert_module_sources.rs
@@ -57,27 +57,26 @@ struct AssertModuleSource<'tcx> {
 
 impl AssertModuleSource<'tcx> {
     fn check_attr(&self, attr: &ast::Attribute) {
-        let (expected_reuse, comp_kind) =
-            if self.tcx.sess.check_name(attr, sym::rustc_partition_reused) {
-                (CguReuse::PreLto, ComparisonKind::AtLeast)
-            } else if self.tcx.sess.check_name(attr, sym::rustc_partition_codegened) {
-                (CguReuse::No, ComparisonKind::Exact)
-            } else if self.tcx.sess.check_name(attr, sym::rustc_expected_cgu_reuse) {
-                match self.field(attr, sym::kind) {
-                    sym::no => (CguReuse::No, ComparisonKind::Exact),
-                    sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
-                    sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
-                    sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
-                    other => {
-                        self.tcx.sess.span_fatal(
-                            attr.span,
-                            &format!("unknown cgu-reuse-kind `{}` specified", other),
-                        );
-                    }
+        let (expected_reuse, comp_kind) = if attr.has_name(sym::rustc_partition_reused) {
+            (CguReuse::PreLto, ComparisonKind::AtLeast)
+        } else if attr.has_name(sym::rustc_partition_codegened) {
+            (CguReuse::No, ComparisonKind::Exact)
+        } else if attr.has_name(sym::rustc_expected_cgu_reuse) {
+            match self.field(attr, sym::kind) {
+                sym::no => (CguReuse::No, ComparisonKind::Exact),
+                sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
+                sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
+                sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
+                other => {
+                    self.tcx.sess.span_fatal(
+                        attr.span,
+                        &format!("unknown cgu-reuse-kind `{}` specified", other),
+                    );
                 }
-            } else {
-                return;
-            };
+            }
+        } else {
+            return;
+        };
 
         if !self.tcx.sess.opts.debugging_opts.query_dep_graph {
             self.tcx.sess.span_fatal(
diff --git a/compiler/rustc_incremental/src/persist/dirty_clean.rs b/compiler/rustc_incremental/src/persist/dirty_clean.rs
index 9abd4eae914..c4dc0fbadc8 100644
--- a/compiler/rustc_incremental/src/persist/dirty_clean.rs
+++ b/compiler/rustc_incremental/src/persist/dirty_clean.rs
@@ -159,7 +159,7 @@ pub struct DirtyCleanVisitor<'tcx> {
 impl DirtyCleanVisitor<'tcx> {
     /// Possibly "deserialize" the attribute into a clean/dirty assertion
     fn assertion_maybe(&mut self, item_id: LocalDefId, attr: &Attribute) -> Option<Assertion> {
-        if !self.tcx.sess.check_name(attr, sym::rustc_clean) {
+        if !attr.has_name(sym::rustc_clean) {
             // skip: not rustc_clean/dirty
             return None;
         }
@@ -427,7 +427,7 @@ pub struct FindAllAttrs<'tcx> {
 
 impl FindAllAttrs<'tcx> {
     fn is_active_attr(&mut self, attr: &Attribute) -> bool {
-        if self.tcx.sess.check_name(attr, sym::rustc_clean) && check_config(self.tcx, attr) {
+        if attr.has_name(sym::rustc_clean) && check_config(self.tcx, attr) {
             return true;
         }
 
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 8a0964e6b9f..2f540395b2d 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -266,7 +266,7 @@ impl<'tcx> Queries<'tcx> {
         };
 
         let attrs = &*tcx.get_attrs(def_id);
-        let attrs = attrs.iter().filter(|attr| tcx.sess.check_name(attr, sym::rustc_error));
+        let attrs = attrs.iter().filter(|attr| attr.has_name(sym::rustc_error));
         for attr in attrs {
             match attr.meta_item_list() {
                 // Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`.
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 8b41a0ff176..6b64614363f 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -488,13 +488,13 @@ pub fn get_codegen_sysroot(
 }
 
 pub(crate) fn check_attr_crate_type(
-    sess: &Session,
+    _sess: &Session,
     attrs: &[ast::Attribute],
     lint_buffer: &mut LintBuffer,
 ) {
     // Unconditionally collect crate types from attributes to make them used
     for a in attrs.iter() {
-        if sess.check_name(a, sym::crate_type) {
+        if a.has_name(sym::crate_type) {
             if let Some(n) = a.value_str() {
                 if categorize_crate_type(n).is_some() {
                     return;
@@ -552,7 +552,7 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
     let attr_types: Vec<CrateType> = attrs
         .iter()
         .filter_map(|a| {
-            if session.check_name(a, sym::crate_type) {
+            if a.has_name(sym::crate_type) {
                 match a.value_str() {
                     Some(s) => categorize_crate_type(s),
                     _ => None,
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 5a72db7752d..afa2cfca188 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -46,7 +46,6 @@ use rustc_middle::ty::subst::{GenericArgKind, Subst};
 use rustc_middle::ty::Instance;
 use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt};
 use rustc_session::lint::FutureIncompatibilityReason;
-use rustc_session::Session;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -344,7 +343,7 @@ impl UnsafeCode {
 
 impl EarlyLintPass for UnsafeCode {
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
-        if cx.sess().check_name(attr, sym::allow_internal_unsafe) {
+        if attr.has_name(sym::allow_internal_unsafe) {
             self.report_unsafe(cx, attr.span, |lint| {
                 lint.build(
                     "`allow_internal_unsafe` allows defining \
@@ -492,12 +491,12 @@ pub struct MissingDoc {
 
 impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
 
-fn has_doc(sess: &Session, attr: &ast::Attribute) -> bool {
+fn has_doc(attr: &ast::Attribute) -> bool {
     if attr.is_doc_comment() {
         return true;
     }
 
-    if !sess.check_name(attr, sym::doc) {
+    if !attr.has_name(sym::doc) {
         return false;
     }
 
@@ -554,7 +553,7 @@ impl MissingDoc {
         }
 
         let attrs = cx.tcx.get_attrs(def_id.to_def_id());
-        let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a));
+        let has_doc = attrs.iter().any(has_doc);
         if !has_doc {
             cx.struct_span_lint(
                 MISSING_DOCS,
@@ -568,10 +567,10 @@ impl MissingDoc {
 }
 
 impl<'tcx> LateLintPass<'tcx> for MissingDoc {
-    fn enter_lint_attrs(&mut self, cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
+    fn enter_lint_attrs(&mut self, _cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
         let doc_hidden = self.doc_hidden()
             || attrs.iter().any(|attr| {
-                cx.sess().check_name(attr, sym::doc)
+                attr.has_name(sym::doc)
                     && match attr.meta_item_list() {
                         None => false,
                         Some(l) => attr::list_contains_name(&l, sym::hidden),
@@ -595,7 +594,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
             }
 
             let attrs = cx.tcx.hir().attrs(macro_def.hir_id());
-            let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a));
+            let has_doc = attrs.iter().any(has_doc);
             if !has_doc {
                 cx.struct_span_lint(
                     MISSING_DOCS,
@@ -999,7 +998,7 @@ impl EarlyLintPass for DeprecatedAttr {
                 return;
             }
         }
-        if cx.sess().check_name(attr, sym::no_start) || cx.sess().check_name(attr, sym::crate_id) {
+        if attr.has_name(sym::no_start) || attr.has_name(sym::crate_id) {
             let path_str = pprust::path_to_string(&attr.get_normal_item().path);
             let msg = format!("use of deprecated attribute `{}`: no longer used.", path_str);
             lint_deprecated_attr(cx, attr, &msg, None);
@@ -1028,7 +1027,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
 
         let span = sugared_span.take().unwrap_or(attr.span);
 
-        if is_doc_comment || cx.sess().check_name(attr, sym::doc) {
+        if is_doc_comment || attr.has_name(sym::doc) {
             cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| {
                 let mut err = lint.build("unused doc comment");
                 err.span_label(
@@ -1301,7 +1300,7 @@ declare_lint_pass!(
 
 impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
     fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
-        if cx.sess().check_name(attr, sym::feature) {
+        if attr.has_name(sym::feature) {
             if let Some(items) = attr.meta_item_list() {
                 for item in items {
                     cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| {
@@ -2771,7 +2770,7 @@ impl ClashingExternDeclarations {
                     overridden_link_name,
                     tcx.get_attrs(fi.def_id.to_def_id())
                         .iter()
-                        .find(|at| tcx.sess.check_name(at, sym::link_name))
+                        .find(|at| at.has_name(sym::link_name))
                         .unwrap()
                         .span,
                 )
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index bca3023664a..e6cbfa0c9e2 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -236,8 +236,6 @@ impl<'s> LintLevelsBuilder<'s> {
                 Some(lvl) => lvl,
             };
 
-            self.sess.mark_attr_used(attr);
-
             let mut metas = unwrap_or!(attr.meta_item_list(), continue);
 
             if metas.is_empty() {
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 0ffcd0154de..c4008e77bab 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -151,8 +151,6 @@ macro_rules! late_lint_passes {
                 // FIXME: Look into regression when this is used as a module lint
                 // May Depend on constants elsewhere
                 UnusedBrokenConst: UnusedBrokenConst,
-                // Uses attr::is_used which is untracked, can't be an incremental module pass.
-                UnusedAttributes: UnusedAttributes::new(),
                 // Needs to run after UnusedAttributes as it marks all `feature` attributes as used.
                 UnstableFeatures: UnstableFeatures,
                 // Tracks state across modules
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 34d342e6694..b8f8d5983a2 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -669,9 +669,7 @@ enum FfiResult<'tcx> {
 }
 
 crate fn nonnull_optimization_guaranteed<'tcx>(tcx: TyCtxt<'tcx>, def: &ty::AdtDef) -> bool {
-    tcx.get_attrs(def.did)
-        .iter()
-        .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed))
+    tcx.get_attrs(def.did).iter().any(|a| a.has_name(sym::rustc_nonnull_optimization_guaranteed))
 }
 
 /// `repr(transparent)` structs can have a single non-ZST field, this function returns that
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index f59ad0ac82d..be137884b4b 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -4,21 +4,16 @@ use rustc_ast as ast;
 use rustc_ast::util::parser;
 use rustc_ast::{ExprKind, StmtKind};
 use rustc_ast_pretty::pprust;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, Applicability};
-use rustc_feature::{AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::adjustment;
 use rustc_middle::ty::{self, Ty};
-use rustc_session::lint::builtin::UNUSED_ATTRIBUTES;
 use rustc_span::symbol::Symbol;
 use rustc_span::symbol::{kw, sym};
 use rustc_span::{BytePos, Span, DUMMY_SP};
 
-use tracing::debug;
-
 declare_lint! {
     /// The `unused_must_use` lint detects unused result of a type flagged as
     /// `#[must_use]`.
@@ -308,7 +303,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
             descr_post_path: &str,
         ) -> bool {
             for attr in cx.tcx.get_attrs(def_id).iter() {
-                if cx.sess().check_name(attr, sym::must_use) {
+                if attr.has_name(sym::must_use) {
                     cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
                         let msg = format!(
                             "unused {}`{}`{} that must be used",
@@ -382,62 +377,6 @@ impl<'tcx> LateLintPass<'tcx> for PathStatements {
     }
 }
 
-#[derive(Copy, Clone)]
-pub struct UnusedAttributes {
-    builtin_attributes: &'static FxHashMap<Symbol, &'static BuiltinAttribute>,
-}
-
-impl UnusedAttributes {
-    pub fn new() -> Self {
-        UnusedAttributes { builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP }
-    }
-}
-
-impl_lint_pass!(UnusedAttributes => [UNUSED_ATTRIBUTES]);
-
-impl<'tcx> LateLintPass<'tcx> for UnusedAttributes {
-    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
-        debug!("checking attribute: {:?}", attr);
-
-        if attr.is_doc_comment() {
-            return;
-        }
-
-        let attr_info = attr.ident().and_then(|ident| self.builtin_attributes.get(&ident.name));
-
-        if let Some(&&(name, ty, ..)) = attr_info {
-            if let AttributeType::AssumedUsed = ty {
-                debug!("{:?} is AssumedUsed", name);
-                return;
-            }
-        }
-
-        if !cx.sess().is_attr_used(attr) {
-            debug!("emitting warning for: {:?}", attr);
-            cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
-                // Mark as used to avoid duplicate warnings.
-                cx.sess().mark_attr_used(attr);
-                lint.build("unused attribute").emit()
-            });
-            // Is it a builtin attribute that must be used at the crate level?
-            if attr_info.map_or(false, |(_, ty, ..)| ty == &AttributeType::CrateLevel) {
-                cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
-                    let msg = match attr.style {
-                        ast::AttrStyle::Outer => {
-                            "crate-level attribute should be an inner attribute: add an exclamation \
-                             mark: `#![foo]`"
-                        }
-                        ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
-                    };
-                    lint.build(msg).emit()
-                });
-            }
-        } else {
-            debug!("Attr was used: {:?}", attr);
-        }
-    }
-}
-
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 enum UnusedDelimsCtx {
     FunctionArg,
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 467ec73663b..6dac3f54cfa 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -3042,6 +3042,7 @@ declare_lint_pass! {
         RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
         UNSUPPORTED_CALLING_CONVENTIONS,
         BREAK_WITH_LABEL_AND_LOOP,
+        UNUSED_ATTRIBUTES,
     ]
 }
 
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 804b86cd864..5f0d8c46f20 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -44,8 +44,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
 
         // Process all of the #[link(..)]-style arguments
         let sess = &self.tcx.sess;
-        for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| sess.check_name(a, sym::link))
-        {
+        for m in self.tcx.hir().attrs(it.hir_id()).iter().filter(|a| a.has_name(sym::link)) {
             let items = match m.meta_item_list() {
                 Some(item) => item,
                 None => continue,
diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs
index 7ea4902f4bc..20dcb670cd6 100644
--- a/compiler/rustc_middle/src/middle/limits.rs
+++ b/compiler/rustc_middle/src/middle/limits.rs
@@ -48,7 +48,7 @@ pub fn get_recursion_limit(krate_attrs: &[Attribute], sess: &Session) -> Limit {
 
 fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: usize) -> Limit {
     for attr in krate_attrs {
-        if !sess.check_name(attr, name) {
+        if !attr.has_name(name) {
             continue;
         }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 106b443ee3c..9c6ec25213c 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1136,7 +1136,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound<u128>, Bound<u128>) {
         let attrs = self.get_attrs(def_id);
         let get = |name| {
-            let attr = match attrs.iter().find(|a| self.sess.check_name(a, name)) {
+            let attr = match attrs.iter().find(|a| a.has_name(name)) {
                 Some(attr) => attr,
                 None => return Bound::Unbounded,
             };
diff --git a/compiler/rustc_mir/src/dataflow/framework/engine.rs b/compiler/rustc_mir/src/dataflow/framework/engine.rs
index 3f9f558223b..7ff7c860591 100644
--- a/compiler/rustc_mir/src/dataflow/framework/engine.rs
+++ b/compiler/rustc_mir/src/dataflow/framework/engine.rs
@@ -338,7 +338,7 @@ impl RustcMirAttrs {
 
         let rustc_mir_attrs = attrs
             .iter()
-            .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
+            .filter(|attr| attr.has_name(sym::rustc_mir))
             .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
 
         for attr in rustc_mir_attrs {
diff --git a/compiler/rustc_mir/src/dataflow/mod.rs b/compiler/rustc_mir/src/dataflow/mod.rs
index 4ca757cf269..8a426cc1015 100644
--- a/compiler/rustc_mir/src/dataflow/mod.rs
+++ b/compiler/rustc_mir/src/dataflow/mod.rs
@@ -30,12 +30,12 @@ pub struct MoveDataParamEnv<'tcx> {
 }
 
 pub(crate) fn has_rustc_mir_with(
-    sess: &Session,
+    _sess: &Session,
     attrs: &[ast::Attribute],
     name: Symbol,
 ) -> Option<MetaItem> {
     for attr in attrs {
-        if sess.check_name(attr, sym::rustc_mir) {
+        if attr.has_name(sym::rustc_mir) {
             let items = attr.meta_item_list();
             for item in items.iter().flat_map(|l| l.iter()) {
                 match item.meta_item() {
diff --git a/compiler/rustc_mir/src/monomorphize/polymorphize.rs b/compiler/rustc_mir/src/monomorphize/polymorphize.rs
index 30e758c7fdf..fea1adfa3d5 100644
--- a/compiler/rustc_mir/src/monomorphize/polymorphize.rs
+++ b/compiler/rustc_mir/src/monomorphize/polymorphize.rs
@@ -204,11 +204,7 @@ fn emit_unused_generic_params_error<'tcx>(
     unused_parameters: &FiniteBitSet<u32>,
 ) {
     let base_def_id = tcx.closure_base_def_id(def_id);
-    if !tcx
-        .get_attrs(base_def_id)
-        .iter()
-        .any(|a| tcx.sess.check_name(a, sym::rustc_polymorphize_error))
-    {
+    if !tcx.get_attrs(base_def_id).iter().any(|a| a.has_name(sym::rustc_polymorphize_error)) {
         return;
     }
 
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 611d72e61d0..07f972c2fa8 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -13,9 +13,10 @@ use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
 use rustc_ast::tokenstream::{Spacing, TokenStream};
 use rustc_ast::AstLike;
 use rustc_ast::Attribute;
+use rustc_ast::{AttrItem, MetaItem};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Diagnostic, FatalError, Level, PResult};
+use rustc_errors::{Applicability, Diagnostic, FatalError, Level, PResult};
 use rustc_session::parse::ParseSess;
 use rustc_span::{FileName, SourceFile, Span};
 
@@ -324,3 +325,44 @@ pub fn fake_token_stream(sess: &ParseSess, nt: &Nonterminal) -> TokenStream {
     let filename = FileName::macro_expansion_source_code(&source);
     parse_stream_from_source_str(filename, source, sess, Some(nt.span()))
 }
+
+pub fn parse_cfg_attr(
+    attr: &Attribute,
+    parse_sess: &ParseSess,
+) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
+    match attr.get_normal_item().args {
+        ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
+            let msg = "wrong `cfg_attr` delimiters";
+            crate::validate_attr::check_meta_bad_delim(parse_sess, dspan, delim, msg);
+            match parse_in(parse_sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
+                Ok(r) => return Some(r),
+                Err(mut e) => {
+                    e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
+                        .note(CFG_ATTR_NOTE_REF)
+                        .emit();
+                }
+            }
+        }
+        _ => error_malformed_cfg_attr_missing(attr.span, parse_sess),
+    }
+    None
+}
+
+const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
+const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
+    <https://doc.rust-lang.org/reference/conditional-compilation.html\
+    #the-cfg_attr-attribute>";
+
+fn error_malformed_cfg_attr_missing(span: Span, parse_sess: &ParseSess) {
+    parse_sess
+        .span_diagnostic
+        .struct_span_err(span, "malformed `cfg_attr` attribute input")
+        .span_suggestion(
+            span,
+            "missing condition and attribute",
+            CFG_ATTR_GRAMMAR_HELP.to_string(),
+            Applicability::HasPlaceholders,
+        )
+        .note(CFG_ATTR_NOTE_REF)
+        .emit();
+}
diff --git a/compiler/rustc_passes/Cargo.toml b/compiler/rustc_passes/Cargo.toml
index 5e49df841d7..bf1e52cd9a8 100644
--- a/compiler/rustc_passes/Cargo.toml
+++ b/compiler/rustc_passes/Cargo.toml
@@ -11,6 +11,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
+rustc_parse = { path = "../rustc_parse" }
 rustc_session = { path = "../rustc_session" }
 rustc_target = { path = "../rustc_target" }
 rustc_ast = { path = "../rustc_ast" }
@@ -18,3 +19,4 @@ rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
 rustc_lexer = { path = "../rustc_lexer" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
+rustc_feature = { path = "../rustc_feature" }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 3fea75954b9..3009c2c3f2f 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -8,8 +8,10 @@ use rustc_middle::hir::map::Map;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 
-use rustc_ast::{AttrStyle, Attribute, Lit, LitKind, NestedMetaItem};
+use rustc_ast::{ast, AttrStyle, Attribute, Lit, LitKind, NestedMetaItem};
+use rustc_data_structures::stable_set::FxHashSet;
 use rustc_errors::{pluralize, struct_span_err, Applicability};
+use rustc_feature::{AttributeType, BUILTIN_ATTRIBUTE_MAP};
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
@@ -66,9 +68,10 @@ impl CheckAttrVisitor<'tcx> {
     ) {
         let mut is_valid = true;
         let mut specified_inline = None;
+        let mut seen = FxHashSet::default();
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
-            is_valid &= match attr.name_or_empty() {
+            let attr_is_valid = match attr.name_or_empty() {
                 sym::inline => self.check_inline(hir_id, attr, span, target),
                 sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
                 sym::marker => self.check_marker(hir_id, attr, span, target),
@@ -101,14 +104,66 @@ impl CheckAttrVisitor<'tcx> {
                 sym::default_method_body_is_const => {
                     self.check_default_method_body_is_const(attr, span, target)
                 }
+                sym::rustc_const_unstable
+                | sym::rustc_const_stable
+                | sym::unstable
+                | sym::stable
+                | sym::rustc_promotable => self.check_stability_promotable(&attr, span, target),
                 _ => true,
             };
+            is_valid &= attr_is_valid;
+
             // lint-only checks
             match attr.name_or_empty() {
                 sym::cold => self.check_cold(hir_id, attr, span, target),
                 sym::link_name => self.check_link_name(hir_id, attr, span, target),
                 sym::link_section => self.check_link_section(hir_id, attr, span, target),
                 sym::no_mangle => self.check_no_mangle(hir_id, attr, span, target),
+                sym::deprecated | sym::rustc_deprecated => {
+                    self.check_deprecated(hir_id, attr, span, target)
+                }
+                sym::macro_use | sym::macro_escape => self.check_macro_use(hir_id, attr, target),
+                sym::path => self.check_generic_attr(hir_id, attr, target, &[Target::Mod]),
+                sym::cfg_attr => self.check_cfg_attr(hir_id, attr),
+                sym::plugin_registrar => self.check_plugin_registrar(hir_id, attr, target),
+                sym::macro_export => self.check_macro_export(hir_id, attr, target),
+                sym::ignore | sym::should_panic | sym::proc_macro_derive => {
+                    self.check_generic_attr(hir_id, attr, target, &[Target::Fn])
+                }
+                _ => {}
+            }
+
+            if hir_id != CRATE_HIR_ID {
+                if let Some((_, AttributeType::CrateLevel, ..)) =
+                    attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name))
+                {
+                    self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                        let msg = match attr.style {
+                            ast::AttrStyle::Outer => {
+                                "crate-level attribute should be an inner attribute: add an exclamation \
+                                 mark: `#![foo]`"
+                            }
+                            ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
+                        };
+                        lint.build(msg).emit()
+                    });
+                }
+            }
+
+            // Duplicate attributes
+            match attr.name_or_empty() {
+                name @ sym::macro_use => {
+                    let args = attr.meta_item_list().unwrap_or_else(Vec::new);
+                    let args: Vec<_> = args.iter().map(|arg| arg.name_or_empty()).collect();
+                    if !seen.insert((name, args)) {
+                        self.tcx.struct_span_lint_hir(
+                            UNUSED_ATTRIBUTES,
+                            hir_id,
+                            attr.span,
+                            |lint| lint.build("unused attribute").emit(),
+                        );
+                    }
+                }
                 _ => {}
             }
         }
@@ -211,6 +266,38 @@ impl CheckAttrVisitor<'tcx> {
         }
     }
 
+    fn check_generic_attr(
+        &self,
+        hir_id: HirId,
+        attr: &Attribute,
+        target: Target,
+        allowed_targets: &[Target],
+    ) {
+        if !allowed_targets.iter().any(|t| t == &target) {
+            let name = attr.name_or_empty();
+            let mut i = allowed_targets.iter();
+            // Pluralize
+            let b = i.next().map_or_else(String::new, |t| t.to_string() + "s");
+            let supported_names = i.enumerate().fold(b, |mut b, (i, allowed_target)| {
+                if allowed_targets.len() > 2 && i == allowed_targets.len() - 2 {
+                    b.push_str(", and ");
+                } else if allowed_targets.len() == 2 && i == allowed_targets.len() - 2 {
+                    b.push_str(" and ");
+                } else {
+                    b.push_str(", ");
+                }
+                // Pluralize
+                b.push_str(&(allowed_target.to_string() + "s"));
+                b
+            });
+            //let supported_names = allowed_targets.iter().fold(String::new(), |msg, t| msg + ", " + &t.to_string());
+            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                lint.build(&format!("`#[{name}]` only has an effect on {}", supported_names))
+                    .emit();
+            });
+        }
+    }
+
     /// Checks if `#[naked]` is applied to a function definition.
     fn check_naked(&self, hir_id: HirId, attr: &Attribute, span: &Span, target: Target) -> bool {
         match target {
@@ -1555,6 +1642,72 @@ impl CheckAttrVisitor<'tcx> {
             }
         }
     }
+
+    fn check_stability_promotable(&self, attr: &Attribute, _span: &Span, target: Target) -> bool {
+        match target {
+            Target::Expression => {
+                self.tcx
+                    .sess
+                    .struct_span_err(attr.span, "attribute cannot be applied to an expression")
+                    .emit();
+                false
+            }
+            _ => true,
+        }
+    }
+
+    fn check_deprecated(&self, hir_id: HirId, attr: &Attribute, _span: &Span, target: Target) {
+        match target {
+            Target::Closure | Target::Expression | Target::Statement | Target::Arm => {
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("attribute is ignored here").emit();
+                });
+            }
+            _ => {}
+        }
+    }
+
+    fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
+        let name = attr.name_or_empty();
+        match target {
+            Target::ExternCrate | Target::Mod => {}
+            _ => {
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build(&format!(
+                        "`#[{name}]` only has an effect on `extern crate` and modules"
+                    ))
+                    .emit();
+                });
+            }
+        }
+    }
+
+    fn check_macro_export(&self, hir_id: HirId, attr: &Attribute, target: Target) {
+        if target != Target::MacroDef {
+            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                lint.build(&format!("`#[macro_export]` only has an effect on macro definitions"))
+                    .emit();
+            });
+        }
+    }
+
+    fn check_cfg_attr(&self, hir_id: HirId, attr: &Attribute) {
+        if let Some((_, attrs)) = rustc_parse::parse_cfg_attr(&attr, &self.tcx.sess.parse_sess) {
+            if attrs.is_empty() {
+                self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                    lint.build("`#[cfg_attr]` does not expand to any attributes").emit();
+                });
+            }
+        }
+    }
+
+    fn check_plugin_registrar(&self, hir_id: HirId, attr: &Attribute, target: Target) {
+        if target != Target::Fn {
+            self.tcx.struct_span_lint_hir(UNUSED_ATTRIBUTES, hir_id, attr.span, |lint| {
+                lint.build("`#[plugin_registrar]` only has an effect on functions").emit();
+            });
+        }
+    }
 }
 
 impl Visitor<'tcx> for CheckAttrVisitor<'tcx> {
@@ -1675,7 +1828,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
 
     for attr in attrs {
         for attr_to_check in ATTRS_TO_CHECK {
-            if tcx.sess.check_name(attr, *attr_to_check) {
+            if attr.has_name(*attr_to_check) {
                 tcx.sess
                     .struct_span_err(
                         attr.span,
@@ -1692,7 +1845,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
 
 fn check_invalid_macro_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
     for attr in attrs {
-        if tcx.sess.check_name(attr, sym::inline) {
+        if attr.has_name(sym::inline) {
             struct_span_err!(
                 tcx.sess,
                 attr.span,
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index 3cf1d0cdd94..e43abda7133 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -15,7 +15,6 @@ use rustc_hir as hir;
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::Session;
 use rustc_span::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 use rustc_span::symbol::{sym, Symbol};
 
@@ -51,7 +50,7 @@ impl<'tcx> DiagnosticItemCollector<'tcx> {
     fn observe_item(&mut self, def_id: LocalDefId) {
         let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
         let attrs = self.tcx.hir().attrs(hir_id);
-        if let Some(name) = extract(&self.tcx.sess, attrs) {
+        if let Some(name) = extract(attrs) {
             // insert into our table
             collect_item(self.tcx, &mut self.items, name, def_id.to_def_id());
         }
@@ -91,10 +90,10 @@ fn collect_item(
     }
 }
 
-/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
-fn extract(sess: &Session, attrs: &[ast::Attribute]) -> Option<Symbol> {
+/// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.p
+fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
     attrs.iter().find_map(|attr| {
-        if sess.check_name(attr, sym::rustc_diagnostic_item) { attr.value_str() } else { None }
+        if attr.has_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None }
     })
 }
 
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 3a88d1932a8..62f9bcfdc47 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -13,6 +13,7 @@ use crate::weak_lang_items;
 use rustc_middle::middle::cstore::ExternCrate;
 use rustc_middle::ty::TyCtxt;
 
+use rustc_ast::Attribute;
 use rustc_errors::{pluralize, struct_span_err};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -57,7 +58,7 @@ impl LanguageItemCollector<'tcx> {
 
     fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId) {
         let attrs = self.tcx.hir().attrs(hir_id);
-        let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
+        let check_name = |attr: &Attribute, sym| attr.has_name(sym);
         if let Some((value, span)) = extract(check_name, &attrs) {
             match ITEM_REFS.get(&value).cloned() {
                 // Known lang item with attribute on correct target.
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index 18c1d647060..43a3a3062e7 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -27,7 +27,7 @@ impl ItemLikeVisitor<'tcx> for LayoutTest<'tcx> {
             | ItemKind::Struct(..)
             | ItemKind::Union(..) => {
                 for attr in self.tcx.get_attrs(item.def_id.to_def_id()).iter() {
-                    if self.tcx.sess.check_name(attr, sym::rustc_layout) {
+                    if attr.has_name(sym::rustc_layout) {
                         self.dump_layout_of(item.def_id, item, attr);
                     }
                 }
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index cadb8d23580..f583a5d58d5 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -7,6 +7,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(crate_visibility_modifier)]
 #![feature(in_band_lifetimes)]
+#![feature(format_args_capture)]
 #![feature(iter_zip)]
 #![feature(nll)]
 #![feature(min_specialization)]
diff --git a/compiler/rustc_passes/src/lib_features.rs b/compiler/rustc_passes/src/lib_features.rs
index 363a6417f99..605b52f1891 100644
--- a/compiler/rustc_passes/src/lib_features.rs
+++ b/compiler/rustc_passes/src/lib_features.rs
@@ -33,9 +33,7 @@ impl LibFeatureCollector<'tcx> {
 
         // Find a stability attribute (i.e., `#[stable (..)]`, `#[unstable (..)]`,
         // `#[rustc_const_unstable (..)]`).
-        if let Some(stab_attr) =
-            stab_attrs.iter().find(|stab_attr| self.tcx.sess.check_name(attr, **stab_attr))
-        {
+        if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) {
             let meta_item = attr.meta();
             if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta_item {
                 let mut feature = None;
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 484040de0fb..b64dcb0bbf0 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -148,7 +148,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         }
 
         if self.tcx.features().staged_api {
-            if let Some(a) = attrs.iter().find(|a| self.tcx.sess.check_name(a, sym::deprecated)) {
+            if let Some(a) = attrs.iter().find(|a| a.has_name(sym::deprecated)) {
                 self.tcx
                     .sess
                     .struct_span_err(a.span, "`#[deprecated]` cannot be used in staged API")
@@ -350,7 +350,6 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         for attr in attrs {
             let name = attr.name_or_empty();
             if unstable_attrs.contains(&name) {
-                self.tcx.sess.mark_attr_used(attr);
                 struct_span_err!(
                     self.tcx.sess,
                     attr.span,
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index 629513c7cfe..3a662a9bab2 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -1,5 +1,6 @@
 //! Validity checking for weak lang items
 
+use rustc_ast::Attribute;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
@@ -96,7 +97,7 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
     }
 
     fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) {
-        let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
+        let check_name = |attr: &Attribute, sym| attr.has_name(sym);
         let attrs = self.tcx.hir().attrs(i.hir_id());
         if let Some((lang_item, _)) = lang_items::extract(check_name, attrs) {
             self.register(lang_item, i.span);
diff --git a/compiler/rustc_plugin_impl/src/load.rs b/compiler/rustc_plugin_impl/src/load.rs
index 5da02e3a489..51cf85f794b 100644
--- a/compiler/rustc_plugin_impl/src/load.rs
+++ b/compiler/rustc_plugin_impl/src/load.rs
@@ -32,7 +32,7 @@ pub fn load_plugins(
     let mut plugins = Vec::new();
 
     for attr in &krate.attrs {
-        if !sess.check_name(attr, sym::plugin) {
+        if !attr.has_name(sym::plugin) {
             continue;
         }
 
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index a683cb05e16..9e4c4b443e7 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -885,9 +885,7 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
     fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
         // Non-opaque macros cannot make other items more accessible than they already are.
         let attrs = self.tcx.hir().attrs(md.hir_id());
-        if attr::find_transparency(&self.tcx.sess, &attrs, md.ast.macro_rules).0
-            != Transparency::Opaque
-        {
+        if attr::find_transparency(&attrs, md.ast.macro_rules).0 != Transparency::Opaque {
             // `#[macro_export]`-ed `macro_rules!` are `Public` since they
             // ignore their containing path to always appear at the crate root.
             if md.ast.macro_rules {
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 178d727418d..c06ef08e0a8 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1059,7 +1059,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         let mut import_all = None;
         let mut single_imports = Vec::new();
         for attr in &item.attrs {
-            if self.r.session.check_name(attr, sym::macro_use) {
+            if attr.has_name(sym::macro_use) {
                 if self.parent_scope.module.parent.is_some() {
                     struct_span_err!(
                         self.r.session,
@@ -1165,7 +1165,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
     /// Returns `true` if this attribute list contains `macro_use`.
     fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
         for attr in attrs {
-            if self.r.session.check_name(attr, sym::macro_escape) {
+            if attr.has_name(sym::macro_escape) {
                 let msg = "`#[macro_escape]` is a deprecated synonym for `#[macro_use]`";
                 let mut err = self.r.session.struct_span_warn(attr.span, msg);
                 if let ast::AttrStyle::Inner = attr.style {
@@ -1173,7 +1173,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 } else {
                     err.emit();
                 }
-            } else if !self.r.session.check_name(attr, sym::macro_use) {
+            } else if !attr.has_name(sym::macro_use) {
                 continue;
             }
 
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index d79fd910b7e..882d15cf892 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -2057,9 +2057,13 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                             if let Some(def_id) = parent_def_id.as_local() {
                                 let parent_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
                                 // lifetimes in `derive` expansions don't count (Issue #53738)
-                                if self.tcx.hir().attrs(parent_hir_id).iter().any(|attr| {
-                                    self.tcx.sess.check_name(attr, sym::automatically_derived)
-                                }) {
+                                if self
+                                    .tcx
+                                    .hir()
+                                    .attrs(parent_hir_id)
+                                    .iter()
+                                    .any(|attr| attr.has_name(sym::automatically_derived))
+                                {
                                     continue;
                                 }
                             }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 7114fd33188..438a109f967 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -3382,9 +3382,8 @@ impl<'a> Resolver<'a> {
 
                 let parse_attrs = || {
                     let attrs = self.cstore().item_attrs(def_id, self.session);
-                    let attr = attrs
-                        .iter()
-                        .find(|a| self.session.check_name(a, sym::rustc_legacy_const_generics))?;
+                    let attr =
+                        attrs.iter().find(|a| a.has_name(sym::rustc_legacy_const_generics))?;
                     let mut ret = vec![];
                     for meta in attr.meta_item_list()? {
                         match meta.literal()?.kind {
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 5b163603d5f..d49ba861785 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -213,8 +213,6 @@ pub struct Session {
     /// Set of enabled features for the current target.
     pub target_features: FxHashSet<Symbol>,
 
-    used_attrs: Lock<MarkedAttrs>,
-
     /// `Span`s for `if` conditions that we have suggested turning into `if let`.
     pub if_let_suggestions: Lock<FxHashSet<Span>>,
 }
@@ -1066,39 +1064,14 @@ impl Session {
             == config::InstrumentCoverage::ExceptUnusedFunctions
     }
 
-    pub fn mark_attr_used(&self, attr: &Attribute) {
-        self.used_attrs.lock().mark(attr)
-    }
-
-    pub fn is_attr_used(&self, attr: &Attribute) -> bool {
-        self.used_attrs.lock().is_marked(attr)
-    }
-
-    /// Returns `true` if the attribute's path matches the argument. If it
-    /// matches, then the attribute is marked as used.
-    ///
-    /// This method should only be used by rustc, other tools can use
-    /// `Attribute::has_name` instead, because only rustc is supposed to report
-    /// the `unused_attributes` lint. (`MetaItem` and `NestedMetaItem` are
-    /// produced by lowering an `Attribute` and don't have identity, so they
-    /// only have the `has_name` method, and you need to mark the original
-    /// `Attribute` as used when necessary.)
-    pub fn check_name(&self, attr: &Attribute, name: Symbol) -> bool {
-        let matches = attr.has_name(name);
-        if matches {
-            self.mark_attr_used(attr);
-        }
-        matches
-    }
-
     pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
         [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
             .iter()
-            .any(|kind| self.check_name(attr, *kind))
+            .any(|kind| attr.has_name(*kind))
     }
 
     pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
-        attrs.iter().any(|item| self.check_name(item, name))
+        attrs.iter().any(|item| item.has_name(name))
     }
 
     pub fn find_by_name<'a>(
@@ -1106,7 +1079,7 @@ impl Session {
         attrs: &'a [Attribute],
         name: Symbol,
     ) -> Option<&'a Attribute> {
-        attrs.iter().find(|attr| self.check_name(attr, name))
+        attrs.iter().find(|attr| attr.has_name(name))
     }
 
     pub fn filter_by_name<'a>(
@@ -1114,7 +1087,7 @@ impl Session {
         attrs: &'a [Attribute],
         name: Symbol,
     ) -> impl Iterator<Item = &'a Attribute> {
-        attrs.iter().filter(move |attr| self.check_name(attr, name))
+        attrs.iter().filter(move |attr| attr.has_name(name))
     }
 
     pub fn first_attr_value_str_by_name(
@@ -1122,7 +1095,7 @@ impl Session {
         attrs: &[Attribute],
         name: Symbol,
     ) -> Option<Symbol> {
-        attrs.iter().find(|at| self.check_name(at, name)).and_then(|at| at.value_str())
+        attrs.iter().find(|at| at.has_name(name)).and_then(|at| at.value_str())
     }
 }
 
@@ -1359,7 +1332,6 @@ pub fn build_session(
         miri_unleashed_features: Lock::new(Default::default()),
         asm_arch,
         target_features: FxHashSet::default(),
-        used_attrs: Lock::new(MarkedAttrs::new()),
         if_let_suggestions: Default::default(),
     };
 
diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs
index bfe9c4d6de3..183df96f316 100644
--- a/compiler/rustc_symbol_mangling/src/test.rs
+++ b/compiler/rustc_symbol_mangling/src/test.rs
@@ -35,7 +35,7 @@ impl SymbolNamesTest<'tcx> {
     fn process_attrs(&mut self, def_id: LocalDefId) {
         let tcx = self.tcx;
         for attr in tcx.get_attrs(def_id.to_def_id()).iter() {
-            if tcx.sess.check_name(attr, SYMBOL_NAME) {
+            if attr.has_name(SYMBOL_NAME) {
                 let def_id = def_id.to_def_id();
                 let instance = Instance::new(
                     def_id,
@@ -47,7 +47,7 @@ impl SymbolNamesTest<'tcx> {
                     tcx.sess.span_err(attr.span, &format!("demangling({})", demangling));
                     tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling));
                 }
-            } else if tcx.sess.check_name(attr, DEF_PATH) {
+            } else if attr.has_name(DEF_PATH) {
                 let path = with_no_trimmed_paths(|| tcx.def_path_str(def_id.to_def_id()));
                 tcx.sess.span_err(attr.span, &format!("def-path({})", path));
             }
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index e5a00f70639..ccfa0893d11 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -257,7 +257,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     //
                     // FIXME? Other potential candidate methods: `as_ref` and
                     // `as_mut`?
-                    .any(|a| self.sess().check_name(a, sym::rustc_conversion_suggestion))
+                    .any(|a| a.has_name(sym::rustc_conversion_suggestion))
         });
 
         methods
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 9fbf5ab8533..1c6fd5983cb 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -21,6 +21,7 @@ use crate::constrained_generic_params as cgp;
 use crate::errors;
 use crate::middle::resolve_lifetime as rl;
 use rustc_ast as ast;
+use rustc_ast::Attribute;
 use rustc_ast::{MetaItemKind, NestedMetaItem};
 use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
 use rustc_data_structures::captures::Captures;
@@ -2769,11 +2770,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
     let mut link_ordinal_span = None;
     let mut no_sanitize_span = None;
     for attr in attrs.iter() {
-        if tcx.sess.check_name(attr, sym::cold) {
+        if attr.has_name(sym::cold) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
-        } else if tcx.sess.check_name(attr, sym::rustc_allocator) {
+        } else if attr.has_name(sym::rustc_allocator) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
-        } else if tcx.sess.check_name(attr, sym::ffi_returns_twice) {
+        } else if attr.has_name(sym::ffi_returns_twice) {
             if tcx.is_foreign_item(id) {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
             } else {
@@ -2786,9 +2787,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 )
                 .emit();
             }
-        } else if tcx.sess.check_name(attr, sym::ffi_pure) {
+        } else if attr.has_name(sym::ffi_pure) {
             if tcx.is_foreign_item(id) {
-                if attrs.iter().any(|a| tcx.sess.check_name(a, sym::ffi_const)) {
+                if attrs.iter().any(|a| a.has_name(sym::ffi_const)) {
                     // `#[ffi_const]` functions cannot be `#[ffi_pure]`
                     struct_span_err!(
                         tcx.sess,
@@ -2810,7 +2811,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 )
                 .emit();
             }
-        } else if tcx.sess.check_name(attr, sym::ffi_const) {
+        } else if attr.has_name(sym::ffi_const) {
             if tcx.is_foreign_item(id) {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
             } else {
@@ -2823,19 +2824,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 )
                 .emit();
             }
-        } else if tcx.sess.check_name(attr, sym::rustc_allocator_nounwind) {
+        } else if attr.has_name(sym::rustc_allocator_nounwind) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
-        } else if tcx.sess.check_name(attr, sym::naked) {
+        } else if attr.has_name(sym::naked) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
-        } else if tcx.sess.check_name(attr, sym::no_mangle) {
+        } else if attr.has_name(sym::no_mangle) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
-        } else if tcx.sess.check_name(attr, sym::no_coverage) {
+        } else if attr.has_name(sym::no_coverage) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
-        } else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) {
+        } else if attr.has_name(sym::rustc_std_internal_symbol) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
-        } else if tcx.sess.check_name(attr, sym::used) {
+        } else if attr.has_name(sym::used) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
-        } else if tcx.sess.check_name(attr, sym::cmse_nonsecure_entry) {
+        } else if attr.has_name(sym::cmse_nonsecure_entry) {
             if !matches!(tcx.fn_sig(id).abi(), abi::Abi::C { .. }) {
                 struct_span_err!(
                     tcx.sess,
@@ -2850,15 +2851,15 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                     .emit();
             }
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY;
-        } else if tcx.sess.check_name(attr, sym::thread_local) {
+        } else if attr.has_name(sym::thread_local) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
-        } else if tcx.sess.check_name(attr, sym::track_caller) {
+        } else if attr.has_name(sym::track_caller) {
             if tcx.is_closure(id) || tcx.fn_sig(id).abi() != abi::Abi::Rust {
                 struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
                     .emit();
             }
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
-        } else if tcx.sess.check_name(attr, sym::export_name) {
+        } else if attr.has_name(sym::export_name) {
             if let Some(s) = attr.value_str() {
                 if s.as_str().contains('\0') {
                     // `#[export_name = ...]` will be converted to a null-terminated string,
@@ -2873,7 +2874,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 }
                 codegen_fn_attrs.export_name = Some(s);
             }
-        } else if tcx.sess.check_name(attr, sym::target_feature) {
+        } else if attr.has_name(sym::target_feature) {
             if !tcx.is_closure(id) && tcx.fn_sig(id).unsafety() == hir::Unsafety::Normal {
                 if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
                     // The `#[target_feature]` attribute is allowed on
@@ -2913,11 +2914,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 &supported_target_features,
                 &mut codegen_fn_attrs.target_features,
             );
-        } else if tcx.sess.check_name(attr, sym::linkage) {
+        } else if attr.has_name(sym::linkage) {
             if let Some(val) = attr.value_str() {
                 codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str()));
             }
-        } else if tcx.sess.check_name(attr, sym::link_section) {
+        } else if attr.has_name(sym::link_section) {
             if let Some(val) = attr.value_str() {
                 if val.as_str().bytes().any(|b| b == 0) {
                     let msg = format!(
@@ -2930,14 +2931,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                     codegen_fn_attrs.link_section = Some(val);
                 }
             }
-        } else if tcx.sess.check_name(attr, sym::link_name) {
+        } else if attr.has_name(sym::link_name) {
             codegen_fn_attrs.link_name = attr.value_str();
-        } else if tcx.sess.check_name(attr, sym::link_ordinal) {
+        } else if attr.has_name(sym::link_ordinal) {
             link_ordinal_span = Some(attr.span);
             if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
                 codegen_fn_attrs.link_ordinal = ordinal;
             }
-        } else if tcx.sess.check_name(attr, sym::no_sanitize) {
+        } else if attr.has_name(sym::no_sanitize) {
             no_sanitize_span = Some(attr.span);
             if let Some(list) = attr.meta_item_list() {
                 for item in list.iter() {
@@ -2957,7 +2958,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                     }
                 }
             }
-        } else if tcx.sess.check_name(attr, sym::instruction_set) {
+        } else if attr.has_name(sym::instruction_set) {
             codegen_fn_attrs.instruction_set = match attr.meta().map(|i| i.kind) {
                 Some(MetaItemKind::List(ref items)) => match items.as_slice() {
                     [NestedMetaItem::MetaItem(set)] => {
@@ -3026,7 +3027,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                     None
                 }
             };
-        } else if tcx.sess.check_name(attr, sym::repr) {
+        } else if attr.has_name(sym::repr) {
             codegen_fn_attrs.alignment = match attr.meta_item_list() {
                 Some(items) => match items.as_slice() {
                     [item] => match item.name_value_literal() {
@@ -3064,12 +3065,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
             return ia;
         }
         match attr.meta().map(|i| i.kind) {
-            Some(MetaItemKind::Word) => {
-                tcx.sess.mark_attr_used(attr);
-                InlineAttr::Hint
-            }
+            Some(MetaItemKind::Word) => InlineAttr::Hint,
             Some(MetaItemKind::List(ref items)) => {
-                tcx.sess.mark_attr_used(attr);
                 inline_span = Some(attr.span);
                 if items.len() != 1 {
                     struct_span_err!(
@@ -3112,7 +3109,6 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 ia
             }
             Some(MetaItemKind::List(ref items)) => {
-                tcx.sess.mark_attr_used(attr);
                 inline_span = Some(attr.span);
                 if items.len() != 1 {
                     err(attr.span, "expected one argument");
@@ -3181,7 +3177,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
     if tcx.is_weak_lang_item(id) {
         codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
     }
-    let check_name = |attr, sym| tcx.sess.check_name(attr, sym);
+    let check_name = |attr: &Attribute, sym| attr.has_name(sym);
     if let Some(name) = weak_lang_items::link_name(check_name, &attrs) {
         codegen_fn_attrs.export_name = Some(name);
         codegen_fn_attrs.link_name = Some(name);
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index 9ed2a965dbe..61631f3b14b 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -291,7 +291,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
     }
 
     for attr in tcx.get_attrs(main_def_id) {
-        if tcx.sess.check_name(attr, sym::track_caller) {
+        if attr.has_name(sym::track_caller) {
             tcx.sess
                 .struct_span_err(
                     attr.span,
@@ -405,7 +405,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
 
                     let attrs = tcx.hir().attrs(start_id);
                     for attr in attrs {
-                        if tcx.sess.check_name(attr, sym::track_caller) {
+                        if attr.has_name(sym::track_caller) {
                             tcx.sess
                                 .struct_span_err(
                                     attr.span,
diff --git a/src/test/ui/attributes/register-attr-tool-unused.rs b/src/test/ui/attributes/register-attr-tool-unused.rs
index 546e372f5e3..68061465653 100644
--- a/src/test/ui/attributes/register-attr-tool-unused.rs
+++ b/src/test/ui/attributes/register-attr-tool-unused.rs
@@ -4,7 +4,5 @@
 #![feature(register_tool)]
 
 #[register_attr(attr)] //~ ERROR crate-level attribute should be an inner attribute
-                       //~| ERROR unused attribute
 #[register_tool(tool)] //~ ERROR crate-level attribute should be an inner attribute
-                       //~| ERROR unused attribute
 fn main() {}
diff --git a/src/test/ui/attributes/register-attr-tool-unused.stderr b/src/test/ui/attributes/register-attr-tool-unused.stderr
index 85a4fa4a748..8d2e1b6bc28 100644
--- a/src/test/ui/attributes/register-attr-tool-unused.stderr
+++ b/src/test/ui/attributes/register-attr-tool-unused.stderr
@@ -1,4 +1,4 @@
-error: unused attribute
+error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
   --> $DIR/register-attr-tool-unused.rs:6:1
    |
 LL | #[register_attr(attr)]
@@ -12,22 +12,10 @@ LL | #![deny(unused)]
    = note: `#[deny(unused_attributes)]` implied by `#[deny(unused)]`
 
 error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
-  --> $DIR/register-attr-tool-unused.rs:6:1
-   |
-LL | #[register_attr(attr)]
-   | ^^^^^^^^^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/register-attr-tool-unused.rs:8:1
-   |
-LL | #[register_tool(tool)]
-   | ^^^^^^^^^^^^^^^^^^^^^^
-
-error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
-  --> $DIR/register-attr-tool-unused.rs:8:1
+  --> $DIR/register-attr-tool-unused.rs:7:1
    |
 LL | #[register_tool(tool)]
    | ^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.rs b/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.rs
index 4c96d6e7ca1..2600ec7c444 100644
--- a/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.rs
+++ b/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.rs
@@ -4,10 +4,10 @@
 
 #![deny(unused)]
 
-#[cfg_attr(FALSE,)] //~ ERROR unused attribute
+#[cfg_attr(FALSE,)] //~ ERROR `#[cfg_attr]` does not expand to any attributes
 fn _f() {}
 
-#[cfg_attr(TRUE,)] //~ ERROR unused attribute
+#[cfg_attr(TRUE,)] //~ ERROR `#[cfg_attr]` does not expand to any attributes
 fn _g() {}
 
 fn main() {}
diff --git a/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr b/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr
index 67cb6530e38..87b69881353 100644
--- a/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr
+++ b/src/test/ui/conditional-compilation/cfg-attr-empty-is-unused.stderr
@@ -1,4 +1,4 @@
-error: unused attribute
+error: `#[cfg_attr]` does not expand to any attributes
   --> $DIR/cfg-attr-empty-is-unused.rs:7:1
    |
 LL | #[cfg_attr(FALSE,)]
@@ -11,7 +11,7 @@ LL | #![deny(unused)]
    |         ^^^^^^
    = note: `#[deny(unused_attributes)]` implied by `#[deny(unused)]`
 
-error: unused attribute
+error: `#[cfg_attr]` does not expand to any attributes
   --> $DIR/cfg-attr-empty-is-unused.rs:10:1
    |
 LL | #[cfg_attr(TRUE,)]
diff --git a/src/test/ui/const-generics/defaults/default-annotation.rs b/src/test/ui/const-generics/defaults/default-annotation.rs
index 3febb7cffbf..8eb8f368b12 100644
--- a/src/test/ui/const-generics/defaults/default-annotation.rs
+++ b/src/test/ui/const-generics/defaults/default-annotation.rs
@@ -13,7 +13,6 @@ pub struct ConstDefaultUnstable<const N: usize = 3>;
 
 #[stable(feature = "const_default_unstable", since="none")]
 pub struct ConstDefaultStable<const N: usize = {
-    #[stable(feature = "const_default_unstable_val", since="none")]
     3
 }>;
 
diff --git a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs
index b30ab446d7a..767c521b91f 100644
--- a/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs
+++ b/src/test/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs
@@ -49,14 +49,14 @@
 #![macro_use] // (allowed if no argument; see issue-43160-gating-of-macro_use.rs)
 // skipping testing of cfg
 // skipping testing of cfg_attr
-#![should_panic] //~ WARN unused attribute
-#![ignore] //~ WARN unused attribute
+#![should_panic] //~ WARN `#[should_panic]` only has an effect
+#![ignore] //~ WARN `#[ignore]` only has an effect on functions
 #![no_implicit_prelude]
 #![reexport_test_harness_main = "2900"]
 // see gated-link-args.rs
 // see issue-43106-gating-of-macro_escape.rs for crate-level; but non crate-level is below at "2700"
 // (cannot easily test gating of crate-level #[no_std]; but non crate-level is below at "2600")
-#![proc_macro_derive()] //~ WARN unused attribute
+#![proc_macro_derive()] //~ WARN `#[proc_macro_derive]` only has an effect
 #![doc = "2400"]
 #![cold] //~ WARN attribute should be applied to a function
 //~^ WARN
@@ -182,35 +182,35 @@ mod macro_use {
     mod inner { #![macro_use] }
 
     #[macro_use] fn f() { }
-    //~^ WARN unused attribute
+    //~^ `#[macro_use]` only has an effect
 
     #[macro_use] struct S;
-    //~^ WARN unused attribute
+    //~^ `#[macro_use]` only has an effect
 
     #[macro_use] type T = S;
-    //~^ WARN unused attribute
+    //~^ `#[macro_use]` only has an effect
 
     #[macro_use] impl S { }
-    //~^ WARN unused attribute
+    //~^ `#[macro_use]` only has an effect
 }
 
 #[macro_export]
-//~^ WARN unused attribute
+//~^ WARN `#[macro_export]` only has an effect on macro definitions
 mod macro_export {
     mod inner { #![macro_export] }
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_export]` only has an effect on macro definitions
 
     #[macro_export] fn f() { }
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_export]` only has an effect on macro definitions
 
     #[macro_export] struct S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_export]` only has an effect on macro definitions
 
     #[macro_export] type T = S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_export]` only has an effect on macro definitions
 
     #[macro_export] impl S { }
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_export]` only has an effect on macro definitions
 }
 
 // At time of unit test authorship, if compiling without `--test` then
@@ -263,35 +263,32 @@ mod path {
     mod inner { #![path="3800"] }
 
     #[path = "3800"] fn f() { }
-    //~^ WARN unused attribute
+    //~^ WARN `#[path]` only has an effect
 
     #[path = "3800"]  struct S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[path]` only has an effect
 
     #[path = "3800"] type T = S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[path]` only has an effect
 
     #[path = "3800"] impl S { }
-    //~^ WARN unused attribute
+    //~^ WARN `#[path]` only has an effect
 }
 
+// Don't warn on `automatically_derived` - a custom derive
+// could reasonally annotate anything that it emits with
+// this attribute
 #[automatically_derived]
-//~^ WARN unused attribute
 mod automatically_derived {
     mod inner { #![automatically_derived] }
-    //~^ WARN unused attribute
 
     #[automatically_derived] fn f() { }
-    //~^ WARN unused attribute
 
     #[automatically_derived] struct S;
-    //~^ WARN unused attribute
 
     #[automatically_derived] type T = S;
-    //~^ WARN unused attribute
 
     #[automatically_derived] impl S { }
-    //~^ WARN unused attribute
 }
 
 #[no_mangle]
@@ -335,79 +332,77 @@ mod no_mangle {
 }
 
 #[should_panic]
-//~^ WARN unused attribute
+//~^ WARN `#[should_panic]` only has an effect on
 mod should_panic {
     mod inner { #![should_panic] }
-    //~^ WARN unused attribute
+    //~^ WARN `#[should_panic]` only has an effect on
 
     #[should_panic] fn f() { }
-    //~^ WARN unused attribute
 
     #[should_panic] struct S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[should_panic]` only has an effect on
 
     #[should_panic] type T = S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[should_panic]` only has an effect on
 
     #[should_panic] impl S { }
-    //~^ WARN unused attribute
+    //~^ WARN `#[should_panic]` only has an effect on
 }
 
 #[ignore]
-//~^ WARN unused attribute
+//~^ WARN `#[ignore]` only has an effect on functions
 mod ignore {
     mod inner { #![ignore] }
-    //~^ WARN unused attribute
+    //~^ WARN `#[ignore]` only has an effect on functions
 
     #[ignore] fn f() { }
-    //~^ WARN unused attribute
 
     #[ignore] struct S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[ignore]` only has an effect on functions
 
     #[ignore] type T = S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[ignore]` only has an effect on functions
 
     #[ignore] impl S { }
-    //~^ WARN unused attribute
+    //~^ WARN `#[ignore]` only has an effect on functions
 }
 
 #[no_implicit_prelude]
-//~^ WARN unused attribute
+//~^ WARN crate-level attribute
 mod no_implicit_prelude {
     mod inner { #![no_implicit_prelude] }
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute
 
     #[no_implicit_prelude] fn f() { }
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute
 
     #[no_implicit_prelude] struct S;
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute
 
     #[no_implicit_prelude] type T = S;
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute
 
     #[no_implicit_prelude] impl S { }
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute
 }
 
 #[reexport_test_harness_main = "2900"]
-//~^ WARN unused attribute
+//~^ WARN crate-level attribute should be
 mod reexport_test_harness_main {
     mod inner { #![reexport_test_harness_main="2900"] }
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute should be
 
     #[reexport_test_harness_main = "2900"] fn f() { }
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute should be
 
     #[reexport_test_harness_main = "2900"] struct S;
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute should be
 
     #[reexport_test_harness_main = "2900"] type T = S;
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute should be
 
     #[reexport_test_harness_main = "2900"] impl S { }
-    //~^ WARN unused attribute
+    //~^ WARN crate-level attribute should be
 }
 
 // Cannot feed "2700" to `#[macro_escape]` without signaling an error.
@@ -419,41 +414,35 @@ mod macro_escape {
     //~| HELP try an outer attribute: `#[macro_use]`
 
     #[macro_escape] fn f() { }
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_escape]` only has an effect
 
     #[macro_escape] struct S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_escape]` only has an effect
 
     #[macro_escape] type T = S;
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_escape]` only has an effect
 
     #[macro_escape] impl S { }
-    //~^ WARN unused attribute
+    //~^ WARN `#[macro_escape]` only has an effect
 }
 
 #[no_std]
-//~^ WARN unused attribute
-//~| WARN crate-level attribute should be an inner attribute
+//~^ WARN crate-level attribute should be an inner attribute
 mod no_std {
     mod inner { #![no_std] }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be in the root module
+//~^ WARN crate-level attribute should be in the root module
 
     #[no_std] fn f() { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[no_std] struct S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[no_std] type T = S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[no_std] impl S { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 }
 
 // At time of authorship, #[proc_macro_derive = "2500"] signals error
@@ -633,104 +622,80 @@ mod windows_subsystem {
 // BROKEN USES OF CRATE-LEVEL BUILT-IN ATTRIBUTES
 
 #[crate_name = "0900"]
-//~^ WARN unused attribute
-//~| WARN crate-level attribute should be an inner attribute
+//~^ WARN crate-level attribute should be an inner attribute
 mod crate_name {
     mod inner { #![crate_name="0900"] }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be in the root module
+//~^ WARN crate-level attribute should be in the root module
 
     #[crate_name = "0900"] fn f() { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[crate_name = "0900"] struct S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[crate_name = "0900"] type T = S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[crate_name = "0900"] impl S { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 }
 
 #[crate_type = "0800"]
-//~^ WARN unused attribute
-//~| WARN crate-level attribute should be an inner attribute
+//~^ WARN crate-level attribute should be an inner attribute
 mod crate_type {
     mod inner { #![crate_type="0800"] }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be in the root module
+//~^ WARN crate-level attribute should be in the root module
 
     #[crate_type = "0800"] fn f() { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[crate_type = "0800"] struct S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[crate_type = "0800"] type T = S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[crate_type = "0800"] impl S { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 }
 
 #[feature(x0600)]
-//~^ WARN unused attribute
-//~| WARN crate-level attribute should be an inner attribute
+//~^ WARN crate-level attribute should be an inner attribute
 mod feature {
     mod inner { #![feature(x0600)] }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be in the root module
+//~^ WARN crate-level attribute should be in the root module
 
     #[feature(x0600)] fn f() { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[feature(x0600)] struct S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[feature(x0600)] type T = S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[feature(x0600)] impl S { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 }
 
 
 #[no_main]
-//~^ WARN unused attribute
-//~| WARN crate-level attribute should be an inner attribute
+//~^ WARN crate-level attribute should be an inner attribute
 mod no_main_1 {
     mod inner { #![no_main] }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be in the root module
+//~^ WARN crate-level attribute should be in the root module
 
     #[no_main] fn f() { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[no_main] struct S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[no_main] type T = S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[no_main] impl S { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 }
 
 #[no_builtins]
@@ -747,53 +712,41 @@ mod no_builtins {
 }
 
 #[recursion_limit="0200"]
-//~^ WARN unused attribute
-//~| WARN crate-level attribute should be an inner attribute
+//~^ WARN crate-level attribute should be an inner attribute
 mod recursion_limit {
     mod inner { #![recursion_limit="0200"] }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be in the root module
+//~^ WARN crate-level attribute should be in the root module
 
     #[recursion_limit="0200"] fn f() { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[recursion_limit="0200"] struct S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[recursion_limit="0200"] type T = S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[recursion_limit="0200"] impl S { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 }
 
 #[type_length_limit="0100"]
-//~^ WARN unused attribute
-//~| WARN crate-level attribute should be an inner attribute
+//~^ WARN crate-level attribute should be an inner attribute
 mod type_length_limit {
     mod inner { #![type_length_limit="0100"] }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be in the root module
+//~^ WARN crate-level attribute should be in the root module
 
     #[type_length_limit="0100"] fn f() { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[type_length_limit="0100"] struct S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[type_length_limit="0100"] type T = S;
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 
     #[type_length_limit="0100"] impl S { }
-    //~^ WARN unused attribute
-    //~| WARN crate-level attribute should be an inner attribute
+    //~^ WARN crate-level attribute should be an inner attribute
 }
 
 fn main() {}
diff --git a/src/test/ui/invalid/invalid-plugin-attr.rs b/src/test/ui/invalid/invalid-plugin-attr.rs
index 3ba7389d5db..3080af24500 100644
--- a/src/test/ui/invalid/invalid-plugin-attr.rs
+++ b/src/test/ui/invalid/invalid-plugin-attr.rs
@@ -1,8 +1,7 @@
 #![deny(unused_attributes)]
 #![feature(plugin)]
 
-#[plugin(bla)]  //~ ERROR unused attribute
-                //~^ ERROR should be an inner attribute
+#[plugin(bla)] //~ ERROR should be an inner attribute
 //~| WARN use of deprecated attribute `plugin`: compiler plugins are deprecated
 
 fn main() {}
diff --git a/src/test/ui/invalid/invalid-plugin-attr.stderr b/src/test/ui/invalid/invalid-plugin-attr.stderr
index c822d908ddd..d3882d33fc4 100644
--- a/src/test/ui/invalid/invalid-plugin-attr.stderr
+++ b/src/test/ui/invalid/invalid-plugin-attr.stderr
@@ -6,7 +6,7 @@ LL | #[plugin(bla)]
    |
    = note: `#[warn(deprecated)]` on by default
 
-error: unused attribute
+error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
   --> $DIR/invalid-plugin-attr.rs:4:1
    |
 LL | #[plugin(bla)]
@@ -18,11 +18,5 @@ note: the lint level is defined here
 LL | #![deny(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
 
-error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
-  --> $DIR/invalid-plugin-attr.rs:4:1
-   |
-LL | #[plugin(bla)]
-   | ^^^^^^^^^^^^^^
-
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to previous error; 1 warning emitted
 
diff --git a/src/test/ui/lint/lint-misplaced-attr.rs b/src/test/ui/lint/lint-misplaced-attr.rs
index e5ad7a53e0c..d06917ea3f8 100644
--- a/src/test/ui/lint/lint-misplaced-attr.rs
+++ b/src/test/ui/lint/lint-misplaced-attr.rs
@@ -4,9 +4,7 @@
 #![deny(unused_attributes)]
 
 mod a {
-    #![crate_type = "bin"] //~ ERROR unused attribute
-                           //~^ ERROR should be in the root module
+    #![crate_type = "bin"] //~ ERROR should be in the root module
 }
 
-#[crate_type = "bin"] fn main() {} //~ ERROR unused attribute
-                                   //~^ ERROR should be an inner
+#[crate_type = "bin"] fn main() {} //~ ERROR should be an inner
diff --git a/src/test/ui/lint/lint-misplaced-attr.stderr b/src/test/ui/lint/lint-misplaced-attr.stderr
index 3a7ca2f83ae..abaf4620e6f 100644
--- a/src/test/ui/lint/lint-misplaced-attr.stderr
+++ b/src/test/ui/lint/lint-misplaced-attr.stderr
@@ -1,4 +1,4 @@
-error: unused attribute
+error: crate-level attribute should be in the root module
   --> $DIR/lint-misplaced-attr.rs:7:5
    |
 LL |     #![crate_type = "bin"]
@@ -10,23 +10,11 @@ note: the lint level is defined here
 LL | #![deny(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
 
-error: crate-level attribute should be in the root module
-  --> $DIR/lint-misplaced-attr.rs:7:5
-   |
-LL |     #![crate_type = "bin"]
-   |     ^^^^^^^^^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/lint-misplaced-attr.rs:11:1
-   |
-LL | #[crate_type = "bin"] fn main() {}
-   | ^^^^^^^^^^^^^^^^^^^^^
-
 error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
-  --> $DIR/lint-misplaced-attr.rs:11:1
+  --> $DIR/lint-misplaced-attr.rs:10:1
    |
 LL | #[crate_type = "bin"] fn main() {}
    | ^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/lint/unused/unused-attr-macro-rules.rs b/src/test/ui/lint/unused/unused-attr-macro-rules.rs
index 396137a11d0..c0fc280ab1a 100644
--- a/src/test/ui/lint/unused/unused-attr-macro-rules.rs
+++ b/src/test/ui/lint/unused/unused-attr-macro-rules.rs
@@ -4,10 +4,9 @@
 
 // A sample of various built-in attributes.
 #[macro_export]
-#[macro_use] //~ ERROR unused attribute
-#[path="foo"] //~ ERROR unused attribute
-#[recursion_limit="1"] //~ ERROR unused attribute
-                       //~| ERROR crate-level attribute should be an inner attribute
+#[macro_use] //~ ERROR `#[macro_use]` only has an effect
+#[path="foo"] //~ ERROR #[path]` only has an effect
+#[recursion_limit="1"] //~ ERROR crate-level attribute should be an inner attribute
 macro_rules! foo {
     () => {};
 }
diff --git a/src/test/ui/lint/unused/unused-attr-macro-rules.stderr b/src/test/ui/lint/unused/unused-attr-macro-rules.stderr
index 4606be01ac0..e3ca90d9acd 100644
--- a/src/test/ui/lint/unused/unused-attr-macro-rules.stderr
+++ b/src/test/ui/lint/unused/unused-attr-macro-rules.stderr
@@ -1,4 +1,4 @@
-error: unused attribute
+error: `#[macro_use]` only has an effect on `extern crate` and modules
   --> $DIR/unused-attr-macro-rules.rs:7:1
    |
 LL | #[macro_use]
@@ -10,23 +10,17 @@ note: the lint level is defined here
 LL | #![deny(unused_attributes)]
    |         ^^^^^^^^^^^^^^^^^
 
-error: unused attribute
+error: `#[path]` only has an effect on modules
   --> $DIR/unused-attr-macro-rules.rs:8:1
    |
 LL | #[path="foo"]
    | ^^^^^^^^^^^^^
 
-error: unused attribute
-  --> $DIR/unused-attr-macro-rules.rs:9:1
-   |
-LL | #[recursion_limit="1"]
-   | ^^^^^^^^^^^^^^^^^^^^^^
-
 error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![foo]`
   --> $DIR/unused-attr-macro-rules.rs:9:1
    |
 LL | #[recursion_limit="1"]
    | ^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/test/ui/lint/unused/unused-attr.rs b/src/test/ui/lint/unused/unused-attr.rs
deleted file mode 100644
index cb8ac0e6a05..00000000000
--- a/src/test/ui/lint/unused/unused-attr.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-#![deny(unused_attributes)]
-#![feature(rustc_attrs)]
-
-#![rustc_dummy] //~ ERROR unused attribute
-
-#[rustc_dummy] //~ ERROR unused attribute
-extern crate core;
-
-#[rustc_dummy] //~ ERROR unused attribute
-use std::collections;
-
-#[rustc_dummy] //~ ERROR unused attribute
-extern "C" {
-    #[rustc_dummy] //~ ERROR unused attribute
-    fn foo();
-}
-
-#[rustc_dummy] //~ ERROR unused attribute
-mod foo {
-    #[rustc_dummy] //~ ERROR unused attribute
-    pub enum Foo {
-        #[rustc_dummy] //~ ERROR unused attribute
-        Bar,
-    }
-}
-
-#[rustc_dummy] //~ ERROR unused attribute
-fn bar(f: foo::Foo) {
-    match f {
-        #[rustc_dummy] //~ ERROR unused attribute
-        foo::Foo::Bar => {}
-    }
-}
-
-#[rustc_dummy] //~ ERROR unused attribute
-struct Foo {
-    #[rustc_dummy] //~ ERROR unused attribute
-    a: isize
-}
-
-#[rustc_dummy] //~ ERROR unused attribute
-trait Baz {
-    #[rustc_dummy] //~ ERROR unused attribute
-    fn blah(&self);
-    #[rustc_dummy] //~ ERROR unused attribute
-    fn blah2(&self) {}
-}
-
-fn main() {}
diff --git a/src/test/ui/lint/unused/unused-attr.stderr b/src/test/ui/lint/unused/unused-attr.stderr
deleted file mode 100644
index 707521b7802..00000000000
--- a/src/test/ui/lint/unused/unused-attr.stderr
+++ /dev/null
@@ -1,98 +0,0 @@
-error: unused attribute
-  --> $DIR/unused-attr.rs:4:1
-   |
-LL | #![rustc_dummy]
-   | ^^^^^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/unused-attr.rs:1:9
-   |
-LL | #![deny(unused_attributes)]
-   |         ^^^^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:6:1
-   |
-LL | #[rustc_dummy]
-   | ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:9:1
-   |
-LL | #[rustc_dummy]
-   | ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:12:1
-   |
-LL | #[rustc_dummy]
-   | ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:14:5
-   |
-LL |     #[rustc_dummy]
-   |     ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:18:1
-   |
-LL | #[rustc_dummy]
-   | ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:20:5
-   |
-LL |     #[rustc_dummy]
-   |     ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:22:9
-   |
-LL |         #[rustc_dummy]
-   |         ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:27:1
-   |
-LL | #[rustc_dummy]
-   | ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:30:9
-   |
-LL |         #[rustc_dummy]
-   |         ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:35:1
-   |
-LL | #[rustc_dummy]
-   | ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:37:5
-   |
-LL |     #[rustc_dummy]
-   |     ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:41:1
-   |
-LL | #[rustc_dummy]
-   | ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:43:5
-   |
-LL |     #[rustc_dummy]
-   |     ^^^^^^^^^^^^^^
-
-error: unused attribute
-  --> $DIR/unused-attr.rs:45:5
-   |
-LL |     #[rustc_dummy]
-   |     ^^^^^^^^^^^^^^
-
-error: aborting due to 15 previous errors
-