about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/token.rs22
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs2
-rw-r--r--compiler/rustc_ast_passes/messages.ftl4
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs6
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs10
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs17
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs127
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/inline.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/link_attrs.rs20
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/prototype.rs4
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/test_attrs.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/traits.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/attributes/transparency.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs15
-rw-r--r--compiler/rustc_attr_parsing/src/session_diagnostics.rs14
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs12
-rw-r--r--compiler/rustc_data_structures/src/lib.rs1
-rw-r--r--compiler/rustc_data_structures/src/union_find.rs (renamed from compiler/rustc_mir_transform/src/coverage/counters/union_find.rs)10
-rw-r--r--compiler/rustc_data_structures/src/union_find/tests.rs (renamed from compiler/rustc_mir_transform/src/coverage/counters/union_find/tests.rs)0
-rw-r--r--compiler/rustc_errors/Cargo.toml1
-rw-r--r--compiler/rustc_errors/src/decorate_diag.rs85
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs12
-rw-r--r--compiler/rustc_errors/src/lib.rs4
-rw-r--r--compiler/rustc_expand/src/base.rs4
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs16
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs4
-rw-r--r--compiler/rustc_feature/src/unstable.rs3
-rw-r--r--compiler/rustc_hir/src/attrs/data_structures.rs5
-rw-r--r--compiler/rustc_hir/src/attrs/encode_cross_crate.rs2
-rw-r--r--compiler/rustc_hir/src/lang_items.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs123
-rw-r--r--compiler/rustc_interface/src/util.rs3
-rw-r--r--compiler/rustc_lexer/src/lib.rs4
-rw-r--r--compiler/rustc_lint/messages.ftl25
-rw-r--r--compiler/rustc_lint/src/context.rs4
-rw-r--r--compiler/rustc_lint/src/early.rs10
-rw-r--r--compiler/rustc_lint/src/early/diagnostics.rs33
-rw-r--r--compiler/rustc_lint/src/lib.rs5
-rw-r--r--compiler/rustc_lint/src/lints.rs101
-rw-r--r--compiler/rustc_lint_defs/Cargo.toml1
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs70
-rw-r--r--compiler/rustc_middle/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs16
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs4
-rw-r--r--compiler/rustc_middle/src/mir/statement.rs36
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs3
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs4
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs38
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs5
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/mod.rs17
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters.rs1
-rw-r--r--compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs3
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs62
-rw-r--r--compiler/rustc_mir_transform/src/ref_prop.rs39
-rw-r--r--compiler/rustc_parse/messages.ftl19
-rw-r--r--compiler/rustc_parse/src/errors.rs81
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs18
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs2
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs30
-rw-r--r--compiler/rustc_passes/src/check_attr.rs2
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs10
-rw-r--r--compiler/rustc_resolve/src/ident.rs59
-rw-r--r--compiler/rustc_resolve/src/imports.rs2
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs35
-rw-r--r--compiler/rustc_resolve/src/lib.rs31
-rw-r--r--compiler/rustc_resolve/src/macros.rs8
-rw-r--r--compiler/rustc_session/messages.ftl4
-rw-r--r--compiler/rustc_session/src/config/cfg.rs5
-rw-r--r--compiler/rustc_session/src/errors.rs12
-rw-r--r--compiler/rustc_session/src/parse.rs14
-rw-r--r--compiler/rustc_span/src/symbol.rs77
-rw-r--r--compiler/rustc_target/src/spec/mod.rs1
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_be_unknown_hermit.rs25
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs47
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs96
79 files changed, 857 insertions, 759 deletions
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index fc816f2cb79..ea98bebd305 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -7,6 +7,7 @@ pub use NtPatKind::*;
 pub use TokenKind::*;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::edition::Edition;
+use rustc_span::symbol::IdentPrintMode;
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, kw, sym};
 #[allow(clippy::useless_attribute)] // FIXME: following use of `hidden_glob_reexports` incorrectly triggers `useless_attribute` lint.
 #[allow(hidden_glob_reexports)]
@@ -344,15 +345,24 @@ pub enum IdentIsRaw {
     Yes,
 }
 
-impl From<bool> for IdentIsRaw {
-    fn from(b: bool) -> Self {
-        if b { Self::Yes } else { Self::No }
+impl IdentIsRaw {
+    pub fn to_print_mode_ident(self) -> IdentPrintMode {
+        match self {
+            IdentIsRaw::No => IdentPrintMode::Normal,
+            IdentIsRaw::Yes => IdentPrintMode::RawIdent,
+        }
+    }
+    pub fn to_print_mode_lifetime(self) -> IdentPrintMode {
+        match self {
+            IdentIsRaw::No => IdentPrintMode::Normal,
+            IdentIsRaw::Yes => IdentPrintMode::RawLifetime,
+        }
     }
 }
 
-impl From<IdentIsRaw> for bool {
-    fn from(is_raw: IdentIsRaw) -> bool {
-        matches!(is_raw, IdentIsRaw::Yes)
+impl From<bool> for IdentIsRaw {
+    fn from(b: bool) -> Self {
+        if b { Self::Yes } else { Self::No }
     }
 }
 
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index cd0f9f2403e..bb559bd8921 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1596,7 +1596,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let safety = self.lower_safety(h.safety, default_safety);
 
         // Treat safe `#[target_feature]` functions as unsafe, but also remember that we did so.
-        let safety = if find_attr!(attrs, AttributeKind::TargetFeature { .. })
+        let safety = if find_attr!(attrs, AttributeKind::TargetFeature { was_forced: false, .. })
             && safety.is_safe()
             && !self.tcx.sess.target.is_like_wasm
         {
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index c0679c1b8ff..a95f1443968 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -113,6 +113,10 @@ ast_passes_extern_without_abi = `extern` declarations without an explicit ABI ar
     .suggestion = specify an ABI
     .help = prior to Rust 2024, a default ABI was inferred
 
+ast_passes_extern_without_abi_sugg = `extern` declarations without an explicit ABI are deprecated
+    .label = ABI should be specified here
+    .suggestion = explicitly specify the {$default_abi} ABI
+
 ast_passes_feature_on_non_nightly = `#![feature]` may not be used on the {$channel} release channel
     .suggestion = remove the attribute
     .stable_since = the feature `{$name}` has been stable since `{$since}` and no longer requires an attribute to enable
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 508eb083672..6133cc3548b 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -27,14 +27,14 @@ use rustc_ast::*;
 use rustc_ast_pretty::pprust::{self, State};
 use rustc_attr_parsing::validate_attr;
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::DiagCtxtHandle;
+use rustc_errors::{DiagCtxtHandle, LintBuffer};
 use rustc_feature::Features;
 use rustc_session::Session;
+use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::{
     DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN,
     PATTERNS_IN_FNS_WITHOUT_BODY,
 };
-use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
 use rustc_span::{Ident, Span, kw, sym};
 use rustc_target::spec::{AbiMap, AbiMapping};
 use thin_vec::thin_vec;
@@ -876,7 +876,7 @@ impl<'a> AstValidator<'a> {
                 MISSING_ABI,
                 id,
                 span,
-                BuiltinLintDiag::MissingAbi(span, ExternAbi::FALLBACK),
+                errors::MissingAbiSugg { span, default_abi: ExternAbi::FALLBACK },
             )
         }
     }
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index b9b2d271954..ae8f056cb4e 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -4,7 +4,7 @@ use rustc_abi::ExternAbi;
 use rustc_ast::ParamKindOrd;
 use rustc_errors::codes::*;
 use rustc_errors::{Applicability, Diag, EmissionGuarantee, Subdiagnostic};
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_span::{Ident, Span, Symbol};
 
 use crate::fluent_generated as fluent;
@@ -815,6 +815,14 @@ pub(crate) struct MissingAbi {
     pub span: Span,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(ast_passes_extern_without_abi_sugg)]
+pub(crate) struct MissingAbiSugg {
+    #[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")]
+    pub span: Span,
+    pub default_abi: ExternAbi,
+}
+
 #[derive(Diagnostic)]
 #[diag(ast_passes_abi_custom_safe_foreign_function)]
 pub(crate) struct AbiCustomSafeForeignFunction {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 85f76036df7..a056ce3e29d 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -10,7 +10,7 @@ use std::borrow::Cow;
 use std::sync::Arc;
 
 use rustc_ast::attr::AttrIdGenerator;
-use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
+use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{Spacing, TokenStream, TokenTree};
 use rustc_ast::util::classify;
 use rustc_ast::util::comments::{Comment, CommentStyle};
@@ -441,7 +441,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
     fn print_generic_args(&mut self, args: &ast::GenericArgs, colons_before_params: bool);
 
     fn print_ident(&mut self, ident: Ident) {
-        self.word(IdentPrinter::for_ast_ident(ident, ident.is_raw_guess()).to_string());
+        self.word(IdentPrinter::for_ast_ident(ident, ident.guess_print_mode()).to_string());
         self.ann_post(ident)
     }
 
@@ -1015,17 +1015,16 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
 
             /* Name components */
             token::Ident(name, is_raw) => {
-                IdentPrinter::new(name, is_raw.into(), convert_dollar_crate).to_string().into()
+                IdentPrinter::new(name, is_raw.to_print_mode_ident(), convert_dollar_crate)
+                    .to_string()
+                    .into()
             }
             token::NtIdent(ident, is_raw) => {
-                IdentPrinter::for_ast_ident(ident, is_raw.into()).to_string().into()
+                IdentPrinter::for_ast_ident(ident, is_raw.to_print_mode_ident()).to_string().into()
             }
 
-            token::Lifetime(name, IdentIsRaw::No)
-            | token::NtLifetime(Ident { name, .. }, IdentIsRaw::No) => name.to_string().into(),
-            token::Lifetime(name, IdentIsRaw::Yes)
-            | token::NtLifetime(Ident { name, .. }, IdentIsRaw::Yes) => {
-                format!("'r#{}", &name.as_str()[1..]).into()
+            token::Lifetime(name, is_raw) | token::NtLifetime(Ident { name, .. }, is_raw) => {
+                IdentPrinter::new(name, is_raw.to_print_mode_lifetime(), None).to_string().into()
             }
 
             /* Other */
diff --git a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
index 843e411d25b..91053811a0b 100644
--- a/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/codegen_attrs.rs
@@ -35,7 +35,7 @@ impl<S: Stage> SingleAttributeParser<S> for OptimizeParser {
             Some(sym::speed) => OptimizeAttr::Speed,
             Some(sym::none) => OptimizeAttr::DoNotOptimize,
             _ => {
-                cx.expected_specific_argument(single.span(), vec!["size", "speed", "none"]);
+                cx.expected_specific_argument(single.span(), &[sym::size, sym::speed, sym::none]);
                 OptimizeAttr::Default
             }
         };
@@ -82,7 +82,7 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
 
     fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
         let Some(args) = args.list() else {
-            cx.expected_specific_argument_and_list(cx.attr_span, vec!["on", "off"]);
+            cx.expected_specific_argument_and_list(cx.attr_span, &[sym::on, sym::off]);
             return None;
         };
 
@@ -91,7 +91,8 @@ impl<S: Stage> SingleAttributeParser<S> for CoverageParser {
             return None;
         };
 
-        let fail_incorrect_argument = |span| cx.expected_specific_argument(span, vec!["on", "off"]);
+        let fail_incorrect_argument =
+            |span| cx.expected_specific_argument(span, &[sym::on, sym::off]);
 
         let Some(arg) = arg.meta_item() else {
             fail_incorrect_argument(args.span);
@@ -343,7 +344,7 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
                             UsedBy::Linker
                         }
                         _ => {
-                            cx.expected_specific_argument(l.span(), vec!["compiler", "linker"]);
+                            cx.expected_specific_argument(l.span(), &[sym::compiler, sym::linker]);
                             return;
                         }
                     }
@@ -376,57 +377,68 @@ impl<S: Stage> AttributeParser<S> for UsedParser {
     }
 }
 
+fn parse_tf_attribute<'c, S: Stage>(
+    cx: &'c mut AcceptContext<'_, '_, S>,
+    args: &'c ArgParser<'_>,
+) -> impl IntoIterator<Item = (Symbol, Span)> + 'c {
+    let mut features = Vec::new();
+    let ArgParser::List(list) = args else {
+        cx.expected_list(cx.attr_span);
+        return features;
+    };
+    if list.is_empty() {
+        cx.warn_empty_attribute(cx.attr_span);
+        return features;
+    }
+    for item in list.mixed() {
+        let Some(name_value) = item.meta_item() else {
+            cx.expected_name_value(item.span(), Some(sym::enable));
+            return features;
+        };
+
+        // Validate name
+        let Some(name) = name_value.path().word_sym() else {
+            cx.expected_name_value(name_value.path().span(), Some(sym::enable));
+            return features;
+        };
+        if name != sym::enable {
+            cx.expected_name_value(name_value.path().span(), Some(sym::enable));
+            return features;
+        }
+
+        // Use value
+        let Some(name_value) = name_value.args().name_value() else {
+            cx.expected_name_value(item.span(), Some(sym::enable));
+            return features;
+        };
+        let Some(value_str) = name_value.value_as_str() else {
+            cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
+            return features;
+        };
+        for feature in value_str.as_str().split(",") {
+            features.push((Symbol::intern(feature), item.span()));
+        }
+    }
+    features
+}
+
 pub(crate) struct TargetFeatureParser;
 
 impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
     type Item = (Symbol, Span);
     const PATH: &[Symbol] = &[sym::target_feature];
-    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature(items, span);
+    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
+        features: items,
+        attr_span: span,
+        was_forced: false,
+    };
     const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
 
     fn extend<'c>(
         cx: &'c mut AcceptContext<'_, '_, S>,
         args: &'c ArgParser<'_>,
     ) -> impl IntoIterator<Item = Self::Item> + 'c {
-        let mut features = Vec::new();
-        let ArgParser::List(list) = args else {
-            cx.expected_list(cx.attr_span);
-            return features;
-        };
-        if list.is_empty() {
-            cx.warn_empty_attribute(cx.attr_span);
-            return features;
-        }
-        for item in list.mixed() {
-            let Some(name_value) = item.meta_item() else {
-                cx.expected_name_value(item.span(), Some(sym::enable));
-                return features;
-            };
-
-            // Validate name
-            let Some(name) = name_value.path().word_sym() else {
-                cx.expected_name_value(name_value.path().span(), Some(sym::enable));
-                return features;
-            };
-            if name != sym::enable {
-                cx.expected_name_value(name_value.path().span(), Some(sym::enable));
-                return features;
-            }
-
-            // Use value
-            let Some(name_value) = name_value.args().name_value() else {
-                cx.expected_name_value(item.span(), Some(sym::enable));
-                return features;
-            };
-            let Some(value_str) = name_value.value_as_str() else {
-                cx.expected_string_literal(name_value.value_span, Some(name_value.value_as_lit()));
-                return features;
-            };
-            for feature in value_str.as_str().split(",") {
-                features.push((Symbol::intern(feature), item.span()));
-            }
-        }
-        features
+        parse_tf_attribute(cx, args)
     }
 
     const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
@@ -440,3 +452,30 @@ impl<S: Stage> CombineAttributeParser<S> for TargetFeatureParser {
         Warn(Target::MacroDef),
     ]);
 }
+
+pub(crate) struct ForceTargetFeatureParser;
+
+impl<S: Stage> CombineAttributeParser<S> for ForceTargetFeatureParser {
+    type Item = (Symbol, Span);
+    const PATH: &[Symbol] = &[sym::force_target_feature];
+    const CONVERT: ConvertFn<Self::Item> = |items, span| AttributeKind::TargetFeature {
+        features: items,
+        attr_span: span,
+        was_forced: true,
+    };
+    const TEMPLATE: AttributeTemplate = template!(List: &["enable = \"feat1, feat2\""]);
+
+    fn extend<'c>(
+        cx: &'c mut AcceptContext<'_, '_, S>,
+        args: &'c ArgParser<'_>,
+    ) -> impl IntoIterator<Item = Self::Item> + 'c {
+        parse_tf_attribute(cx, args)
+    }
+
+    const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
+        Allow(Target::Fn),
+        Allow(Target::Method(MethodKind::Inherent)),
+        Allow(Target::Method(MethodKind::Trait { body: true })),
+        Allow(Target::Method(MethodKind::TraitImpl)),
+    ]);
+}
diff --git a/compiler/rustc_attr_parsing/src/attributes/inline.rs b/compiler/rustc_attr_parsing/src/attributes/inline.rs
index 101fa71b8a6..39d5ff85d9f 100644
--- a/compiler/rustc_attr_parsing/src/attributes/inline.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/inline.rs
@@ -49,7 +49,7 @@ impl<S: Stage> SingleAttributeParser<S> for InlineParser {
                         Some(AttributeKind::Inline(InlineAttr::Never, cx.attr_span))
                     }
                     _ => {
-                        cx.expected_specific_argument(l.span(), vec!["always", "never"]);
+                        cx.expected_specific_argument(l.span(), &[sym::always, sym::never]);
                         return None;
                     }
                 }
diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
index 7a765f71a5e..66b02697c77 100644
--- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs
@@ -206,16 +206,16 @@ impl<S: Stage> SingleAttributeParser<S> for LinkageParser {
             _ => {
                 cx.expected_specific_argument(
                     name_value.value_span,
-                    vec![
-                        "available_externally",
-                        "common",
-                        "extern_weak",
-                        "external",
-                        "internal",
-                        "linkonce",
-                        "linkonce_odr",
-                        "weak",
-                        "weak_odr",
+                    &[
+                        sym::available_externally,
+                        sym::common,
+                        sym::extern_weak,
+                        sym::external,
+                        sym::internal,
+                        sym::linkonce,
+                        sym::linkonce_odr,
+                        sym::weak,
+                        sym::weak_odr,
                     ],
                 );
                 return None;
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 076b45f1013..9ac18c04e32 100644
--- a/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/proc_macro_attrs.rs
@@ -100,7 +100,7 @@ fn parse_derive_like<S: Stage>(
             return None;
         };
         if !attr_list.path().word_is(sym::attributes) {
-            cx.expected_specific_argument(attrs.span(), vec!["attributes"]);
+            cx.expected_specific_argument(attrs.span(), &[sym::attributes]);
             return None;
         }
         let Some(attr_list) = attr_list.args().list() else {
diff --git a/compiler/rustc_attr_parsing/src/attributes/prototype.rs b/compiler/rustc_attr_parsing/src/attributes/prototype.rs
index 0ef36bc7e4c..80fe82bf542 100644
--- a/compiler/rustc_attr_parsing/src/attributes/prototype.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/prototype.rs
@@ -109,7 +109,7 @@ fn parse_dialect<S: Stage>(
         sym::runtime => MirDialect::Runtime,
 
         _ => {
-            cx.expected_specific_argument(span, vec!["analysis", "built", "runtime"]);
+            cx.expected_specific_argument(span, &[sym::analysis, sym::built, sym::runtime]);
             *failed = true;
             return None;
         }
@@ -131,7 +131,7 @@ fn parse_phase<S: Stage>(
         sym::optimized => MirPhase::Optimized,
 
         _ => {
-            cx.expected_specific_argument(span, vec!["initial", "post-cleanup", "optimized"]);
+            cx.expected_specific_argument(span, &[sym::initial, sym::post_cleanup, sym::optimized]);
             *failed = true;
             return None;
         }
diff --git a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
index 2b01c09ab96..510ff1ded49 100644
--- a/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/test_attrs.rs
@@ -81,7 +81,7 @@ impl<S: Stage> SingleAttributeParser<S> for ShouldPanicParser {
                         return None;
                     };
                     if !single.path().word_is(sym::expected) {
-                        cx.expected_specific_argument_strings(list.span, vec!["expected"]);
+                        cx.expected_specific_argument_strings(list.span, &[sym::expected]);
                         return None;
                     }
                     let Some(nv) = single.args().name_value() else {
diff --git a/compiler/rustc_attr_parsing/src/attributes/traits.rs b/compiler/rustc_attr_parsing/src/attributes/traits.rs
index 0410d818f74..89ac1b07d16 100644
--- a/compiler/rustc_attr_parsing/src/attributes/traits.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/traits.rs
@@ -42,7 +42,7 @@ impl<S: Stage> SingleAttributeParser<S> for SkipDuringMethodDispatchParser {
                 Some(key @ sym::array) => (key, &mut array),
                 Some(key @ sym::boxed_slice) => (key, &mut boxed_slice),
                 _ => {
-                    cx.expected_specific_argument(path.span(), vec!["array", "boxed_slice"]);
+                    cx.expected_specific_argument(path.span(), &[sym::array, sym::boxed_slice]);
                     continue;
                 }
             };
diff --git a/compiler/rustc_attr_parsing/src/attributes/transparency.rs b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
index ce638d09530..ea1f5549c4e 100644
--- a/compiler/rustc_attr_parsing/src/attributes/transparency.rs
+++ b/compiler/rustc_attr_parsing/src/attributes/transparency.rs
@@ -29,7 +29,7 @@ impl<S: Stage> SingleAttributeParser<S> for TransparencyParser {
             Some(_) => {
                 cx.expected_specific_argument_strings(
                     nv.value_span,
-                    vec!["transparent", "semitransparent", "opaque"],
+                    &[sym::transparent, sym::semitransparent, sym::opaque],
                 );
                 None
             }
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index d79177076c7..b9c415e8085 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -19,8 +19,8 @@ use crate::attributes::allow_unstable::{
 };
 use crate::attributes::body::CoroutineParser;
 use crate::attributes::codegen_attrs::{
-    ColdParser, CoverageParser, ExportNameParser, NakedParser, NoMangleParser, OptimizeParser,
-    TargetFeatureParser, TrackCallerParser, UsedParser,
+    ColdParser, CoverageParser, ExportNameParser, ForceTargetFeatureParser, NakedParser,
+    NoMangleParser, OptimizeParser, TargetFeatureParser, TrackCallerParser, UsedParser,
 };
 use crate::attributes::confusables::ConfusablesParser;
 use crate::attributes::deprecation::DeprecationParser;
@@ -157,6 +157,7 @@ attribute_parsers!(
         // tidy-alphabetical-start
         Combine<AllowConstFnUnstableParser>,
         Combine<AllowInternalUnstableParser>,
+        Combine<ForceTargetFeatureParser>,
         Combine<ReprParser>,
         Combine<TargetFeatureParser>,
         Combine<UnstableFeatureBoundParser>,
@@ -502,10 +503,11 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         })
     }
 
+    /// produces an error along the lines of `expected one of [foo, meow]`
     pub(crate) fn expected_specific_argument(
         &self,
         span: Span,
-        possibilities: Vec<&'static str>,
+        possibilities: &[Symbol],
     ) -> ErrorGuaranteed {
         self.emit_err(AttributeParseError {
             span,
@@ -521,10 +523,12 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         })
     }
 
+    /// produces an error along the lines of `expected one of [foo, meow] as an argument`.
+    /// i.e. slightly different wording to [`expected_specific_argument`](Self::expected_specific_argument).
     pub(crate) fn expected_specific_argument_and_list(
         &self,
         span: Span,
-        possibilities: Vec<&'static str>,
+        possibilities: &[Symbol],
     ) -> ErrorGuaranteed {
         self.emit_err(AttributeParseError {
             span,
@@ -540,10 +544,11 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> {
         })
     }
 
+    /// produces an error along the lines of `expected one of ["foo", "meow"]`
     pub(crate) fn expected_specific_argument_strings(
         &self,
         span: Span,
-        possibilities: Vec<&'static str>,
+        possibilities: &[Symbol],
     ) -> ErrorGuaranteed {
         self.emit_err(AttributeParseError {
             span,
diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
index 593affc0537..2192e8f8f83 100644
--- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs
+++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs
@@ -558,7 +558,7 @@ pub(crate) struct LinkOrdinalOutOfRange {
     pub ordinal: u128,
 }
 
-pub(crate) enum AttributeParseErrorReason {
+pub(crate) enum AttributeParseErrorReason<'a> {
     ExpectedNoArgs,
     ExpectedStringLiteral {
         byte_string: Option<Span>,
@@ -571,7 +571,7 @@ pub(crate) enum AttributeParseErrorReason {
     ExpectedNameValue(Option<Symbol>),
     DuplicateKey(Symbol),
     ExpectedSpecificArgument {
-        possibilities: Vec<&'static str>,
+        possibilities: &'a [Symbol],
         strings: bool,
         /// Should we tell the user to write a list when they didn't?
         list: bool,
@@ -579,16 +579,16 @@ pub(crate) enum AttributeParseErrorReason {
     ExpectedIdentifier,
 }
 
-pub(crate) struct AttributeParseError {
+pub(crate) struct AttributeParseError<'a> {
     pub(crate) span: Span,
     pub(crate) attr_span: Span,
     pub(crate) attr_style: AttrStyle,
     pub(crate) template: AttributeTemplate,
     pub(crate) attribute: AttrPath,
-    pub(crate) reason: AttributeParseErrorReason,
+    pub(crate) reason: AttributeParseErrorReason<'a>,
 }
 
-impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
+impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> {
     fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let name = self.attribute.to_string();
 
@@ -657,7 +657,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                 list: false,
             } => {
                 let quote = if strings { '"' } else { '`' };
-                match possibilities.as_slice() {
+                match possibilities {
                     &[] => {}
                     &[x] => {
                         diag.span_label(
@@ -687,7 +687,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError {
                 list: true,
             } => {
                 let quote = if strings { '"' } else { '`' };
-                match possibilities.as_slice() {
+                match possibilities {
                     &[] => {}
                     &[x] => {
                         diag.span_label(
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index ec613b7b710..6415e55e0b0 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -10,11 +10,12 @@ use rustc_ast::{
 };
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    Applicability, Diag, MultiSpan, PResult, SingleLabelManySpans, listify, pluralize,
+    Applicability, BufferedEarlyLint, Diag, MultiSpan, PResult, SingleLabelManySpans, listify,
+    pluralize,
 };
 use rustc_expand::base::*;
 use rustc_lint_defs::builtin::NAMED_ARGUMENTS_USED_POSITIONALLY;
-use rustc_lint_defs::{BufferedEarlyLint, BuiltinLintDiag, LintId};
+use rustc_lint_defs::{BuiltinLintDiag, LintId};
 use rustc_parse::exp;
 use rustc_parse_format as parse;
 use rustc_span::{BytePos, ErrorGuaranteed, Ident, InnerSpan, Span, Symbol};
@@ -595,7 +596,8 @@ fn make_format_args(
                     named_arg_sp: arg_name.span,
                     named_arg_name: arg_name.name.to_string(),
                     is_formatting_arg: matches!(used_as, Width | Precision),
-                },
+                }
+                .into(),
             });
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index c8690251bd0..23e2abd6de3 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -193,7 +193,7 @@ fn process_builtin_attrs(
                     }
                 }
                 AttributeKind::Optimize(optimize, _) => codegen_fn_attrs.optimize = *optimize,
-                AttributeKind::TargetFeature(features, attr_span) => {
+                AttributeKind::TargetFeature { features, attr_span, was_forced } => {
                     let Some(sig) = tcx.hir_node_by_def_id(did).fn_sig() else {
                         tcx.dcx().span_delayed_bug(*attr_span, "target_feature applied to non-fn");
                         continue;
@@ -201,7 +201,7 @@ fn process_builtin_attrs(
                     let safe_target_features =
                         matches!(sig.header.safety, hir::HeaderSafety::SafeTargetFeatures);
                     codegen_fn_attrs.safe_target_features = safe_target_features;
-                    if safe_target_features {
+                    if safe_target_features && !was_forced {
                         if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
                             // The `#[target_feature]` attribute is allowed on
                             // WebAssembly targets on all functions. Prior to stabilizing
@@ -232,6 +232,7 @@ fn process_builtin_attrs(
                         tcx,
                         did,
                         features,
+                        *was_forced,
                         rust_target_features,
                         &mut codegen_fn_attrs.target_features,
                     );
@@ -462,7 +463,7 @@ fn check_result(
             .collect(),
     ) {
         let span =
-            find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature(_, span) => *span)
+            find_attr!(tcx.get_all_attrs(did), AttributeKind::TargetFeature{attr_span: span, ..} => *span)
                 .unwrap_or_else(|| tcx.def_span(did));
 
         tcx.dcx()
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index b5aa50f4851..54584999d61 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -3,7 +3,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_hir::attrs::InstructionSetAttr;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
-use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
+use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
@@ -22,6 +22,7 @@ pub(crate) fn from_target_feature_attr(
     tcx: TyCtxt<'_>,
     did: LocalDefId,
     features: &[(Symbol, Span)],
+    was_forced: bool,
     rust_target_features: &UnordMap<String, target_features::Stability>,
     target_features: &mut Vec<TargetFeature>,
 ) {
@@ -88,7 +89,14 @@ pub(crate) fn from_target_feature_attr(
                         }
                     }
                 }
-                target_features.push(TargetFeature { name, implied: name != feature })
+                let kind = if name != feature {
+                    TargetFeatureKind::Implied
+                } else if was_forced {
+                    TargetFeatureKind::Forced
+                } else {
+                    TargetFeatureKind::Enabled
+                };
+                target_features.push(TargetFeature { name, kind })
             }
         }
     }
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index 53178d09348..17da3ea83c8 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -77,6 +77,7 @@ pub mod thinvec;
 pub mod thousands;
 pub mod transitive_relation;
 pub mod unhash;
+pub mod union_find;
 pub mod unord;
 pub mod vec_cache;
 pub mod work_queue;
diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs b/compiler/rustc_data_structures/src/union_find.rs
index a826a953fa6..ef73cd7ab40 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters/union_find.rs
+++ b/compiler/rustc_data_structures/src/union_find.rs
@@ -9,7 +9,7 @@ mod tests;
 /// Simple implementation of a union-find data structure, i.e. a disjoint-set
 /// forest.
 #[derive(Debug)]
-pub(crate) struct UnionFind<Key: Idx> {
+pub struct UnionFind<Key: Idx> {
     table: IndexVec<Key, UnionFindEntry<Key>>,
 }
 
@@ -28,7 +28,7 @@ struct UnionFindEntry<Key> {
 impl<Key: Idx> UnionFind<Key> {
     /// Creates a new disjoint-set forest containing the keys `0..num_keys`.
     /// Initially, every key is part of its own one-element set.
-    pub(crate) fn new(num_keys: usize) -> Self {
+    pub fn new(num_keys: usize) -> Self {
         // Initially, every key is the root of its own set, so its parent is itself.
         Self { table: IndexVec::from_fn_n(|key| UnionFindEntry { parent: key, rank: 0 }, num_keys) }
     }
@@ -38,7 +38,7 @@ impl<Key: Idx> UnionFind<Key> {
     ///
     /// Also updates internal data structures to make subsequent `find`
     /// operations faster.
-    pub(crate) fn find(&mut self, key: Key) -> Key {
+    pub fn find(&mut self, key: Key) -> Key {
         // Loop until we find a key that is its own parent.
         let mut curr = key;
         while let parent = self.table[curr].parent
@@ -60,7 +60,7 @@ impl<Key: Idx> UnionFind<Key> {
     /// Merges the set containing `a` and the set containing `b` into one set.
     ///
     /// Returns the common root of both keys, after the merge.
-    pub(crate) fn unify(&mut self, a: Key, b: Key) -> Key {
+    pub fn unify(&mut self, a: Key, b: Key) -> Key {
         let mut a = self.find(a);
         let mut b = self.find(b);
 
@@ -90,7 +90,7 @@ impl<Key: Idx> UnionFind<Key> {
 
     /// Takes a "snapshot" of the current state of this disjoint-set forest, in
     /// the form of a vector that directly maps each key to its current root.
-    pub(crate) fn snapshot(&mut self) -> IndexVec<Key, Key> {
+    pub fn snapshot(&mut self) -> IndexVec<Key, Key> {
         self.table.indices().map(|key| self.find(key)).collect()
     }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/counters/union_find/tests.rs b/compiler/rustc_data_structures/src/union_find/tests.rs
index 34a4e4f8e6e..34a4e4f8e6e 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters/union_find/tests.rs
+++ b/compiler/rustc_data_structures/src/union_find/tests.rs
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index ad6d29e21fc..f37b6fb748f 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2024"
 annotate-snippets = "0.11"
 derive_setters = "0.1.6"
 rustc_abi = { path = "../rustc_abi" }
+rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_error_codes = { path = "../rustc_error_codes" }
 rustc_error_messages = { path = "../rustc_error_messages" }
diff --git a/compiler/rustc_errors/src/decorate_diag.rs b/compiler/rustc_errors/src/decorate_diag.rs
new file mode 100644
index 00000000000..5aef26ccf97
--- /dev/null
+++ b/compiler/rustc_errors/src/decorate_diag.rs
@@ -0,0 +1,85 @@
+/// This module provides types and traits for buffering lints until later in compilation.
+use rustc_ast::node_id::NodeId;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_error_messages::MultiSpan;
+use rustc_lint_defs::{BuiltinLintDiag, Lint, LintId};
+
+use crate::{DynSend, LintDiagnostic, LintDiagnosticBox};
+
+/// We can't implement `LintDiagnostic` for `BuiltinLintDiag`, because decorating some of its
+/// variants requires types we don't have yet. So, handle that case separately.
+pub enum DecorateDiagCompat {
+    Dynamic(Box<dyn for<'a> LintDiagnosticBox<'a, ()> + DynSend + 'static>),
+    Builtin(BuiltinLintDiag),
+}
+
+impl std::fmt::Debug for DecorateDiagCompat {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("DecorateDiagCompat").finish()
+    }
+}
+
+impl !LintDiagnostic<'_, ()> for BuiltinLintDiag {}
+
+impl<D: for<'a> LintDiagnostic<'a, ()> + DynSend + 'static> From<D> for DecorateDiagCompat {
+    #[inline]
+    fn from(d: D) -> Self {
+        Self::Dynamic(Box::new(d))
+    }
+}
+
+impl From<BuiltinLintDiag> for DecorateDiagCompat {
+    #[inline]
+    fn from(b: BuiltinLintDiag) -> Self {
+        Self::Builtin(b)
+    }
+}
+
+/// Lints that are buffered up early on in the `Session` before the
+/// `LintLevels` is calculated.
+#[derive(Debug)]
+pub struct BufferedEarlyLint {
+    /// The span of code that we are linting on.
+    pub span: Option<MultiSpan>,
+
+    /// The `NodeId` of the AST node that generated the lint.
+    pub node_id: NodeId,
+
+    /// A lint Id that can be passed to
+    /// `rustc_lint::early::EarlyContextAndPass::check_id`.
+    pub lint_id: LintId,
+
+    /// Customization of the `Diag<'_>` for the lint.
+    pub diagnostic: DecorateDiagCompat,
+}
+
+#[derive(Default, Debug)]
+pub struct LintBuffer {
+    pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
+}
+
+impl LintBuffer {
+    pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
+        self.map.entry(early_lint.node_id).or_default().push(early_lint);
+    }
+
+    pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
+        // FIXME(#120456) - is `swap_remove` correct?
+        self.map.swap_remove(&id).unwrap_or_default()
+    }
+
+    pub fn buffer_lint(
+        &mut self,
+        lint: &'static Lint,
+        node_id: NodeId,
+        span: impl Into<MultiSpan>,
+        decorate: impl Into<DecorateDiagCompat>,
+    ) {
+        self.add_early_lint(BufferedEarlyLint {
+            lint_id: LintId::of(lint),
+            node_id,
+            span: Some(span.into()),
+            diagnostic: decorate.into(),
+        });
+    }
+}
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 183dceddd2c..43ce886975c 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -138,10 +138,20 @@ where
 /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].
 #[rustc_diagnostic_item = "LintDiagnostic"]
 pub trait LintDiagnostic<'a, G: EmissionGuarantee> {
-    /// Decorate and emit a lint.
+    /// Decorate a lint with the information from this type.
     fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, G>);
 }
 
+pub trait LintDiagnosticBox<'a, G: EmissionGuarantee> {
+    fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>);
+}
+
+impl<'a, G: EmissionGuarantee, D: LintDiagnostic<'a, G>> LintDiagnosticBox<'a, G> for D {
+    fn decorate_lint_box<'b>(self: Box<Self>, diag: &'b mut Diag<'a, G>) {
+        self.decorate_lint(diag);
+    }
+}
+
 #[derive(Clone, Debug, Encodable, Decodable)]
 pub(crate) struct DiagLocation {
     file: Cow<'static, str>,
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index a775b70dbee..38c5716348f 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -40,9 +40,10 @@ use std::{fmt, panic};
 
 use Level::*;
 pub use codes::*;
+pub use decorate_diag::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
 pub use diagnostic::{
     BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee,
-    FatalAbort, LintDiagnostic, StringPart, Subdiag, Subdiagnostic,
+    FatalAbort, LintDiagnostic, LintDiagnosticBox, StringPart, Subdiag, Subdiagnostic,
 };
 pub use diagnostic_impls::{
     DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
@@ -80,6 +81,7 @@ use crate::timings::TimingRecord;
 
 pub mod annotate_snippet_emitter_writer;
 pub mod codes;
+mod decorate_diag;
 mod diagnostic;
 mod diagnostic_impls;
 pub mod emitter;
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index f2c15071532..8ff21509f4a 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -13,13 +13,13 @@ use rustc_ast::visit::{AssocCtxt, Visitor};
 use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind};
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_data_structures::sync;
-use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
+use rustc_errors::{BufferedEarlyLint, DiagCtxtHandle, ErrorGuaranteed, PResult};
 use rustc_feature::Features;
 use rustc_hir as hir;
 use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation};
 use rustc_hir::def::MacroKinds;
 use rustc_hir::{Stability, find_attr};
-use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
+use rustc_lint_defs::RegisteredTools;
 use rustc_parse::MACRO_ARGUMENTS;
 use rustc_parse::parser::{ForceCollect, Parser};
 use rustc_session::config::CollapseMacroDebuginfo;
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index fd71f2ce948..5b1d3d6d35b 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -250,12 +250,14 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
                 Question => op("?"),
                 SingleQuote => op("'"),
 
-                Ident(sym, is_raw) => {
-                    trees.push(TokenTree::Ident(Ident { sym, is_raw: is_raw.into(), span }))
-                }
+                Ident(sym, is_raw) => trees.push(TokenTree::Ident(Ident {
+                    sym,
+                    is_raw: matches!(is_raw, IdentIsRaw::Yes),
+                    span,
+                })),
                 NtIdent(ident, is_raw) => trees.push(TokenTree::Ident(Ident {
                     sym: ident.name,
-                    is_raw: is_raw.into(),
+                    is_raw: matches!(is_raw, IdentIsRaw::Yes),
                     span: ident.span,
                 })),
 
@@ -263,7 +265,11 @@ impl FromInternal<(TokenStream, &mut Rustc<'_, '_>)> for Vec<TokenTree<TokenStre
                     let ident = rustc_span::Ident::new(name, span).without_first_quote();
                     trees.extend([
                         TokenTree::Punct(Punct { ch: b'\'', joint: true, span }),
-                        TokenTree::Ident(Ident { sym: ident.name, is_raw: is_raw.into(), span }),
+                        TokenTree::Ident(Ident {
+                            sym: ident.name,
+                            is_raw: matches!(is_raw, IdentIsRaw::Yes),
+                            span,
+                        }),
                     ]);
                 }
                 NtLifetime(ident, is_raw) => {
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 8f632bcebc7..e81003b1897 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -745,6 +745,10 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         ErrorPreceding, EncodeCrossCrate::No
     ),
     gated!(
+        unsafe force_target_feature, Normal, template!(List: &[r#"enable = "name""#]),
+        DuplicatesOk, EncodeCrossCrate::No, effective_target_features, experimental!(force_target_feature)
+    ),
+    gated!(
         sanitize, Normal, template!(List: &[r#"address = "on|off""#, r#"kernel_address = "on|off""#, r#"cfi = "on|off""#, r#"hwaddress = "on|off""#, r#"kcfi = "on|off""#, r#"memory = "on|off""#, r#"memtag = "on|off""#, r#"shadow_call_stack = "on|off""#, r#"thread = "on|off""#]), ErrorPreceding,
         EncodeCrossCrate::No, sanitize, experimental!(sanitize),
     ),
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 746871982ce..92b435b4b01 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -480,6 +480,8 @@ declare_features! (
     (unstable, doc_cfg_hide, "1.57.0", Some(43781)),
     /// Allows `#[doc(masked)]`.
     (unstable, doc_masked, "1.21.0", Some(44027)),
+    /// Allows features to allow target_feature to better interact with traits.
+    (incomplete, effective_target_features, "CURRENT_RUSTC_VERSION", Some(143352)),
     /// Allows the .use postfix syntax `x.use` and use closures `use |x| { ... }`
     (incomplete, ergonomic_clones, "1.87.0", Some(132290)),
     /// Allows exhaustive pattern matching on types that contain uninhabited types.
@@ -614,6 +616,7 @@ declare_features! (
     (unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
     /// Allows the use of raw-dylibs on ELF platforms
     (incomplete, raw_dylib_elf, "1.87.0", Some(135694)),
+    (unstable, reborrow, "CURRENT_RUSTC_VERSION", Some(145612)),
     /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
     (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
     /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
index a17350f0392..2209b18df3f 100644
--- a/compiler/rustc_hir/src/attrs/data_structures.rs
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -524,8 +524,9 @@ pub enum AttributeKind {
     /// Represents `#[rustc_std_internal_symbol]`.
     StdInternalSymbol(Span),
 
-    /// Represents `#[target_feature(enable = "...")]`
-    TargetFeature(ThinVec<(Symbol, Span)>, Span),
+    /// Represents `#[target_feature(enable = "...")]` and
+    /// `#[unsafe(force_target_feature(enable = "...")]`.
+    TargetFeature { features: ThinVec<(Symbol, Span)>, attr_span: Span, was_forced: bool },
 
     /// Represents `#[track_caller]`
     TrackCaller(Span),
diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
index defabdccc02..485ded3981f 100644
--- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
+++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs
@@ -78,7 +78,7 @@ impl AttributeKind {
             SpecializationTrait(..) => No,
             Stability { .. } => Yes,
             StdInternalSymbol(..) => No,
-            TargetFeature(..) => No,
+            TargetFeature { .. } => No,
             TrackCaller(..) => Yes,
             TypeConst(..) => Yes,
             UnsafeSpecializationMarker(..) => No,
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 905b84a8cbe..0464665b41f 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -437,6 +437,9 @@ language_item_table! {
     DefaultTrait1,           sym::default_trait1,      default_trait1_trait,       Target::Trait,          GenericRequirement::None;
 
     ContractCheckEnsures,     sym::contract_check_ensures,      contract_check_ensures_fn,      Target::Fn, GenericRequirement::None;
+
+    // Reborrowing related lang-items
+    Reborrow,                sym::reborrow,            reborrow,                   Target::Trait,          GenericRequirement::Exact(0);
 }
 
 /// The requirement imposed on the generics of a lang item
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 824d592fa6c..c8f6c06b720 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -13,9 +13,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
-use rustc_errors::{
-    Applicability, Diag, DiagStyledString, MultiSpan, StashKey, pluralize, struct_span_code_err,
-};
+use rustc_errors::{Applicability, Diag, MultiSpan, StashKey, pluralize, struct_span_code_err};
 use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -1560,11 +1558,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             );
         }
 
-        if rcvr_ty.is_numeric() && rcvr_ty.is_fresh()
-            || restrict_type_params
-            || suggested_derive
-            || self.lookup_alternative_tuple_impls(&mut err, &unsatisfied_predicates)
-        {
+        if rcvr_ty.is_numeric() && rcvr_ty.is_fresh() || restrict_type_params || suggested_derive {
         } else {
             self.suggest_traits_to_import(
                 &mut err,
@@ -1741,119 +1735,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err.emit()
     }
 
-    /// If the predicate failure is caused by an unmet bound on a tuple, recheck if the bound would
-    /// succeed if all the types on the tuple had no borrows. This is a common problem for libraries
-    /// like Bevy and ORMs, which rely heavily on traits being implemented on tuples.
-    fn lookup_alternative_tuple_impls(
-        &self,
-        err: &mut Diag<'_>,
-        unsatisfied_predicates: &[(
-            ty::Predicate<'tcx>,
-            Option<ty::Predicate<'tcx>>,
-            Option<ObligationCause<'tcx>>,
-        )],
-    ) -> bool {
-        let mut found_tuple = false;
-        for (pred, root, _ob) in unsatisfied_predicates {
-            let mut preds = vec![pred];
-            if let Some(root) = root {
-                // We will look at both the current predicate and the root predicate that caused it
-                // to be needed. If calling something like `<(A, &B)>::default()`, then `pred` is
-                // `&B: Default` and `root` is `(A, &B): Default`, which is the one we are checking
-                // for further down, so we check both.
-                preds.push(root);
-            }
-            for pred in preds {
-                if let Some(clause) = pred.as_clause()
-                    && let Some(clause) = clause.as_trait_clause()
-                    && let ty = clause.self_ty().skip_binder()
-                    && let ty::Tuple(types) = ty.kind()
-                {
-                    let path = clause.skip_binder().trait_ref.print_only_trait_path();
-                    let def_id = clause.def_id();
-                    let ty = Ty::new_tup(
-                        self.tcx,
-                        self.tcx.mk_type_list_from_iter(types.iter().map(|ty| ty.peel_refs())),
-                    );
-                    let args = ty::GenericArgs::for_item(self.tcx, def_id, |param, _| {
-                        if param.index == 0 {
-                            ty.into()
-                        } else {
-                            self.infcx.var_for_def(DUMMY_SP, param)
-                        }
-                    });
-                    if self
-                        .infcx
-                        .type_implements_trait(def_id, args, self.param_env)
-                        .must_apply_modulo_regions()
-                    {
-                        // "`Trait` is implemented for `(A, B)` but not for `(A, &B)`"
-                        let mut msg = DiagStyledString::normal(format!("`{path}` "));
-                        msg.push_highlighted("is");
-                        msg.push_normal(" implemented for `(");
-                        let len = types.len();
-                        for (i, t) in types.iter().enumerate() {
-                            msg.push(
-                                format!("{}", with_forced_trimmed_paths!(t.peel_refs())),
-                                t.peel_refs() != t,
-                            );
-                            if i < len - 1 {
-                                msg.push_normal(", ");
-                            }
-                        }
-                        msg.push_normal(")` but ");
-                        msg.push_highlighted("not");
-                        msg.push_normal(" for `(");
-                        for (i, t) in types.iter().enumerate() {
-                            msg.push(
-                                format!("{}", with_forced_trimmed_paths!(t)),
-                                t.peel_refs() != t,
-                            );
-                            if i < len - 1 {
-                                msg.push_normal(", ");
-                            }
-                        }
-                        msg.push_normal(")`");
-
-                        // Find the span corresponding to the impl that was found to point at it.
-                        if let Some(impl_span) = self
-                            .tcx
-                            .all_impls(def_id)
-                            .filter(|&impl_def_id| {
-                                let header = self.tcx.impl_trait_header(impl_def_id).unwrap();
-                                let trait_ref = header.trait_ref.instantiate(
-                                    self.tcx,
-                                    self.infcx.fresh_args_for_item(DUMMY_SP, impl_def_id),
-                                );
-
-                                let value = ty::fold_regions(self.tcx, ty, |_, _| {
-                                    self.tcx.lifetimes.re_erased
-                                });
-                                // FIXME: Don't bother dealing with non-lifetime binders here...
-                                if value.has_escaping_bound_vars() {
-                                    return false;
-                                }
-                                self.infcx.can_eq(ty::ParamEnv::empty(), trait_ref.self_ty(), value)
-                                    && header.polarity == ty::ImplPolarity::Positive
-                            })
-                            .map(|impl_def_id| self.tcx.def_span(impl_def_id))
-                            .next()
-                        {
-                            err.highlighted_span_note(impl_span, msg.0);
-                        } else {
-                            err.highlighted_note(msg.0);
-                        }
-                        found_tuple = true;
-                    }
-                    // If `pred` was already on the tuple, we don't need to look at the root
-                    // obligation too.
-                    break;
-                }
-            }
-        }
-        found_tuple
-    }
-
     /// If an appropriate error source is not found, check method chain for possible candidates
     fn lookup_segments_chain_for_no_match_method(
         &self,
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 49ac3b7a10d..9a432a60819 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -9,10 +9,11 @@ use rustc_attr_parsing::validate_attr;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_data_structures::jobserver::Proxy;
 use rustc_data_structures::sync;
+use rustc_errors::LintBuffer;
 use rustc_metadata::{DylibError, load_symbol_from_dylib};
 use rustc_middle::ty::CurrentGcx;
 use rustc_session::config::{Cfg, OutFileName, OutputFilenames, OutputTypes, Sysroot, host_tuple};
-use rustc_session::lint::{self, BuiltinLintDiag, LintBuffer};
+use rustc_session::lint::{self, BuiltinLintDiag};
 use rustc_session::output::{CRATE_TYPES, categorize_crate_type};
 use rustc_session::{EarlyDiagCtxt, Session, filesearch};
 use rustc_span::edit_distance::find_best_match_for_name;
diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs
index e80196ed567..483cc3e93dc 100644
--- a/compiler/rustc_lexer/src/lib.rs
+++ b/compiler/rustc_lexer/src/lib.rs
@@ -540,11 +540,11 @@ impl Cursor<'_> {
         // whitespace between the opening and the infostring.
         self.eat_while(|ch| ch != '\n' && is_whitespace(ch));
 
-        // copied from `eat_identifier`, but allows `.` in infostring to allow something like
+        // copied from `eat_identifier`, but allows `-` and `.` in infostring to allow something like
         // `---Cargo.toml` as a valid opener
         if is_id_start(self.first()) {
             self.bump();
-            self.eat_while(|c| is_id_continue(c) || c == '.');
+            self.eat_while(|c| is_id_continue(c) || c == '-' || c == '.');
         }
 
         self.eat_while(|ch| ch != '\n' && is_whitespace(ch));
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index f26e5f05e1a..940a07c94df 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -205,8 +205,6 @@ lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as i
     .current_use = this identifier can be confused with `{$existing_sym}`
     .other_use = other identifier used here
 
-lint_custom_inner_attribute_unstable = custom inner attributes are unstable
-
 lint_dangling_pointers_from_locals = a dangling pointer will be produced because the local variable `{$local_var_name}` will be dropped
     .ret_ty = return type of the {$fn_kind} is `{$ret_ty}`
     .local_var = `{$local_var_name}` is part the {$fn_kind} and will be dropped at the end of the {$fn_kind}
@@ -271,10 +269,6 @@ lint_expectation = this lint expectation is unfulfilled
 lint_extern_crate_not_idiomatic = `extern crate` is not idiomatic in the new edition
     .suggestion = convert it to a `use`
 
-lint_extern_without_abi = `extern` declarations without an explicit ABI are deprecated
-    .label = ABI should be specified here
-    .suggestion = explicitly specify the {$default_abi} ABI
-
 lint_for_loops_over_fallibles =
     for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement
     .suggestion = consider using `if let` to clear intent
@@ -294,19 +288,6 @@ lint_hidden_glob_reexport = private item shadows public glob re-export
 
 lint_hidden_lifetime_parameters = hidden lifetime parameters in types are deprecated
 
-lint_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label}
-    .label = this {$label} contains {$count ->
-        [one] an invisible
-        *[other] invisible
-    } unicode text flow control {$count ->
-        [one] codepoint
-        *[other] codepoints
-    }
-    .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen
-    .suggestion_remove = if their presence wasn't intentional, you can remove them
-    .suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them
-    .no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped}
-
 lint_identifier_non_ascii_char = identifier contains non-ASCII characters
 
 lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len ->
@@ -431,8 +412,6 @@ lint_improper_ctypes_union_non_exhaustive = this union is non-exhaustive
 lint_incomplete_include =
     include macro expected single expression in source
 
-lint_inner_macro_attribute_unstable = inner macro attributes are unstable
-
 lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly
     .label = use a different label that doesn't start with `0` or `1`
     .help = start numbering with `2` instead
@@ -870,10 +849,6 @@ lint_undropped_manually_drops = calls to `std::mem::drop` with `std::mem::Manual
     .label = argument has type `{$arg_ty}`
     .suggestion = use `std::mem::ManuallyDrop::into_inner` to get the inner value
 
-lint_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag
-    .controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`
-    .incoherent = manually setting a built-in cfg can and does create incoherent behaviors
-
 lint_unexpected_cfg_add_build_rs_println = or consider adding `{$build_rs_println}` to the top of the `build.rs`
 lint_unexpected_cfg_add_cargo_feature = consider using a Cargo feature instead
 lint_unexpected_cfg_add_cargo_toml_lint_cfg = or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:{$cargo_toml_lint_cfg}
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index e9bd9dccdf1..0669da1a025 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::sync;
 use rustc_data_structures::unord::UnordMap;
-use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
+use rustc_errors::{Diag, LintBuffer, LintDiagnostic, MultiSpan};
 use rustc_feature::Features;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{CrateNum, DefId};
@@ -23,7 +23,7 @@ use rustc_middle::middle::privacy::EffectiveVisibilities;
 use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
 use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
-use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId};
+use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId};
 use rustc_session::{DynLintStore, Session};
 use rustc_span::edit_distance::find_best_match_for_names;
 use rustc_span::{Ident, Span, Symbol, sym};
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 58205087def..dff1fc43670 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -7,10 +7,11 @@
 use rustc_ast::visit::{self as ast_visit, Visitor, walk_list};
 use rustc_ast::{self as ast, HasAttrs};
 use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_errors::{BufferedEarlyLint, DecorateDiagCompat, LintBuffer};
 use rustc_feature::Features;
 use rustc_middle::ty::{RegisteredTools, TyCtxt};
 use rustc_session::Session;
-use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
+use rustc_session::lint::LintPass;
 use rustc_span::{Ident, Span};
 use tracing::debug;
 
@@ -36,8 +37,11 @@ impl<'ecx, 'tcx, T: EarlyLintPass> EarlyContextAndPass<'ecx, 'tcx, T> {
     fn check_id(&mut self, id: ast::NodeId) {
         for early_lint in self.context.buffered.take(id) {
             let BufferedEarlyLint { span, node_id: _, lint_id, diagnostic } = early_lint;
-            self.context.opt_span_lint(lint_id.lint, span, |diag| {
-                diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, diagnostic, diag);
+            self.context.opt_span_lint(lint_id.lint, span, |diag| match diagnostic {
+                DecorateDiagCompat::Builtin(b) => {
+                    diagnostics::decorate_builtin_lint(self.context.sess(), self.tcx, b, diag);
+                }
+                DecorateDiagCompat::Dynamic(d) => d.decorate_lint_box(diag),
             });
         }
     }
diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs
index 0e283ed923a..7300490b838 100644
--- a/compiler/rustc_lint/src/early/diagnostics.rs
+++ b/compiler/rustc_lint/src/early/diagnostics.rs
@@ -158,9 +158,6 @@ pub fn decorate_builtin_lint(
             }
             .decorate_lint(diag);
         }
-        BuiltinLintDiag::MissingAbi(label_span, default_abi) => {
-            lints::MissingAbi { span: label_span, default_abi }.decorate_lint(diag);
-        }
         BuiltinLintDiag::LegacyDeriveHelpers(label_span) => {
             lints::LegacyDeriveHelpers { span: label_span }.decorate_lint(diag);
         }
@@ -186,27 +183,6 @@ pub fn decorate_builtin_lint(
                 lints::ReservedMultihash { suggestion }.decorate_lint(diag);
             }
         }
-        BuiltinLintDiag::HiddenUnicodeCodepoints {
-            label,
-            count,
-            span_label,
-            labels,
-            escape,
-            spans,
-        } => {
-            lints::HiddenUnicodeCodepointsDiag {
-                label: &label,
-                count,
-                span_label,
-                labels: labels.map(|spans| lints::HiddenUnicodeCodepointsDiagLabels { spans }),
-                sub: if escape {
-                    lints::HiddenUnicodeCodepointsDiagSub::Escape { spans }
-                } else {
-                    lints::HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
-                },
-            }
-            .decorate_lint(diag);
-        }
         BuiltinLintDiag::UnusedBuiltinAttribute {
             attr_name,
             macro_name,
@@ -466,17 +442,8 @@ pub fn decorate_builtin_lint(
             }
             .decorate_lint(diag)
         }
-        BuiltinLintDiag::InnerAttributeUnstable { is_macro } => if is_macro {
-            lints::InnerAttributeUnstable::InnerMacroAttribute
-        } else {
-            lints::InnerAttributeUnstable::CustomInnerAttribute
-        }
-        .decorate_lint(diag),
         BuiltinLintDiag::OutOfScopeMacroCalls { span, path, location } => {
             lints::OutOfScopeMacroCalls { span, path, location }.decorate_lint(diag)
         }
-        BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by } => {
-            lints::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.decorate_lint(diag)
-        }
     }
 }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index f06757b3c23..bdbac7fc4d1 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -133,10 +133,9 @@ pub use early::{EarlyCheckNode, check_ast_node};
 pub use late::{check_crate, late_lint_mod, unerased_lint_store};
 pub use levels::LintLevelsBuilder;
 pub use passes::{EarlyLintPass, LateLintPass};
+pub use rustc_errors::BufferedEarlyLint;
 pub use rustc_session::lint::Level::{self, *};
-pub use rustc_session::lint::{
-    BufferedEarlyLint, FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec,
-};
+pub use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintId, LintPass, LintVec};
 
 rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index a1e26bf1503..6c509734626 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1,7 +1,6 @@
 #![allow(rustc::untranslatable_diagnostic)]
 use std::num::NonZero;
 
-use rustc_abi::ExternAbi;
 use rustc_errors::codes::*;
 use rustc_errors::{
     Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag,
@@ -816,80 +815,6 @@ pub(crate) enum InvalidReferenceCastingDiag<'tcx> {
     },
 }
 
-// hidden_unicode_codepoints.rs
-#[derive(LintDiagnostic)]
-#[diag(lint_hidden_unicode_codepoints)]
-#[note]
-pub(crate) struct HiddenUnicodeCodepointsDiag<'a> {
-    pub label: &'a str,
-    pub count: usize,
-    #[label]
-    pub span_label: Span,
-    #[subdiagnostic]
-    pub labels: Option<HiddenUnicodeCodepointsDiagLabels>,
-    #[subdiagnostic]
-    pub sub: HiddenUnicodeCodepointsDiagSub,
-}
-
-pub(crate) struct HiddenUnicodeCodepointsDiagLabels {
-    pub spans: Vec<(char, Span)>,
-}
-
-impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels {
-    fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
-        for (c, span) in self.spans {
-            diag.span_label(span, format!("{c:?}"));
-        }
-    }
-}
-
-pub(crate) enum HiddenUnicodeCodepointsDiagSub {
-    Escape { spans: Vec<(char, Span)> },
-    NoEscape { spans: Vec<(char, Span)> },
-}
-
-// Used because of multiple multipart_suggestion and note
-impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub {
-    fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
-        match self {
-            HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
-                diag.multipart_suggestion_with_style(
-                    fluent::lint_suggestion_remove,
-                    spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
-                    Applicability::MachineApplicable,
-                    SuggestionStyle::HideCodeAlways,
-                );
-                diag.multipart_suggestion(
-                    fluent::lint_suggestion_escape,
-                    spans
-                        .into_iter()
-                        .map(|(c, span)| {
-                            let c = format!("{c:?}");
-                            (span, c[1..c.len() - 1].to_string())
-                        })
-                        .collect(),
-                    Applicability::MachineApplicable,
-                );
-            }
-            HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => {
-                // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
-                // should do the same here to provide the same good suggestions as we do for
-                // literals above.
-                diag.arg(
-                    "escaped",
-                    spans
-                        .into_iter()
-                        .map(|(c, _)| format!("{c:?}"))
-                        .collect::<Vec<String>>()
-                        .join(", "),
-                );
-                diag.note(fluent::lint_suggestion_remove);
-                diag.note(fluent::lint_no_suggestion_note_escape);
-            }
-        }
-    }
-}
-
 // map_unit_fn.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_map_unit_fn)]
@@ -2567,16 +2492,6 @@ pub(crate) mod unexpected_cfg_value {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_unexpected_builtin_cfg)]
-#[note(lint_controlled_by)]
-#[note(lint_incoherent)]
-pub(crate) struct UnexpectedBuiltinCfg {
-    pub(crate) cfg: String,
-    pub(crate) cfg_name: Symbol,
-    pub(crate) controlled_by: &'static str,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_macro_use_deprecated)]
 #[help]
 pub(crate) struct MacroUseDeprecated;
@@ -2690,14 +2605,6 @@ pub(crate) struct IllFormedAttributeInput {
 }
 
 #[derive(LintDiagnostic)]
-pub(crate) enum InnerAttributeUnstable {
-    #[diag(lint_inner_macro_attribute_unstable)]
-    InnerMacroAttribute,
-    #[diag(lint_custom_inner_attribute_unstable)]
-    CustomInnerAttribute,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_unknown_diagnostic_attribute)]
 pub(crate) struct UnknownDiagnosticAttribute {
     #[subdiagnostic]
@@ -2890,14 +2797,6 @@ pub(crate) struct PatternsInFnsWithoutBodySub {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_extern_without_abi)]
-pub(crate) struct MissingAbi {
-    #[suggestion(code = "extern {default_abi}", applicability = "machine-applicable")]
-    pub span: Span,
-    pub default_abi: ExternAbi,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_legacy_derive_helpers)]
 pub(crate) struct LegacyDeriveHelpers {
     #[label]
diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml
index 152eb4fb380..c8201d5ea8c 100644
--- a/compiler/rustc_lint_defs/Cargo.toml
+++ b/compiler/rustc_lint_defs/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2024"
 
 [dependencies]
 # tidy-alphabetical-start
-rustc_abi = { path = "../rustc_abi" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_error_messages = { path = "../rustc_error_messages" }
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index d1f5cc21277..2e84233e5a5 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -1,10 +1,8 @@
 use std::borrow::Cow;
 
-use rustc_abi::ExternAbi;
 use rustc_ast::AttrId;
 use rustc_ast::attr::AttributeExt;
-use rustc_ast::node_id::NodeId;
-use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::stable_hasher::{
     HashStable, StableCompare, StableHasher, ToStableHashKey,
 };
@@ -648,7 +646,6 @@ pub enum BuiltinLintDiag {
         path: String,
         since_kind: DeprecatedSinceKind,
     },
-    MissingAbi(Span, ExternAbi),
     UnusedDocComment(Span),
     UnusedBuiltinAttribute {
         attr_name: Symbol,
@@ -671,14 +668,6 @@ pub enum BuiltinLintDiag {
         is_string: bool,
         suggestion: Span,
     },
-    HiddenUnicodeCodepoints {
-        label: String,
-        count: usize,
-        span_label: Span,
-        labels: Option<Vec<(char, Span)>>,
-        escape: bool,
-        spans: Vec<(char, Span)>,
-    },
     TrailingMacro(bool, Ident),
     BreakWithLabelAndLoop(Span),
     UnicodeTextFlow(Span, String),
@@ -803,68 +792,11 @@ pub enum BuiltinLintDiag {
         suggestions: Vec<String>,
         docs: Option<&'static str>,
     },
-    InnerAttributeUnstable {
-        is_macro: bool,
-    },
     OutOfScopeMacroCalls {
         span: Span,
         path: String,
         location: String,
     },
-    UnexpectedBuiltinCfg {
-        cfg: String,
-        cfg_name: Symbol,
-        controlled_by: &'static str,
-    },
-}
-
-/// Lints that are buffered up early on in the `Session` before the
-/// `LintLevels` is calculated.
-#[derive(Debug)]
-pub struct BufferedEarlyLint {
-    /// The span of code that we are linting on.
-    pub span: Option<MultiSpan>,
-
-    /// The `NodeId` of the AST node that generated the lint.
-    pub node_id: NodeId,
-
-    /// A lint Id that can be passed to
-    /// `rustc_lint::early::EarlyContextAndPass::check_id`.
-    pub lint_id: LintId,
-
-    /// Customization of the `Diag<'_>` for the lint.
-    pub diagnostic: BuiltinLintDiag,
-}
-
-#[derive(Default, Debug)]
-pub struct LintBuffer {
-    pub map: FxIndexMap<NodeId, Vec<BufferedEarlyLint>>,
-}
-
-impl LintBuffer {
-    pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
-        self.map.entry(early_lint.node_id).or_default().push(early_lint);
-    }
-
-    pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
-        // FIXME(#120456) - is `swap_remove` correct?
-        self.map.swap_remove(&id).unwrap_or_default()
-    }
-
-    pub fn buffer_lint(
-        &mut self,
-        lint: &'static Lint,
-        node_id: NodeId,
-        span: impl Into<MultiSpan>,
-        diagnostic: BuiltinLintDiag,
-    ) {
-        self.add_early_lint(BufferedEarlyLint {
-            lint_id: LintId::of(lint),
-            node_id,
-            span: Some(span.into()),
-            diagnostic,
-        });
-    }
 }
 
 pub type RegisteredTools = FxIndexSet<Ident>;
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index e5cc23c213d..cf0549fa668 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -60,6 +60,7 @@
 #![feature(try_trait_v2_residual)]
 #![feature(try_trait_v2_yeet)]
 #![feature(type_alias_impl_trait)]
+#![feature(unwrap_infallible)]
 #![feature(yeet_expr)]
 #![recursion_limit = "256"]
 // tidy-alphabetical-end
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index 347319b07c9..78cafe8566b 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -72,13 +72,23 @@ pub struct CodegenFnAttrs {
     pub patchable_function_entry: Option<PatchableFunctionEntry>,
 }
 
+#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, PartialEq, Eq)]
+pub enum TargetFeatureKind {
+    /// The feature is implied by another feature, rather than explicitly added by the
+    /// `#[target_feature]` attribute
+    Implied,
+    /// The feature is added by the regular `target_feature` attribute.
+    Enabled,
+    /// The feature is added by the unsafe `force_target_feature` attribute.
+    Forced,
+}
+
 #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
 pub struct TargetFeature {
     /// The name of the target feature (e.g. "avx")
     pub name: Symbol,
-    /// The feature is implied by another feature, rather than explicitly added by the
-    /// `#[target_feature]` attribute
-    pub implied: bool,
+    /// The way this feature was enabled.
+    pub kind: TargetFeatureKind,
 }
 
 #[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 4a19cf1563c..18520089e3e 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -4,7 +4,7 @@
 use std::num::NonZero;
 
 use rustc_ast::NodeId;
-use rustc_errors::{Applicability, Diag, EmissionGuarantee};
+use rustc_errors::{Applicability, Diag, EmissionGuarantee, LintBuffer};
 use rustc_feature::GateIssue;
 use rustc_hir::attrs::{DeprecatedSince, Deprecation};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -12,7 +12,7 @@ use rustc_hir::{self as hir, ConstStability, DefaultBodyStability, HirId, Stabil
 use rustc_macros::{Decodable, Encodable, HashStable, Subdiagnostic};
 use rustc_session::Session;
 use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
-use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint, LintBuffer};
+use rustc_session::lint::{BuiltinLintDiag, DeprecatedSinceKind, Level, Lint};
 use rustc_session::parse::feature_err_issue;
 use rustc_span::{Span, Symbol, sym};
 use tracing::debug;
diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs
index 683d7b48617..6e52bc775ef 100644
--- a/compiler/rustc_middle/src/mir/statement.rs
+++ b/compiler/rustc_middle/src/mir/statement.rs
@@ -160,7 +160,11 @@ impl<'tcx> PlaceTy<'tcx> {
     /// Convenience wrapper around `projection_ty_core` for `PlaceElem`,
     /// where we can just use the `Ty` that is already stored inline on
     /// field projection elems.
-    pub fn projection_ty(self, tcx: TyCtxt<'tcx>, elem: PlaceElem<'tcx>) -> PlaceTy<'tcx> {
+    pub fn projection_ty<V: ::std::fmt::Debug>(
+        self,
+        tcx: TyCtxt<'tcx>,
+        elem: ProjectionElem<V, Ty<'tcx>>,
+    ) -> PlaceTy<'tcx> {
         self.projection_ty_core(tcx, &elem, |ty| ty, |_, _, _, ty| ty, |ty| ty)
     }
 
@@ -290,6 +294,36 @@ impl<V, T> ProjectionElem<V, T> {
             Self::UnwrapUnsafeBinder(..) => false,
         }
     }
+
+    /// Returns the `ProjectionKind` associated to this projection.
+    pub fn kind(self) -> ProjectionKind {
+        self.try_map(|_| Some(()), |_| ()).unwrap()
+    }
+
+    /// Apply functions to types and values in this projection and return the result.
+    pub fn try_map<V2, T2>(
+        self,
+        v: impl FnOnce(V) -> Option<V2>,
+        t: impl FnOnce(T) -> T2,
+    ) -> Option<ProjectionElem<V2, T2>> {
+        Some(match self {
+            ProjectionElem::Deref => ProjectionElem::Deref,
+            ProjectionElem::Downcast(name, read_variant) => {
+                ProjectionElem::Downcast(name, read_variant)
+            }
+            ProjectionElem::Field(f, ty) => ProjectionElem::Field(f, t(ty)),
+            ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
+                ProjectionElem::ConstantIndex { offset, min_length, from_end }
+            }
+            ProjectionElem::Subslice { from, to, from_end } => {
+                ProjectionElem::Subslice { from, to, from_end }
+            }
+            ProjectionElem::OpaqueCast(ty) => ProjectionElem::OpaqueCast(t(ty)),
+            ProjectionElem::Subtype(ty) => ProjectionElem::Subtype(t(ty)),
+            ProjectionElem::UnwrapUnsafeBinder(ty) => ProjectionElem::UnwrapUnsafeBinder(t(ty)),
+            ProjectionElem::Index(val) => ProjectionElem::Index(v(val)?),
+        })
+    }
 }
 
 /// Alias for projections as they appear in `UserTypeProjection`, where we
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index e70c98ab704..a7298af502e 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -32,7 +32,7 @@ use rustc_data_structures::intern::Interned;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::unord::{UnordMap, UnordSet};
-use rustc_errors::{Diag, ErrorGuaranteed};
+use rustc_errors::{Diag, ErrorGuaranteed, LintBuffer};
 use rustc_hir::attrs::{AttributeKind, StrippedCfgItem};
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
@@ -46,7 +46,6 @@ use rustc_macros::{
 };
 use rustc_query_system::ich::StableHashingContext;
 use rustc_serialize::{Decodable, Encodable};
-use rustc_session::lint::LintBuffer;
 pub use rustc_session::lint::RegisteredTools;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::{DUMMY_SP, ExpnId, ExpnKind, Ident, Span, Symbol, sym};
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index cdab785e842..b5e165c7517 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -8,7 +8,7 @@ use rustc_errors::DiagArgValue;
 use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::DefKind;
 use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, find_attr};
-use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
+use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind};
 use rustc_middle::mir::BorrowKind;
 use rustc_middle::span_bug;
 use rustc_middle::thir::visit::Visitor;
@@ -522,7 +522,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                             .iter()
                             .copied()
                             .filter(|feature| {
-                                !feature.implied
+                                feature.kind == TargetFeatureKind::Enabled
                                     && !self
                                         .body_target_features
                                         .iter()
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs b/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs
deleted file mode 100644
index d056ad3d4b4..00000000000
--- a/compiler/rustc_mir_dataflow/src/move_paths/abs_domain.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-//! The move-analysis portion of borrowck needs to work in an abstract
-//! domain of lifted `Place`s. Most of the `Place` variants fall into a
-//! one-to-one mapping between the concrete and abstract (e.g., a
-//! field-deref on a local variable, `x.field`, has the same meaning
-//! in both domains). Indexed projections are the exception: `a[x]`
-//! needs to be treated as mapping to the same move path as `a[y]` as
-//! well as `a[13]`, etc. So we map these `x`/`y` values to `()`.
-//!
-//! (In theory, the analysis could be extended to work with sets of
-//! paths, so that `a[0]` and `a[13]` could be kept distinct, while
-//! `a[x]` would still overlap them both. But that is not this
-//! representation does today.)
-
-use rustc_middle::mir::{PlaceElem, ProjectionElem, ProjectionKind};
-
-pub(crate) trait Lift {
-    fn lift(&self) -> ProjectionKind;
-}
-
-impl<'tcx> Lift for PlaceElem<'tcx> {
-    fn lift(&self) -> ProjectionKind {
-        match *self {
-            ProjectionElem::Deref => ProjectionElem::Deref,
-            ProjectionElem::Field(f, _ty) => ProjectionElem::Field(f, ()),
-            ProjectionElem::OpaqueCast(_ty) => ProjectionElem::OpaqueCast(()),
-            ProjectionElem::Index(_i) => ProjectionElem::Index(()),
-            ProjectionElem::Subslice { from, to, from_end } => {
-                ProjectionElem::Subslice { from, to, from_end }
-            }
-            ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
-                ProjectionElem::ConstantIndex { offset, min_length, from_end }
-            }
-            ProjectionElem::Downcast(a, u) => ProjectionElem::Downcast(a, u),
-            ProjectionElem::Subtype(_ty) => ProjectionElem::Subtype(()),
-            ProjectionElem::UnwrapUnsafeBinder(_ty) => ProjectionElem::UnwrapUnsafeBinder(()),
-        }
-    }
-}
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 8bbc89fdcec..48718cad597 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -7,7 +7,6 @@ use rustc_middle::{bug, span_bug};
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
 
-use super::abs_domain::Lift;
 use super::{
     Init, InitIndex, InitKind, InitLocation, LocationMap, LookupResult, MoveData, MoveOut,
     MoveOutIndex, MovePath, MovePathIndex, MovePathLookup,
@@ -241,7 +240,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
             if union_path.is_none() {
                 // inlined from add_move_path because of a borrowck conflict with the iterator
                 base =
-                    *data.rev_lookup.projections.entry((base, elem.lift())).or_insert_with(|| {
+                    *data.rev_lookup.projections.entry((base, elem.kind())).or_insert_with(|| {
                         new_move_path(
                             &mut data.move_paths,
                             &mut data.path_map,
@@ -272,7 +271,7 @@ impl<'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> MoveDataBuilder<'a, 'tcx, F> {
             tcx,
             ..
         } = self;
-        *rev_lookup.projections.entry((base, elem.lift())).or_insert_with(move || {
+        *rev_lookup.projections.entry((base, elem.kind())).or_insert_with(move || {
             new_move_path(move_paths, path_map, init_path_map, Some(base), mk_place(*tcx))
         })
     }
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
index 18985ba0da2..466416d63f5 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/mod.rs
@@ -1,3 +1,15 @@
+//! The move-analysis portion of borrowck needs to work in an abstract domain of lifted `Place`s.
+//! Most of the `Place` variants fall into a one-to-one mapping between the concrete and abstract
+//! (e.g., a field projection on a local variable, `x.field`, has the same meaning in both
+//! domains). In other words, all field projections for the same field on the same local do not
+//! have meaningfully different types if ever. Indexed projections are the exception: `a[x]` needs
+//! to be treated as mapping to the same move path as `a[y]` as well as `a[13]`, etc. So we map
+//! these `x`/`y` values to `()`.
+//!
+//! (In theory, the analysis could be extended to work with sets of paths, so that `a[0]` and
+//! `a[13]` could be kept distinct, while `a[x]` would still overlap them both. But that is not
+//! what this representation does today.)
+
 use std::fmt;
 use std::ops::{Index, IndexMut};
 
@@ -8,11 +20,8 @@ use rustc_middle::ty::{Ty, TyCtxt};
 use rustc_span::Span;
 use smallvec::SmallVec;
 
-use self::abs_domain::Lift;
 use crate::un_derefer::UnDerefer;
 
-mod abs_domain;
-
 rustc_index::newtype_index! {
     #[orderable]
     #[debug_format = "mp{}"]
@@ -324,7 +333,7 @@ impl<'tcx> MovePathLookup<'tcx> {
         };
 
         for (_, elem) in self.un_derefer.iter_projections(place) {
-            if let Some(&subpath) = self.projections.get(&(result, elem.lift())) {
+            if let Some(&subpath) = self.projections.get(&(result, elem.kind())) {
                 result = subpath;
             } else {
                 return LookupResult::Parent(Some(result));
diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs
index 5568d42ab8f..879a20e771d 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters.rs
@@ -16,7 +16,6 @@ use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
 
 mod balanced_flow;
 pub(crate) mod node_flow;
-mod union_find;
 
 /// Struct containing the results of [`prepare_bcb_counters_data`].
 pub(crate) struct BcbCountersData {
diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
index 91ed54b8b59..e063f75887b 100644
--- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
+++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs
@@ -7,13 +7,12 @@
 //! (Knuth & Stevenson, 1973).
 
 use rustc_data_structures::graph;
+use rustc_data_structures::union_find::UnionFind;
 use rustc_index::bit_set::DenseBitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 pub(crate) use rustc_middle::mir::coverage::NodeFlowData;
 use rustc_middle::mir::coverage::Op;
 
-use crate::coverage::counters::union_find::UnionFind;
-
 #[cfg(test)]
 mod tests;
 
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 952da2cdf72..5a13394543b 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -447,26 +447,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
 
             Projection(base, elem) => {
                 let base = self.evaluated[base].as_ref()?;
-                let elem = match elem {
-                    ProjectionElem::Deref => ProjectionElem::Deref,
-                    ProjectionElem::Downcast(name, read_variant) => {
-                        ProjectionElem::Downcast(name, read_variant)
-                    }
-                    ProjectionElem::Field(f, ()) => ProjectionElem::Field(f, ty.ty),
-                    ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
-                        ProjectionElem::ConstantIndex { offset, min_length, from_end }
-                    }
-                    ProjectionElem::Subslice { from, to, from_end } => {
-                        ProjectionElem::Subslice { from, to, from_end }
-                    }
-                    ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty.ty),
-                    ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty.ty),
-                    ProjectionElem::UnwrapUnsafeBinder(()) => {
-                        ProjectionElem::UnwrapUnsafeBinder(ty.ty)
-                    }
-                    // This should have been replaced by a `ConstantIndex` earlier.
-                    ProjectionElem::Index(_) => return None,
-                };
+                // `Index` by constants should have been replaced by `ConstantIndex` by
+                // `simplify_place_projection`.
+                let elem = elem.try_map(|_| None, |()| ty.ty)?;
                 self.ecx.project(base, elem).discard_err()?
             }
             Address { place, kind: _, provenance: _ } => {
@@ -476,13 +459,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                 let local = self.locals[place.local]?;
                 let pointer = self.evaluated[local].as_ref()?;
                 let mut mplace = self.ecx.deref_pointer(pointer).discard_err()?;
-                for proj in place.projection.iter().skip(1) {
-                    // We have no call stack to associate a local with a value, so we cannot
-                    // interpret indexing.
-                    if matches!(proj, ProjectionElem::Index(_)) {
-                        return None;
-                    }
-                    mplace = self.ecx.project(&mplace, proj).discard_err()?;
+                for elem in place.projection.iter().skip(1) {
+                    // `Index` by constants should have been replaced by `ConstantIndex` by
+                    // `simplify_place_projection`.
+                    let elem = elem.try_map(|_| None, |ty| ty)?;
+                    mplace = self.ecx.project(&mplace, elem).discard_err()?;
                 }
                 let pointer = mplace.to_ref(&self.ecx);
                 ImmTy::from_immediate(pointer, ty).into()
@@ -902,27 +883,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         proj: ProjectionElem<VnIndex, ()>,
         loc: Location,
     ) -> Option<PlaceElem<'tcx>> {
-        Some(match proj {
-            ProjectionElem::Deref => ProjectionElem::Deref,
-            ProjectionElem::Field(idx, ()) => ProjectionElem::Field(idx, ty),
-            ProjectionElem::Index(idx) => {
-                let Some(local) = self.try_as_local(idx, loc) else {
-                    return None;
-                };
+        proj.try_map(
+            |value| {
+                let local = self.try_as_local(value, loc)?;
                 self.reused_locals.insert(local);
-                ProjectionElem::Index(local)
-            }
-            ProjectionElem::ConstantIndex { offset, min_length, from_end } => {
-                ProjectionElem::ConstantIndex { offset, min_length, from_end }
-            }
-            ProjectionElem::Subslice { from, to, from_end } => {
-                ProjectionElem::Subslice { from, to, from_end }
-            }
-            ProjectionElem::Downcast(symbol, idx) => ProjectionElem::Downcast(symbol, idx),
-            ProjectionElem::OpaqueCast(()) => ProjectionElem::OpaqueCast(ty),
-            ProjectionElem::Subtype(()) => ProjectionElem::Subtype(ty),
-            ProjectionElem::UnwrapUnsafeBinder(()) => ProjectionElem::UnwrapUnsafeBinder(ty),
-        })
+                Some(local)
+            },
+            |()| ty,
+        )
     }
 
     fn simplify_aggregate_to_copy(
diff --git a/compiler/rustc_mir_transform/src/ref_prop.rs b/compiler/rustc_mir_transform/src/ref_prop.rs
index d1c2d6b508f..6f61215cee2 100644
--- a/compiler/rustc_mir_transform/src/ref_prop.rs
+++ b/compiler/rustc_mir_transform/src/ref_prop.rs
@@ -79,6 +79,7 @@ impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation {
     #[instrument(level = "trace", skip(self, tcx, body))]
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         debug!(def_id = ?body.source.def_id());
+        move_to_copy_pointers(tcx, body);
         while propagate_ssa(tcx, body) {}
     }
 
@@ -87,11 +88,43 @@ impl<'tcx> crate::MirPass<'tcx> for ReferencePropagation {
     }
 }
 
+/// The SSA analysis done by [`SsaLocals`] treats [`Operand::Move`] as a read, even though in
+/// general [`Operand::Move`] represents pass-by-pointer where the callee can overwrite the
+/// pointee (Miri always considers the place deinitialized). CopyProp has a similar trick to
+/// turn [`Operand::Move`] into [`Operand::Copy`] when required for an optimization, but in this
+/// pass we just turn all moves of pointers into copies because pointers should be by-value anyway.
+fn move_to_copy_pointers<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    let mut visitor = MoveToCopyVisitor { tcx, local_decls: &body.local_decls };
+    for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
+        visitor.visit_basic_block_data(bb, data);
+    }
+
+    struct MoveToCopyVisitor<'a, 'tcx> {
+        tcx: TyCtxt<'tcx>,
+        local_decls: &'a IndexVec<Local, LocalDecl<'tcx>>,
+    }
+
+    impl<'a, 'tcx> MutVisitor<'tcx> for MoveToCopyVisitor<'a, 'tcx> {
+        fn tcx(&self) -> TyCtxt<'tcx> {
+            self.tcx
+        }
+
+        fn visit_operand(&mut self, operand: &mut Operand<'tcx>, loc: Location) {
+            if let Operand::Move(place) = *operand {
+                if place.ty(self.local_decls, self.tcx).ty.is_any_ptr() {
+                    *operand = Operand::Copy(place);
+                }
+            }
+            self.super_operand(operand, loc);
+        }
+    }
+}
+
 fn propagate_ssa<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
     let typing_env = body.typing_env(tcx);
     let ssa = SsaLocals::new(tcx, body, typing_env);
 
-    let mut replacer = compute_replacement(tcx, body, &ssa);
+    let mut replacer = compute_replacement(tcx, body, ssa);
     debug!(?replacer.targets);
     debug!(?replacer.allowed_replacements);
     debug!(?replacer.storage_to_remove);
@@ -119,7 +152,7 @@ enum Value<'tcx> {
 fn compute_replacement<'tcx>(
     tcx: TyCtxt<'tcx>,
     body: &Body<'tcx>,
-    ssa: &SsaLocals,
+    ssa: SsaLocals,
 ) -> Replacer<'tcx> {
     let always_live_locals = always_storage_live_locals(body);
 
@@ -138,7 +171,7 @@ fn compute_replacement<'tcx>(
     // reborrowed references.
     let mut storage_to_remove = DenseBitSet::new_empty(body.local_decls.len());
 
-    let fully_replaceable_locals = fully_replaceable_locals(ssa);
+    let fully_replaceable_locals = fully_replaceable_locals(&ssa);
 
     // Returns true iff we can use `place` as a pointee.
     //
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 1c09cab53b8..4ca2f57bd87 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -359,6 +359,20 @@ parse_generics_in_path = unexpected generic arguments in path
 
 parse_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
 parse_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
+
+parse_hidden_unicode_codepoints = unicode codepoint changing visible direction of text present in {$label}
+    .label = this {$label} contains {$count ->
+        [one] an invisible
+        *[other] invisible
+    } unicode text flow control {$count ->
+        [one] codepoint
+        *[other] codepoints
+    }
+    .note = these kind of unicode codepoints change the way text flows on applications that support them, but can cause confusion because they change the order of characters on the screen
+    .suggestion_remove = if their presence wasn't intentional, you can remove them
+    .suggestion_escape = if you want to keep them but make them visible in your source code, you can escape them
+    .no_suggestion_note_escape = if you want to keep them but make them visible in your source code, you can escape them: {$escaped}
+
 parse_if_expression_missing_condition = missing condition for `if` expression
     .condition_label = expected condition here
     .block_label = if this block is the condition of the `if` expression, then it must be followed by another block
@@ -463,9 +477,6 @@ parse_invalid_dyn_keyword = invalid `dyn` keyword
 parse_invalid_expression_in_let_else = a `{$operator}` expression cannot be directly assigned in `let...else`
 parse_invalid_identifier_with_leading_number = identifiers cannot start with a number
 
-parse_invalid_label =
-    invalid label name `{$name}`
-
 parse_invalid_literal_suffix_on_tuple_index = suffixes on a tuple index are invalid
     .label = invalid suffix `{$suffix}`
     .tuple_exception_line_1 = `{$suffix}` is *temporarily* accepted on tuple index fields as it was incorrectly accepted on stable for a few releases
@@ -495,6 +506,8 @@ parse_invalid_unicode_escape = invalid unicode character escape
 parse_invalid_variable_declaration =
     invalid variable declaration
 
+parse_keyword_label = labels cannot use keyword names
+
 parse_keyword_lifetime =
     lifetimes cannot use keyword names
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index dfd4f38cf03..797d4830c2f 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -8,8 +8,9 @@ use rustc_ast::{Path, Visibility};
 use rustc_errors::codes::*;
 use rustc_errors::{
     Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic,
+    SuggestionStyle,
 };
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::edition::{Edition, LATEST_STABLE_EDITION};
 use rustc_span::{Ident, Span, Symbol};
@@ -2228,11 +2229,10 @@ pub(crate) struct KeywordLifetime {
 }
 
 #[derive(Diagnostic)]
-#[diag(parse_invalid_label)]
-pub(crate) struct InvalidLabel {
+#[diag(parse_keyword_label)]
+pub(crate) struct KeywordLabel {
     #[primary_span]
     pub span: Span,
-    pub name: Symbol,
 }
 
 #[derive(Diagnostic)]
@@ -3602,3 +3602,76 @@ pub(crate) struct ExpectedRegisterClassOrExplicitRegister {
     #[primary_span]
     pub(crate) span: Span,
 }
+
+#[derive(LintDiagnostic)]
+#[diag(parse_hidden_unicode_codepoints)]
+#[note]
+pub(crate) struct HiddenUnicodeCodepointsDiag {
+    pub label: String,
+    pub count: usize,
+    #[label]
+    pub span_label: Span,
+    #[subdiagnostic]
+    pub labels: Option<HiddenUnicodeCodepointsDiagLabels>,
+    #[subdiagnostic]
+    pub sub: HiddenUnicodeCodepointsDiagSub,
+}
+
+pub(crate) struct HiddenUnicodeCodepointsDiagLabels {
+    pub spans: Vec<(char, Span)>,
+}
+
+impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels {
+    fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
+        for (c, span) in self.spans {
+            diag.span_label(span, format!("{c:?}"));
+        }
+    }
+}
+
+pub(crate) enum HiddenUnicodeCodepointsDiagSub {
+    Escape { spans: Vec<(char, Span)> },
+    NoEscape { spans: Vec<(char, Span)> },
+}
+
+// Used because of multiple multipart_suggestion and note
+impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub {
+    fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
+        match self {
+            HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
+                diag.multipart_suggestion_with_style(
+                    fluent::parse_suggestion_remove,
+                    spans.iter().map(|(_, span)| (*span, "".to_string())).collect(),
+                    Applicability::MachineApplicable,
+                    SuggestionStyle::HideCodeAlways,
+                );
+                diag.multipart_suggestion(
+                    fluent::parse_suggestion_escape,
+                    spans
+                        .into_iter()
+                        .map(|(c, span)| {
+                            let c = format!("{c:?}");
+                            (span, c[1..c.len() - 1].to_string())
+                        })
+                        .collect(),
+                    Applicability::MachineApplicable,
+                );
+            }
+            HiddenUnicodeCodepointsDiagSub::NoEscape { spans } => {
+                // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
+                // should do the same here to provide the same good suggestions as we do for
+                // literals above.
+                diag.arg(
+                    "escaped",
+                    spans
+                        .into_iter()
+                        .map(|(c, _)| format!("{c:?}"))
+                        .collect::<Vec<String>>()
+                        .join(", "),
+                );
+                diag.note(fluent::parse_suggestion_remove);
+                diag.note(fluent::parse_no_suggestion_note_escape);
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 7c7e7e50b27..9792240a548 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -543,21 +543,21 @@ impl<'psess, 'src> Lexer<'psess, 'src> {
             })
             .collect();
 
+        let label = label.to_string();
         let count = spans.len();
-        let labels = point_at_inner_spans.then_some(spans.clone());
+        let labels = point_at_inner_spans
+            .then_some(errors::HiddenUnicodeCodepointsDiagLabels { spans: spans.clone() });
+        let sub = if point_at_inner_spans && !spans.is_empty() {
+            errors::HiddenUnicodeCodepointsDiagSub::Escape { spans }
+        } else {
+            errors::HiddenUnicodeCodepointsDiagSub::NoEscape { spans }
+        };
 
         self.psess.buffer_lint(
             TEXT_DIRECTION_CODEPOINT_IN_LITERAL,
             span,
             ast::CRATE_NODE_ID,
-            BuiltinLintDiag::HiddenUnicodeCodepoints {
-                label: label.to_string(),
-                count,
-                span_label: span,
-                labels,
-                escape: point_at_inner_spans && !spans.is_empty(),
-                spans,
-            },
+            errors::HiddenUnicodeCodepointsDiag { label, count, span_label: span, labels, sub },
         );
     }
 
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 6aa6fc2c790..1499808966c 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -3094,7 +3094,7 @@ impl<'a> Parser<'a> {
         if let Some((ident, is_raw)) = self.token.lifetime() {
             // Disallow `'fn`, but with a better error message than `expect_lifetime`.
             if matches!(is_raw, IdentIsRaw::No) && ident.without_first_quote().is_reserved() {
-                self.dcx().emit_err(errors::InvalidLabel { span: ident.span, name: ident.name });
+                self.dcx().emit_err(errors::KeywordLabel { span: ident.span });
             }
 
             self.bump();
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 899a43955ab..6168647183f 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -137,14 +137,37 @@ impl<'a> Parser<'a> {
     /// The difference from `parse_ty` is that this version allows `...`
     /// (`CVarArgs`) at the top level of the type.
     pub(super) fn parse_ty_for_param(&mut self) -> PResult<'a, Box<Ty>> {
-        self.parse_ty_common(
+        let ty = self.parse_ty_common(
             AllowPlus::Yes,
             AllowCVariadic::Yes,
             RecoverQPath::Yes,
             RecoverReturnSign::Yes,
             None,
             RecoverQuestionMark::Yes,
-        )
+        )?;
+
+        // Recover a trailing `= EXPR` if present.
+        if self.may_recover()
+            && self.check_noexpect(&token::Eq)
+            && self.look_ahead(1, |tok| tok.can_begin_expr())
+        {
+            let snapshot = self.create_snapshot_for_diagnostic();
+            self.bump();
+            let eq_span = self.prev_token.span;
+            match self.parse_expr() {
+                Ok(e) => {
+                    self.dcx()
+                        .struct_span_err(eq_span.to(e.span), "parameter defaults are not supported")
+                        .emit();
+                }
+                Err(diag) => {
+                    diag.cancel();
+                    self.restore_snapshot(snapshot);
+                }
+            }
+        }
+
+        Ok(ty)
     }
 
     /// Parses a type in restricted contexts where `+` is not permitted.
@@ -1479,8 +1502,7 @@ impl<'a> Parser<'a> {
     pub(super) fn expect_lifetime(&mut self) -> Lifetime {
         if let Some((ident, is_raw)) = self.token.lifetime() {
             if matches!(is_raw, IdentIsRaw::No)
-                && ident.without_first_quote().is_reserved()
-                && ![kw::UnderscoreLifetime, kw::StaticLifetime].contains(&ident.name)
+                && ident.without_first_quote().is_reserved_lifetime()
             {
                 self.dcx().emit_err(errors::KeywordLifetime { span: ident.span });
             }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 70eae82392c..077afd6dd23 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -167,7 +167,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 Attribute::Parsed(AttributeKind::Deprecation { .. }) => {
                     self.check_deprecated(hir_id, attr, span, target)
                 }
-                Attribute::Parsed(AttributeKind::TargetFeature(_, attr_span)) => {
+                Attribute::Parsed(AttributeKind::TargetFeature{ attr_span, ..}) => {
                     self.check_target_feature(hir_id, *attr_span, target, attrs)
                 }
                 Attribute::Parsed(AttributeKind::RustcObjectLifetimeDefault) => {
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 64641ecc080..337e7d2dd86 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1020,11 +1020,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         &mut self,
         suggestions: &mut Vec<TypoSuggestion>,
         scope_set: ScopeSet<'ra>,
-        parent_scope: &ParentScope<'ra>,
+        ps: &ParentScope<'ra>,
         ctxt: SyntaxContext,
         filter_fn: &impl Fn(Res) -> bool,
     ) {
-        self.cm().visit_scopes(scope_set, parent_scope, ctxt, |this, scope, use_prelude, _| {
+        self.cm().visit_scopes(scope_set, ps, ctxt, None, |this, scope, use_prelude, _| {
             match scope {
                 Scope::DeriveHelpers(expn_id) => {
                     let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper);
@@ -1557,7 +1557,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             });
         }
         for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
-            let Ok(binding) = self.cm().early_resolve_ident_in_lexical_scope(
+            let Ok(binding) = self.cm().resolve_ident_in_scope_set(
                 ident,
                 ScopeSet::All(ns),
                 parent_scope,
@@ -2371,7 +2371,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     }
                 } else {
                     self.cm()
-                        .early_resolve_ident_in_lexical_scope(
+                        .resolve_ident_in_scope_set(
                             ident,
                             ScopeSet::All(ns_to_try),
                             parent_scope,
@@ -2474,7 +2474,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     },
                 )
             });
-            if let Ok(binding) = self.cm().early_resolve_ident_in_lexical_scope(
+            if let Ok(binding) = self.cm().resolve_ident_in_scope_set(
                 ident,
                 ScopeSet::All(ValueNS),
                 parent_scope,
diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs
index 650154d586f..dae42645bec 100644
--- a/compiler/rustc_resolve/src/ident.rs
+++ b/compiler/rustc_resolve/src/ident.rs
@@ -19,7 +19,7 @@ use crate::{
     AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BindingKey, CmResolver, Determinacy,
     Finalize, ImportKind, LexicalScopeBinding, Module, ModuleKind, ModuleOrUniformRoot,
     NameBinding, NameBindingKind, ParentScope, PathResult, PrivacyError, Res, ResolutionError,
-    Resolver, Scope, ScopeSet, Segment, Used, Weak, errors,
+    Resolver, Scope, ScopeSet, Segment, Stage, Used, Weak, errors,
 };
 
 #[derive(Copy, Clone)]
@@ -49,6 +49,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         scope_set: ScopeSet<'ra>,
         parent_scope: &ParentScope<'ra>,
         ctxt: SyntaxContext,
+        derive_fallback_lint_id: Option<NodeId>,
         mut visitor: impl FnMut(
             &mut CmResolver<'r, 'ra, 'tcx>,
             Scope<'ra>,
@@ -99,15 +100,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
 
         let rust_2015 = ctxt.edition().is_rust_2015();
         let (ns, macro_kind) = match scope_set {
-            ScopeSet::All(ns)
-            | ScopeSet::ModuleAndExternPrelude(ns, _)
-            | ScopeSet::Late(ns, ..) => (ns, None),
+            ScopeSet::All(ns) | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None),
             ScopeSet::ExternPrelude => (TypeNS, None),
             ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)),
         };
         let module = match scope_set {
             // Start with the specified module.
-            ScopeSet::Late(_, module, _) | ScopeSet::ModuleAndExternPrelude(_, module) => module,
+            ScopeSet::ModuleAndExternPrelude(_, module) => module,
             // Jump out of trait or enum modules, they do not act as scopes.
             _ => parent_scope.module.nearest_item_scope(),
         };
@@ -193,10 +192,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 },
                 Scope::Module(module, prev_lint_id) => {
                     use_prelude = !module.no_implicit_prelude;
-                    let derive_fallback_lint_id = match scope_set {
-                        ScopeSet::Late(.., lint_id) => lint_id,
-                        _ => None,
-                    };
                     match self.hygienic_lexical_parent(module, &mut ctxt, derive_fallback_lint_id) {
                         Some((parent_module, lint_id)) => {
                             Scope::Module(parent_module, lint_id.or(prev_lint_id))
@@ -349,11 +344,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 return Some(LexicalScopeBinding::Item(binding));
             } else if let RibKind::Module(module) = rib.kind {
                 // Encountered a module item, abandon ribs and look into that module and preludes.
+                let parent_scope = &ParentScope { module, ..*parent_scope };
+                let finalize = finalize.map(|f| Finalize { stage: Stage::Late, ..f });
                 return self
                     .cm()
-                    .early_resolve_ident_in_lexical_scope(
+                    .resolve_ident_in_scope_set(
                         orig_ident,
-                        ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)),
+                        ScopeSet::All(ns),
                         parent_scope,
                         finalize,
                         finalize.is_some(),
@@ -376,13 +373,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         unreachable!()
     }
 
-    /// Resolve an identifier in lexical scope.
-    /// This is a variation of `fn resolve_ident_in_lexical_scope` that can be run during
-    /// expansion and import resolution (perhaps they can be merged in the future).
-    /// The function is used for resolving initial segments of macro paths (e.g., `foo` in
-    /// `foo::bar!();` or `foo!();`) and also for import paths on 2018 edition.
+    /// Resolve an identifier in the specified set of scopes.
     #[instrument(level = "debug", skip(self))]
-    pub(crate) fn early_resolve_ident_in_lexical_scope<'r>(
+    pub(crate) fn resolve_ident_in_scope_set<'r>(
         self: CmResolver<'r, 'ra, 'tcx>,
         orig_ident: Ident,
         scope_set: ScopeSet<'ra>,
@@ -411,9 +404,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
 
         let (ns, macro_kind) = match scope_set {
-            ScopeSet::All(ns)
-            | ScopeSet::ModuleAndExternPrelude(ns, _)
-            | ScopeSet::Late(ns, ..) => (ns, None),
+            ScopeSet::All(ns) | ScopeSet::ModuleAndExternPrelude(ns, _) => (ns, None),
             ScopeSet::ExternPrelude => (TypeNS, None),
             ScopeSet::Macro(macro_kind) => (MacroNS, Some(macro_kind)),
         };
@@ -437,10 +428,15 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         }
 
         // Go through all the scopes and try to resolve the name.
+        let derive_fallback_lint_id = match finalize {
+            Some(Finalize { node_id, stage: Stage::Late, .. }) => Some(node_id),
+            _ => None,
+        };
         let break_result = self.visit_scopes(
             scope_set,
             parent_scope,
             orig_ident.span.ctxt(),
+            derive_fallback_lint_id,
             |this, scope, use_prelude, ctxt| {
                 let ident = Ident::new(orig_ident.name, orig_ident.span.with_ctxt(ctxt));
                 let result = match scope {
@@ -510,11 +506,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                             ident,
                             ns,
                             adjusted_parent_scope,
-                            if matches!(scope_set, ScopeSet::Late(..)) {
-                                Shadowing::Unrestricted
-                            } else {
-                                Shadowing::Restricted
-                            },
+                            Shadowing::Restricted,
                             adjusted_finalize,
                             ignore_binding,
                             ignore_import,
@@ -643,7 +635,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                             return None;
                         }
 
-                        if finalize.is_none() || matches!(scope_set, ScopeSet::Late(..)) {
+                        // Below we report various ambiguity errors.
+                        // We do not need to report them if we are either in speculative resolution,
+                        // or in late resolution when everything is already imported and expanded
+                        // and no ambiguities exist.
+                        if matches!(finalize, None | Some(Finalize { stage: Stage::Late, .. })) {
                             return Some(Ok(binding));
                         }
 
@@ -811,7 +807,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             ModuleOrUniformRoot::Module(module) => module,
             ModuleOrUniformRoot::ModuleAndExternPrelude(module) => {
                 assert_eq!(shadowing, Shadowing::Unrestricted);
-                let binding = self.early_resolve_ident_in_lexical_scope(
+                let binding = self.resolve_ident_in_scope_set(
                     ident,
                     ScopeSet::ModuleAndExternPrelude(ns, module),
                     parent_scope,
@@ -827,7 +823,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                 return if ns != TypeNS {
                     Err((Determined, Weak::No))
                 } else {
-                    let binding = self.early_resolve_ident_in_lexical_scope(
+                    let binding = self.resolve_ident_in_scope_set(
                         ident,
                         ScopeSet::ExternPrelude,
                         parent_scope,
@@ -852,7 +848,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     }
                 }
 
-                let binding = self.early_resolve_ident_in_lexical_scope(
+                let binding = self.resolve_ident_in_scope_set(
                     ident,
                     ScopeSet::All(ns),
                     parent_scope,
@@ -945,7 +941,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         // Now we are in situation when new item/import can appear only from a glob or a macro
         // expansion. With restricted shadowing names from globs and macro expansions cannot
         // shadow names from outer scopes, so we can freely fallback from module search to search
-        // in outer scopes. For `early_resolve_ident_in_lexical_scope` to continue search in outer
+        // in outer scopes. For `resolve_ident_in_scope_set` to continue search in outer
         // scopes we return `Undetermined` with `Weak::Yes`.
 
         // Check if one of unexpanded macros can still define the name,
@@ -1040,6 +1036,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         // Forbid expanded shadowing to avoid time travel.
         if let Some(shadowed_glob) = shadowed_glob
             && shadowing == Shadowing::Restricted
+            && finalize.stage == Stage::Early
             && binding.expansion != LocalExpnId::ROOT
             && binding.res() != shadowed_glob.res()
         {
@@ -1635,7 +1632,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     _ => Err(Determinacy::determined(finalize.is_some())),
                 }
             } else {
-                self.reborrow().early_resolve_ident_in_lexical_scope(
+                self.reborrow().resolve_ident_in_scope_set(
                     ident,
                     ScopeSet::All(ns),
                     parent_scope,
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index d3790394d2a..d7fc028287f 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -1446,7 +1446,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
                     return;
                 }
 
-                match this.cm().early_resolve_ident_in_lexical_scope(
+                match this.cm().resolve_ident_in_scope_set(
                     target,
                     ScopeSet::All(ns),
                     &import.parent_scope,
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 9b201e40d13..80b2095d8cc 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -38,8 +38,8 @@ use crate::late::{
 };
 use crate::ty::fast_reject::SimplifiedType;
 use crate::{
-    Module, ModuleKind, ModuleOrUniformRoot, PathResult, PathSource, Resolver, ScopeSet, Segment,
-    errors, path_names_to_string,
+    Module, ModuleKind, ModuleOrUniformRoot, ParentScope, PathResult, PathSource, Resolver,
+    ScopeSet, Segment, errors, path_names_to_string,
 };
 
 type Res = def::Res<ast::NodeId>;
@@ -2460,10 +2460,11 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     self.r.add_module_candidates(module, &mut names, &filter_fn, Some(ctxt));
                 } else if let RibKind::Module(module) = rib.kind {
                     // Encountered a module item, abandon ribs and look into that module and preludes.
+                    let parent_scope = &ParentScope { module, ..self.parent_scope };
                     self.r.add_scope_set_candidates(
                         &mut names,
-                        ScopeSet::Late(ns, module, None),
-                        &self.parent_scope,
+                        ScopeSet::All(ns),
+                        parent_scope,
                         ctxt,
                         filter_fn,
                     );
@@ -3094,7 +3095,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         } else {
             self.suggest_introducing_lifetime(
                 &mut err,
-                Some(lifetime_ref.ident.name.as_str()),
+                Some(lifetime_ref.ident),
                 |err, _, span, message, suggestion, span_suggs| {
                     err.multipart_suggestion_verbose(
                         message,
@@ -3112,7 +3113,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
     fn suggest_introducing_lifetime(
         &self,
         err: &mut Diag<'_>,
-        name: Option<&str>,
+        name: Option<Ident>,
         suggest: impl Fn(
             &mut Diag<'_>,
             bool,
@@ -3159,7 +3160,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                     let mut rm_inner_binders: FxIndexSet<Span> = Default::default();
                     let (span, sugg) = if span.is_empty() {
                         let mut binder_idents: FxIndexSet<Ident> = Default::default();
-                        binder_idents.insert(Ident::from_str(name.unwrap_or("'a")));
+                        binder_idents.insert(name.unwrap_or(Ident::from_str("'a")));
 
                         // We need to special case binders in the following situation:
                         // Change `T: for<'a> Trait<T> + 'b` to `for<'a, 'b> T: Trait<T> + 'b`
@@ -3189,16 +3190,11 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                             }
                         }
 
-                        let binders_sugg = binder_idents.into_iter().enumerate().fold(
-                            "".to_string(),
-                            |mut binders, (i, x)| {
-                                if i != 0 {
-                                    binders += ", ";
-                                }
-                                binders += x.as_str();
-                                binders
-                            },
-                        );
+                        let binders_sugg: String = binder_idents
+                            .into_iter()
+                            .map(|ident| ident.to_string())
+                            .intersperse(", ".to_owned())
+                            .collect();
                         let sugg = format!(
                             "{}<{}>{}",
                             if higher_ranked { "for" } else { "" },
@@ -3214,7 +3210,8 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                             .source_map()
                             .span_through_char(span, '<')
                             .shrink_to_hi();
-                        let sugg = format!("{}, ", name.unwrap_or("'a"));
+                        let sugg =
+                            format!("{}, ", name.map(|i| i.to_string()).as_deref().unwrap_or("'a"));
                         (span, sugg)
                     };
 
@@ -3222,7 +3219,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
                         let message = Cow::from(format!(
                             "consider making the {} lifetime-generic with a new `{}` lifetime",
                             kind.descr(),
-                            name.unwrap_or("'a"),
+                            name.map(|i| i.to_string()).as_deref().unwrap_or("'a"),
                         ));
                         should_continue = suggest(
                             err,
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 2063c46124c..2afb52ef4d4 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -49,7 +49,7 @@ use rustc_data_structures::intern::Interned;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{FreezeReadGuard, FreezeWriteGuard};
 use rustc_data_structures::unord::{UnordMap, UnordSet};
-use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed};
+use rustc_errors::{Applicability, Diag, ErrCode, ErrorGuaranteed, LintBuffer};
 use rustc_expand::base::{DeriveResolution, SyntaxExtension, SyntaxExtensionKind};
 use rustc_feature::BUILTIN_ATTRIBUTES;
 use rustc_hir::attrs::StrippedCfgItem;
@@ -72,8 +72,8 @@ use rustc_middle::ty::{
     ResolverGlobalCtxt, TyCtxt, TyCtxtFeed, Visibility,
 };
 use rustc_query_system::ich::StableHashingContext;
+use rustc_session::lint::BuiltinLintDiag;
 use rustc_session::lint::builtin::PRIVATE_MACRO_USE;
-use rustc_session::lint::{BuiltinLintDiag, LintBuffer};
 use rustc_span::hygiene::{ExpnId, LocalExpnId, MacroKind, SyntaxContext, Transparency};
 use rustc_span::{DUMMY_SP, Ident, Macros20NormalizedIdent, Span, Symbol, kw, sym};
 use smallvec::{SmallVec, smallvec};
@@ -156,11 +156,8 @@ enum ScopeSet<'ra> {
     ModuleAndExternPrelude(Namespace, Module<'ra>),
     /// Just two extern prelude scopes.
     ExternPrelude,
-    /// All scopes with macro namespace and the given macro kind restriction.
+    /// Same as `All(MacroNS)`, but with the given macro kind restriction.
     Macro(MacroKind),
-    /// All scopes with the given namespace, used for partially performing late resolution.
-    /// The node id enables lints and is used for reporting them.
-    Late(Namespace, Module<'ra>, Option<NodeId>),
 }
 
 /// Everything you need to know about a name's location to resolve it.
@@ -1888,7 +1885,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             }
         }
 
-        self.cm().visit_scopes(ScopeSet::All(TypeNS), parent_scope, ctxt, |this, scope, _, _| {
+        let scope_set = ScopeSet::All(TypeNS);
+        self.cm().visit_scopes(scope_set, parent_scope, ctxt, None, |this, scope, _, _| {
             match scope {
                 Scope::Module(module, _) => {
                     this.get_mut().traits_in_module(module, assoc_item, &mut found_traits);
@@ -2455,6 +2453,17 @@ fn module_to_string(mut module: Module<'_>) -> Option<String> {
     Some(names_to_string(names.iter().rev().copied()))
 }
 
+#[derive(Copy, Clone, PartialEq, Debug)]
+enum Stage {
+    /// Resolving an import or a macro.
+    /// Used when macro expansion is either not yet finished, or we are finalizing its results.
+    /// Used by default as a more restrictive variant that can produce additional errors.
+    Early,
+    /// Resolving something in late resolution when all imports are resolved
+    /// and all macros are expanded.
+    Late,
+}
+
 #[derive(Copy, Clone, Debug)]
 struct Finalize {
     /// Node ID for linting.
@@ -2467,9 +2476,11 @@ struct Finalize {
     root_span: Span,
     /// Whether to report privacy errors or silently return "no resolution" for them,
     /// similarly to speculative resolution.
-    report_private: bool,
+    report_private: bool = true,
     /// Tracks whether an item is used in scope or used relatively to a module.
-    used: Used,
+    used: Used = Used::Other,
+    /// Finalizing early or late resolution.
+    stage: Stage = Stage::Early,
 }
 
 impl Finalize {
@@ -2478,7 +2489,7 @@ impl Finalize {
     }
 
     fn with_root_span(node_id: NodeId, path_span: Span, root_span: Span) -> Finalize {
-        Finalize { node_id, path_span, root_span, report_private: true, used: Used::Other }
+        Finalize { node_id, path_span, root_span, .. }
     }
 }
 
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 72ed8990241..5fa87b4cd9b 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -789,7 +789,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             self.prohibit_imported_non_macro_attrs(None, res.ok(), path_span);
             res
         } else {
-            let binding = self.reborrow().early_resolve_ident_in_lexical_scope(
+            let binding = self.reborrow().resolve_ident_in_scope_set(
                 path[0].ident,
                 ScopeSet::Macro(kind),
                 parent_scope,
@@ -951,7 +951,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
         // FIXME: Should be an output of Speculative Resolution.
         let macro_resolutions = self.single_segment_macro_resolutions.take();
         for (ident, kind, parent_scope, initial_binding, sugg_span) in macro_resolutions {
-            match self.cm().early_resolve_ident_in_lexical_scope(
+            match self.cm().resolve_ident_in_scope_set(
                 ident,
                 ScopeSet::Macro(kind),
                 &parent_scope,
@@ -1006,7 +1006,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
 
         let builtin_attrs = mem::take(&mut self.builtin_attrs);
         for (ident, parent_scope) in builtin_attrs {
-            let _ = self.cm().early_resolve_ident_in_lexical_scope(
+            let _ = self.cm().resolve_ident_in_scope_set(
                 ident,
                 ScopeSet::Macro(MacroKind::Attr),
                 &parent_scope,
@@ -1112,7 +1112,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
             // If such resolution is successful and gives the same result
             // (e.g. if the macro is re-imported), then silence the lint.
             let no_macro_rules = self.arenas.alloc_macro_rules_scope(MacroRulesScope::Empty);
-            let fallback_binding = self.reborrow().early_resolve_ident_in_lexical_scope(
+            let fallback_binding = self.reborrow().resolve_ident_in_scope_set(
                 path.segments[0].ident,
                 ScopeSet::Macro(MacroKind::Bang),
                 &ParentScope { macro_rules: no_macro_rules, ..*parent_scope },
diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl
index 80754964c43..b46e8ab4fdc 100644
--- a/compiler/rustc_session/messages.ftl
+++ b/compiler/rustc_session/messages.ftl
@@ -131,6 +131,10 @@ session_target_small_data_threshold_not_supported = `-Z small-data-threshold` is
 
 session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored
 
+session_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag
+    .controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`
+    .incoherent = manually setting a built-in cfg can and does create incoherent behaviors
+
 session_unleashed_feature_help_named = skipping check for `{$gate}` feature
 session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate
 
diff --git a/compiler/rustc_session/src/config/cfg.rs b/compiler/rustc_session/src/config/cfg.rs
index 62891eb4f26..8f63ce6f0ae 100644
--- a/compiler/rustc_session/src/config/cfg.rs
+++ b/compiler/rustc_session/src/config/cfg.rs
@@ -26,13 +26,12 @@ use std::iter;
 use rustc_abi::Align;
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-use rustc_lint_defs::BuiltinLintDiag;
 use rustc_lint_defs::builtin::EXPLICIT_BUILTIN_CFGS_IN_FLAGS;
 use rustc_span::{Symbol, sym};
 use rustc_target::spec::{PanicStrategy, RelocModel, SanitizerSet, Target};
 
-use crate::Session;
 use crate::config::{CrateType, FmtDebug};
+use crate::{Session, errors};
 
 /// The parsed `--cfg` options that define the compilation environment of the
 /// crate, used to drive conditional compilation.
@@ -99,7 +98,7 @@ pub(crate) fn disallow_cfgs(sess: &Session, user_cfgs: &Cfg) {
             EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
             None,
             ast::CRATE_NODE_ID,
-            BuiltinLintDiag::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by },
+            errors::UnexpectedBuiltinCfg { cfg, cfg_name, controlled_by }.into(),
         )
     };
 
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 9471807df01..34da54a20bf 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -7,7 +7,7 @@ use rustc_errors::{
     Diag, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, Level,
     MultiSpan,
 };
-use rustc_macros::{Diagnostic, Subdiagnostic};
+use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_span::{Span, Symbol};
 use rustc_target::spec::{SplitDebuginfo, StackProtector, TargetTuple};
 
@@ -505,3 +505,13 @@ pub(crate) struct SoftFloatIgnored;
 #[note]
 #[note(session_soft_float_deprecated_issue)]
 pub(crate) struct SoftFloatDeprecated;
+
+#[derive(LintDiagnostic)]
+#[diag(session_unexpected_builtin_cfg)]
+#[note(session_controlled_by)]
+#[note(session_incoherent)]
+pub(crate) struct UnexpectedBuiltinCfg {
+    pub(crate) cfg: String,
+    pub(crate) cfg_name: Symbol,
+    pub(crate) controlled_by: &'static str,
+}
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 426480f0dba..9048c51d5b4 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -11,8 +11,8 @@ use rustc_data_structures::sync::{AppendOnlyVec, Lock};
 use rustc_errors::emitter::{FatalOnlyEmitter, HumanEmitter, stderr_destination};
 use rustc_errors::translation::Translator;
 use rustc_errors::{
-    ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, EmissionGuarantee, MultiSpan,
-    StashKey,
+    BufferedEarlyLint, ColorConfig, DecorateDiagCompat, Diag, DiagCtxt, DiagCtxtHandle,
+    DiagMessage, EmissionGuarantee, MultiSpan, StashKey,
 };
 use rustc_feature::{GateIssue, UnstableFeatures, find_feature_issue};
 use rustc_span::edition::Edition;
@@ -27,7 +27,7 @@ use crate::errors::{
     FeatureDiagnosticSuggestion, FeatureGateError, SuggestUpgradeCompiler,
 };
 use crate::lint::builtin::UNSTABLE_SYNTAX_PRE_EXPANSION;
-use crate::lint::{BufferedEarlyLint, BuiltinLintDiag, Lint, LintId};
+use crate::lint::{Lint, LintId};
 
 /// Collected spans during parsing for places where a certain feature was
 /// used and should be feature gated accordingly in `check_crate`.
@@ -342,17 +342,17 @@ impl ParseSess {
         lint: &'static Lint,
         span: impl Into<MultiSpan>,
         node_id: NodeId,
-        diagnostic: BuiltinLintDiag,
+        diagnostic: impl Into<DecorateDiagCompat>,
     ) {
-        self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic)
+        self.opt_span_buffer_lint(lint, Some(span.into()), node_id, diagnostic.into())
     }
 
-    pub fn opt_span_buffer_lint(
+    pub(crate) fn opt_span_buffer_lint(
         &self,
         lint: &'static Lint,
         span: Option<MultiSpan>,
         node_id: NodeId,
-        diagnostic: BuiltinLintDiag,
+        diagnostic: DecorateDiagCompat,
     ) {
         self.buffered_lints.with_lock(|buffered_lints| {
             buffered_lints.push(BufferedEarlyLint {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index dcb1becc957..5d140cc6117 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -901,6 +901,7 @@ symbols! {
         dynamic_no_pic: "dynamic-no-pic",
         e,
         edition_panic,
+        effective_target_features,
         effects,
         eh_catch_typeinfo,
         eh_personality,
@@ -1061,6 +1062,7 @@ symbols! {
         fn_ptr_addr,
         fn_ptr_trait,
         forbid,
+        force_target_feature,
         forget,
         format,
         format_args,
@@ -1754,6 +1756,7 @@ symbols! {
         readonly,
         realloc,
         reason,
+        reborrow,
         receiver,
         receiver_target,
         recursion_limit,
@@ -2533,10 +2536,16 @@ impl fmt::Debug for Ident {
 /// except that AST identifiers don't keep the rawness flag, so we have to guess it.
 impl fmt::Display for Ident {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(&IdentPrinter::new(self.name, self.is_raw_guess(), None), f)
+        fmt::Display::fmt(&IdentPrinter::new(self.name, self.guess_print_mode(), None), f)
     }
 }
 
+pub enum IdentPrintMode {
+    Normal,
+    RawIdent,
+    RawLifetime,
+}
+
 /// The most general type to print identifiers.
 ///
 /// AST pretty-printer is used as a fallback for turning AST structures into token streams for
@@ -2552,7 +2561,7 @@ impl fmt::Display for Ident {
 /// done for a token stream or a single token.
 pub struct IdentPrinter {
     symbol: Symbol,
-    is_raw: bool,
+    mode: IdentPrintMode,
     /// Span used for retrieving the crate name to which `$crate` refers to,
     /// if this field is `None` then the `$crate` conversion doesn't happen.
     convert_dollar_crate: Option<Span>,
@@ -2560,32 +2569,51 @@ pub struct IdentPrinter {
 
 impl IdentPrinter {
     /// The most general `IdentPrinter` constructor. Do not use this.
-    pub fn new(symbol: Symbol, is_raw: bool, convert_dollar_crate: Option<Span>) -> IdentPrinter {
-        IdentPrinter { symbol, is_raw, convert_dollar_crate }
+    pub fn new(
+        symbol: Symbol,
+        mode: IdentPrintMode,
+        convert_dollar_crate: Option<Span>,
+    ) -> IdentPrinter {
+        IdentPrinter { symbol, mode, convert_dollar_crate }
     }
 
     /// This implementation is supposed to be used when printing identifiers
     /// as a part of pretty-printing for larger AST pieces.
     /// Do not use this either.
-    pub fn for_ast_ident(ident: Ident, is_raw: bool) -> IdentPrinter {
-        IdentPrinter::new(ident.name, is_raw, Some(ident.span))
+    pub fn for_ast_ident(ident: Ident, mode: IdentPrintMode) -> IdentPrinter {
+        IdentPrinter::new(ident.name, mode, Some(ident.span))
     }
 }
 
 impl fmt::Display for IdentPrinter {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.is_raw {
-            f.write_str("r#")?;
-        } else if self.symbol == kw::DollarCrate {
-            if let Some(span) = self.convert_dollar_crate {
+        let s = match self.mode {
+            IdentPrintMode::Normal
+                if self.symbol == kw::DollarCrate
+                    && let Some(span) = self.convert_dollar_crate =>
+            {
                 let converted = span.ctxt().dollar_crate_name();
                 if !converted.is_path_segment_keyword() {
                     f.write_str("::")?;
                 }
-                return fmt::Display::fmt(&converted, f);
+                converted
             }
-        }
-        fmt::Display::fmt(&self.symbol, f)
+            IdentPrintMode::Normal => self.symbol,
+            IdentPrintMode::RawIdent => {
+                f.write_str("r#")?;
+                self.symbol
+            }
+            IdentPrintMode::RawLifetime => {
+                f.write_str("'r#")?;
+                let s = self
+                    .symbol
+                    .as_str()
+                    .strip_prefix("'")
+                    .expect("only lifetime idents should be passed with RawLifetime mode");
+                Symbol::intern(s)
+            }
+        };
+        s.fmt(f)
     }
 }
 
@@ -3020,6 +3048,29 @@ impl Ident {
         self.name.can_be_raw() && self.is_reserved()
     }
 
+    /// Given the name of a lifetime without the first quote (`'`),
+    /// returns whether the lifetime name is reserved (therefore invalid)
+    pub fn is_reserved_lifetime(self) -> bool {
+        self.is_reserved() && ![kw::Underscore, kw::Static].contains(&self.name)
+    }
+
+    pub fn is_raw_lifetime_guess(self) -> bool {
+        let name_without_apostrophe = self.without_first_quote();
+        name_without_apostrophe.name != self.name
+            && name_without_apostrophe.name.can_be_raw()
+            && name_without_apostrophe.is_reserved_lifetime()
+    }
+
+    pub fn guess_print_mode(self) -> IdentPrintMode {
+        if self.is_raw_lifetime_guess() {
+            IdentPrintMode::RawLifetime
+        } else if self.is_raw_guess() {
+            IdentPrintMode::RawIdent
+        } else {
+            IdentPrintMode::Normal
+        }
+    }
+
     /// Whether this would be the identifier for a tuple field like `self.0`, as
     /// opposed to a named field like `self.thing`.
     pub fn is_numeric(self) -> bool {
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 399770022b2..a67795e3e93 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -2116,6 +2116,7 @@ supported_targets! {
 
     ("msp430-none-elf", msp430_none_elf),
 
+    ("aarch64_be-unknown-hermit", aarch64_be_unknown_hermit),
     ("aarch64-unknown-hermit", aarch64_unknown_hermit),
     ("riscv64gc-unknown-hermit", riscv64gc_unknown_hermit),
     ("x86_64-unknown-hermit", x86_64_unknown_hermit),
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_hermit.rs b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_hermit.rs
new file mode 100644
index 00000000000..cad57abc2b1
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/aarch64_be_unknown_hermit.rs
@@ -0,0 +1,25 @@
+use rustc_abi::Endian;
+
+use crate::spec::{StackProbeType, Target, TargetMetadata, TargetOptions, base};
+
+pub(crate) fn target() -> Target {
+    Target {
+        llvm_target: "aarch64_be-unknown-hermit".into(),
+        metadata: TargetMetadata {
+            description: Some("ARM64 Hermit (big-endian)".into()),
+            tier: Some(3),
+            host_tools: Some(false),
+            std: Some(true),
+        },
+        pointer_width: 64,
+        arch: "aarch64".into(),
+        data_layout: "E-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(),
+        options: TargetOptions {
+            features: "+v8a,+strict-align,+neon,+fp-armv8".into(),
+            max_atomic_width: Some(128),
+            stack_probes: StackProbeType::Inline,
+            endian: Endian::Big,
+            ..base::hermit::opts()
+        },
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
index f31a85ec07a..40285e5d0e9 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs
@@ -537,7 +537,7 @@ impl<T> Trait<T> for X {
                 }
             }
             TypeError::TargetFeatureCast(def_id) => {
-                let target_spans = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TargetFeature(.., span) => *span);
+                let target_spans = find_attr!(tcx.get_all_attrs(def_id), AttributeKind::TargetFeature{attr_span: span, was_forced: false, ..} => *span);
                 diag.note(
                     "functions with `#[target_feature]` can only be coerced to `unsafe` function pointers"
                 );
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 214a6e86d39..d768e0bf63f 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -275,8 +275,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                         *err.long_ty_path() = long_ty_file;
 
                         let mut suggested = false;
+                        let mut noted_missing_impl = false;
                         if is_try_conversion || is_question_mark {
-                            suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
+                            (suggested, noted_missing_impl) = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
                         }
 
                         if let Some(ret_span) = self.return_type_span(&obligation) {
@@ -335,6 +336,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             return err.emit();
                         }
 
+                        let ty_span = match leaf_trait_predicate.self_ty().skip_binder().kind() {
+                            ty::Adt(def, _) if def.did().is_local()
+                                && !self.can_suggest_derive(&obligation, leaf_trait_predicate) => self.tcx.def_span(def.did()),
+                            _ => DUMMY_SP,
+                        };
                         if let Some(s) = label {
                             // If it has a custom `#[rustc_on_unimplemented]`
                             // error message, let's display it as the label!
@@ -347,15 +353,28 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                                     // Don't say "the trait `FromResidual<Option<Infallible>>` is
                                     // not implemented for `Result<T, E>`".
                             {
-                                err.help(explanation);
+                                // We do this just so that the JSON output's `help` position is the
+                                // right one and not `file.rs:1:1`. The render is the same.
+                                if ty_span == DUMMY_SP {
+                                    err.help(explanation);
+                                } else {
+                                    err.span_help(ty_span, explanation);
+                                }
                             }
                         } else if let Some(custom_explanation) = safe_transmute_explanation {
                             err.span_label(span, custom_explanation);
-                        } else if explanation.len() > self.tcx.sess.diagnostic_width() {
+                        } else if (explanation.len() > self.tcx.sess.diagnostic_width() || ty_span != DUMMY_SP) && !noted_missing_impl {
                             // Really long types don't look good as span labels, instead move it
                             // to a `help`.
                             err.span_label(span, "unsatisfied trait bound");
-                            err.help(explanation);
+
+                            // We do this just so that the JSON output's `help` position is the
+                            // right one and not `file.rs:1:1`. The render is the same.
+                            if ty_span == DUMMY_SP {
+                                err.help(explanation);
+                            } else {
+                                err.span_help(ty_span, explanation);
+                            }
                         } else {
                             err.span_label(span, explanation);
                         }
@@ -939,7 +958,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         err: &mut Diag<'_>,
-    ) -> bool {
+    ) -> (bool, bool) {
         let span = obligation.cause.span;
         /// Look for the (direct) sub-expr of `?`, and return it if it's a `.` method call.
         struct FindMethodSubexprOfTry {
@@ -959,21 +978,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
         }
         let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
-        let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return false };
+        let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return (false, false) };
         let ControlFlow::Break(expr) =
             (FindMethodSubexprOfTry { search_span: span }).visit_body(self.tcx.hir_body(body_id))
         else {
-            return false;
+            return (false, false);
         };
         let Some(typeck) = &self.typeck_results else {
-            return false;
+            return (false, false);
         };
         let ObligationCauseCode::QuestionMark = obligation.cause.code().peel_derives() else {
-            return false;
+            return (false, false);
         };
         let self_ty = trait_pred.skip_binder().self_ty();
         let found_ty = trait_pred.skip_binder().trait_ref.args.get(1).and_then(|a| a.as_type());
-        self.note_missing_impl_for_question_mark(err, self_ty, found_ty, trait_pred);
+        let noted_missing_impl =
+            self.note_missing_impl_for_question_mark(err, self_ty, found_ty, trait_pred);
 
         let mut prev_ty = self.resolve_vars_if_possible(
             typeck.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(self.tcx)),
@@ -1137,7 +1157,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
             }
             prev = Some(err_ty);
         }
-        suggested
+        (suggested, noted_missing_impl)
     }
 
     fn note_missing_impl_for_question_mark(
@@ -1146,7 +1166,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         self_ty: Ty<'_>,
         found_ty: Option<Ty<'_>>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    ) {
+    ) -> bool {
         match (self_ty.kind(), found_ty) {
             (ty::Adt(def, _), Some(ty))
                 if let ty::Adt(found, _) = ty.kind()
@@ -1187,8 +1207,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     format!("`{ty}` needs to implement `Into<{self_ty}>`"),
                 );
             }
-            _ => {}
+            _ => return false,
         }
+        true
     }
 
     fn report_const_param_not_wf(
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 953449c6758..e31ff8b8729 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3853,59 +3853,71 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         }
     }
 
-    pub fn suggest_derive(
+    pub fn can_suggest_derive(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diag<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
-    ) {
+    ) -> bool {
         if trait_pred.polarity() == ty::PredicatePolarity::Negative {
-            return;
+            return false;
         }
         let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
-            return;
+            return false;
         };
         let (adt, args) = match trait_pred.skip_binder().self_ty().kind() {
             ty::Adt(adt, args) if adt.did().is_local() => (adt, args),
-            _ => return,
+            _ => return false,
         };
-        let can_derive = {
-            let is_derivable_trait = match diagnostic_name {
-                sym::Default => !adt.is_enum(),
-                sym::PartialEq | sym::PartialOrd => {
-                    let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
-                    trait_pred.skip_binder().self_ty() == rhs_ty
-                }
-                sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
-                _ => false,
-            };
-            is_derivable_trait &&
-                // Ensure all fields impl the trait.
-                adt.all_fields().all(|field| {
-                    let field_ty = ty::GenericArg::from(field.ty(self.tcx, args));
-                    let trait_args = match diagnostic_name {
-                        sym::PartialEq | sym::PartialOrd => {
-                            Some(field_ty)
-                        }
-                        _ => None,
-                    };
-                    let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
-                        trait_ref: ty::TraitRef::new(self.tcx,
-                            trait_pred.def_id(),
-                            [field_ty].into_iter().chain(trait_args),
-                        ),
-                        ..*tr
-                    });
-                    let field_obl = Obligation::new(
-                        self.tcx,
-                        obligation.cause.clone(),
-                        obligation.param_env,
-                        trait_pred,
-                    );
-                    self.predicate_must_hold_modulo_regions(&field_obl)
-                })
+        let is_derivable_trait = match diagnostic_name {
+            sym::Default => !adt.is_enum(),
+            sym::PartialEq | sym::PartialOrd => {
+                let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
+                trait_pred.skip_binder().self_ty() == rhs_ty
+            }
+            sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
+            _ => false,
+        };
+        is_derivable_trait &&
+            // Ensure all fields impl the trait.
+            adt.all_fields().all(|field| {
+                let field_ty = ty::GenericArg::from(field.ty(self.tcx, args));
+                let trait_args = match diagnostic_name {
+                    sym::PartialEq | sym::PartialOrd => {
+                        Some(field_ty)
+                    }
+                    _ => None,
+                };
+                let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
+                    trait_ref: ty::TraitRef::new(self.tcx,
+                        trait_pred.def_id(),
+                        [field_ty].into_iter().chain(trait_args),
+                    ),
+                    ..*tr
+                });
+                let field_obl = Obligation::new(
+                    self.tcx,
+                    obligation.cause.clone(),
+                    obligation.param_env,
+                    trait_pred,
+                );
+                self.predicate_must_hold_modulo_regions(&field_obl)
+            })
+    }
+
+    pub fn suggest_derive(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut Diag<'_>,
+        trait_pred: ty::PolyTraitPredicate<'tcx>,
+    ) {
+        let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
+            return;
+        };
+        let adt = match trait_pred.skip_binder().self_ty().kind() {
+            ty::Adt(adt, _) if adt.did().is_local() => adt,
+            _ => return,
         };
-        if can_derive {
+        if self.can_suggest_derive(obligation, trait_pred) {
             err.span_suggestion_verbose(
                 self.tcx.def_span(adt.did()).shrink_to_lo(),
                 format!(