about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/crate_level.rs34
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/prelude.rs3
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs13
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs2
-rw-r--r--compiler/rustc_expand/src/base.rs3
-rw-r--r--compiler/rustc_expand/src/errors.rs2
-rw-r--r--compiler/rustc_expand/src/expand.rs4
-rw-r--r--compiler/rustc_hir/src/attrs/data_structures.rs9
-rw-r--r--compiler/rustc_hir/src/attrs/encode_cross_crate.rs2
-rw-r--r--compiler/rustc_hir/src/attrs/pretty_printing.rs4
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_hir/src/limit.rs63
-rw-r--r--compiler/rustc_hir_analysis/src/autoderef.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs3
-rw-r--r--compiler/rustc_interface/messages.ftl4
-rw-r--r--compiler/rustc_interface/src/errors.rs10
-rw-r--r--compiler/rustc_interface/src/limits.rs88
-rw-r--r--compiler/rustc_interface/src/passes.rs54
-rw-r--r--compiler/rustc_middle/src/error.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs3
-rw-r--r--compiler/rustc_middle/src/ty/error.rs3
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs2
-rw-r--r--compiler/rustc_mir_transform/src/inline/cycle.rs2
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs2
-rw-r--r--compiler/rustc_monomorphize/src/mono_checks/move_check.rs2
-rw-r--r--compiler/rustc_query_impl/Cargo.toml1
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs2
-rw-r--r--compiler/rustc_query_system/src/error.rs2
-rw-r--r--compiler/rustc_session/src/session.rs62
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs4
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs2
-rw-r--r--tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs24
-rw-r--r--tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr262
-rw-r--r--tests/ui/lint/unused/unused-attr-duplicate.stderr52
-rw-r--r--tests/ui/lint/unused/unused-attr-macro-rules.stderr35
-rw-r--r--tests/ui/recursion_limit/empty.rs8
-rw-r--r--tests/ui/recursion_limit/empty.stderr12
-rw-r--r--tests/ui/recursion_limit/invalid_digit.rs8
-rw-r--r--tests/ui/recursion_limit/invalid_digit.stderr12
-rw-r--r--tests/ui/recursion_limit/invalid_digit_type.stderr8
-rw-r--r--tests/ui/recursion_limit/invalid_macro.rs2
-rw-r--r--tests/ui/recursion_limit/invalid_macro.stderr8
-rw-r--r--tests/ui/recursion_limit/no-value.stderr3
-rw-r--r--tests/ui/recursion_limit/overflow.rs3
-rw-r--r--tests/ui/recursion_limit/overflow.stderr12
47 files changed, 385 insertions, 457 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 81f19668c24..78be59b3091 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4409,7 +4409,6 @@ dependencies = [
  "rustc_middle",
  "rustc_query_system",
  "rustc_serialize",
- "rustc_session",
  "rustc_span",
  "tracing",
 ]
diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
index 6fd4a4a753b..d23a7ae72f8 100644
--- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs
@@ -1,18 +1,19 @@
 use std::num::IntErrorKind;
 
-use crate::session_diagnostics::LimitInvalid;
+use rustc_hir::limit::Limit;
 
 use super::prelude::*;
