about summary refs log tree commit diff
path: root/compiler/rustc_attr_parsing/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_attr_parsing/src')
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs10
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/body.rs8
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs15
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/confusables.rs15
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/deprecation.rs23
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/dummy.rs4
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/inline.rs11
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/link_attrs.rs16
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs7
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/loop_match.rs7
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs16
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/mod.rs10
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/must_use.rs14
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs7
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs6
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/path.rs9
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/prelude.rs20
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs12
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/prototype.rs7
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/repr.rs16
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs12
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/semantics.rs5
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/stability.rs9
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/test_attrs.rs10
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/traits.rs14
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/transparency.rs10
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/util.rs33
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs592
-rw-r--r--compiler/rustc_attr_parsing/src/interface.rs321
-rw-r--r--compiler/rustc_attr_parsing/src/lib.rs18
-rw-r--r--compiler/rustc_attr_parsing/src/lints.rs12
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs12
-rw-r--r--compiler/rustc_attr_parsing/src/target_checking.rs247
33 files changed, 745 insertions, 783 deletions
diff --git a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
index 4d995027814..088fa73d742 100644
--- a/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/allow_unstable.rs
@@ -1,14 +1,6 @@
 use std::iter;
 
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
-
-use super::{CombineAttributeParser, ConvertFn};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
+use super::prelude::*;
 use crate::session_diagnostics;
 
 pub(crate) struct AllowInternalUnstableParser;
diff --git a/compiler/rustc_attr_parsing/src/attributes/body.rs b/compiler/rustc_attr_parsing/src/attributes/body.rs
index 88540384621..a1492d76194 100644
--- a/compiler/rustc_attr_parsing/src/attributes/body.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/body.rs
@@ -1,12 +1,6 @@
 //! Attributes that can be found in function body.
 
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Symbol, sym};
-
-use super::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AllowedTargets, Stage};
+use super::prelude::*;
 
 pub(crate) struct CoroutineParser;
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 6ea073896c2..843e411d25b 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -1,16 +1,7 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::{AttributeKind, CoverageAttrKind, OptimizeAttr, UsedBy};
-use rustc_hir::{MethodKind, Target};
+use rustc_hir::attrs::{CoverageAttrKind, OptimizeAttr, UsedBy};
 use rustc_session::parse::feature_err;
-use rustc_span::{Span, Symbol, sym};
-
-use super::{
-    AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn,
-    NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
-};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
-use crate::parser::ArgParser;
+
+use super::prelude::*;
 use crate::session_diagnostics::{NakedFunctionIncompatibleAttribute, NullOnExport};
 
 pub(crate) struct OptimizeParser;
diff --git a/compiler/rustc_attr_parsing/src/attributes/confusables.rs b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
index 00f949c82c5..97e78dfb136 100644
--- a/compiler/rustc_attr_parsing/src/attributes/confusables.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/confusables.rs
@@ -1,13 +1,6 @@
-use rustc_feature::template;
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
-use thin_vec::ThinVec;
-
-use super::{AcceptMapping, AttributeParser};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AllowedTargets, FinalizeContext, Stage};
-use crate::session_diagnostics;
+use super::prelude::*;
+use crate::session_diagnostics::EmptyConfusables;
+
 #[derive(Default)]
 pub(crate) struct ConfusablesParser {
     confusables: ThinVec<Symbol>,
@@ -25,7 +18,7 @@ impl<S: Stage> AttributeParser<S> for ConfusablesParser {
             };
 
             if list.is_empty() {
-                cx.emit_err(session_diagnostics::EmptyConfusables { span: cx.attr_span });
+                cx.emit_err(EmptyConfusables { span: cx.attr_span });
             }
 
             for param in list.mixed() {
diff --git a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
index d3a61f3a653..31c698228ef 100644
--- a/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/deprecation.rs
@@ -1,14 +1,11 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::{AttributeKind, DeprecatedSince, Deprecation};
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
+use rustc_hir::attrs::{DeprecatedSince, Deprecation};
 
+use super::prelude::*;
 use super::util::parse_version;
-use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
-use crate::session_diagnostics;
+use crate::session_diagnostics::{
+    DeprecatedItemSuggestion, InvalidSince, MissingNote, MissingSince,
+};
+
 pub(crate) struct DeprecationParser;
 
 fn get<S: Stage>(
@@ -102,7 +99,7 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
                         }
                         Some(name @ sym::suggestion) => {
                             if !features.deprecated_suggestion() {
-                                cx.emit_err(session_diagnostics::DeprecatedItemSuggestion {
+                                cx.emit_err(DeprecatedItemSuggestion {
                                     span: param.span(),
                                     is_nightly: cx.sess().is_nightly_build(),
                                     details: (),
@@ -144,18 +141,18 @@ impl<S: Stage> SingleAttributeParser<S> for DeprecationParser {
             } else if let Some(version) = parse_version(since) {
                 DeprecatedSince::RustcVersion(version)
             } else {
-                cx.emit_err(session_diagnostics::InvalidSince { span: cx.attr_span });
+                cx.emit_err(InvalidSince { span: cx.attr_span });
                 DeprecatedSince::Err
             }
         } else if is_rustc {
-            cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
+            cx.emit_err(MissingSince { span: cx.attr_span });
             DeprecatedSince::Err
         } else {
             DeprecatedSince::Unspecified
         };
 
         if is_rustc && note.is_none() {
-            cx.emit_err(session_diagnostics::MissingNote { span: cx.attr_span });
+            cx.emit_err(MissingNote { span: cx.attr_span });
             return None;
         }
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/dummy.rs b/compiler/rustc_attr_parsing/src/attributes/dummy.rs
index 85842b1b5c5..7293cee842c 100644
--- a/compiler/rustc_attr_parsing/src/attributes/dummy.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/dummy.rs
@@ -3,8 +3,10 @@ use rustc_hir::attrs::AttributeKind;
 use rustc_span::{Symbol, sym};
 
 use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
+use crate::context::{AcceptContext, Stage};
 use crate::parser::ArgParser;
+use crate::target_checking::{ALL_TARGETS, AllowedTargets};
+
 pub(crate) struct DummyParser;
 impl<S: Stage> SingleAttributeParser<S> for DummyParser {
     const PATH: &[Symbol] = &[sym::rustc_dummy];
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
index 33c21bad240..101fa71b8a6 100644
--- a/compiler/rustc_attr_parsing/src/attributes/inline.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -2,17 +2,10 @@
 //                      note: need to model better how duplicate attr errors work when not using
 //                      SingleAttributeParser which is what we have two of here.
 
-use rustc_feature::{AttributeTemplate, template};
 use rustc_hir::attrs::{AttributeKind, InlineAttr};
-use rustc_hir::lints::AttributeLintKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Symbol, sym};
 
-use super::{AcceptContext, AttributeOrder, OnDuplicate};
-use crate::attributes::SingleAttributeParser;
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AllowedTargets, Stage};
-use crate::parser::ArgParser;
+use super::prelude::*;
+
 pub(crate) struct InlineParser;
 
 impl<S: Stage> SingleAttributeParser<S> for InlineParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
index 552b9dfabc2..7a765f71a5e 100644
--- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
@@ -1,16 +1,10 @@
-use rustc_feature::{AttributeTemplate, template};
 use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection};
-use rustc_hir::attrs::{AttributeKind, Linkage};
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
-
-use crate::attributes::{
-    AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
-};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage, parse_single_integer};
-use crate::parser::ArgParser;
+use rustc_hir::attrs::Linkage;
+
+use super::prelude::*;
+use super::util::parse_single_integer;
 use crate::session_diagnostics::{LinkOrdinalOutOfRange, NullOnLinkSection};
+
 pub(crate) struct LinkNameParser;
 
 impl<S: Stage> SingleAttributeParser<S> for LinkNameParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
index 2b586d4003c..63b0809d0d8 100644
--- a/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
@@ -1,10 +1,5 @@
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
+use super::prelude::*;
 
-use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AllowedTargets, Stage};
 pub(crate) struct AsPtrParser;
 impl<S: Stage> NoArgsAttributeParser<S> for AsPtrParser {
     const PATH: &[Symbol] = &[sym::rustc_as_ptr];
diff --git a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
index 242e2f2c1bc..528090b8673 100644
--- a/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/loop_match.rs
@@ -1,10 +1,5 @@
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Span, Symbol, sym};
+use super::prelude::*;
 
-use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AllowedTargets, Stage};
 pub(crate) struct LoopMatchParser;
 impl<S: Stage> NoArgsAttributeParser<S> for LoopMatchParser {
     const PATH: &[Symbol] = &[sym::loop_match];
diff --git a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
index 8928129c201..180130c7be4 100644
--- a/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/macro_attrs.rs
@@ -1,15 +1,9 @@
 use rustc_errors::DiagArgValue;
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::{AttributeKind, MacroUseArgs};
-use rustc_span::{Span, Symbol, sym};
-use thin_vec::ThinVec;
+use rustc_hir::attrs::MacroUseArgs;
+
+use super::prelude::*;
+use crate::session_diagnostics::IllFormedAttributeInputLint;
 
-use crate::attributes::{AcceptMapping, AttributeParser, NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::{Allow, Error, Warn};
-use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
-use crate::parser::ArgParser;
-use crate::session_diagnostics;
 pub(crate) struct MacroEscapeParser;
 impl<S: Stage> NoArgsAttributeParser<S> for MacroEscapeParser {
     const PATH: &[Symbol] = &[sym::macro_escape];
@@ -108,7 +102,7 @@ impl<S: Stage> AttributeParser<S> for MacroUseParser {
                 }
                 ArgParser::NameValue(_) => {
                     let suggestions = MACRO_USE_TEMPLATE.suggestions(cx.attr_style, sym::macro_use);
-                    cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
+                    cx.emit_err(IllFormedAttributeInputLint {
                         num_suggestions: suggestions.len(),
                         suggestions: DiagArgValue::StrListSepByAnd(
                             suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
diff --git a/compiler/rustc_attr_parsing/src/attributes/mod.rs b/compiler/rustc_attr_parsing/src/attributes/mod.rs
index 3d6e26a24b8..b98678041d7 100644
--- a/compiler/rustc_attr_parsing/src/attributes/mod.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/mod.rs
@@ -7,9 +7,9 @@
 //! Specifically, you might not care about managing the state of your [`AttributeParser`]
 //! state machine yourself. In this case you can choose to implement:
 //!
-//! - [`SingleAttributeParser`]: makes it easy to implement an attribute which should error if it
+//! - [`SingleAttributeParser`](crate::attributes::SingleAttributeParser): makes it easy to implement an attribute which should error if it
 //! appears more than once in a list of attributes
-//! - [`CombineAttributeParser`]: makes it easy to implement an attribute which should combine the
+//! - [`CombineAttributeParser`](crate::attributes::CombineAttributeParser): makes it easy to implement an attribute which should combine the
 //! contents of attributes, if an attribute appear multiple times in a list
 //!
 //! Attributes should be added to `crate::context::ATTRIBUTE_PARSERS` to be parsed.
@@ -21,9 +21,13 @@ use rustc_hir::attrs::AttributeKind;
 use rustc_span::{Span, Symbol};
 use thin_vec::ThinVec;
 
-use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
+use crate::context::{AcceptContext, FinalizeContext, Stage};
 use crate::parser::ArgParser;
 use crate::session_diagnostics::UnusedMultiple;
+use crate::target_checking::AllowedTargets;
+
+/// All the parsers require roughly the same imports, so this prelude has most of the often-needed ones.
+mod prelude;
 
 pub(crate) mod allow_unstable;
 pub(crate) mod body;
diff --git a/compiler/rustc_attr_parsing/src/attributes/must_use.rs b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
index eb2b39298bc..e6a5141d783 100644
--- a/compiler/rustc_attr_parsing/src/attributes/must_use.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/must_use.rs
@@ -1,14 +1,8 @@
 use rustc_errors::DiagArgValue;
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Symbol, sym};
 
-use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
-use crate::session_diagnostics;
+use super::prelude::*;
+use crate::session_diagnostics::IllFormedAttributeInputLint;
+
 pub(crate) struct MustUseParser;
 
 impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
@@ -53,7 +47,7 @@ impl<S: Stage> SingleAttributeParser<S> for MustUseParser {
                 ArgParser::List(_) => {
                     let suggestions = <Self as SingleAttributeParser<S>>::TEMPLATE
                         .suggestions(cx.attr_style, "must_use");
-                    cx.emit_err(session_diagnostics::IllFormedAttributeInputLint {
+                    cx.emit_err(IllFormedAttributeInputLint {
                         num_suggestions: suggestions.len(),
                         suggestions: DiagArgValue::StrListSepByAnd(
                             suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
diff --git a/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs b/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs
index 589faf38f73..40073ea0f46 100644
--- a/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/no_implicit_prelude.rs
@@ -1,10 +1,5 @@
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Span, sym};
+use super::prelude::*;
 
-use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AllowedTargets, Stage};
 pub(crate) struct NoImplicitPreludeParser;
 
 impl<S: Stage> NoArgsAttributeParser<S> for NoImplicitPreludeParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs
index 41e9ca4de41..4e6aec95e66 100644
--- a/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/non_exhaustive.rs
@@ -3,8 +3,10 @@ use rustc_hir::attrs::AttributeKind;
 use rustc_span::{Span, Symbol, sym};
 
 use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AllowedTargets, Stage};
+use crate::context::Stage;
+use crate::target_checking::AllowedTargets;
+use crate::target_checking::Policy::{Allow, Warn};
+
 pub(crate) struct NonExhaustiveParser;
 
 impl<S: Stage> NoArgsAttributeParser<S> for NonExhaustiveParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/path.rs b/compiler/rustc_attr_parsing/src/attributes/path.rs
index f9191d1abed..e4cb806bb42 100644
--- a/compiler/rustc_attr_parsing/src/attributes/path.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/path.rs
@@ -1,12 +1,5 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Symbol, sym};
+use super::prelude::*;
 
-use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
 pub(crate) struct PathParser;
 
 impl<S: Stage> SingleAttributeParser<S> for PathParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs
new file mode 100644
index 00000000000..2bcdee55c75
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs
@@ -0,0 +1,20 @@
+// parsing
+// templates
+pub(super) use rustc_feature::{AttributeTemplate, template};
+// data structures
+pub(super) use rustc_hir::attrs::AttributeKind;
+pub(super) use rustc_hir::lints::AttributeLintKind;
+pub(super) use rustc_hir::{MethodKind, Target};
+pub(super) use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
+pub(super) use thin_vec::ThinVec;
+
+pub(super) use crate::attributes::{
+    AcceptMapping, AttributeOrder, AttributeParser, CombineAttributeParser, ConvertFn,
+    NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
+};
+// contexts
+pub(super) use crate::context::{AcceptContext, FinalizeContext, Stage};
+pub(super) use crate::parser::*;
+// target checking
+pub(super) use crate::target_checking::Policy::{Allow, Error, Warn};
+pub(super) use crate::target_checking::{ALL_TARGETS, AllowedTargets};
diff --git a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
index 4624fa36287..076b45f1013 100644
--- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
@@ -1,15 +1,5 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Span, Symbol, sym};
-use thin_vec::ThinVec;
+use super::prelude::*;
 
-use crate::attributes::{
-    AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
-};
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
 pub(crate) struct ProcMacroParser;
 impl<S: Stage> NoArgsAttributeParser<S> for ProcMacroParser {
     const PATH: &[Symbol] = &[sym::proc_macro];
diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs
index fb1e47298b4..0ef36bc7e4c 100644
--- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs
@@ -7,8 +7,10 @@ use rustc_span::{Span, Symbol, sym};
 
 use super::{AttributeOrder, OnDuplicate};
 use crate::attributes::SingleAttributeParser;
-use crate::context::{AcceptContext, AllowedTargets, MaybeWarn, Stage};
+use crate::context::{AcceptContext, Stage};
 use crate::parser::ArgParser;
+use crate::target_checking::AllowedTargets;
+use crate::target_checking::Policy::Allow;
 
 pub(crate) struct CustomMirParser;
 
@@ -19,8 +21,7 @@ impl<S: Stage> SingleAttributeParser<S> for CustomMirParser {
 
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
 
-    const ALLOWED_TARGETS: AllowedTargets =
-        AllowedTargets::AllowList(&[MaybeWarn::Allow(Target::Fn)]);
+    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]);
 
     const TEMPLATE: AttributeTemplate = template!(List: &[r#"dialect = "...", phase = "...""#]);
 
diff --git a/compiler/rustc_attr_parsing/src/attributes/repr.rs b/compiler/rustc_attr_parsing/src/attributes/repr.rs
index 7ab58ed9347..23aabd15597 100644
--- a/compiler/rustc_attr_parsing/src/attributes/repr.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/repr.rs
@@ -1,16 +1,10 @@
 use rustc_abi::Align;
 use rustc_ast::{IntTy, LitIntType, LitKind, UintTy};
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::{AttributeKind, IntType, ReprAttr};
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{DUMMY_SP, Span, Symbol, sym};
-
-use super::{AcceptMapping, AttributeParser, CombineAttributeParser, ConvertFn, FinalizeContext};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
-use crate::parser::{ArgParser, MetaItemListParser, MetaItemParser};
-use crate::session_diagnostics;
-use crate::session_diagnostics::IncorrectReprFormatGenericCause;
+use rustc_hir::attrs::{IntType, ReprAttr};
+
+use super::prelude::*;
+use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
+
 /// Parse #[repr(...)] forms.
 ///
 /// Valid repr contents: any of the primitive integral type names (see
diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
index efd7b650e44..a995549fc7c 100644
--- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs
@@ -1,12 +1,6 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Symbol, sym};
-
-use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AcceptContext, AllowedTargets, Stage, parse_single_integer};
-use crate::parser::ArgParser;
+use super::prelude::*;
+use super::util::parse_single_integer;
+
 pub(crate) struct RustcLayoutScalarValidRangeStart;
 
 impl<S: Stage> SingleAttributeParser<S> for RustcLayoutScalarValidRangeStart {
diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
index d4ad861a3a2..d7f62483297 100644
--- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs
@@ -1,8 +1,5 @@
-use rustc_hir::attrs::AttributeKind;
-use rustc_span::{Span, Symbol, sym};
+use super::prelude::*;
 
-use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
-use crate::context::{ALL_TARGETS, AllowedTargets, Stage};
 pub(crate) struct MayDangleParser;
 impl<S: Stage> NoArgsAttributeParser<S> for MayDangleParser {
     const PATH: &[Symbol] = &[sym::may_dangle];
diff --git a/compiler/rustc_attr_parsing/src/attributes/stability.rs b/compiler/rustc_attr_parsing/src/attributes/stability.rs
index c7a809d7d88..b94e23477ff 100644
--- a/compiler/rustc_attr_parsing/src/attributes/stability.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/stability.rs
@@ -1,20 +1,13 @@
 use std::num::NonZero;
 
 use rustc_errors::ErrorGuaranteed;
-use rustc_feature::template;
-use rustc_hir::attrs::AttributeKind;
 use rustc_hir::{
     DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel,
     StableSince, Target, UnstableReason, VERSION_PLACEHOLDER,
 };
-use rustc_span::{Ident, Span, Symbol, sym};
 
+use super::prelude::*;
 use super::util::parse_version;
-use super::{AcceptMapping, AttributeParser, OnDuplicate};
-use crate::attributes::NoArgsAttributeParser;
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AcceptContext, AllowedTargets, FinalizeContext, Stage};
-use crate::parser::{ArgParser, MetaItemParser};
 use crate::session_diagnostics::{self, UnsupportedLiteralReason};
 
 macro_rules! reject_outside_std {
diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
index 164c680b8a8..2b01c09ab96 100644
--- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
@@ -1,13 +1,5 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::lints::AttributeLintKind;
-use rustc_span::{Symbol, sym};
+use super::prelude::*;
 
-use crate::attributes::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::{Allow, Error};
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
 pub(crate) struct IgnoreParser;
 
 impl<S: Stage> SingleAttributeParser<S> for IgnoreParser {
diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs
index ee9d7ba99cd..0410d818f74 100644
--- a/compiler/rustc_attr_parsing/src/attributes/traits.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs
@@ -1,16 +1,14 @@
-use core::mem;
-
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::attrs::AttributeKind;
-use rustc_hir::{MethodKind, Target};
-use rustc_span::{Span, Symbol, sym};
+use std::mem;
 
+use super::prelude::*;
 use crate::attributes::{
     AttributeOrder, NoArgsAttributeParser, OnDuplicate, SingleAttributeParser,
 };
-use crate::context::MaybeWarn::{Allow, Warn};
-use crate::context::{ALL_TARGETS, AcceptContext, AllowedTargets, Stage};
+use crate::context::{AcceptContext, Stage};
 use crate::parser::ArgParser;
+use crate::target_checking::Policy::{Allow, Warn};
+use crate::target_checking::{ALL_TARGETS, AllowedTargets};
+
 pub(crate) struct SkipDuringMethodDispatchParser;
 impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
     const PATH: &[Symbol] = &[sym::rustc_skip_during_method_dispatch];
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index 0ffcf434b52..ce638d09530 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -1,13 +1,7 @@
-use rustc_feature::{AttributeTemplate, template};
-use rustc_hir::Target;
-use rustc_hir::attrs::AttributeKind;
 use rustc_span::hygiene::Transparency;
-use rustc_span::{Symbol, sym};
 
-use super::{AttributeOrder, OnDuplicate, SingleAttributeParser};
-use crate::context::MaybeWarn::Allow;
-use crate::context::{AcceptContext, AllowedTargets, Stage};
-use crate::parser::ArgParser;
+use super::prelude::*;
+
 pub(crate) struct TransparencyParser;
 
 // FIXME(jdonszelmann): make these proper diagnostics
diff --git a/compiler/rustc_attr_parsing/src/attributes/util.rs b/compiler/rustc_attr_parsing/src/attributes/util.rs
index 10134915b27..ef9701cb7cb 100644
--- a/compiler/rustc_attr_parsing/src/attributes/util.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/util.rs
@@ -1,8 +1,12 @@
+use rustc_ast::LitKind;
 use rustc_ast::attr::{AttributeExt, first_attr_value_str_by_name};
 use rustc_feature::is_builtin_attr_name;
 use rustc_hir::RustcVersion;
 use rustc_span::{Symbol, sym};
 
+use crate::context::{AcceptContext, Stage};
+use crate::parser::ArgParser;
+
 /// Parse a rustc version number written inside string literal in an attribute,
 /// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
 /// not accepted in this position, unlike when parsing CFG_RELEASE.
@@ -56,3 +60,32 @@ pub fn is_doc_alias_attrs_contain_symbol<'tcx, T: AttributeExt + 'tcx>(
     }
     false
 }
+
+/// Parse a single integer.
+///
+/// Used by attributes that take a single integer as argument, such as
+/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
+/// `cx` is the context given to the attribute.
+/// `args` is the parser for the attribute arguments.
+pub(crate) fn parse_single_integer<S: Stage>(
+    cx: &mut AcceptContext<'_, '_, S>,
+    args: &ArgParser<'_>,
+) -> Option<u128> {
+    let Some(list) = args.list() else {
+        cx.expected_list(cx.attr_span);
+        return None;
+    };
+    let Some(single) = list.single() else {
+        cx.expected_single_argument(list.span);
+        return None;
+    };
+    let Some(lit) = single.lit() else {
+        cx.expected_integer_literal(single.span());
+        return None;
+    };
+    let LitKind::Int(num, _ty) = lit.kind else {
+        cx.expected_integer_literal(single.span());
+        return None;
+    };
+    Some(num.0)
+}
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index c0d3bc99ba9..d79177076c7 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -3,19 +3,17 @@ use std::collections::BTreeMap;
 use std::ops::{Deref, DerefMut};
 use std::sync::LazyLock;
 
-use itertools::Itertools;
 use private::Sealed;
-use rustc_ast::{self as ast, AttrStyle, LitKind, MetaItemLit, NodeId};
-use rustc_errors::{DiagCtxtHandle, Diagnostic};
-use rustc_feature::{AttributeTemplate, Features};
+use rustc_ast::{AttrStyle, MetaItemLit, NodeId};
+use rustc_errors::Diagnostic;
+use rustc_feature::AttributeTemplate;
 use rustc_hir::attrs::AttributeKind;
 use rustc_hir::lints::{AttributeLint, AttributeLintKind};
-use rustc_hir::{
-    AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, HirId, MethodKind, Target,
-};
+use rustc_hir::{AttrPath, HirId};
 use rustc_session::Session;
-use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, Symbol, sym};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
 
+use crate::AttributeParser;
 use crate::attributes::allow_unstable::{
     AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser,
 };
@@ -65,23 +63,21 @@ use crate::attributes::traits::{
 };
 use crate::attributes::transparency::TransparencyParser;
 use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs};
-use crate::context::MaybeWarn::{Allow, Error, Warn};
-use crate::parser::{ArgParser, MetaItemParser, PathParser};
-use crate::session_diagnostics::{
-    AttributeParseError, AttributeParseErrorReason, InvalidTarget, UnknownMetaItem,
-};
+use crate::parser::{ArgParser, PathParser};
+use crate::session_diagnostics::{AttributeParseError, AttributeParseErrorReason, UnknownMetaItem};
+use crate::target_checking::AllowedTargets;
 
 type GroupType<S> = LazyLock<GroupTypeInner<S>>;
 
-struct GroupTypeInner<S: Stage> {
-    accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
-    finalizers: Vec<FinalizeFn<S>>,
+pub(super) struct GroupTypeInner<S: Stage> {
+    pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
+    pub(super) finalizers: Vec<FinalizeFn<S>>,
 }
 
-struct GroupTypeInnerAccept<S: Stage> {
-    template: AttributeTemplate,
-    accept_fn: AcceptFn<S>,
-    allowed_targets: AllowedTargets,
+pub(super) struct GroupTypeInnerAccept<S: Stage> {
+    pub(super) template: AttributeTemplate,
+    pub(super) accept_fn: AcceptFn<S>,
+    pub(super) allowed_targets: AllowedTargets,
 }
 
 type AcceptFn<S> =
@@ -595,7 +591,7 @@ pub struct SharedContext<'p, 'sess, S: Stage> {
     /// The id ([`NodeId`] if `S` is `Early`, [`HirId`] if `S` is `Late`) of the syntactical component this attribute was applied to
     pub(crate) target_id: S::Id,
 
-    emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
+    pub(crate) emit_lint: &'p mut dyn FnMut(AttributeLint<S::Id>),
 }
 
 /// Context given to every attribute parser during finalization.
@@ -666,557 +662,3 @@ impl ShouldEmit {
         }
     }
 }
-
-#[derive(Debug)]
-pub(crate) enum AllowedTargets {
-    AllowList(&'static [MaybeWarn]),
-    AllowListWarnRest(&'static [MaybeWarn]),
-}
-
-pub(crate) enum AllowedResult {
-    Allowed,
-    Warn,
-    Error,
-}
-
-impl AllowedTargets {
-    pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
-        match self {
-            AllowedTargets::AllowList(list) => {
-                if list.contains(&Allow(target)) {
-                    AllowedResult::Allowed
-                } else if list.contains(&Warn(target)) {
-                    AllowedResult::Warn
-                } else {
-                    AllowedResult::Error
-                }
-            }
-            AllowedTargets::AllowListWarnRest(list) => {
-                if list.contains(&Allow(target)) {
-                    AllowedResult::Allowed
-                } else if list.contains(&Error(target)) {
-                    AllowedResult::Error
-                } else {
-                    AllowedResult::Warn
-                }
-            }
-        }
-    }
-
-    pub(crate) fn allowed_targets(&self) -> Vec<Target> {
-        match self {
-            AllowedTargets::AllowList(list) => list,
-            AllowedTargets::AllowListWarnRest(list) => list,
-        }
-        .iter()
-        .filter_map(|target| match target {
-            Allow(target) => Some(*target),
-            Warn(_) => None,
-            Error(_) => None,
-        })
-        .collect()
-    }
-}
-
-#[derive(Debug, Eq, PartialEq)]
-pub(crate) enum MaybeWarn {
-    Allow(Target),
-    Warn(Target),
-    Error(Target),
-}
-
-/// Context created once, for example as part of the ast lowering
-/// context, through which all attributes can be lowered.
-pub struct AttributeParser<'sess, S: Stage = Late> {
-    pub(crate) tools: Vec<Symbol>,
-    features: Option<&'sess Features>,
-    sess: &'sess Session,
-    stage: S,
-
-    /// *Only* parse attributes with this symbol.
-    ///
-    /// Used in cases where we want the lowering infrastructure for parse just a single attribute.
-    parse_only: Option<Symbol>,
-}
-
-impl<'sess> AttributeParser<'sess, Early> {
-    /// This method allows you to parse attributes *before* you have access to features or tools.
-    /// One example where this is necessary, is to parse `feature` attributes themselves for
-    /// example.
-    ///
-    /// Try to use this as little as possible. Attributes *should* be lowered during
-    /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
-    /// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
-    ///
-    /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
-    /// that symbol are picked out of the list of instructions and parsed. Those are returned.
-    ///
-    /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
-    /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
-    /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
-    pub fn parse_limited(
-        sess: &'sess Session,
-        attrs: &[ast::Attribute],
-        sym: Symbol,
-        target_span: Span,
-        target_node_id: NodeId,
-        features: Option<&'sess Features>,
-    ) -> Option<Attribute> {
-        let mut p = Self {
-            features,
-            tools: Vec::new(),
-            parse_only: Some(sym),
-            sess,
-            stage: Early { emit_errors: ShouldEmit::Nothing },
-        };
-        let mut parsed = p.parse_attribute_list(
-            attrs,
-            target_span,
-            target_node_id,
-            Target::Crate, // Does not matter, we're not going to emit errors anyways
-            OmitDoc::Skip,
-            std::convert::identity,
-            |_lint| {
-                panic!("can't emit lints here for now (nothing uses this atm)");
-            },
-        );
-        assert!(parsed.len() <= 1);
-
-        parsed.pop()
-    }
-
-    pub fn parse_single<T>(
-        sess: &'sess Session,
-        attr: &ast::Attribute,
-        target_span: Span,
-        target_node_id: NodeId,
-        features: Option<&'sess Features>,
-        emit_errors: ShouldEmit,
-        parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
-        template: &AttributeTemplate,
-    ) -> T {
-        let mut parser = Self {
-            features,
-            tools: Vec::new(),
-            parse_only: None,
-            sess,
-            stage: Early { emit_errors },
-        };
-        let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
-            panic!("parse_single called on a doc attr")
-        };
-        let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx());
-        let path = meta_parser.path();
-        let args = meta_parser.args();
-        let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
-            shared: SharedContext {
-                cx: &mut parser,
-                target_span,
-                target_id: target_node_id,
-                emit_lint: &mut |_lint| {
-                    panic!("can't emit lints here for now (nothing uses this atm)");
-                },
-            },
-            attr_span: attr.span,
-            attr_style: attr.style,
-            template,
-            attr_path: path.get_attribute_path(),
-        };
-        parse_fn(&mut cx, args)
-    }
-}
-
-impl<'sess, S: Stage> AttributeParser<'sess, S> {
-    pub fn new(
-        sess: &'sess Session,
-        features: &'sess Features,
-        tools: Vec<Symbol>,
-        stage: S,
-    ) -> Self {
-        Self { features: Some(features), tools, parse_only: None, sess, stage }
-    }
-
-    pub(crate) fn sess(&self) -> &'sess Session {
-        &self.sess
-    }
-
-    pub(crate) fn features(&self) -> &'sess Features {
-        self.features.expect("features not available at this point in the compiler")
-    }
-
-    pub(crate) fn features_option(&self) -> Option<&'sess Features> {
-        self.features
-    }
-
-    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
-        self.sess().dcx()
-    }
-
-    /// Parse a list of attributes.
-    ///
-    /// `target_span` is the span of the thing this list of attributes is applied to,
-    /// and when `omit_doc` is set, doc attributes are filtered out.
-    pub fn parse_attribute_list(
-        &mut self,
-        attrs: &[ast::Attribute],
-        target_span: Span,
-        target_id: S::Id,
-        target: Target,
-        omit_doc: OmitDoc,
-
-        lower_span: impl Copy + Fn(Span) -> Span,
-        mut emit_lint: impl FnMut(AttributeLint<S::Id>),
-    ) -> Vec<Attribute> {
-        let mut attributes = Vec::new();
-        let mut attr_paths = Vec::new();
-
-        for attr in attrs {
-            // If we're only looking for a single attribute, skip all the ones we don't care about.
-            if let Some(expected) = self.parse_only {
-                if !attr.has_name(expected) {
-                    continue;
-                }
-            }
-
-            // Sometimes, for example for `#![doc = include_str!("readme.md")]`,
-            // doc still contains a non-literal. You might say, when we're lowering attributes
-            // that's expanded right? But no, sometimes, when parsing attributes on macros,
-            // we already use the lowering logic and these are still there. So, when `omit_doc`
-            // is set we *also* want to ignore these.
-            if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
-                continue;
-            }
-
-            match &attr.kind {
-                ast::AttrKind::DocComment(comment_kind, symbol) => {
-                    if omit_doc == OmitDoc::Skip {
-                        continue;
-                    }
-
-                    attributes.push(Attribute::Parsed(AttributeKind::DocComment {
-                        style: attr.style,
-                        kind: *comment_kind,
-                        span: lower_span(attr.span),
-                        comment: *symbol,
-                    }))
-                }
-                // // FIXME: make doc attributes go through a proper attribute parser
-                // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
-                //     let p = GenericMetaItemParser::from_attr(&n, self.dcx());
-                //
-                //     attributes.push(Attribute::Parsed(AttributeKind::DocComment {
-                //         style: attr.style,
-                //         kind: CommentKind::Line,
-                //         span: attr.span,
-                //         comment: p.args().name_value(),
-                //     }))
-                // }
-                ast::AttrKind::Normal(n) => {
-                    attr_paths.push(PathParser::Ast(&n.item.path));
-
-                    let parser = MetaItemParser::from_attr(n, self.dcx());
-                    let path = parser.path();
-                    let args = parser.args();
-                    let parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
-
-                    if let Some(accepts) = S::parsers().accepters.get(parts.as_slice()) {
-                        for accept in accepts {
-                            let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
-                                shared: SharedContext {
-                                    cx: self,
-                                    target_span,
-                                    target_id,
-                                    emit_lint: &mut emit_lint,
-                                },
-                                attr_span: lower_span(attr.span),
-                                attr_style: attr.style,
-                                template: &accept.template,
-                                attr_path: path.get_attribute_path(),
-                            };
-
-                            (accept.accept_fn)(&mut cx, args);
-
-                            if self.stage.should_emit().should_emit() {
-                                match accept.allowed_targets.is_allowed(target) {
-                                    AllowedResult::Allowed => {}
-                                    AllowedResult::Warn => {
-                                        let allowed_targets =
-                                            accept.allowed_targets.allowed_targets();
-                                        let (applied, only) = allowed_targets_applied(
-                                            allowed_targets,
-                                            target,
-                                            self.features,
-                                        );
-                                        emit_lint(AttributeLint {
-                                            id: target_id,
-                                            span: attr.span,
-                                            kind: AttributeLintKind::InvalidTarget {
-                                                name: parts[0],
-                                                target,
-                                                only: if only { "only " } else { "" },
-                                                applied,
-                                            },
-                                        });
-                                    }
-                                    AllowedResult::Error => {
-                                        let allowed_targets =
-                                            accept.allowed_targets.allowed_targets();
-                                        let (applied, only) = allowed_targets_applied(
-                                            allowed_targets,
-                                            target,
-                                            self.features,
-                                        );
-                                        self.dcx().emit_err(InvalidTarget {
-                                            span: attr.span,
-                                            name: parts[0],
-                                            target: target.plural_name(),
-                                            only: if only { "only " } else { "" },
-                                            applied,
-                                        });
-                                    }
-                                }
-                            }
-                        }
-                    } else {
-                        // If we're here, we must be compiling a tool attribute... Or someone
-                        // forgot to parse their fancy new attribute. Let's warn them in any case.
-                        // If you are that person, and you really think your attribute should
-                        // remain unparsed, carefully read the documentation in this module and if
-                        // you still think so you can add an exception to this assertion.
-
-                        // FIXME(jdonszelmann): convert other attributes, and check with this that
-                        // we caught em all
-                        // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
-                        // assert!(
-                        //     self.tools.contains(&parts[0]) || true,
-                        //     // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
-                        //     "attribute {path} wasn't parsed and isn't a know tool attribute",
-                        // );
-
-                        attributes.push(Attribute::Unparsed(Box::new(AttrItem {
-                            path: AttrPath::from_ast(&n.item.path),
-                            args: self.lower_attr_args(&n.item.args, lower_span),
-                            id: HashIgnoredAttrId { attr_id: attr.id },
-                            style: attr.style,
-                            span: lower_span(attr.span),
-                        })));
-                    }
-                }
-            }
-        }
-
-        let mut parsed_attributes = Vec::new();
-        for f in &S::parsers().finalizers {
-            if let Some(attr) = f(&mut FinalizeContext {
-                shared: SharedContext {
-                    cx: self,
-                    target_span,
-                    target_id,
-                    emit_lint: &mut emit_lint,
-                },
-                all_attrs: &attr_paths,
-            }) {
-                parsed_attributes.push(Attribute::Parsed(attr));
-            }
-        }
-
-        attributes.extend(parsed_attributes);
-
-        attributes
-    }
-
-    /// Returns whether there is a parser for an attribute with this name
-    pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
-        Late::parsers().accepters.contains_key(path)
-    }
-
-    fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
-        match args {
-            ast::AttrArgs::Empty => AttrArgs::Empty,
-            ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
-            // This is an inert key-value attribute - it will never be visible to macros
-            // after it gets lowered to HIR. Therefore, we can extract literals to handle
-            // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
-            ast::AttrArgs::Eq { eq_span, expr } => {
-                // In valid code the value always ends up as a single literal. Otherwise, a dummy
-                // literal suffices because the error is handled elsewhere.
-                let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
-                    && let Ok(lit) =
-                        ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
-                {
-                    lit
-                } else {
-                    let guar = self.dcx().span_delayed_bug(
-                        args.span().unwrap_or(DUMMY_SP),
-                        "expr in place where literal is expected (builtin attr parsing)",
-                    );
-                    ast::MetaItemLit {
-                        symbol: sym::dummy,
-                        suffix: None,
-                        kind: ast::LitKind::Err(guar),
-                        span: DUMMY_SP,
-                    }
-                };
-                AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
-            }
-        }
-    }
-}
-
-/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.
-/// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string
-pub(crate) fn allowed_targets_applied(
-    mut allowed_targets: Vec<Target>,
-    target: Target,
-    features: Option<&Features>,
-) -> (String, bool) {
-    // Remove unstable targets from `allowed_targets` if their features are not enabled
-    if let Some(features) = features {
-        if !features.fn_delegation() {
-            allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
-        }
-        if !features.stmt_expr_attributes() {
-            allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
-        }
-        if !features.extern_types() {
-            allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
-        }
-    }
-
-    // We define groups of "similar" targets.
-    // If at least two of the targets are allowed, and the `target` is not in the group,
-    // we collapse the entire group to a single entry to simplify the target list
-    const FUNCTION_LIKE: &[Target] = &[
-        Target::Fn,
-        Target::Closure,
-        Target::ForeignFn,
-        Target::Method(MethodKind::Inherent),
-        Target::Method(MethodKind::Trait { body: false }),
-        Target::Method(MethodKind::Trait { body: true }),
-        Target::Method(MethodKind::TraitImpl),
-    ];
-    const METHOD_LIKE: &[Target] = &[
-        Target::Method(MethodKind::Inherent),
-        Target::Method(MethodKind::Trait { body: false }),
-        Target::Method(MethodKind::Trait { body: true }),
-        Target::Method(MethodKind::TraitImpl),
-    ];
-    const IMPL_LIKE: &[Target] =
-        &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
-    const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
-
-    let mut added_fake_targets = Vec::new();
-    filter_targets(
-        &mut allowed_targets,
-        FUNCTION_LIKE,
-        "functions",
-        target,
-        &mut added_fake_targets,
-    );
-    filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
-    filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
-    filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
-
-    // If there is now only 1 target left, show that as the only possible target
-    (
-        added_fake_targets
-            .iter()
-            .copied()
-            .chain(allowed_targets.iter().map(|t| t.plural_name()))
-            .join(", "),
-        allowed_targets.len() + added_fake_targets.len() == 1,
-    )
-}
-
-fn filter_targets(
-    allowed_targets: &mut Vec<Target>,
-    target_group: &'static [Target],
-    target_group_name: &'static str,
-    target: Target,
-    added_fake_targets: &mut Vec<&'static str>,
-) {
-    if target_group.contains(&target) {
-        return;
-    }
-    if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
-        return;
-    }
-    allowed_targets.retain(|t| !target_group.contains(t));
-    added_fake_targets.push(target_group_name);
-}
-
-/// This is the list of all targets to which a attribute can be applied
-/// This is used for:
-/// - `rustc_dummy`, which can be applied to all targets
-/// - Attributes that are not parted to the new target system yet can use this list as a placeholder
-pub(crate) const ALL_TARGETS: &'static [MaybeWarn] = &[
-    Allow(Target::ExternCrate),
-    Allow(Target::Use),
-    Allow(Target::Static),
-    Allow(Target::Const),
-    Allow(Target::Fn),
-    Allow(Target::Closure),
-    Allow(Target::Mod),
-    Allow(Target::ForeignMod),
-    Allow(Target::GlobalAsm),
-    Allow(Target::TyAlias),
-    Allow(Target::Enum),
-    Allow(Target::Variant),
-    Allow(Target::Struct),
-    Allow(Target::Field),
-    Allow(Target::Union),
-    Allow(Target::Trait),
-    Allow(Target::TraitAlias),
-    Allow(Target::Impl { of_trait: false }),
-    Allow(Target::Impl { of_trait: true }),
-    Allow(Target::Expression),
-    Allow(Target::Statement),
-    Allow(Target::Arm),
-    Allow(Target::AssocConst),
-    Allow(Target::Method(MethodKind::Inherent)),
-    Allow(Target::Method(MethodKind::Trait { body: false })),
-    Allow(Target::Method(MethodKind::Trait { body: true })),
-    Allow(Target::Method(MethodKind::TraitImpl)),
-    Allow(Target::AssocTy),
-    Allow(Target::ForeignFn),
-    Allow(Target::ForeignStatic),
-    Allow(Target::ForeignTy),
-    Allow(Target::MacroDef),
-    Allow(Target::Param),
-    Allow(Target::PatField),
-    Allow(Target::ExprField),
-    Allow(Target::WherePredicate),
-    Allow(Target::MacroCall),
-    Allow(Target::Crate),
-    Allow(Target::Delegation { mac: false }),
-    Allow(Target::Delegation { mac: true }),
-];
-
-/// Parse a single integer.
-///
-/// Used by attributes that take a single integer as argument, such as
-/// `#[link_ordinal]` and `#[rustc_layout_scalar_valid_range_start]`.
-/// `cx` is the context given to the attribute.
-/// `args` is the parser for the attribute arguments.
-pub(crate) fn parse_single_integer<S: Stage>(
-    cx: &mut AcceptContext<'_, '_, S>,
-    args: &ArgParser<'_>,
-) -> Option<u128> {
-    let Some(list) = args.list() else {
-        cx.expected_list(cx.attr_span);
-        return None;
-    };
-    let Some(single) = list.single() else {
-        cx.expected_single_argument(list.span);
-        return None;
-    };
-    let Some(lit) = single.lit() else {
-        cx.expected_integer_literal(single.span());
-        return None;
-    };
-    let LitKind::Int(num, _ty) = lit.kind else {
-        cx.expected_integer_literal(single.span());
-        return None;
-    };
-    Some(num.0)
-}
diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs
new file mode 100644
index 00000000000..22bbea766f2
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/interface.rs
@@ -0,0 +1,321 @@
+use rustc_ast as ast;
+use rustc_ast::NodeId;
+use rustc_errors::DiagCtxtHandle;
+use rustc_feature::{AttributeTemplate, Features};
+use rustc_hir::attrs::AttributeKind;
+use rustc_hir::lints::AttributeLint;
+use rustc_hir::{AttrArgs, AttrItem, AttrPath, Attribute, HashIgnoredAttrId, Target};
+use rustc_session::Session;
+use rustc_span::{DUMMY_SP, Span, Symbol, sym};
+
+use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
+use crate::parser::{ArgParser, MetaItemParser, PathParser};
+use crate::{Early, Late, OmitDoc, ShouldEmit};
+
+/// Context created once, for example as part of the ast lowering
+/// context, through which all attributes can be lowered.
+pub struct AttributeParser<'sess, S: Stage = Late> {
+    pub(crate) tools: Vec<Symbol>,
+    pub(crate) features: Option<&'sess Features>,
+    pub(crate) sess: &'sess Session,
+    pub(crate) stage: S,
+
+    /// *Only* parse attributes with this symbol.
+    ///
+    /// Used in cases where we want the lowering infrastructure for parse just a single attribute.
+    parse_only: Option<Symbol>,
+}
+
+impl<'sess> AttributeParser<'sess, Early> {
+    /// This method allows you to parse attributes *before* you have access to features or tools.
+    /// One example where this is necessary, is to parse `feature` attributes themselves for
+    /// example.
+    ///
+    /// Try to use this as little as possible. Attributes *should* be lowered during
+    /// `rustc_ast_lowering`. Some attributes require access to features to parse, which would
+    /// crash if you tried to do so through [`parse_limited`](Self::parse_limited).
+    ///
+    /// To make sure use is limited, supply a `Symbol` you'd like to parse. Only attributes with
+    /// that symbol are picked out of the list of instructions and parsed. Those are returned.
+    ///
+    /// No diagnostics will be emitted when parsing limited. Lints are not emitted at all, while
+    /// errors will be emitted as a delayed bugs. in other words, we *expect* attributes parsed
+    /// with `parse_limited` to be reparsed later during ast lowering where we *do* emit the errors
+    pub fn parse_limited(
+        sess: &'sess Session,
+        attrs: &[ast::Attribute],
+        sym: Symbol,
+        target_span: Span,
+        target_node_id: NodeId,
+        features: Option<&'sess Features>,
+    ) -> Option<Attribute> {
+        let mut p = Self {
+            features,
+            tools: Vec::new(),
+            parse_only: Some(sym),
+            sess,
+            stage: Early { emit_errors: ShouldEmit::Nothing },
+        };
+        let mut parsed = p.parse_attribute_list(
+            attrs,
+            target_span,
+            target_node_id,
+            Target::Crate, // Does not matter, we're not going to emit errors anyways
+            OmitDoc::Skip,
+            std::convert::identity,
+            |_lint| {
+                panic!("can't emit lints here for now (nothing uses this atm)");
+            },
+        );
+        assert!(parsed.len() <= 1);
+
+        parsed.pop()
+    }
+
+    pub fn parse_single<T>(
+        sess: &'sess Session,
+        attr: &ast::Attribute,
+        target_span: Span,
+        target_node_id: NodeId,
+        features: Option<&'sess Features>,
+        emit_errors: ShouldEmit,
+        parse_fn: fn(cx: &mut AcceptContext<'_, '_, Early>, item: &ArgParser<'_>) -> T,
+        template: &AttributeTemplate,
+    ) -> T {
+        let mut parser = Self {
+            features,
+            tools: Vec::new(),
+            parse_only: None,
+            sess,
+            stage: Early { emit_errors },
+        };
+        let ast::AttrKind::Normal(normal_attr) = &attr.kind else {
+            panic!("parse_single called on a doc attr")
+        };
+        let meta_parser = MetaItemParser::from_attr(normal_attr, parser.dcx());
+        let path = meta_parser.path();
+        let args = meta_parser.args();
+        let mut cx: AcceptContext<'_, 'sess, Early> = AcceptContext {
+            shared: SharedContext {
+                cx: &mut parser,
+                target_span,
+                target_id: target_node_id,
+                emit_lint: &mut |_lint| {
+                    panic!("can't emit lints here for now (nothing uses this atm)");
+                },
+            },
+            attr_span: attr.span,
+            attr_style: attr.style,
+            template,
+            attr_path: path.get_attribute_path(),
+        };
+        parse_fn(&mut cx, args)
+    }
+}
+
+impl<'sess, S: Stage> AttributeParser<'sess, S> {
+    pub fn new(
+        sess: &'sess Session,
+        features: &'sess Features,
+        tools: Vec<Symbol>,
+        stage: S,
+    ) -> Self {
+        Self { features: Some(features), tools, parse_only: None, sess, stage }
+    }
+
+    pub(crate) fn sess(&self) -> &'sess Session {
+        &self.sess
+    }
+
+    pub(crate) fn features(&self) -> &'sess Features {
+        self.features.expect("features not available at this point in the compiler")
+    }
+
+    pub(crate) fn features_option(&self) -> Option<&'sess Features> {
+        self.features
+    }
+
+    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'sess> {
+        self.sess().dcx()
+    }
+
+    /// Parse a list of attributes.
+    ///
+    /// `target_span` is the span of the thing this list of attributes is applied to,
+    /// and when `omit_doc` is set, doc attributes are filtered out.
+    pub fn parse_attribute_list(
+        &mut self,
+        attrs: &[ast::Attribute],
+        target_span: Span,
+        target_id: S::Id,
+        target: Target,
+        omit_doc: OmitDoc,
+
+        lower_span: impl Copy + Fn(Span) -> Span,
+        mut emit_lint: impl FnMut(AttributeLint<S::Id>),
+    ) -> Vec<Attribute> {
+        let mut attributes = Vec::new();
+        let mut attr_paths = Vec::new();
+
+        for attr in attrs {
+            // If we're only looking for a single attribute, skip all the ones we don't care about.
+            if let Some(expected) = self.parse_only {
+                if !attr.has_name(expected) {
+                    continue;
+                }
+            }
+
+            // Sometimes, for example for `#![doc = include_str!("readme.md")]`,
+            // doc still contains a non-literal. You might say, when we're lowering attributes
+            // that's expanded right? But no, sometimes, when parsing attributes on macros,
+            // we already use the lowering logic and these are still there. So, when `omit_doc`
+            // is set we *also* want to ignore these.
+            if omit_doc == OmitDoc::Skip && attr.has_name(sym::doc) {
+                continue;
+            }
+
+            match &attr.kind {
+                ast::AttrKind::DocComment(comment_kind, symbol) => {
+                    if omit_doc == OmitDoc::Skip {
+                        continue;
+                    }
+
+                    attributes.push(Attribute::Parsed(AttributeKind::DocComment {
+                        style: attr.style,
+                        kind: *comment_kind,
+                        span: lower_span(attr.span),
+                        comment: *symbol,
+                    }))
+                }
+                // // FIXME: make doc attributes go through a proper attribute parser
+                // ast::AttrKind::Normal(n) if n.has_name(sym::doc) => {
+                //     let p = GenericMetaItemParser::from_attr(&n, self.dcx());
+                //
+                //     attributes.push(Attribute::Parsed(AttributeKind::DocComment {
+                //         style: attr.style,
+                //         kind: CommentKind::Line,
+                //         span: attr.span,
+                //         comment: p.args().name_value(),
+                //     }))
+                // }
+                ast::AttrKind::Normal(n) => {
+                    attr_paths.push(PathParser::Ast(&n.item.path));
+
+                    let parser = MetaItemParser::from_attr(n, self.dcx());
+                    let path = parser.path();
+                    let args = parser.args();
+                    let path_parts = path.segments().map(|i| i.name).collect::<Vec<_>>();
+
+                    if let Some(accepts) = S::parsers().accepters.get(path_parts.as_slice()) {
+                        for accept in accepts {
+                            let mut cx: AcceptContext<'_, 'sess, S> = AcceptContext {
+                                shared: SharedContext {
+                                    cx: self,
+                                    target_span,
+                                    target_id,
+                                    emit_lint: &mut emit_lint,
+                                },
+                                attr_span: lower_span(attr.span),
+                                attr_style: attr.style,
+                                template: &accept.template,
+                                attr_path: path.get_attribute_path(),
+                            };
+
+                            (accept.accept_fn)(&mut cx, args);
+
+                            if self.stage.should_emit().should_emit() {
+                                self.check_target(
+                                    path.get_attribute_path(),
+                                    attr.span,
+                                    &accept.allowed_targets,
+                                    target,
+                                    target_id,
+                                    &mut emit_lint,
+                                );
+                            }
+                        }
+                    } else {
+                        // If we're here, we must be compiling a tool attribute... Or someone
+                        // forgot to parse their fancy new attribute. Let's warn them in any case.
+                        // If you are that person, and you really think your attribute should
+                        // remain unparsed, carefully read the documentation in this module and if
+                        // you still think so you can add an exception to this assertion.
+
+                        // FIXME(jdonszelmann): convert other attributes, and check with this that
+                        // we caught em all
+                        // const FIXME_TEMPORARY_ATTR_ALLOWLIST: &[Symbol] = &[sym::cfg];
+                        // assert!(
+                        //     self.tools.contains(&parts[0]) || true,
+                        //     // || FIXME_TEMPORARY_ATTR_ALLOWLIST.contains(&parts[0]),
+                        //     "attribute {path} wasn't parsed and isn't a know tool attribute",
+                        // );
+
+                        attributes.push(Attribute::Unparsed(Box::new(AttrItem {
+                            path: AttrPath::from_ast(&n.item.path),
+                            args: self.lower_attr_args(&n.item.args, lower_span),
+                            id: HashIgnoredAttrId { attr_id: attr.id },
+                            style: attr.style,
+                            span: lower_span(attr.span),
+                        })));
+                    }
+                }
+            }
+        }
+
+        let mut parsed_attributes = Vec::new();
+        for f in &S::parsers().finalizers {
+            if let Some(attr) = f(&mut FinalizeContext {
+                shared: SharedContext {
+                    cx: self,
+                    target_span,
+                    target_id,
+                    emit_lint: &mut emit_lint,
+                },
+                all_attrs: &attr_paths,
+            }) {
+                parsed_attributes.push(Attribute::Parsed(attr));
+            }
+        }
+
+        attributes.extend(parsed_attributes);
+
+        attributes
+    }
+
+    /// Returns whether there is a parser for an attribute with this name
+    pub fn is_parsed_attribute(path: &[Symbol]) -> bool {
+        Late::parsers().accepters.contains_key(path)
+    }
+
+    fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
+        match args {
+            ast::AttrArgs::Empty => AttrArgs::Empty,
+            ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
+            // This is an inert key-value attribute - it will never be visible to macros
+            // after it gets lowered to HIR. Therefore, we can extract literals to handle
+            // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
+            ast::AttrArgs::Eq { eq_span, expr } => {
+                // In valid code the value always ends up as a single literal. Otherwise, a dummy
+                // literal suffices because the error is handled elsewhere.
+                let lit = if let ast::ExprKind::Lit(token_lit) = expr.kind
+                    && let Ok(lit) =
+                        ast::MetaItemLit::from_token_lit(token_lit, lower_span(expr.span))
+                {
+                    lit
+                } else {
+                    let guar = self.dcx().span_delayed_bug(
+                        args.span().unwrap_or(DUMMY_SP),
+                        "expr in place where literal is expected (builtin attr parsing)",
+                    );
+                    ast::MetaItemLit {
+                        symbol: sym::dummy,
+                        suffix: None,
+                        kind: ast::LitKind::Err(guar),
+                        span: DUMMY_SP,
+                    }
+                };
+                AttrArgs::Eq { eq_span: lower_span(*eq_span), expr: lit }
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs
index fc1377e5314..99842cd9687 100644
--- a/compiler/rustc_attr_parsing/src/lib.rs
+++ b/compiler/rustc_attr_parsing/src/lib.rs
@@ -84,18 +84,32 @@
 // tidy-alphabetical-end
 
 #[macro_use]
+/// All the individual attribute parsers for each of rustc's built-in attributes.
 mod attributes;
+
+/// All the important types given to attribute parsers when parsing
 pub(crate) mod context;
-mod lints;
+
+/// Code that other crates interact with, to actually parse a list (or sometimes single)
+/// attribute.
+mod interface;
+
+/// Despite this entire module called attribute parsing and the term being a little overloaded,
+/// in this module the code lives that actually breaks up tokenstreams into semantic pieces of attributes,
+/// like lists or name-value pairs.
 pub mod parser;
+
+mod lints;
 mod session_diagnostics;
+mod target_checking;
 
 pub use attributes::cfg::{CFG_TEMPLATE, EvalConfigResult, eval_config_entry, parse_cfg_attr};
 pub use attributes::cfg_old::*;
 pub use attributes::util::{
     find_crate_name, is_builtin_attr, is_doc_alias_attrs_contain_symbol, parse_version,
 };
-pub use context::{AttributeParser, Early, Late, OmitDoc, ShouldEmit};
+pub use context::{Early, Late, OmitDoc, ShouldEmit};
+pub use interface::AttributeParser;
 pub use lints::emit_attribute_lint;
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
diff --git a/compiler/rustc_attr_parsing/src/lints.rs b/compiler/rustc_attr_parsing/src/lints.rs
index 2813fef3148..7030f28f23c 100644
--- a/compiler/rustc_attr_parsing/src/lints.rs
+++ b/compiler/rustc_attr_parsing/src/lints.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
 use rustc_errors::{DiagArgValue, LintEmitter};
 use rustc_hir::lints::{AttributeLint, AttributeLintKind};
 use rustc_hir::{HirId, Target};
@@ -35,12 +37,12 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
             *first_span,
             session_diagnostics::EmptyAttributeList { attr_span: *first_span },
         ),
-        &AttributeLintKind::InvalidTarget { name, target, ref applied, only } => lint_emitter
+        AttributeLintKind::InvalidTarget { name, target, applied, only } => lint_emitter
             .emit_node_span_lint(
                 // This check is here because `deprecated` had its own lint group and removing this would be a breaking change
-                if name == sym::deprecated
+                if name.segments[0].name == sym::deprecated
                     && ![Target::Closure, Target::Expression, Target::Statement, Target::Arm]
-                        .contains(&target)
+                        .contains(target)
                 {
                     rustc_session::lint::builtin::USELESS_DEPRECATED
                 } else {
@@ -51,7 +53,9 @@ pub fn emit_attribute_lint<L: LintEmitter>(lint: &AttributeLint<HirId>, lint_emi
                 session_diagnostics::InvalidTargetLint {
                     name,
                     target: target.plural_name(),
-                    applied: applied.clone(),
+                    applied: DiagArgValue::StrListSepByAnd(
+                        applied.into_iter().map(|i| Cow::Owned(i.to_string())).collect(),
+                    ),
                     only,
                     attr_span: *span,
                 },
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index aec970a3ce9..2993881f717 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -484,10 +484,10 @@ pub(crate) struct EmptyAttributeList {
 #[diag(attr_parsing_invalid_target_lint)]
 #[warning]
 #[help]
-pub(crate) struct InvalidTargetLint {
-    pub name: Symbol,
-    pub target: &'static str,
-    pub applied: String,
+pub(crate) struct InvalidTargetLint<'a> {
+    pub name: &'a AttrPath,
+    pub target: &'a str,
+    pub applied: DiagArgValue,
     pub only: &'static str,
     #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
     pub attr_span: Span,
@@ -500,9 +500,9 @@ pub(crate) struct InvalidTarget {
     #[primary_span]
     #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
     pub span: Span,
-    pub name: Symbol,
+    pub name: AttrPath,
     pub target: &'static str,
-    pub applied: String,
+    pub applied: DiagArgValue,
     pub only: &'static str,
 }
 
diff --git a/compiler/rustc_attr_parsing/src/target_checking.rs b/compiler/rustc_attr_parsing/src/target_checking.rs
new file mode 100644
index 00000000000..9568b791b3f
--- /dev/null
+++ b/compiler/rustc_attr_parsing/src/target_checking.rs
@@ -0,0 +1,247 @@
+use std::borrow::Cow;
+
+use rustc_errors::DiagArgValue;
+use rustc_feature::Features;
+use rustc_hir::lints::{AttributeLint, AttributeLintKind};
+use rustc_hir::{AttrPath, MethodKind, Target};
+use rustc_span::Span;
+
+use crate::AttributeParser;
+use crate::context::Stage;
+use crate::session_diagnostics::InvalidTarget;
+
+#[derive(Debug)]
+pub(crate) enum AllowedTargets {
+    AllowList(&'static [Policy]),
+    AllowListWarnRest(&'static [Policy]),
+}
+
+pub(crate) enum AllowedResult {
+    Allowed,
+    Warn,
+    Error,
+}
+
+impl AllowedTargets {
+    pub(crate) fn is_allowed(&self, target: Target) -> AllowedResult {
+        match self {
+            AllowedTargets::AllowList(list) => {
+                if list.contains(&Policy::Allow(target)) {
+                    AllowedResult::Allowed
+                } else if list.contains(&Policy::Warn(target)) {
+                    AllowedResult::Warn
+                } else {
+                    AllowedResult::Error
+                }
+            }
+            AllowedTargets::AllowListWarnRest(list) => {
+                if list.contains(&Policy::Allow(target)) {
+                    AllowedResult::Allowed
+                } else if list.contains(&Policy::Error(target)) {
+                    AllowedResult::Error
+                } else {
+                    AllowedResult::Warn
+                }
+            }
+        }
+    }
+
+    pub(crate) fn allowed_targets(&self) -> Vec<Target> {
+        match self {
+            AllowedTargets::AllowList(list) => list,
+            AllowedTargets::AllowListWarnRest(list) => list,
+        }
+        .iter()
+        .filter_map(|target| match target {
+            Policy::Allow(target) => Some(*target),
+            Policy::Warn(_) => None,
+            Policy::Error(_) => None,
+        })
+        .collect()
+    }
+}
+
+#[derive(Debug, Eq, PartialEq)]
+pub(crate) enum Policy {
+    Allow(Target),
+    Warn(Target),
+    Error(Target),
+}
+
+impl<S: Stage> AttributeParser<'_, S> {
+    pub(crate) fn check_target(
+        &self,
+        attr_name: AttrPath,
+        attr_span: Span,
+        allowed_targets: &AllowedTargets,
+        target: Target,
+        target_id: S::Id,
+        mut emit_lint: impl FnMut(AttributeLint<S::Id>),
+    ) {
+        match allowed_targets.is_allowed(target) {
+            AllowedResult::Allowed => {}
+            AllowedResult::Warn => {
+                let allowed_targets = allowed_targets.allowed_targets();
+                let (applied, only) =
+                    allowed_targets_applied(allowed_targets, target, self.features);
+                emit_lint(AttributeLint {
+                    id: target_id,
+                    span: attr_span,
+                    kind: AttributeLintKind::InvalidTarget {
+                        name: attr_name,
+                        target,
+                        only: if only { "only " } else { "" },
+                        applied,
+                    },
+                });
+            }
+            AllowedResult::Error => {
+                let allowed_targets = allowed_targets.allowed_targets();
+                let (applied, only) =
+                    allowed_targets_applied(allowed_targets, target, self.features);
+                self.dcx().emit_err(InvalidTarget {
+                    span: attr_span,
+                    name: attr_name,
+                    target: target.plural_name(),
+                    only: if only { "only " } else { "" },
+                    applied: DiagArgValue::StrListSepByAnd(
+                        applied.into_iter().map(Cow::Owned).collect(),
+                    ),
+                });
+            }
+        }
+    }
+}
+
+/// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to.
+/// Does some heuristic-based filtering to remove uninteresting targets, and formats the targets into a string
+pub(crate) fn allowed_targets_applied(
+    mut allowed_targets: Vec<Target>,
+    target: Target,
+    features: Option<&Features>,
+) -> (Vec<String>, bool) {
+    // Remove unstable targets from `allowed_targets` if their features are not enabled
+    if let Some(features) = features {
+        if !features.fn_delegation() {
+            allowed_targets.retain(|t| !matches!(t, Target::Delegation { .. }));
+        }
+        if !features.stmt_expr_attributes() {
+            allowed_targets.retain(|t| !matches!(t, Target::Expression | Target::Statement));
+        }
+        if !features.extern_types() {
+            allowed_targets.retain(|t| !matches!(t, Target::ForeignTy));
+        }
+    }
+
+    // We define groups of "similar" targets.
+    // If at least two of the targets are allowed, and the `target` is not in the group,
+    // we collapse the entire group to a single entry to simplify the target list
+    const FUNCTION_LIKE: &[Target] = &[
+        Target::Fn,
+        Target::Closure,
+        Target::ForeignFn,
+        Target::Method(MethodKind::Inherent),
+        Target::Method(MethodKind::Trait { body: false }),
+        Target::Method(MethodKind::Trait { body: true }),
+        Target::Method(MethodKind::TraitImpl),
+    ];
+    const METHOD_LIKE: &[Target] = &[
+        Target::Method(MethodKind::Inherent),
+        Target::Method(MethodKind::Trait { body: false }),
+        Target::Method(MethodKind::Trait { body: true }),
+        Target::Method(MethodKind::TraitImpl),
+    ];
+    const IMPL_LIKE: &[Target] =
+        &[Target::Impl { of_trait: false }, Target::Impl { of_trait: true }];
+    const ADT_LIKE: &[Target] = &[Target::Struct, Target::Enum];
+
+    let mut added_fake_targets = Vec::new();
+    filter_targets(
+        &mut allowed_targets,
+        FUNCTION_LIKE,
+        "functions",
+        target,
+        &mut added_fake_targets,
+    );
+    filter_targets(&mut allowed_targets, METHOD_LIKE, "methods", target, &mut added_fake_targets);
+    filter_targets(&mut allowed_targets, IMPL_LIKE, "impl blocks", target, &mut added_fake_targets);
+    filter_targets(&mut allowed_targets, ADT_LIKE, "data types", target, &mut added_fake_targets);
+
+    // If there is now only 1 target left, show that as the only possible target
+    (
+        added_fake_targets
+            .iter()
+            .copied()
+            .chain(allowed_targets.iter().map(|t| t.plural_name()))
+            .map(|i| i.to_string())
+            .collect(),
+        allowed_targets.len() + added_fake_targets.len() == 1,
+    )
+}
+
+fn filter_targets(
+    allowed_targets: &mut Vec<Target>,
+    target_group: &'static [Target],
+    target_group_name: &'static str,
+    target: Target,
+    added_fake_targets: &mut Vec<&'static str>,
+) {
+    if target_group.contains(&target) {
+        return;
+    }
+    if allowed_targets.iter().filter(|at| target_group.contains(at)).count() < 2 {
+        return;
+    }
+    allowed_targets.retain(|t| !target_group.contains(t));
+    added_fake_targets.push(target_group_name);
+}
+
+/// This is the list of all targets to which a attribute can be applied
+/// This is used for:
+/// - `rustc_dummy`, which can be applied to all targets
+/// - Attributes that are not parted to the new target system yet can use this list as a placeholder
+pub(crate) const ALL_TARGETS: &'static [Policy] = {
+    use Policy::Allow;
+    &[
+        Allow(Target::ExternCrate),
+        Allow(Target::Use),
+        Allow(Target::Static),
+        Allow(Target::Const),
+        Allow(Target::Fn),
+        Allow(Target::Closure),
+        Allow(Target::Mod),
+        Allow(Target::ForeignMod),
+        Allow(Target::GlobalAsm),
+        Allow(Target::TyAlias),
+        Allow(Target::Enum),
+        Allow(Target::Variant),
+        Allow(Target::Struct),
+        Allow(Target::Field),
+        Allow(Target::Union),
+        Allow(Target::Trait),
+        Allow(Target::TraitAlias),
+        Allow(Target::Impl { of_trait: false }),
+        Allow(Target::Impl { of_trait: true }),
+        Allow(Target::Expression),
+        Allow(Target::Statement),
+        Allow(Target::Arm),
+        Allow(Target::AssocConst),
+        Allow(Target::Method(MethodKind::Inherent)),
+        Allow(Target::Method(MethodKind::Trait { body: false })),
+        Allow(Target::Method(MethodKind::Trait { body: true })),
+        Allow(Target::Method(MethodKind::TraitImpl)),
+        Allow(Target::AssocTy),
+        Allow(Target::ForeignFn),
+        Allow(Target::ForeignStatic),
+        Allow(Target::ForeignTy),
+        Allow(Target::MacroDef),
+        Allow(Target::Param),
+        Allow(Target::PatField),
+        Allow(Target::ExprField),
+        Allow(Target::WherePredicate),
+        Allow(Target::MacroCall),
+        Allow(Target::Crate),
+        Allow(Target::Delegation { mac: false }),
+        Allow(Target::Delegation { mac: true }),
+    ]
+};