+use crate::session_diagnostics::LimitInvalid;
 
 impl<S: Stage> AcceptContext<'_, '_, S> {
-    fn parse_limit_int(&self, nv: &NameValueParser) -> Option<usize> {
+    fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
         let Some(limit) = nv.value_as_str() else {
             self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
             return None;
         };
 
         let error_str = match limit.as_str().parse() {
-            Ok(i) => return Some(i),
+            Ok(i) => return Some(Limit::new(i)),
             Err(e) => match e.kind() {
                 IntErrorKind::PosOverflow => "`limit` is too large",
                 IntErrorKind::Empty => "`limit` must be a non-negative integer",
@@ -44,8 +45,8 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
     const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
     const TYPE: AttributeType = AttributeType::CrateLevel;
 
-    // FIXME: crate name is allowed on all targets and ignored,
-    //        even though it should only be valid on crates of course
+    // because it's a crate-level attribute, we already warn about it.
+    // Putting target limitations here would give duplicate warnings
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
@@ -77,8 +78,8 @@ impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
     const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute");
     const TYPE: AttributeType = AttributeType::CrateLevel;
 
-    // FIXME: recursion limit is allowed on all targets and ignored,
-    //        even though it should only be valid on crates of course
+    // because it's a crate-level attribute, we already warn about it.
+    // Putting target limitations here would give duplicate warnings
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
@@ -102,10 +103,11 @@ impl<S: Stage> SingleAttributeParser<S> for MoveSizeLimitParser {
     const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
     const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
     const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
+    const TYPE: AttributeType = AttributeType::CrateLevel;
 
-    // FIXME: move size limit is allowed on all targets and ignored,
-    //        even though it should only be valid on crates of course
-    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
+    // because it's a crate-level attribute, we already warn about it.
+    // Putting target limitations here would give duplicate warnings
+    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let ArgParser::NameValue(nv) = args else {
@@ -130,9 +132,9 @@ impl<S: Stage> SingleAttributeParser<S> for TypeLengthLimitParser {
     const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
     const TYPE: AttributeType = AttributeType::CrateLevel;
 
-    // FIXME: recursion limit is allowed on all targets and ignored,
-    //        even though it should only be valid on crates of course
-    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowListWarnRest(&[Target::Crate]);
+    // because it's a crate-level attribute, we already warn about it.
+    // Putting target limitations here would give duplicate warnings
+    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let ArgParser::NameValue(nv) = args else {
@@ -157,9 +159,9 @@ impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser {
     const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
     const TYPE: AttributeType = AttributeType::CrateLevel;
 
-    // FIXME: recursion limit is allowed on all targets and ignored,
-    //        even though it should only be valid on crates of course
-    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
+    // because it's a crate-level attribute, we already warn about it.
+    // Putting target limitations here would give duplicate warnings
+    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let ArgParser::NameValue(nv) = args else {
diff --git a/compiler/rustc_attr_parsing/src/attributes/prelude.rs b/compiler/rustc_attr_parsing/src/attributes/prelude.rs
index e53a4366131..8f040ffb9d4 100644
--- a/compiler/rustc_attr_parsing/src/attributes/prelude.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/prelude.rs
@@ -1,7 +1,6 @@
-// templates
+// data structures
 #[doc(hidden)]
 pub(super) use rustc_feature::{AttributeTemplate, AttributeType, template};
-// data structures
 #[doc(hidden)]
 pub(super) use rustc_hir::attrs::AttributeKind;
 #[doc(hidden)]
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index fc41e38c797..98e86838a3a 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -353,7 +353,10 @@ impl<'f, 'sess: 'f, S: Stage> SharedContext<'f, 'sess, S> {
     /// must be delayed until after HIR is built. This method will take care of the details of
     /// that.
     pub(crate) fn emit_lint(&mut self, lint: AttributeLintKind, span: Span) {
-        if matches!(self.stage.should_emit(), ShouldEmit::Nothing) {
+        if !matches!(
+            self.stage.should_emit(),
+            ShouldEmit::ErrorsAndLints | ShouldEmit::EarlyFatal { also_emit_lints: true }
+        ) {
             return;
         }
         let id = self.target_id;
@@ -677,20 +680,20 @@ pub enum ShouldEmit {
     ///
     /// Only relevant when early parsing, in late parsing equivalent to `ErrorsAndLints`.
     /// Late parsing is never fatal, and instead tries to emit as many diagnostics as possible.
-    EarlyFatal,
+    EarlyFatal { also_emit_lints: bool },
     /// The operation will emit errors and lints.
     /// This is usually what you need.
     ErrorsAndLints,
     /// The operation will emit *not* errors and lints.
-    /// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::Emit`.
+    /// Use this if you are *sure* that this operation will be called at a different time with `ShouldEmit::ErrorsAndLints`.
     Nothing,
 }
 
 impl ShouldEmit {
     pub(crate) fn emit_err(&self, diag: Diag<'_>) -> ErrorGuaranteed {
         match self {
-            ShouldEmit::EarlyFatal if diag.level() == Level::DelayedBug => diag.emit(),
-            ShouldEmit::EarlyFatal => diag.upgrade_to_fatal().emit(),
+            ShouldEmit::EarlyFatal { .. } if diag.level() == Level::DelayedBug => diag.emit(),
+            ShouldEmit::EarlyFatal { .. } => diag.upgrade_to_fatal().emit(),
             ShouldEmit::ErrorsAndLints => diag.emit(),
             ShouldEmit::Nothing => diag.delay_as_bug(),
         }
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 9681d89ce35..91ed71ac3e5 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -4,6 +4,7 @@ use either::{Left, Right};
 use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout};
 use rustc_errors::DiagCtxtHandle;
 use rustc_hir::def_id::DefId;
+use rustc_hir::limit::Limit;
 use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo};
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::layout::{
@@ -12,7 +13,6 @@ use rustc_middle::ty::layout::{
 };
 use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypingEnv, Variance};
 use rustc_middle::{mir, span_bug};
-use rustc_session::Limit;
 use rustc_span::Span;
 use rustc_target::callconv::FnAbi;
 use tracing::{debug, trace};
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 8ff21509f4a..88f88f30a8c 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -18,13 +18,14 @@ use rustc_feature::Features;
 use rustc_hir as hir;
 use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation};
 use rustc_hir::def::MacroKinds;
+use rustc_hir::limit::Limit;
 use rustc_hir::{Stability, find_attr};
 use rustc_lint_defs::RegisteredTools;
 use rustc_parse::MACRO_ARGUMENTS;
 use rustc_parse::parser::{ForceCollect, Parser};
+use rustc_session::Session;
 use rustc_session::config::CollapseMacroDebuginfo;
 use rustc_session::parse::ParseSess;
-use rustc_session::{Limit, Session};
 use rustc_span::def_id::{CrateNum, DefId, LocalDefId};
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::{AstPass, ExpnData, ExpnKind, LocalExpnId, MacroKind};
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index ba9d76970f0..6eb5cd65846 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -2,8 +2,8 @@ use std::borrow::Cow;
 
 use rustc_ast::ast;
 use rustc_errors::codes::*;
+use rustc_hir::limit::Limit;
 use rustc_macros::{Diagnostic, Subdiagnostic};
-use rustc_session::Limit;
 use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index dbb4a9de9e0..38e057d2776 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -19,14 +19,15 @@ use rustc_errors::PResult;
 use rustc_feature::Features;
 use rustc_hir::Target;
 use rustc_hir::def::MacroKinds;
+use rustc_hir::limit::Limit;
 use rustc_parse::parser::{
     AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
     token_descr,
 };
+use rustc_session::Session;
 use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
 use rustc_session::parse::feature_err;
-use rustc_session::{Limit, Session};
 use rustc_span::hygiene::SyntaxContext;
 use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sym};
 use smallvec::SmallVec;
@@ -2529,6 +2530,7 @@ impl ExpansionConfig<'_> {
         ExpansionConfig {
             crate_name,
             features,
+            // FIXME should this limit be configurable?
             recursion_limit: Limit::new(1024),
             trace_mac: false,
             should_test: false,
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
index c4e068ad425..b4c1b61b9b5 100644
--- a/compiler/rustc_hir/src/attrs/data_structures.rs
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -14,6 +14,7 @@ pub use rustc_target::spec::SanitizerSet;
 use thin_vec::ThinVec;
 
 use crate::attrs::pretty_printing::PrintAttribute;
+use crate::limit::Limit;
 use crate::{DefaultBodyStability, PartialConstStability, RustcVersion, Stability};
 
 #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug, HashStable_Generic, PrintAttribute)]
@@ -566,7 +567,7 @@ pub enum AttributeKind {
     MayDangle(Span),
 
     /// Represents `#[move_size_limit]`
-    MoveSizeLimit { attr_span: Span, limit_span: Span, limit: usize },
+    MoveSizeLimit { attr_span: Span, limit_span: Span, limit: Limit },
 
     /// Represents `#[must_use]`.
     MustUse {
@@ -600,7 +601,7 @@ pub enum AttributeKind {
     Path(Symbol, Span),
 
     /// Represents `#[pattern_complexity_limit]`
-    PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: usize },
+    PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: Limit },
 
     /// Represents `#[pointee]`
     Pointee(Span),
@@ -618,7 +619,7 @@ pub enum AttributeKind {
     PubTransparent(Span),
 
     /// Represents [`#[recursion_limit]`](https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute)
-    RecursionLimit { attr_span: Span, limit_span: Span, limit: usize },
+    RecursionLimit { attr_span: Span, limit_span: Span, limit: Limit },
 
     /// Represents [`#[repr]`](https://doc.rust-lang.org/stable/reference/type-layout.html#representations).
     Repr { reprs: ThinVec<(ReprAttr, Span)>, first_span: Span },
@@ -671,7 +672,7 @@ pub enum AttributeKind {
     TypeConst(Span),
 
     /// Represents `#[type_length_limit]`
-    TypeLengthLimit { attr_span: Span, limit_span: Span, limit: usize },
+    TypeLengthLimit { attr_span: Span, limit_span: Span, limit: Limit },
 
     /// Represents `#[rustc_unsafe_specialization_marker]`.
     UnsafeSpecializationMarker(Span),
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
index 89ba1e0d7de..0e208be3497 100644
--- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -92,7 +92,7 @@ impl AttributeKind {
             TargetFeature { .. } => No,
             TrackCaller(..) => Yes,
             TypeConst(..) => Yes,
-            TypeLengthLimit { .. } => Yes,
+            TypeLengthLimit { .. } => No,
             UnsafeSpecializationMarker(..) => No,
             UnstableFeatureBound(..) => No,
             Used { .. } => No,
diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs
index a4c2beb689d..d13cd3fd1da 100644
--- a/compiler/rustc_hir/src/attrs/pretty_printing.rs
+++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs
@@ -9,6 +9,8 @@ use rustc_span::{ErrorGuaranteed, Ident, Span, Symbol};
 use rustc_target::spec::SanitizerSet;
 use thin_vec::ThinVec;
 
+use crate::limit::Limit;
+
 /// This trait is used to print attributes in `rustc_hir_pretty`.
 ///
 /// For structs and enums it can be derived using [`rustc_macros::PrintAttribute`].
@@ -146,7 +148,7 @@ macro_rules! print_tup {
 
 print_tup!(A B C D E F G H);
 print_skip!(Span, (), ErrorGuaranteed);
-print_disp!(u16, bool, NonZero<u32>, usize);
+print_disp!(u16, bool, NonZero<u32>, Limit);
 print_debug!(
     Symbol,
     Ident,
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index 78fc63753a2..eb630fc8018 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -25,6 +25,7 @@ mod hir;
 pub use rustc_hir_id::{self as hir_id, *};
 pub mod intravisit;
 pub mod lang_items;
+pub mod limit;
 pub mod lints;
 pub mod pat_util;
 mod stability;
diff --git a/compiler/rustc_hir/src/limit.rs b/compiler/rustc_hir/src/limit.rs
new file mode 100644
index 00000000000..7e6d23f51bd
--- /dev/null
+++ b/compiler/rustc_hir/src/limit.rs
@@ -0,0 +1,63 @@
+use std::fmt;
+use std::ops::{Div, Mul};
+
+use rustc_error_messages::{DiagArgValue, IntoDiagArg};
+use rustc_macros::{Decodable, Encodable, HashStable_Generic};
+
+/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against
+/// limits are consistent throughout the compiler.
+#[derive(Clone, Copy, Debug, HashStable_Generic, Encodable, Decodable)]
+pub struct Limit(pub usize);
+
+impl Limit {
+    /// Create a new limit from a `usize`.
+    pub fn new(value: usize) -> Self {
+        Limit(value)
+    }
+
+    /// Create a new unlimited limit.
+    pub fn unlimited() -> Self {
+        Limit(usize::MAX)
+    }
+
+    /// Check that `value` is within the limit. Ensures that the same comparisons are used
+    /// throughout the compiler, as mismatches can cause ICEs, see #72540.
+    #[inline]
+    pub fn value_within_limit(&self, value: usize) -> bool {
+        value <= self.0
+    }
+}
+
+impl From<usize> for Limit {
+    fn from(value: usize) -> Self {
+        Self::new(value)
+    }
+}
+
+impl fmt::Display for Limit {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl Div<usize> for Limit {
+    type Output = Limit;
+
+    fn div(self, rhs: usize) -> Self::Output {
+        Limit::new(self.0 / rhs)
+    }
+}
+
+impl Mul<usize> for Limit {
+    type Output = Limit;
+
+    fn mul(self, rhs: usize) -> Self::Output {
+        Limit::new(self.0 * rhs)
+    }
+}
+
+impl IntoDiagArg for Limit {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        self.to_string().into_diag_arg(&mut None)
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs
index e27e68d3662..e8237471e1b 100644
--- a/compiler/rustc_hir_analysis/src/autoderef.rs
+++ b/compiler/rustc_hir_analysis/src/autoderef.rs
@@ -1,7 +1,7 @@
+use rustc_hir::limit::Limit;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::PredicateObligations;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
-use rustc_session::Limit;
 use rustc_span::def_id::{LOCAL_CRATE, LocalDefId};
 use rustc_span::{ErrorGuaranteed, Span};
 use rustc_trait_selection::traits::ObligationCtxt;
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 6565ad7cf53..3b6367219b7 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -6,6 +6,7 @@ use rustc_errors::{
     Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
     MultiSpan,
 };
+use rustc_hir::limit::Limit;
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
 use rustc_span::{Ident, Span, Symbol};
@@ -557,7 +558,7 @@ pub(crate) struct AutoDerefReachedRecursionLimit<'a> {
     #[label]
     pub span: Span,
     pub ty: Ty<'a>,
-    pub suggested_limit: rustc_session::Limit,
+    pub suggested_limit: Limit,
     pub crate_name: Symbol,
 }
 
diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl
index 4b9c71d7172..d83b18cbc2d 100644
--- a/compiler/rustc_interface/messages.ftl
+++ b/compiler/rustc_interface/messages.ftl
@@ -30,10 +30,6 @@ interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag
 interface_input_file_would_be_overwritten =
     the input file "{$path}" would be overwritten by the generated executable
 
-interface_limit_invalid =
-    `limit` must be a non-negative integer
-    .label = {$error_str}
-
 interface_mixed_bin_crate =
     cannot mix `bin` crate type with others
 
diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs
index 6b39b4f1891..d1082eaf617 100644
--- a/compiler/rustc_interface/src/errors.rs
+++ b/compiler/rustc_interface/src/errors.rs
@@ -108,13 +108,3 @@ pub(crate) struct AbiRequiredTargetFeature<'a> {
     pub feature: &'a str,
     pub enabled: &'a str,
 }
-
-#[derive(Diagnostic)]
-#[diag(interface_limit_invalid)]
-pub(crate) struct LimitInvalid<'a> {
-    #[primary_span]
-    pub span: Span,
-    #[label]
-    pub value_span: Span,
-    pub error_str: &'a str,
-}
diff --git a/compiler/rustc_interface/src/limits.rs b/compiler/rustc_interface/src/limits.rs
index 8f01edec09f..10e58f32256 100644
--- a/compiler/rustc_interface/src/limits.rs
+++ b/compiler/rustc_interface/src/limits.rs
@@ -8,78 +8,32 @@
 //! Users can override these limits via an attribute on the crate like
 //! `#![recursion_limit="22"]`. This pass just looks for those attributes.
 
-use std::num::IntErrorKind;
-
-use rustc_ast::attr::AttributeExt;
-use rustc_middle::bug;
+use rustc_hir::attrs::AttributeKind;
+use rustc_hir::limit::Limit;
+use rustc_hir::{Attribute, find_attr};
 use rustc_middle::query::Providers;
-use rustc_session::{Limit, Limits, Session};
-use rustc_span::{Symbol, sym};
-
-use crate::errors::LimitInvalid;
+use rustc_session::Limits;
 
 pub(crate) fn provide(providers: &mut Providers) {
-    providers.limits = |tcx, ()| Limits {
-        recursion_limit: get_recursion_limit(tcx.hir_krate_attrs(), tcx.sess),
-        move_size_limit: get_limit(
-            tcx.hir_krate_attrs(),
-            tcx.sess,
-            sym::move_size_limit,
-            Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0)),
-        ),
-        type_length_limit: get_limit(
-            tcx.hir_krate_attrs(),
-            tcx.sess,
-            sym::type_length_limit,
-            Limit::new(2usize.pow(24)),
-        ),
-        pattern_complexity_limit: get_limit(
-            tcx.hir_krate_attrs(),
-            tcx.sess,
-            sym::pattern_complexity_limit,
-            Limit::unlimited(),
-        ),
+    providers.limits = |tcx, ()| {
+        let attrs = tcx.hir_krate_attrs();
+        Limits {
+            recursion_limit: get_recursion_limit(tcx.hir_krate_attrs()),
+            move_size_limit:
+                find_attr!(attrs, AttributeKind::MoveSizeLimit { limit, .. } => *limit)
+                    .unwrap_or(Limit::new(tcx.sess.opts.unstable_opts.move_size_limit.unwrap_or(0))),
+            type_length_limit:
+                find_attr!(attrs, AttributeKind::TypeLengthLimit { limit, .. } => *limit)
+                    .unwrap_or(Limit::new(2usize.pow(24))),
+            pattern_complexity_limit:
+                find_attr!(attrs, AttributeKind::PatternComplexityLimit { limit, .. } => *limit)
+                    .unwrap_or(Limit::unlimited()),
+        }
     }
 }
 
 // This one is separate because it must be read prior to macro expansion.
-pub(crate) fn get_recursion_limit(krate_attrs: &[impl AttributeExt], sess: &Session) -> Limit {
-    get_limit(krate_attrs, sess, sym::recursion_limit, Limit::new(128))
-}
-
-fn get_limit(
-    krate_attrs: &[impl AttributeExt],
-    sess: &Session,
-    name: Symbol,
-    default: Limit,
-) -> Limit {
-    for attr in krate_attrs {
-        if !attr.has_name(name) {
-            continue;
-        }
-
-        if let Some(sym) = attr.value_str() {
-            match sym.as_str().parse() {
-                Ok(n) => return Limit::new(n),
-                Err(e) => {
-                    let error_str = match e.kind() {
-                        IntErrorKind::PosOverflow => "`limit` is too large",
-                        IntErrorKind::Empty => "`limit` must be a non-negative integer",
-                        IntErrorKind::InvalidDigit => "not a valid integer",
-                        IntErrorKind::NegOverflow => {
-                            bug!("`limit` should never negatively overflow")
-                        }
-                        IntErrorKind::Zero => bug!("zero is a valid `limit`"),
-                        kind => bug!("unimplemented IntErrorKind variant: {:?}", kind),
-                    };
-                    sess.dcx().emit_err(LimitInvalid {
-                        span: attr.span(),
-                        value_span: attr.value_span().unwrap(),
-                        error_str,
-                    });
-                }
-            }
-        }
-    }
-    default
+pub(crate) fn get_recursion_limit(attrs: &[Attribute]) -> Limit {
+    find_attr!(attrs, AttributeKind::RecursionLimit { limit, .. } => *limit)
+        .unwrap_or(Limit::new(128))
 }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index ca8c10311fb..6d9751d7d4d 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -6,7 +6,7 @@ use std::sync::{Arc, LazyLock, OnceLock};
 use std::{env, fs, iter};
 
 use rustc_ast as ast;
-use rustc_attr_parsing::{AttributeParser, ShouldEmit, validate_attr};
+use rustc_attr_parsing::{AttributeParser, ShouldEmit};
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::jobserver::Proxy;
 use rustc_data_structures::steal::Steal;
@@ -19,6 +19,7 @@ use rustc_fs_util::try_canonicalize;
 use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def_id::{LOCAL_CRATE, StableCrateId, StableCrateIdMap};
 use rustc_hir::definitions::Definitions;
+use rustc_hir::limit::Limit;
 use rustc_incremental::setup_dep_graph;
 use rustc_lint::{BufferedEarlyLint, EarlyCheckNode, LintStore, unerased_lint_store};
 use rustc_metadata::EncodedMetadata;
@@ -30,12 +31,12 @@ use rustc_middle::util::Providers;
 use rustc_parse::{new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
 use rustc_passes::{abi_test, input_stats, layout_test};
 use rustc_resolve::{Resolver, ResolverOutputs};
+use rustc_session::Session;
 use rustc_session::config::{CrateType, Input, OutFileName, OutputFilenames, OutputType};
 use rustc_session::cstore::Untracked;
 use rustc_session::output::{collect_crate_types, filename_for_input};
 use rustc_session::parse::feature_err;
 use rustc_session::search_paths::PathKind;
-use rustc_session::{Limit, Session};
 use rustc_span::{
     DUMMY_SP, ErrorGuaranteed, ExpnKind, FileName, SourceFileHash, SourceFileHashAlgorithm, Span,
     Symbol, sym,
@@ -1246,7 +1247,8 @@ pub fn get_crate_name(sess: &Session, krate_attrs: &[ast::Attribute]) -> Symbol
     // in all code paths that require the crate name very early on, namely before
     // macro expansion.
 
-    let attr_crate_name = parse_crate_name(sess, krate_attrs, ShouldEmit::EarlyFatal);
+    let attr_crate_name =
+        parse_crate_name(sess, krate_attrs, ShouldEmit::EarlyFatal { also_emit_lints: true });
 
     let validate = |name, span| {
         rustc_session::output::validate_crate_name(sess, name, span);
@@ -1307,36 +1309,18 @@ pub(crate) fn parse_crate_name(
 }
 
 fn get_recursion_limit(krate_attrs: &[ast::Attribute], sess: &Session) -> Limit {
-    // We don't permit macro calls inside of the attribute (e.g., #![recursion_limit = `expand!()`])
-    // because that would require expanding this while in the middle of expansion, which needs to
-    // know the limit before expanding.
-    let _ = validate_and_find_value_str_builtin_attr(sym::recursion_limit, sess, krate_attrs);
-    crate::limits::get_recursion_limit(krate_attrs, sess)
-}
-
-/// Validate *all* occurrences of the given "[value-str]" built-in attribute and return the first find.
-///
-/// This validator is intended for built-in attributes whose value needs to be known very early
-/// during compilation (namely, before macro expansion) and it mainly exists to reject macro calls
-/// inside of the attributes, such as in `#![name = expand!()]`. Normal attribute validation happens
-/// during semantic analysis via [`TyCtxt::check_mod_attrs`] which happens *after* macro expansion
-/// when such macro calls (here: `expand`) have already been expanded and we can no longer check for
-/// their presence.
-///
-/// [value-str]: ast::Attribute::value_str
-fn validate_and_find_value_str_builtin_attr(
-    name: Symbol,
-    sess: &Session,
-    krate_attrs: &[ast::Attribute],
-) -> Option<(Symbol, Span)> {
-    let mut result = None;
-    // Validate *all* relevant attributes, not just the first occurrence.
-    for attr in ast::attr::filter_by_name(krate_attrs, name) {
-        let Some(value) = attr.value_str() else {
-            validate_attr::emit_fatal_malformed_builtin_attribute(&sess.psess, attr, name)
-        };
-        // Choose the first occurrence as our result.
-        result.get_or_insert((value, attr.span));
-    }
-    result
+    let attr = AttributeParser::parse_limited_should_emit(
+        sess,
+        &krate_attrs,
+        sym::recursion_limit,
+        DUMMY_SP,
+        rustc_ast::node_id::CRATE_NODE_ID,
+        None,
+        // errors are fatal here, but lints aren't.
+        // If things aren't fatal we continue, and will parse this again.
+        // That makes the same lint trigger again.
+        // So, no lints here to avoid duplicates.
+        ShouldEmit::EarlyFatal { also_emit_lints: false },
+    );
+    crate::limits::get_recursion_limit(attr.as_slice())
 }
diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs
index 7520bc262c6..dad402ec696 100644
--- a/compiler/rustc_middle/src/error.rs
+++ b/compiler/rustc_middle/src/error.rs
@@ -72,7 +72,7 @@ pub enum TypeMismatchReason {
 #[help]
 pub(crate) struct RecursionLimitReached<'tcx> {
     pub ty: Ty<'tcx>,
-    pub suggested_limit: rustc_session::Limit,
+    pub suggested_limit: rustc_hir::limit::Limit,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index de6fa4c344e..218ac2cfbc1 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -38,6 +38,7 @@ use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::definitions::{DefPathData, Definitions, DisambiguatorState};
 use rustc_hir::intravisit::VisitorExt;
 use rustc_hir::lang_items::LangItem;
+use rustc_hir::limit::Limit;
 use rustc_hir::{self as hir, Attribute, HirId, Node, TraitCandidate, find_attr};
 use rustc_index::IndexVec;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable};
@@ -45,10 +46,10 @@ use rustc_query_system::cache::WithDepNode;
 use rustc_query_system::dep_graph::DepNodeIndex;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
+use rustc_session::Session;
 use rustc_session::config::CrateType;
 use rustc_session::cstore::{CrateStoreDyn, Untracked};
 use rustc_session::lint::Lint;
-use rustc_session::{Limit, Session};
 use rustc_span::def_id::{CRATE_DEF_ID, DefPathHash, StableCrateId};
 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym};
 use rustc_type_ir::TyKind::*;
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 3f854038651..66542525d28 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -7,6 +7,7 @@ use std::path::PathBuf;
 use rustc_errors::pluralize;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
+use rustc_hir::limit::Limit;
 use rustc_macros::extension;
 pub use rustc_type_ir::error::ExpectedFound;
 
@@ -233,7 +234,7 @@ impl<'tcx> TyCtxt<'tcx> {
         loop {
             // Look for the longest properly trimmed path that still fits in length_limit.
             short = with_forced_trimmed_paths!({
-                let mut p = FmtPrinter::new_with_limit(self, ns, rustc_session::Limit(type_limit));
+                let mut p = FmtPrinter::new_with_limit(self, ns, Limit(type_limit));
                 self.lift(t)
                     .expect("could not lift for printing")
                     .print(&mut p)
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 74caee7336a..8101cabccdd 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -13,8 +13,8 @@ use rustc_hir::LangItem;
 use rustc_hir::def::{self, CtorKind, DefKind, Namespace};
 use rustc_hir::def_id::{DefIdMap, DefIdSet, LOCAL_CRATE, ModDefId};
 use rustc_hir::definitions::{DefKey, DefPathDataName};
+use rustc_hir::limit::Limit;
 use rustc_macros::{Lift, extension};
-use rustc_session::Limit;
 use rustc_session::cstore::{ExternCrate, ExternCrateSource};
 use rustc_span::{FileNameDisplayPreference, Ident, Symbol, kw, sym};
 use rustc_type_ir::{Upcast as _, elaborate};
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index a7d07adf78f..b8c99e83a8f 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -12,9 +12,9 @@ use rustc_hashes::Hash128;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
+use rustc_hir::limit::Limit;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_macros::{HashStable, TyDecodable, TyEncodable, extension};
-use rustc_session::Limit;
 use rustc_span::sym;
 use rustc_type_ir::solve::SizedTraitKind;
 use smallvec::{SmallVec, smallvec};
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index 7f9234d1dc8..25a9baffe58 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -2,9 +2,9 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::unord::UnordSet;
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::limit::Limit;
 use rustc_middle::mir::TerminatorKind;
 use rustc_middle::ty::{self, GenericArgsRef, InstanceKind, TyCtxt, TypeVisitableExt};
-use rustc_session::Limit;
 use rustc_span::sym;
 use tracing::{instrument, trace};
 
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index cffeb6f9807..3169f2d4e1a 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -217,6 +217,7 @@ use rustc_hir::attrs::InlineAttr;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::lang_items::LangItem;
+use rustc_hir::limit::Limit;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::mir::interpret::{AllocId, ErrorHandled, GlobalAlloc, Scalar};
 use rustc_middle::mir::mono::{CollectionMode, InstantiationMode, MonoItem};
@@ -231,7 +232,6 @@ use rustc_middle::ty::{
 };
 use rustc_middle::util::Providers;
 use rustc_middle::{bug, span_bug};
-use rustc_session::Limit;
 use rustc_session::config::{DebugInfo, EntryFnType};
 use rustc_span::source_map::{Spanned, dummy_spanned, respan};
 use rustc_span::{DUMMY_SP, Span};
diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs
index 7251ef478c6..0adf1b089b5 100644
--- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs
+++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs
@@ -1,10 +1,10 @@
 use rustc_abi::Size;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::def_id::DefId;
+use rustc_hir::limit::Limit;
 use rustc_middle::mir::visit::Visitor as MirVisitor;
 use rustc_middle::mir::{self, Location, traversal};
 use rustc_middle::ty::{self, AssocTag, Instance, Ty, TyCtxt, TypeFoldable};
-use rustc_session::Limit;
 use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
 use rustc_span::source_map::Spanned;
 use rustc_span::{Ident, Span, sym};
diff --git a/compiler/rustc_query_impl/Cargo.toml b/compiler/rustc_query_impl/Cargo.toml
index e5cceacf15d..257785b10c8 100644
--- a/compiler/rustc_query_impl/Cargo.toml
+++ b/compiler/rustc_query_impl/Cargo.toml
@@ -13,7 +13,6 @@ rustc_index = { path = "../rustc_index" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_serialize = { path = "../rustc_serialize" }
-rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index 55549cba737..4e601a6c594 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -9,6 +9,7 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::{DynSend, DynSync};
 use rustc_data_structures::unord::UnordMap;
 use rustc_hashes::Hash64;
+use rustc_hir::limit::Limit;
 use rustc_index::Idx;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::{
@@ -31,7 +32,6 @@ use rustc_query_system::query::{
 };
 use rustc_query_system::{QueryOverflow, QueryOverflowNote};
 use rustc_serialize::{Decodable, Encodable};
-use rustc_session::Limit;
 use rustc_span::def_id::LOCAL_CRATE;
 
 use crate::QueryConfigRestored;
diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs
index 5108ecaeea3..2778b24e774 100644
--- a/compiler/rustc_query_system/src/error.rs
+++ b/compiler/rustc_query_system/src/error.rs
@@ -1,6 +1,6 @@
 use rustc_errors::codes::*;
+use rustc_hir::limit::Limit;
 use rustc_macros::{Diagnostic, Subdiagnostic};
-use rustc_session::Limit;
 use rustc_span::{Span, Symbol};
 
 #[derive(Subdiagnostic)]
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 96767d05439..3525c7c1d1a 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -1,10 +1,9 @@
 use std::any::Any;
-use std::ops::{Div, Mul};
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
 use std::sync::Arc;
 use std::sync::atomic::AtomicBool;
-use std::{env, fmt, io};
+use std::{env, io};
 
 use rand::{RngCore, rng};
 use rustc_ast::NodeId;
@@ -25,6 +24,7 @@ use rustc_errors::{
     Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic, ErrorGuaranteed, FatalAbort,
     LintEmitter, TerminalUrl, fallback_fluent_bundle,
 };
+use rustc_hir::limit::Limit;
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
 use rustc_span::edition::Edition;
@@ -62,64 +62,6 @@ pub enum CtfeBacktrace {
     Immediate,
 }
 
-/// New-type wrapper around `usize` for representing limits. Ensures that comparisons against
-/// limits are consistent throughout the compiler.
-#[derive(Clone, Copy, Debug, HashStable_Generic)]
-pub struct Limit(pub usize);
-
-impl Limit {
-    /// Create a new limit from a `usize`.
-    pub fn new(value: usize) -> Self {
-        Limit(value)
-    }
-
-    /// Create a new unlimited limit.
-    pub fn unlimited() -> Self {
-        Limit(usize::MAX)
-    }
-
-    /// Check that `value` is within the limit. Ensures that the same comparisons are used
-    /// throughout the compiler, as mismatches can cause ICEs, see #72540.
-    #[inline]
-    pub fn value_within_limit(&self, value: usize) -> bool {
-        value <= self.0
-    }
-}
-
-impl From<usize> for Limit {
-    fn from(value: usize) -> Self {
-        Self::new(value)
-    }
-}
-
-impl fmt::Display for Limit {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
-
-impl Div<usize> for Limit {
-    type Output = Limit;
-
-    fn div(self, rhs: usize) -> Self::Output {
-        Limit::new(self.0 / rhs)
-    }
-}
-
-impl Mul<usize> for Limit {
-    type Output = Limit;
-
-    fn mul(self, rhs: usize) -> Self::Output {
-        Limit::new(self.0 * rhs)
-    }
-}
-
-impl rustc_errors::IntoDiagArg for Limit {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
-        self.to_string().into_diag_arg(&mut None)
-    }
-}
-
 #[derive(Clone, Copy, Debug, HashStable_Generic)]
 pub struct Limits {
     /// The maximum recursion limit for potentially infinitely recursive
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
index 4f1f5c330e5..a0876d8fe75 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
@@ -3,10 +3,10 @@ use std::fmt;
 use rustc_errors::{Diag, E0275, EmissionGuarantee, ErrorGuaranteed, struct_span_code_err};
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_hir::limit::Limit;
 use rustc_infer::traits::{Obligation, PredicateObligation};
 use rustc_middle::ty::print::{FmtPrinter, Print};
 use rustc_middle::ty::{self, TyCtxt, Upcast};
-use rustc_session::Limit;
 use rustc_span::Span;
 use tracing::debug;
 
@@ -67,7 +67,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 // We don't need to save the type to a file, we will be talking about this type already
                 // in a separate note when we explain the obligation, so it will be available that way.
                 let mut p: FmtPrinter<'_, '_> =
-                    FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6));
+                    FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, Limit(6));
                 value.print(&mut p).unwrap();
                 p.into_buffer()
             } else {
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index 13d56889bd1..0ef435b1a0e 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -2,11 +2,11 @@
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def_id::DefId;
+use rustc_hir::limit::Limit;
 use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::util::{AlwaysRequiresDrop, needs_drop_components};
 use rustc_middle::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt};
-use rustc_session::Limit;
 use rustc_span::sym;
 use tracing::{debug, instrument};
 
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs
index e5dae4c0069..c2653dd82a9 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.rs
@@ -917,50 +917,50 @@ mod no_builtins {
 
 #[recursion_limit="0200"]
 //~^ WARN crate-level attribute should be an inner attribute
-//~| HELP add a `!`
 mod recursion_limit {
+    //~^ NOTE This attribute does not have an `!`, which means it is applied to this module
     mod inner { #![recursion_limit="0200"] }
-//~^ WARN crate-level attribute should be in the root module
+//~^ WARN the `#![recursion_limit]` attribute can only be used at the crate root
 
     #[recursion_limit="0200"] fn f() { }
     //~^ WARN crate-level attribute should be an inner attribute
-    //~| HELP add a `!`
+    //~| NOTE This attribute does not have an `!`, which means it is applied to this function
 
     #[recursion_limit="0200"] struct S;
     //~^ WARN crate-level attribute should be an inner attribute
-    //~| HELP add a `!`
+    //~| NOTE This attribute does not have an `!`, which means it is applied to this struct
 
     #[recursion_limit="0200"] type T = S;
     //~^ WARN crate-level attribute should be an inner attribute
-    //~| HELP add a `!`
+    //~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
 
     #[recursion_limit="0200"] impl S { }
     //~^ WARN crate-level attribute should be an inner attribute
-    //~| HELP add a `!`
+    //~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block
 }
 
 #[type_length_limit="0100"]
 //~^ WARN crate-level attribute should be an inner attribute
-//~| HELP add a `!`
 mod type_length_limit {
+    //~^ NOTE This attribute does not have an `!`, which means it is applied to this module
     mod inner { #![type_length_limit="0100"] }
-//~^ WARN crate-level attribute should be in the root module
+//~^ WARN the `#![type_length_limit]` attribute can only be used at the crate root
 
     #[type_length_limit="0100"] fn f() { }
     //~^ WARN crate-level attribute should be an inner attribute
-    //~| HELP add a `!`
+    //~| NOTE This attribute does not have an `!`, which means it is applied to this function
 
     #[type_length_limit="0100"] struct S;
     //~^ WARN crate-level attribute should be an inner attribute
-    //~| HELP add a `!`
+    //~| NOTE This attribute does not have an `!`, which means it is applied to this struct
 
     #[type_length_limit="0100"] type T = S;
     //~^ WARN crate-level attribute should be an inner attribute
-    //~| HELP add a `!`
+    //~| NOTE This attribute does not have an `!`, which means it is applied to this type alias
 
     #[type_length_limit="0100"] impl S { }
     //~^ WARN crate-level attribute should be an inner attribute
-    //~| HELP add a `!`
+    //~| NOTE This attribute does not have an `!`, which means it is applied to this implementation block
 }
 
 fn main() {}
diff --git a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
index ef74a00e5a1..4a3520972bf 100644
--- a/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
+++ b/tests/ui/feature-gates/issue-43106-gating-of-builtin-attrs.stderr
@@ -291,28 +291,6 @@ help: add a `!`
 LL | #![no_builtins]
    |  +
 
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:918:1
-   |
-LL | #[recursion_limit="0200"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL | #![recursion_limit="0200"]
-   |  +
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:942:1
-   |
-LL | #[type_length_limit="0100"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL | #![type_length_limit="0100"]
-   |  +
-
 warning: attribute should be applied to an `extern` block with non-Rust ABI
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:72:1
    |
@@ -757,106 +735,6 @@ help: add a `!`
 LL |     #![no_builtins] impl S { }
    |      +
 
-warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:922:17
-   |
-LL |     mod inner { #![recursion_limit="0200"] }
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:5
-   |
-LL |     #[recursion_limit="0200"] fn f() { }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL |     #![recursion_limit="0200"] fn f() { }
-   |      +
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:5
-   |
-LL |     #[recursion_limit="0200"] struct S;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL |     #![recursion_limit="0200"] struct S;
-   |      +
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:5
-   |
-LL |     #[recursion_limit="0200"] type T = S;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL |     #![recursion_limit="0200"] type T = S;
-   |      +
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:5
-   |
-LL |     #[recursion_limit="0200"] impl S { }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL |     #![recursion_limit="0200"] impl S { }
-   |      +
-
-warning: crate-level attribute should be in the root module
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:946:17
-   |
-LL |     mod inner { #![type_length_limit="0100"] }
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:5
-   |
-LL |     #[type_length_limit="0100"] fn f() { }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL |     #![type_length_limit="0100"] fn f() { }
-   |      +
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:5
-   |
-LL |     #[type_length_limit="0100"] struct S;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL |     #![type_length_limit="0100"] struct S;
-   |      +
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:5
-   |
-LL |     #[type_length_limit="0100"] type T = S;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL |     #![type_length_limit="0100"] type T = S;
-   |      +
-
-warning: crate-level attribute should be an inner attribute
-  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:5
-   |
-LL |     #[type_length_limit="0100"] impl S { }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: add a `!`
-   |
-LL |     #![type_length_limit="0100"] impl S { }
-   |      +
-
 warning: `#[macro_use]` attribute cannot be used on functions
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:191:5
    |
@@ -1476,6 +1354,146 @@ note: This attribute does not have an `!`, which means it is applied to this imp
 LL |     #[crate_name = "0900"] impl S { }
    |                            ^^^^^^^^^^
 
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:918:1
+   |
+LL | #[recursion_limit="0200"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this module
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:920:1
+   |
+LL | / mod recursion_limit {
+LL | |
+LL | |     mod inner { #![recursion_limit="0200"] }
+...  |
+LL | | }
+   | |_^
+
+warning: the `#![recursion_limit]` attribute can only be used at the crate root
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:922:17
+   |
+LL |     mod inner { #![recursion_limit="0200"] }
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:5
+   |
+LL |     #[recursion_limit="0200"] fn f() { }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this function
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:925:31
+   |
+LL |     #[recursion_limit="0200"] fn f() { }
+   |                               ^^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:5
+   |
+LL |     #[recursion_limit="0200"] struct S;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this struct
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:929:31
+   |
+LL |     #[recursion_limit="0200"] struct S;
+   |                               ^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:5
+   |
+LL |     #[recursion_limit="0200"] type T = S;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this type alias
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:933:31
+   |
+LL |     #[recursion_limit="0200"] type T = S;
+   |                               ^^^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:5
+   |
+LL |     #[recursion_limit="0200"] impl S { }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this implementation block
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:937:31
+   |
+LL |     #[recursion_limit="0200"] impl S { }
+   |                               ^^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:942:1
+   |
+LL | #[type_length_limit="0100"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this module
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:944:1
+   |
+LL | / mod type_length_limit {
+LL | |
+LL | |     mod inner { #![type_length_limit="0100"] }
+...  |
+LL | | }
+   | |_^
+
+warning: the `#![type_length_limit]` attribute can only be used at the crate root
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:946:17
+   |
+LL |     mod inner { #![type_length_limit="0100"] }
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:5
+   |
+LL |     #[type_length_limit="0100"] fn f() { }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this function
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:949:33
+   |
+LL |     #[type_length_limit="0100"] fn f() { }
+   |                                 ^^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:5
+   |
+LL |     #[type_length_limit="0100"] struct S;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this struct
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:953:33
+   |
+LL |     #[type_length_limit="0100"] struct S;
+   |                                 ^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:5
+   |
+LL |     #[type_length_limit="0100"] type T = S;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this type alias
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:957:33
+   |
+LL |     #[type_length_limit="0100"] type T = S;
+   |                                 ^^^^^^^^^^^
+
+warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![type_length_limit]`
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:5
+   |
+LL |     #[type_length_limit="0100"] impl S { }
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this implementation block
+  --> $DIR/issue-43106-gating-of-builtin-attrs.rs:961:33
+   |
+LL |     #[type_length_limit="0100"] impl S { }
+   |                                 ^^^^^^^^^^
+
 warning: `#[should_panic]` attribute cannot be used on crates
   --> $DIR/issue-43106-gating-of-builtin-attrs.rs:50:1
    |
diff --git a/tests/ui/lint/unused/unused-attr-duplicate.stderr b/tests/ui/lint/unused/unused-attr-duplicate.stderr
index 203211d0d56..076a08ac6f2 100644
--- a/tests/ui/lint/unused/unused-attr-duplicate.stderr
+++ b/tests/ui/lint/unused/unused-attr-duplicate.stderr
@@ -29,32 +29,6 @@ LL | #[no_link]
    | ^^^^^^^^^^
 
 error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:21:1
-   |
-LL | #![recursion_limit = "256"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:20:1
-   |
-LL | #![recursion_limit = "128"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-
-error: unused attribute
-  --> $DIR/unused-attr-duplicate.rs:24:1
-   |
-LL | #![type_length_limit = "1"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
-   |
-note: attribute also specified here
-  --> $DIR/unused-attr-duplicate.rs:23:1
-   |
-LL | #![type_length_limit = "1048576"]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
-
-error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:27:1
    |
 LL | #![no_std]
@@ -305,6 +279,32 @@ LL | #![crate_name = "unused_attr_duplicate"]
    = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:21:1
+   |
+LL | #![recursion_limit = "256"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:20:1
+   |
+LL | #![recursion_limit = "128"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
+error: unused attribute
+  --> $DIR/unused-attr-duplicate.rs:24:1
+   |
+LL | #![type_length_limit = "1"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
+   |
+note: attribute also specified here
+  --> $DIR/unused-attr-duplicate.rs:23:1
+   |
+LL | #![type_length_limit = "1048576"]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+
+error: unused attribute
   --> $DIR/unused-attr-duplicate.rs:29:1
    |
 LL | #![no_implicit_prelude]
diff --git a/tests/ui/lint/unused/unused-attr-macro-rules.stderr b/tests/ui/lint/unused/unused-attr-macro-rules.stderr
index af64be8f6e9..0c6825026ed 100644
--- a/tests/ui/lint/unused/unused-attr-macro-rules.stderr
+++ b/tests/ui/lint/unused/unused-attr-macro-rules.stderr
@@ -1,19 +1,3 @@
-error: crate-level attribute should be an inner attribute
-  --> $DIR/unused-attr-macro-rules.rs:11:1
-   |
-LL | #[recursion_limit="1"]
-   | ^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> $DIR/unused-attr-macro-rules.rs:1:9
-   |
-LL | #![deny(unused_attributes)]
-   |         ^^^^^^^^^^^^^^^^^
-help: add a `!`
-   |
-LL | #![recursion_limit="1"]
-   |  +
-
 error: `#[macro_use]` attribute cannot be used on macro defs
   --> $DIR/unused-attr-macro-rules.rs:7:1
    |
@@ -22,6 +6,11 @@ LL | #[macro_use]
    |
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = help: `#[macro_use]` can be applied to modules, extern crates, and crates
+note: the lint level is defined here
+  --> $DIR/unused-attr-macro-rules.rs:1:9
+   |
+LL | #![deny(unused_attributes)]
+   |         ^^^^^^^^^^^^^^^^^
 
 error: `#[path]` attribute cannot be used on macro defs
   --> $DIR/unused-attr-macro-rules.rs:9:1
@@ -32,5 +21,19 @@ LL | #[path="foo"]
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = help: `#[path]` can only be applied to modules
 
+error: crate-level attribute should be an inner attribute: add an exclamation mark: `#![recursion_limit]`
+  --> $DIR/unused-attr-macro-rules.rs:11:1
+   |
+LL | #[recursion_limit="1"]
+   | ^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: This attribute does not have an `!`, which means it is applied to this macro def
+  --> $DIR/unused-attr-macro-rules.rs:12:1
+   |
+LL | / macro_rules! foo {
+LL | |     () => {};
+LL | | }
+   | |_^
+
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/recursion_limit/empty.rs b/tests/ui/recursion_limit/empty.rs
index 5987fa2f881..a901267d847 100644
--- a/tests/ui/recursion_limit/empty.rs
+++ b/tests/ui/recursion_limit/empty.rs
@@ -1,9 +1,7 @@
 // Test the parse error for an empty recursion_limit
 
-#![recursion_limit = ""] //~ ERROR `limit` must be a non-negative integer
-                         //~| NOTE `limit` must be a non-negative integer
-                         //~| ERROR `limit` must be a non-negative integer
-                         //~| NOTE `limit` must be a non-negative integer
-                         //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+#![recursion_limit = ""]
+//~^ ERROR `limit` must be a non-negative integer
+//~| NOTE `limit` must be a non-negative integer
 
 fn main() {}
diff --git a/tests/ui/recursion_limit/empty.stderr b/tests/ui/recursion_limit/empty.stderr
index 2f730677507..9afe9e6db56 100644
--- a/tests/ui/recursion_limit/empty.stderr
+++ b/tests/ui/recursion_limit/empty.stderr
@@ -6,15 +6,5 @@ LL | #![recursion_limit = ""]
    |                      |
    |                      `limit` must be a non-negative integer
 
-error: `limit` must be a non-negative integer
-  --> $DIR/empty.rs:3:1
-   |
-LL | #![recursion_limit = ""]
-   | ^^^^^^^^^^^^^^^^^^^^^--^
-   |                      |
-   |                      `limit` must be a non-negative integer
-   |
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/recursion_limit/invalid_digit.rs b/tests/ui/recursion_limit/invalid_digit.rs
index 79d8f3708ba..1d0e6a18227 100644
--- a/tests/ui/recursion_limit/invalid_digit.rs
+++ b/tests/ui/recursion_limit/invalid_digit.rs
@@ -1,8 +1,6 @@
 // Test the parse error for an invalid digit in recursion_limit
 
-#![recursion_limit = "-100"] //~ ERROR `limit` must be a non-negative integer
-                             //~| NOTE not a valid integer
-                             //~| ERROR `limit` must be a non-negative integer
-                             //~| NOTE not a valid integer
-                             //~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
+#![recursion_limit = "-100"]
+//~^ ERROR `limit` must be a non-negative integer
+//~| NOTE not a valid integer
 fn main() {}
diff --git a/tests/ui/recursion_limit/invalid_digit.stderr b/tests/ui/recursion_limit/invalid_digit.stderr
index 4fda3039032..9c2d1422df2 100644
--- a/tests/ui/recursion_limit/invalid_digit.stderr
+++ b/tests/ui/recursion_limit/invalid_digit.stderr
@@ -6,15 +6,5 @@ LL | #![recursion_limit = "-100"]
    |                      |
    |                      not a valid integer
 
-error: `limit` must be a non-negative integer
-  --> $DIR/invalid_digit.rs:3:1
-   |
-LL | #![recursion_limit = "-100"]
-   | ^^^^^^^^^^^^^^^^^^^^^------^
-   |                      |
-   |                      not a valid integer
-   |
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/recursion_limit/invalid_digit_type.stderr b/tests/ui/recursion_limit/invalid_digit_type.stderr
index a122262f1df..489e8bd82c2 100644
--- a/tests/ui/recursion_limit/invalid_digit_type.stderr
+++ b/tests/ui/recursion_limit/invalid_digit_type.stderr
@@ -1,10 +1,14 @@
-error: malformed `recursion_limit` attribute input
+error[E0539]: malformed `recursion_limit` attribute input
   --> $DIR/invalid_digit_type.rs:1:1
    |
 LL | #![recursion_limit = 123]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]`
+   | ^^^^^^^^^^^^^^^^^^^^^---^
+   | |                    |
+   | |                    expected a string literal here
+   | help: must be of the form: `#![recursion_limit = "N"]`
    |
    = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute>
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0539`.
diff --git a/tests/ui/recursion_limit/invalid_macro.rs b/tests/ui/recursion_limit/invalid_macro.rs
index 7db67a8d162..eae348523db 100644
--- a/tests/ui/recursion_limit/invalid_macro.rs
+++ b/tests/ui/recursion_limit/invalid_macro.rs
@@ -1,4 +1,4 @@
-#![recursion_limit = foo!()] //~ ERROR malformed `recursion_limit` attribute
+#![recursion_limit = foo!()] //~ ERROR attribute value must be a literal
 
 macro_rules! foo {
     () => {"128"};
diff --git a/tests/ui/recursion_limit/invalid_macro.stderr b/tests/ui/recursion_limit/invalid_macro.stderr
index b4dbc9fcb13..de1df3e9a35 100644
--- a/tests/ui/recursion_limit/invalid_macro.stderr
+++ b/tests/ui/recursion_limit/invalid_macro.stderr
@@ -1,10 +1,8 @@
-error: malformed `recursion_limit` attribute input
-  --> $DIR/invalid_macro.rs:1:1
+error: attribute value must be a literal
+  --> $DIR/invalid_macro.rs:1:22
    |
 LL | #![recursion_limit = foo!()]
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#![recursion_limit = "N"]`
-   |
-   = note: for more information, visit <https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute>
+   |                      ^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/recursion_limit/no-value.stderr b/tests/ui/recursion_limit/no-value.stderr
index 4a0ad04f271..eafc50bafb4 100644
--- a/tests/ui/recursion_limit/no-value.stderr
+++ b/tests/ui/recursion_limit/no-value.stderr
@@ -1,4 +1,4 @@
-error: malformed `recursion_limit` attribute input
+error[E0539]: malformed `recursion_limit` attribute input
   --> $DIR/no-value.rs:3:1
    |
 LL | #![recursion_limit]
@@ -8,3 +8,4 @@ LL | #![recursion_limit]
 
 error: aborting due to 1 previous error
 
+For more information about this error, try `rustc --explain E0539`.
diff --git a/tests/ui/recursion_limit/overflow.rs b/tests/ui/recursion_limit/overflow.rs
index 7cd1d572e09..24cb8e286a8 100644
--- a/tests/ui/recursion_limit/overflow.rs
+++ b/tests/ui/recursion_limit/overflow.rs
@@ -3,8 +3,5 @@
 #![recursion_limit = "999999999999999999999999"]
 //~^ ERROR `limit` must be a non-negative integer
 //~| NOTE `limit` is too large
-//~| ERROR `limit` must be a non-negative integer
-//~| NOTE `limit` is too large
-//~| NOTE duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
 
 fn main() {}
diff --git a/tests/ui/recursion_limit/overflow.stderr b/tests/ui/recursion_limit/overflow.stderr
index 6057177deb2..5a8548b7c7f 100644
--- a/tests/ui/recursion_limit/overflow.stderr
+++ b/tests/ui/recursion_limit/overflow.stderr
@@ -6,15 +6,5 @@ LL | #![recursion_limit = "999999999999999999999999"]
    |                      |
    |                      `limit` is too large
 
-error: `limit` must be a non-negative integer
-  --> $DIR/overflow.rs:3:1
-   |
-LL | #![recursion_limit = "999999999999999999999999"]
-   | ^^^^^^^^^^^^^^^^^^^^^--------------------------^
-   |                      |
-   |                      `limit` is too large
-   |
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error