about summary refs log tree commit diff
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src')
-rw-r--r--compiler/rustc_lint/src/builtin.rs114
-rw-r--r--compiler/rustc_lint/src/context.rs51
-rw-r--r--compiler/rustc_lint/src/dangling.rs152
-rw-r--r--compiler/rustc_lint/src/default_could_be_derived.rs6
-rw-r--r--compiler/rustc_lint/src/deref_into_dyn_supertrait.rs10
-rw-r--r--compiler/rustc_lint/src/drop_forget_useless.rs2
-rw-r--r--compiler/rustc_lint/src/early.rs13
-rw-r--r--compiler/rustc_lint/src/early/diagnostics.rs113
-rw-r--r--compiler/rustc_lint/src/for_loops_over_fallibles.rs2
-rw-r--r--compiler/rustc_lint/src/foreign_modules.rs32
-rw-r--r--compiler/rustc_lint/src/if_let_rescope.rs2
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs2
-rw-r--r--compiler/rustc_lint/src/internal.rs172
-rw-r--r--compiler/rustc_lint/src/late.rs11
-rw-r--r--compiler/rustc_lint/src/levels.rs1
-rw-r--r--compiler/rustc_lint/src/lib.rs8
-rw-r--r--compiler/rustc_lint/src/lifetime_syntax.rs17
-rw-r--r--compiler/rustc_lint/src/lints.rs393
-rw-r--r--compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs4
-rw-r--r--compiler/rustc_lint/src/map_unit_fn.rs117
-rw-r--r--compiler/rustc_lint/src/multiple_supertrait_upcastable.rs3
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs60
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs8
-rw-r--r--compiler/rustc_lint/src/nonstandard_style.rs109
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs2
-rw-r--r--compiler/rustc_lint/src/pass_by_value.rs10
-rw-r--r--compiler/rustc_lint/src/passes.rs6
-rw-r--r--compiler/rustc_lint/src/ptr_nulls.rs4
-rw-r--r--compiler/rustc_lint/src/shadowed_into_iter.rs4
-rw-r--r--compiler/rustc_lint/src/static_mut_refs.rs2
-rw-r--r--compiler/rustc_lint/src/transmute.rs98
-rw-r--r--compiler/rustc_lint/src/types.rs884
-rw-r--r--compiler/rustc_lint/src/types/improper_ctypes.rs1016
-rw-r--r--compiler/rustc_lint/src/types/literal.rs8
-rw-r--r--compiler/rustc_lint/src/unused.rs81
35 files changed, 1815 insertions, 1702 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8244f0bed4c..75a0f89321b 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -21,19 +21,20 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
 use rustc_ast::visit::{FnCtxt, FnKind};
 use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust::expr_to_string;
-use rustc_attr_data_structures::{AttributeKind, find_attr};
+use rustc_attr_parsing::AttributeParser;
 use rustc_errors::{Applicability, LintDiagnostic};
 use rustc_feature::GateIssue;
 use rustc_hir as hir;
+use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
 use rustc_hir::intravisit::FnKind as HirFnKind;
-use rustc_hir::{Body, FnDecl, PatKind, PredicateOrigin};
+use rustc_hir::{Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr};
 use rustc_middle::bug;
 use rustc_middle::lint::LevelAndSource;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef};
+use rustc_middle::ty::{self, AssocContainer, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef};
 use rustc_session::lint::FutureIncompatibilityReason;
 // hardwired lints from rustc_lint_defs
 pub use rustc_session::lint::builtin::*;
@@ -60,7 +61,6 @@ use crate::lints::{
     BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment,
     BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel,
 };
-use crate::nonstandard_style::{MethodLateContext, method_context};
 use crate::{
     EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
     fluent_generated as fluent,
@@ -248,12 +248,6 @@ impl UnsafeCode {
 }
 
 impl EarlyLintPass for UnsafeCode {
-    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
-        if attr.has_name(sym::allow_internal_unsafe) {
-            self.report_unsafe(cx, attr.span, BuiltinUnsafe::AllowInternalUnsafe);
-        }
-    }
-
     #[inline]
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
         if let ast::ExprKind::Block(ref blk, _) = e.kind {
@@ -270,7 +264,10 @@ impl EarlyLintPass for UnsafeCode {
                 self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeTrait);
             }
 
-            ast::ItemKind::Impl(box ast::Impl { safety: ast::Safety::Unsafe(_), .. }) => {
+            ast::ItemKind::Impl(ast::Impl {
+                of_trait: Some(box ast::TraitImplHeader { safety: ast::Safety::Unsafe(_), .. }),
+                ..
+            }) => {
                 self.report_unsafe(cx, it.span, BuiltinUnsafe::UnsafeImpl);
             }
 
@@ -312,6 +309,19 @@ impl EarlyLintPass for UnsafeCode {
                 }
             }
 
+            ast::ItemKind::MacroDef(..) => {
+                if let Some(attr) = AttributeParser::parse_limited(
+                    cx.builder.sess(),
+                    &it.attrs,
+                    sym::allow_internal_unsafe,
+                    it.span,
+                    DUMMY_NODE_ID,
+                    Some(cx.builder.features()),
+                ) {
+                    self.report_unsafe(cx, attr.span(), BuiltinUnsafe::AllowInternalUnsafe);
+                }
+            }
+
             _ => {}
         }
     }
@@ -458,14 +468,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
-        let context = method_context(cx, impl_item.owner_id.def_id);
+        let container = cx.tcx.associated_item(impl_item.owner_id.def_id).container;
 
-        match context {
+        match container {
             // If the method is an impl for a trait, don't doc.
-            MethodLateContext::TraitImpl => return,
-            MethodLateContext::TraitAutoImpl => {}
+            AssocContainer::TraitImpl(_) => return,
+            AssocContainer::Trait => {}
             // If the method is an impl for an item with docs_hidden, don't doc.
-            MethodLateContext::PlainImpl => {
+            AssocContainer::InherentImpl => {
                 let parent = cx.tcx.hir_get_parent_item(impl_item.hir_id());
                 let impl_ty = cx.tcx.type_of(parent).instantiate_identity();
                 let outerdef = match impl_ty.kind() {
@@ -1310,9 +1320,8 @@ impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
-        // Only lint inherent impl items.
-        if cx.tcx.associated_item(impl_item.owner_id).trait_item_def_id.is_none() {
-            self.perform_lint(cx, "item", impl_item.owner_id.def_id, impl_item.vis_span, false);
+        if let ImplItemImplKind::Inherent { vis_span } = impl_item.impl_kind {
+            self.perform_lint(cx, "item", impl_item.owner_id.def_id, vis_span, false);
         }
     }
 }
@@ -1585,6 +1594,8 @@ impl EarlyLintPass for DoubleNegations {
         if let ExprKind::Unary(UnOp::Neg, ref inner) = expr.kind
             && let ExprKind::Unary(UnOp::Neg, ref inner2) = inner.kind
             && !matches!(inner2.kind, ExprKind::Unary(UnOp::Neg, _))
+            // Don't lint if this jumps macro expansion boundary (Issue #143980)
+            && expr.span.eq_ctxt(inner.span)
         {
             cx.emit_span_lint(
                 DOUBLE_NEGATIONS,
@@ -1652,7 +1663,7 @@ declare_lint! {
     "`...` range patterns are deprecated",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/warnings-promoted-to-error.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/warnings-promoted-to-error.html>",
     };
 }
 
@@ -1833,7 +1844,7 @@ declare_lint! {
     "detects edition keywords being used as an identifier",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/gen-keyword.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/gen-keyword.html>",
     };
 }
 
@@ -2444,16 +2455,16 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
 
         /// Determine if this expression is a "dangerous initialization".
         fn is_dangerous_init(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<InitKind> {
-            if let hir::ExprKind::Call(path_expr, args) = expr.kind {
+            if let hir::ExprKind::Call(path_expr, args) = expr.kind
                 // Find calls to `mem::{uninitialized,zeroed}` methods.
-                if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
-                    let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
-                    match cx.tcx.get_diagnostic_name(def_id) {
-                        Some(sym::mem_zeroed) => return Some(InitKind::Zeroed),
-                        Some(sym::mem_uninitialized) => return Some(InitKind::Uninit),
-                        Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed),
-                        _ => {}
-                    }
+                && let hir::ExprKind::Path(ref qpath) = path_expr.kind
+            {
+                let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
+                match cx.tcx.get_diagnostic_name(def_id) {
+                    Some(sym::mem_zeroed) => return Some(InitKind::Zeroed),
+                    Some(sym::mem_uninitialized) => return Some(InitKind::Uninit),
+                    Some(sym::transmute) if is_zero(&args[0]) => return Some(InitKind::Zeroed),
+                    _ => {}
                 }
             } else if let hir::ExprKind::MethodCall(_, receiver, ..) = expr.kind {
                 // Find problematic calls to `MaybeUninit::assume_init`.
@@ -2461,14 +2472,14 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue {
                 if cx.tcx.is_diagnostic_item(sym::assume_init, def_id) {
                     // This is a call to *some* method named `assume_init`.
                     // See if the `self` parameter is one of the dangerous constructors.
-                    if let hir::ExprKind::Call(path_expr, _) = receiver.kind {
-                        if let hir::ExprKind::Path(ref qpath) = path_expr.kind {
-                            let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
-                            match cx.tcx.get_diagnostic_name(def_id) {
-                                Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed),
-                                Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit),
-                                _ => {}
-                            }
+                    if let hir::ExprKind::Call(path_expr, _) = receiver.kind
+                        && let hir::ExprKind::Path(ref qpath) = path_expr.kind
+                    {
+                        let def_id = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()?;
+                        match cx.tcx.get_diagnostic_name(def_id) {
+                            Some(sym::maybe_uninit_zeroed) => return Some(InitKind::Zeroed),
+                            Some(sym::maybe_uninit_uninit) => return Some(InitKind::Uninit),
+                            _ => {}
                         }
                     }
                 }
@@ -2722,13 +2733,13 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
                 }
                 // check for call to `core::ptr::null` or `core::ptr::null_mut`
                 hir::ExprKind::Call(path, _) => {
-                    if let hir::ExprKind::Path(ref qpath) = path.kind {
-                        if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() {
-                            return matches!(
-                                cx.tcx.get_diagnostic_name(def_id),
-                                Some(sym::ptr_null | sym::ptr_null_mut)
-                            );
-                        }
+                    if let hir::ExprKind::Path(ref qpath) = path.kind
+                        && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+                    {
+                        return matches!(
+                            cx.tcx.get_diagnostic_name(def_id),
+                            Some(sym::ptr_null | sym::ptr_null_mut)
+                        );
                     }
                 }
                 _ => {}
@@ -2868,7 +2879,7 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
         if let hir::Expr {
             kind:
                 hir::ExprKind::InlineAsm(hir::InlineAsm {
-                    asm_macro: AsmMacro::Asm | AsmMacro::NakedAsm,
+                    asm_macro: asm_macro @ (AsmMacro::Asm | AsmMacro::NakedAsm),
                     template_strs,
                     options,
                     ..
@@ -2876,6 +2887,15 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels {
             ..
         } = expr
         {
+            // Non-generic naked functions are allowed to define arbitrary
+            // labels.
+            if *asm_macro == AsmMacro::NakedAsm {
+                let def_id = expr.hir_id.owner.def_id;
+                if !cx.tcx.generics_of(def_id).requires_monomorphization(cx.tcx) {
+                    return;
+                }
+            }
+
             // asm with `options(raw)` does not do replacement with `{` and `}`.
             let raw = options.contains(InlineAsmOptions::RAW);
 
@@ -3075,7 +3095,7 @@ impl EarlyLintPass for SpecialModuleName {
             if let ast::ItemKind::Mod(
                 _,
                 ident,
-                ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No, _, _),
+                ast::ModKind::Unloaded | ast::ModKind::Loaded(_, ast::Inline::No { .. }, _),
             ) = item.kind
             {
                 if item.attrs.iter().any(|a| a.has_name(sym::path)) {
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index b694d3dd49b..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,8 +23,8 @@ 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::{LintStoreMarker, Session};
+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};
 use tracing::debug;
@@ -62,7 +62,13 @@ pub struct LintStore {
     lint_groups: FxIndexMap<&'static str, LintGroup>,
 }
 
-impl LintStoreMarker for LintStore {}
+impl DynLintStore for LintStore {
+    fn lint_groups_iter(&self) -> Box<dyn Iterator<Item = rustc_session::LintGroup> + '_> {
+        Box::new(self.get_lint_groups().map(|(name, lints, is_externally_loaded)| {
+            rustc_session::LintGroup { name, lints, is_externally_loaded }
+        }))
+    }
+}
 
 /// The target of the `by_name` map, which accounts for renaming/deprecation.
 #[derive(Debug)]
@@ -745,49 +751,49 @@ impl<'tcx> LateContext<'tcx> {
     /// }
     /// ```
     pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
-        struct AbsolutePathPrinter<'tcx> {
+        struct LintPathPrinter<'tcx> {
             tcx: TyCtxt<'tcx>,
             path: Vec<Symbol>,
         }
 
-        impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
+        impl<'tcx> Printer<'tcx> for LintPathPrinter<'tcx> {
             fn tcx(&self) -> TyCtxt<'tcx> {
                 self.tcx
             }
 
             fn print_region(&mut self, _region: ty::Region<'_>) -> Result<(), PrintError> {
-                Ok(())
+                unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
             }
 
             fn print_type(&mut self, _ty: Ty<'tcx>) -> Result<(), PrintError> {
-                Ok(())
+                unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
             }
 
             fn print_dyn_existential(
                 &mut self,
                 _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
             ) -> Result<(), PrintError> {
-                Ok(())
+                unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
             }
 
             fn print_const(&mut self, _ct: ty::Const<'tcx>) -> Result<(), PrintError> {
-                Ok(())
+                unreachable!(); // because `print_path_with_generic_args` ignores the `GenericArgs`
             }
 
-            fn path_crate(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
+            fn print_crate_name(&mut self, cnum: CrateNum) -> Result<(), PrintError> {
                 self.path = vec![self.tcx.crate_name(cnum)];
                 Ok(())
             }
 
-            fn path_qualified(
+            fn print_path_with_qualified(
                 &mut self,
                 self_ty: Ty<'tcx>,
                 trait_ref: Option<ty::TraitRef<'tcx>>,
             ) -> Result<(), PrintError> {
-                if trait_ref.is_none() {
-                    if let ty::Adt(def, args) = self_ty.kind() {
-                        return self.print_def_path(def.did(), args);
-                    }
+                if trait_ref.is_none()
+                    && let ty::Adt(def, args) = self_ty.kind()
+                {
+                    return self.print_def_path(def.did(), args);
                 }
 
                 // This shouldn't ever be needed, but just in case:
@@ -800,10 +806,9 @@ impl<'tcx> LateContext<'tcx> {
                 })
             }
 
-            fn path_append_impl(
+            fn print_path_with_impl(
                 &mut self,
                 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
-                _disambiguated_data: &DisambiguatedDefPathData,
                 self_ty: Ty<'tcx>,
                 trait_ref: Option<ty::TraitRef<'tcx>>,
             ) -> Result<(), PrintError> {
@@ -826,7 +831,7 @@ impl<'tcx> LateContext<'tcx> {
                 Ok(())
             }
 
-            fn path_append(
+            fn print_path_with_simple(
                 &mut self,
                 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
                 disambiguated_data: &DisambiguatedDefPathData,
@@ -845,7 +850,7 @@ impl<'tcx> LateContext<'tcx> {
                 Ok(())
             }
 
-            fn path_generic_args(
+            fn print_path_with_generic_args(
                 &mut self,
                 print_prefix: impl FnOnce(&mut Self) -> Result<(), PrintError>,
                 _args: &[GenericArg<'tcx>],
@@ -854,9 +859,9 @@ impl<'tcx> LateContext<'tcx> {
             }
         }
 
-        let mut printer = AbsolutePathPrinter { tcx: self.tcx, path: vec![] };
-        printer.print_def_path(def_id, &[]).unwrap();
-        printer.path
+        let mut p = LintPathPrinter { tcx: self.tcx, path: vec![] };
+        p.print_def_path(def_id, &[]).unwrap();
+        p.path
     }
 
     /// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`.
diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs
index c737919db9c..af4457f4314 100644
--- a/compiler/rustc_lint/src/dangling.rs
+++ b/compiler/rustc_lint/src/dangling.rs
@@ -1,13 +1,14 @@
 use rustc_ast::visit::{visit_opt, walk_list};
-use rustc_attr_data_structures::{AttributeKind, find_attr};
+use rustc_hir::attrs::AttributeKind;
+use rustc_hir::def::Res;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
-use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, LangItem};
-use rustc_middle::ty::{Ty, TyCtxt};
+use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, LangItem, TyKind, find_attr};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::{Span, sym};
 
-use crate::lints::DanglingPointersFromTemporaries;
+use crate::lints::{DanglingPointersFromLocals, DanglingPointersFromTemporaries};
 use crate::{LateContext, LateLintPass};
 
 declare_lint! {
@@ -42,6 +43,36 @@ declare_lint! {
     "detects getting a pointer from a temporary"
 }
 
+declare_lint! {
+    /// The `dangling_pointers_from_locals` lint detects getting a pointer to data
+    /// of a local that will be dropped at the end of the function.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn f() -> *const u8 {
+    ///     let x = 0;
+    ///     &x // returns a dangling ptr to `x`
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Returning a pointer from a local value will not prolong its lifetime,
+    /// which means that the value can be dropped and the allocation freed
+    /// while the pointer still exists, making the pointer dangling.
+    /// This is not an error (as far as the type system is concerned)
+    /// but probably is not what the user intended either.
+    ///
+    /// If you need stronger guarantees, consider using references instead,
+    /// as they are statically verified by the borrow-checker to never dangle.
+    pub DANGLING_POINTERS_FROM_LOCALS,
+    Warn,
+    "detects returning a pointer from a local variable"
+}
+
 /// FIXME: false negatives (i.e. the lint is not emitted when it should be)
 /// 1. Ways to get a temporary that are not recognized:
 ///    - `owning_temporary.field`
@@ -53,20 +84,123 @@ declare_lint! {
 #[derive(Clone, Copy, Default)]
 pub(crate) struct DanglingPointers;
 
-impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES]);
+impl_lint_pass!(DanglingPointers => [DANGLING_POINTERS_FROM_TEMPORARIES, DANGLING_POINTERS_FROM_LOCALS]);
 
 // This skips over const blocks, but they cannot use or return a dangling pointer anyways.
 impl<'tcx> LateLintPass<'tcx> for DanglingPointers {
     fn check_fn(
         &mut self,
         cx: &LateContext<'tcx>,
-        _: FnKind<'tcx>,
-        _: &'tcx FnDecl<'tcx>,
+        fn_kind: FnKind<'tcx>,
+        fn_decl: &'tcx FnDecl<'tcx>,
         body: &'tcx Body<'tcx>,
         _: Span,
-        _: LocalDefId,
+        def_id: LocalDefId,
     ) {
-        DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body)
+        DanglingPointerSearcher { cx, inside_call_args: false }.visit_body(body);
+
+        if let FnRetTy::Return(ret_ty) = &fn_decl.output
+            && let TyKind::Ptr(_) = ret_ty.kind
+        {
+            // get the return type of the function or closure
+            let ty = match cx.tcx.type_of(def_id).instantiate_identity().kind() {
+                ty::FnDef(..) => cx.tcx.fn_sig(def_id).instantiate_identity(),
+                ty::Closure(_, args) => args.as_closure().sig(),
+                _ => return,
+            };
+            let ty = ty.output();
+
+            // this type is only used for layout computation and pretty-printing, neither of them rely on regions
+            let ty = cx.tcx.instantiate_bound_regions_with_erased(ty);
+
+            // verify that we have a pointer type
+            let inner_ty = match ty.kind() {
+                ty::RawPtr(inner_ty, _) => *inner_ty,
+                _ => return,
+            };
+
+            if cx
+                .tcx
+                .layout_of(cx.typing_env().as_query_input(inner_ty))
+                .is_ok_and(|layout| !layout.is_1zst())
+            {
+                let dcx = &DanglingPointerLocalContext {
+                    body: def_id,
+                    fn_ret: ty,
+                    fn_ret_span: ret_ty.span,
+                    fn_ret_inner: inner_ty,
+                    fn_kind: match fn_kind {
+                        FnKind::ItemFn(..) => "function",
+                        FnKind::Method(..) => "method",
+                        FnKind::Closure => "closure",
+                    },
+                };
+
+                // look for `return`s
+                DanglingPointerReturnSearcher { cx, dcx }.visit_body(body);
+
+                // analyze implicit return expression
+                if let ExprKind::Block(block, None) = &body.value.kind
+                    && let innermost_block = block.innermost_block()
+                    && let Some(expr) = innermost_block.expr
+                {
+                    lint_addr_of_local(cx, dcx, expr);
+                }
+            }
+        }
+    }
+}
+
+struct DanglingPointerLocalContext<'tcx> {
+    body: LocalDefId,
+    fn_ret: Ty<'tcx>,
+    fn_ret_span: Span,
+    fn_ret_inner: Ty<'tcx>,
+    fn_kind: &'static str,
+}
+
+struct DanglingPointerReturnSearcher<'lcx, 'tcx> {
+    cx: &'lcx LateContext<'tcx>,
+    dcx: &'lcx DanglingPointerLocalContext<'tcx>,
+}
+
+impl<'tcx> Visitor<'tcx> for DanglingPointerReturnSearcher<'_, 'tcx> {
+    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result {
+        if let ExprKind::Ret(Some(expr)) = expr.kind {
+            lint_addr_of_local(self.cx, self.dcx, expr);
+        }
+        walk_expr(self, expr)
+    }
+}
+
+/// Look for `&<path_to_local_in_same_body>` pattern and emit lint for it
+fn lint_addr_of_local<'a>(
+    cx: &LateContext<'a>,
+    dcx: &DanglingPointerLocalContext<'a>,
+    expr: &'a Expr<'a>,
+) {
+    // peel casts as they do not interest us here, we want the inner expression.
+    let (inner, _) = super::utils::peel_casts(cx, expr);
+
+    if let ExprKind::AddrOf(_, _, inner_of) = inner.kind
+        && let ExprKind::Path(ref qpath) = inner_of.peel_blocks().kind
+        && let Res::Local(from) = cx.qpath_res(qpath, inner_of.hir_id)
+        && cx.tcx.hir_enclosing_body_owner(from) == dcx.body
+    {
+        cx.tcx.emit_node_span_lint(
+            DANGLING_POINTERS_FROM_LOCALS,
+            expr.hir_id,
+            expr.span,
+            DanglingPointersFromLocals {
+                ret_ty: dcx.fn_ret,
+                ret_ty_span: dcx.fn_ret_span,
+                fn_kind: dcx.fn_kind,
+                local_var: cx.tcx.hir_span(from),
+                local_var_name: cx.tcx.hir_ident(from),
+                local_var_ty: dcx.fn_ret_inner,
+                created_at: (expr.hir_id != inner.hir_id).then_some(inner.span),
+            },
+        );
     }
 }
 
diff --git a/compiler/rustc_lint/src/default_could_be_derived.rs b/compiler/rustc_lint/src/default_could_be_derived.rs
index b5fc083a095..1d92cfbc039 100644
--- a/compiler/rustc_lint/src/default_could_be_derived.rs
+++ b/compiler/rustc_lint/src/default_could_be_derived.rs
@@ -1,7 +1,8 @@
-use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir as hir;
+use rustc_hir::attrs::AttributeKind;
+use rustc_hir::find_attr;
 use rustc_middle::ty;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::{declare_lint, impl_lint_pass};
@@ -61,8 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for DefaultCouldBeDerived {
         // Look for manual implementations of `Default`.
         let Some(default_def_id) = cx.tcx.get_diagnostic_item(sym::Default) else { return };
         let hir::ImplItemKind::Fn(_sig, body_id) = impl_item.kind else { return };
-        let assoc = cx.tcx.associated_item(impl_item.owner_id);
-        let parent = assoc.container_id(cx.tcx);
+        let parent = cx.tcx.parent(impl_item.owner_id.to_def_id());
         if find_attr!(cx.tcx.get_all_attrs(parent), AttributeKind::AutomaticallyDerived(..)) {
             // We don't care about what `#[derive(Default)]` produces in this lint.
             return;
diff --git a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
index dd16117db1c..703f757abd5 100644
--- a/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
+++ b/compiler/rustc_lint/src/deref_into_dyn_supertrait.rs
@@ -61,16 +61,16 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
         // `Deref` is being implemented for `t`
         if let hir::ItemKind::Impl(impl_) = item.kind
             // the trait is a `Deref` implementation
-            && let Some(trait_) = &impl_.of_trait
-            && let Some(did) = trait_.trait_def_id()
+            && let Some(of_trait) = &impl_.of_trait
+            && let Some(did) = of_trait.trait_ref.trait_def_id()
             && tcx.is_lang_item(did, LangItem::Deref)
             // the self type is `dyn t_principal`
             && let self_ty = tcx.type_of(item.owner_id).instantiate_identity()
-            && let ty::Dynamic(data, _, ty::Dyn) = self_ty.kind()
+            && let ty::Dynamic(data, _) = self_ty.kind()
             && let Some(self_principal) = data.principal()
             // `<T as Deref>::Target` is `dyn target_principal`
             && let Some(target) = cx.get_associated_type(self_ty, did, sym::Target)
-            && let ty::Dynamic(data, _, ty::Dyn) = target.kind()
+            && let ty::Dynamic(data, _) = target.kind()
             && let Some(target_principal) = data.principal()
             // `target_principal` is a supertrait of `t_principal`
             && let Some(supertrait_principal) = supertraits(tcx, self_principal.with_self_ty(tcx, self_ty))
@@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for DerefIntoDynSupertrait {
         {
             // erase regions in self type for better diagnostic presentation
             let (self_ty, target_principal, supertrait_principal) =
-                tcx.erase_regions((self_ty, target_principal, supertrait_principal));
+                tcx.erase_and_anonymize_regions((self_ty, target_principal, supertrait_principal));
             let label2 = tcx
                 .associated_items(item.owner_id)
                 .find_by_ident_and_kind(
diff --git a/compiler/rustc_lint/src/drop_forget_useless.rs b/compiler/rustc_lint/src/drop_forget_useless.rs
index 7f098893f7d..c2d137986ce 100644
--- a/compiler/rustc_lint/src/drop_forget_useless.rs
+++ b/compiler/rustc_lint/src/drop_forget_useless.rs
@@ -151,7 +151,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
                     && let Node::Stmt(stmt) = node
                     && let StmtKind::Semi(e) = stmt.kind
                     && e.hir_id == expr.hir_id
-                    && let Some(arg_span) = arg.span.find_ancestor_inside(expr.span)
+                    && let Some(arg_span) = arg.span.find_ancestor_inside_same_ctxt(expr.span)
                 {
                     UseLetUnderscoreIgnoreSuggestion::Suggestion {
                         start_span: expr.span.shrink_to_lo().until(arg_span),
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 48e3bbb79fa..dff1fc43670 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -4,14 +4,14 @@
 //! resolution, just before AST lowering. These lints are for purely
 //! syntactical lints.
 
-use rustc_ast::ptr::P;
 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;
 
@@ -37,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),
             });
         }
     }
@@ -297,7 +300,7 @@ impl<'a> EarlyCheckNode<'a> for (&'a ast::Crate, &'a [ast::Attribute]) {
     }
 }
 
-impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::Item>]) {
+impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [Box<ast::Item>]) {
     fn id(self) -> ast::NodeId {
         self.0
     }
diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs
index f0fbf5bc81e..9029ee04471 100644
--- a/compiler/rustc_lint/src/early/diagnostics.rs
+++ b/compiler/rustc_lint/src/early/diagnostics.rs
@@ -64,15 +64,6 @@ pub fn decorate_builtin_lint(
             }
             .decorate_lint(diag);
         }
-        BuiltinLintDiag::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident } => {
-            lints::ProcMacroDeriveResolutionFallback { span: macro_span, ns, ident }
-                .decorate_lint(diag)
-        }
-        BuiltinLintDiag::MacroExpandedMacroExportsAccessedByAbsolutePaths(span_def) => {
-            lints::MacroExpandedMacroExportsAccessedByAbsolutePaths { definition: span_def }
-                .decorate_lint(diag)
-        }
-
         BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
             lints::ElidedLifetimesInPaths {
                 subdiag: elided_lifetime_in_path_suggestion(
@@ -85,10 +76,6 @@ pub fn decorate_builtin_lint(
             }
             .decorate_lint(diag);
         }
-        BuiltinLintDiag::UnknownCrateTypes { span, candidate } => {
-            let sugg = candidate.map(|candidate| lints::UnknownCrateTypesSub { span, candidate });
-            lints::UnknownCrateTypes { sugg }.decorate_lint(diag);
-        }
         BuiltinLintDiag::UnusedImports {
             remove_whole_use,
             num_to_remove,
@@ -144,9 +131,6 @@ pub fn decorate_builtin_lint(
             stability::Deprecated { sub, kind: "macro".to_owned(), path, note, since_kind }
                 .decorate_lint(diag);
         }
-        BuiltinLintDiag::UnusedDocComment(attr_span) => {
-            lints::UnusedDocComment { span: attr_span }.decorate_lint(diag);
-        }
         BuiltinLintDiag::PatternsInFnsWithoutBody { span: remove_span, ident, is_foreign } => {
             let sub = lints::PatternsInFnsWithoutBodySub { ident, span: remove_span };
             if is_foreign {
@@ -156,15 +140,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);
-        }
-        BuiltinLintDiag::OrPatternsBackCompat(suggestion_span, suggestion) => {
-            lints::OrPatternsBackCompat { span: suggestion_span, suggestion }.decorate_lint(diag);
-        }
         BuiltinLintDiag::ReservedPrefix(label_span, prefix) => {
             lints::ReservedPrefix {
                 label: label_span,
@@ -184,33 +159,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, invoc_span } => {
-            lints::UnusedBuiltinAttribute { invoc_span, attr_name, macro_name }.decorate_lint(diag);
-        }
-        BuiltinLintDiag::TrailingMacro(is_trailing, name) => {
-            lints::TrailingMacro { is_trailing, name }.decorate_lint(diag);
-        }
         BuiltinLintDiag::BreakWithLabelAndLoop(sugg_span) => {
             lints::BreakWithLabelAndLoop {
                 sub: lints::BreakWithLabelAndLoopSub {
@@ -237,9 +185,6 @@ pub fn decorate_builtin_lint(
             };
             lints::DeprecatedWhereClauseLocation { suggestion }.decorate_lint(diag);
         }
-        BuiltinLintDiag::MissingUnsafeOnExtern { suggestion } => {
-            lints::MissingUnsafeOnExtern { suggestion }.decorate_lint(diag);
-        }
         BuiltinLintDiag::SingleUseLifetime {
             param_span,
             use_span: Some((use_span, elide)),
@@ -269,7 +214,6 @@ pub fn decorate_builtin_lint(
                 .decorate_lint(diag);
         }
         BuiltinLintDiag::SingleUseLifetime { use_span: None, deletion_span, ident, .. } => {
-            debug!(?deletion_span);
             lints::UnusedLifetime { deletion_span, ident }.decorate_lint(diag);
         }
         BuiltinLintDiag::NamedArgumentUsedPositionally {
@@ -307,12 +251,6 @@ pub fn decorate_builtin_lint(
             }
             .decorate_lint(diag);
         }
-        BuiltinLintDiag::ByteSliceInPackedStructWithDerive { ty } => {
-            lints::ByteSliceInPackedStructWithDerive { ty }.decorate_lint(diag);
-        }
-        BuiltinLintDiag::UnusedExternCrate { span, removal_span } => {
-            lints::UnusedExternCrate { span, removal_span }.decorate_lint(diag);
-        }
         BuiltinLintDiag::ExternCrateNotIdiomatic { vis_span, ident_span } => {
             let suggestion_span = vis_span.between(ident_span);
             let code = if vis_span.is_empty() { "use " } else { " use " };
@@ -351,9 +289,6 @@ pub fn decorate_builtin_lint(
             }
             .decorate_lint(diag);
         }
-        BuiltinLintDiag::ReexportPrivateDependency { name, kind, krate } => {
-            lints::ReexportPrivateDependency { name, kind, krate }.decorate_lint(diag);
-        }
         BuiltinLintDiag::UnusedQualifications { removal_span } => {
             lints::UnusedQualifications { removal_span }.decorate_lint(diag);
         }
@@ -393,15 +328,10 @@ pub fn decorate_builtin_lint(
             });
             lints::UnknownDiagnosticAttribute { typo }.decorate_lint(diag);
         }
-        BuiltinLintDiag::MacroUseDeprecated => {
-            lints::MacroUseDeprecated.decorate_lint(diag);
-        }
-        BuiltinLintDiag::UnusedMacroUse => lints::UnusedMacroUse.decorate_lint(diag),
         BuiltinLintDiag::PrivateExternCrateReexport { source: ident, extern_crate_span } => {
             lints::PrivateExternCrateReexport { ident, sugg: extern_crate_span.shrink_to_lo() }
                 .decorate_lint(diag);
         }
-        BuiltinLintDiag::UnusedLabel => lints::UnusedLabel.decorate_lint(diag),
         BuiltinLintDiag::MacroIsPrivate(ident) => {
             lints::MacroIsPrivate { ident }.decorate_lint(diag);
         }
@@ -414,59 +344,22 @@ pub fn decorate_builtin_lint(
         BuiltinLintDiag::UnstableFeature(msg) => {
             lints::UnstableFeature { msg }.decorate_lint(diag);
         }
-        BuiltinLintDiag::AvoidUsingIntelSyntax => {
-            lints::AvoidIntelSyntax.decorate_lint(diag);
-        }
-        BuiltinLintDiag::AvoidUsingAttSyntax => {
-            lints::AvoidAttSyntax.decorate_lint(diag);
-        }
-        BuiltinLintDiag::IncompleteInclude => {
-            lints::IncompleteInclude.decorate_lint(diag);
-        }
-        BuiltinLintDiag::UnnameableTestItems => {
-            lints::UnnameableTestItems.decorate_lint(diag);
-        }
-        BuiltinLintDiag::DuplicateMacroAttribute => {
-            lints::DuplicateMacroAttribute.decorate_lint(diag);
-        }
-        BuiltinLintDiag::CfgAttrNoAttributes => {
-            lints::CfgAttrNoAttributes.decorate_lint(diag);
-        }
-        BuiltinLintDiag::MetaVariableStillRepeating(name) => {
-            lints::MetaVariableStillRepeating { name }.decorate_lint(diag);
-        }
-        BuiltinLintDiag::MetaVariableWrongOperator => {
-            lints::MetaVariableWrongOperator.decorate_lint(diag);
-        }
-        BuiltinLintDiag::DuplicateMatcherBinding => {
-            lints::DuplicateMatcherBinding.decorate_lint(diag);
-        }
-        BuiltinLintDiag::UnknownMacroVariable(name) => {
-            lints::UnknownMacroVariable { name }.decorate_lint(diag);
-        }
         BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
             lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
         }
-        BuiltinLintDiag::IllFormedAttributeInput { suggestions } => {
+        BuiltinLintDiag::IllFormedAttributeInput { suggestions, docs } => {
             lints::IllFormedAttributeInput {
                 num_suggestions: suggestions.len(),
                 suggestions: DiagArgValue::StrListSepByAnd(
                     suggestions.into_iter().map(|s| format!("`{s}`").into()).collect(),
                 ),
+                has_docs: docs.is_some(),
+                docs: docs.unwrap_or(""),
             }
             .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/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
index a56b753bda7..9e1fc598171 100644
--- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs
+++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs
@@ -176,7 +176,7 @@ fn suggest_question_mark<'tcx>(
         cause,
         param_env,
         // Erase any region vids from the type, which may not be resolved
-        infcx.tcx.erase_regions(ty),
+        infcx.tcx.erase_and_anonymize_regions(ty),
         into_iterator_did,
     );
 
diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs
index d6c402d481f..ad73e15e31f 100644
--- a/compiler/rustc_lint/src/foreign_modules.rs
+++ b/compiler/rustc_lint/src/foreign_modules.rs
@@ -1,9 +1,10 @@
 use rustc_abi::FIRST_VARIANT;
-use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_hir as hir;
+use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::DefKind;
+use rustc_hir::find_attr;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, AdtDef, Instance, Ty, TyCtxt};
 use rustc_session::declare_lint;
@@ -135,7 +136,6 @@ impl ClashingExternDeclarations {
             ty::TypingEnv::non_body_analysis(tcx, this_fi.owner_id),
             existing_decl_ty,
             this_decl_ty,
-            types::CItemKind::Declaration,
         ) {
             let orig = name_of_extern_decl(tcx, existing_did);
 
@@ -178,7 +178,7 @@ impl ClashingExternDeclarations {
 /// symbol's name.
 fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: hir::OwnerId) -> SymbolName {
     if let Some((overridden_link_name, overridden_link_name_span)) =
-        tcx.codegen_fn_attrs(fi).link_name.map(|overridden_link_name| {
+        tcx.codegen_fn_attrs(fi).symbol_name.map(|overridden_link_name| {
             // FIXME: Instead of searching through the attributes again to get span
             // information, we could have codegen_fn_attrs also give span information back for
             // where the attribute was defined. However, until this is found to be a
@@ -213,10 +213,9 @@ fn structurally_same_type<'tcx>(
     typing_env: ty::TypingEnv<'tcx>,
     a: Ty<'tcx>,
     b: Ty<'tcx>,
-    ckind: types::CItemKind,
 ) -> bool {
     let mut seen_types = UnordSet::default();
-    let result = structurally_same_type_impl(&mut seen_types, tcx, typing_env, a, b, ckind);
+    let result = structurally_same_type_impl(&mut seen_types, tcx, typing_env, a, b);
     if cfg!(debug_assertions) && result {
         // Sanity-check: must have same ABI, size and alignment.
         // `extern` blocks cannot be generic, so we'll always get a layout here.
@@ -235,7 +234,6 @@ fn structurally_same_type_impl<'tcx>(
     typing_env: ty::TypingEnv<'tcx>,
     a: Ty<'tcx>,
     b: Ty<'tcx>,
-    ckind: types::CItemKind,
 ) -> bool {
     debug!("structurally_same_type_impl(tcx, a = {:?}, b = {:?})", a, b);
 
@@ -306,7 +304,6 @@ fn structurally_same_type_impl<'tcx>(
                                 typing_env,
                                 tcx.type_of(a_did).instantiate(tcx, a_gen_args),
                                 tcx.type_of(b_did).instantiate(tcx, b_gen_args),
-                                ckind,
                             )
                         },
                     )
@@ -314,25 +311,19 @@ fn structurally_same_type_impl<'tcx>(
                 (ty::Array(a_ty, a_len), ty::Array(b_ty, b_len)) => {
                     // For arrays, we also check the length.
                     a_len == b_len
-                        && structurally_same_type_impl(
-                            seen_types, tcx, typing_env, *a_ty, *b_ty, ckind,
-                        )
+                        && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty)
                 }
                 (ty::Slice(a_ty), ty::Slice(b_ty)) => {
-                    structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty, ckind)
+                    structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty)
                 }
                 (ty::RawPtr(a_ty, a_mutbl), ty::RawPtr(b_ty, b_mutbl)) => {
                     a_mutbl == b_mutbl
-                        && structurally_same_type_impl(
-                            seen_types, tcx, typing_env, *a_ty, *b_ty, ckind,
-                        )
+                        && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty)
                 }
                 (ty::Ref(_a_region, a_ty, a_mut), ty::Ref(_b_region, b_ty, b_mut)) => {
                     // For structural sameness, we don't need the region to be same.
                     a_mut == b_mut
-                        && structurally_same_type_impl(
-                            seen_types, tcx, typing_env, *a_ty, *b_ty, ckind,
-                        )
+                        && structurally_same_type_impl(seen_types, tcx, typing_env, *a_ty, *b_ty)
                 }
                 (ty::FnDef(..), ty::FnDef(..)) => {
                     let a_poly_sig = a.fn_sig(tcx);
@@ -346,7 +337,7 @@ fn structurally_same_type_impl<'tcx>(
                     (a_sig.abi, a_sig.safety, a_sig.c_variadic)
                         == (b_sig.abi, b_sig.safety, b_sig.c_variadic)
                         && a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
-                            structurally_same_type_impl(seen_types, tcx, typing_env, *a, *b, ckind)
+                            structurally_same_type_impl(seen_types, tcx, typing_env, *a, *b)
                         })
                         && structurally_same_type_impl(
                             seen_types,
@@ -354,7 +345,6 @@ fn structurally_same_type_impl<'tcx>(
                             typing_env,
                             a_sig.output(),
                             b_sig.output(),
-                            ckind,
                         )
                 }
                 (ty::Tuple(..), ty::Tuple(..)) => {
@@ -382,14 +372,14 @@ fn structurally_same_type_impl<'tcx>(
                 // An Adt and a primitive or pointer type. This can be FFI-safe if non-null
                 // enum layout optimisation is being applied.
                 (ty::Adt(..) | ty::Pat(..), _) if is_primitive_or_pointer(b) => {
-                    if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a, ckind) {
+                    if let Some(a_inner) = types::repr_nullable_ptr(tcx, typing_env, a) {
                         a_inner == b
                     } else {
                         false
                     }
                 }
                 (_, ty::Adt(..) | ty::Pat(..)) if is_primitive_or_pointer(a) => {
-                    if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b, ckind) {
+                    if let Some(b_inner) = types::repr_nullable_ptr(tcx, typing_env, b) {
                         b_inner == a
                     } else {
                         false
diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs
index 263ea6fa070..ff67ed1bc55 100644
--- a/compiler/rustc_lint/src/if_let_rescope.rs
+++ b/compiler/rustc_lint/src/if_let_rescope.rs
@@ -87,7 +87,7 @@ declare_lint! {
     rewriting in `match` is an option to preserve the semantics up to Edition 2021",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/temporary-if-let-scope.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/temporary-if-let-scope.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index c17281deff4..b9afb62cf1c 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -71,7 +71,7 @@ declare_lint! {
     "`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/rpit-lifetime-capture.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/rpit-lifetime-capture.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index d8fc46aa9ab..929fc8207b0 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -1,10 +1,10 @@
 //! Some lints that are only useful in the compiler or crates that use compiler internals, such as
 //! Clippy.
 
-use rustc_hir::HirId;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
+use rustc_hir::{Expr, ExprKind, HirId};
+use rustc_middle::ty::{self, GenericArgsRef, PredicatePolarity, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::{Span, sym};
@@ -56,25 +56,6 @@ impl LateLintPass<'_> for DefaultHashTypes {
     }
 }
 
-/// Helper function for lints that check for expressions with calls and use typeck results to
-/// get the `DefId` and `GenericArgsRef` of the function.
-fn typeck_results_of_method_fn<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &hir::Expr<'_>,
-) -> Option<(Span, DefId, ty::GenericArgsRef<'tcx>)> {
-    match expr.kind {
-        hir::ExprKind::MethodCall(segment, ..)
-            if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) =>
-        {
-            Some((segment.ident.span, def_id, cx.typeck_results().node_args(expr.hir_id)))
-        }
-        _ => match cx.typeck_results().node_type(expr.hir_id).kind() {
-            &ty::FnDef(def_id, args) => Some((expr.span, def_id, args)),
-            _ => None,
-        },
-    }
-}
-
 declare_tool_lint! {
     /// The `potential_query_instability` lint detects use of methods which can lead to
     /// potential query instability, such as iterating over a `HashMap`.
@@ -101,10 +82,12 @@ declare_tool_lint! {
 
 declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY, UNTRACKED_QUERY_INFORMATION]);
 
-impl LateLintPass<'_> for QueryStability {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
-        let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return };
-        if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.typing_env(), def_id, args)
+impl<'tcx> LateLintPass<'tcx> for QueryStability {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if let Some((callee_def_id, span, generic_args, _recv, _args)) =
+            get_callee_span_generic_args_and_args(cx, expr)
+            && let Ok(Some(instance)) =
+                ty::Instance::try_resolve(cx.tcx, cx.typing_env(), callee_def_id, generic_args)
         {
             let def_id = instance.def_id();
             if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
@@ -113,7 +96,15 @@ impl LateLintPass<'_> for QueryStability {
                     span,
                     QueryInstability { query: cx.tcx.item_name(def_id) },
                 );
+            } else if has_unstable_into_iter_predicate(cx, callee_def_id, generic_args) {
+                let call_span = span.with_hi(expr.span.hi());
+                cx.emit_span_lint(
+                    POTENTIAL_QUERY_INSTABILITY,
+                    call_span,
+                    QueryInstability { query: sym::into_iter },
+                );
             }
+
             if cx.tcx.has_attr(def_id, sym::rustc_lint_untracked_query_information) {
                 cx.emit_span_lint(
                     UNTRACKED_QUERY_INFORMATION,
@@ -125,6 +116,69 @@ impl LateLintPass<'_> for QueryStability {
     }
 }
 
+fn has_unstable_into_iter_predicate<'tcx>(
+    cx: &LateContext<'tcx>,
+    callee_def_id: DefId,
+    generic_args: GenericArgsRef<'tcx>,
+) -> bool {
+    let Some(into_iterator_def_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else {
+        return false;
+    };
+    let Some(into_iter_fn_def_id) = cx.tcx.lang_items().into_iter_fn() else {
+        return false;
+    };
+    let predicates = cx.tcx.predicates_of(callee_def_id).instantiate(cx.tcx, generic_args);
+    for (predicate, _) in predicates {
+        let Some(trait_pred) = predicate.as_trait_clause() else {
+            continue;
+        };
+        if trait_pred.def_id() != into_iterator_def_id
+            || trait_pred.polarity() != PredicatePolarity::Positive
+        {
+            continue;
+        }
+        // `IntoIterator::into_iter` has no additional method args.
+        let into_iter_fn_args =
+            cx.tcx.instantiate_bound_regions_with_erased(trait_pred).trait_ref.args;
+        let Ok(Some(instance)) = ty::Instance::try_resolve(
+            cx.tcx,
+            cx.typing_env(),
+            into_iter_fn_def_id,
+            into_iter_fn_args,
+        ) else {
+            continue;
+        };
+        // Does the input type's `IntoIterator` implementation have the
+        // `rustc_lint_query_instability` attribute on its `into_iter` method?
+        if cx.tcx.has_attr(instance.def_id(), sym::rustc_lint_query_instability) {
+            return true;
+        }
+    }
+    false
+}
+
+/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
+/// `Span`, `GenericArgs`, and arguments. This is a slight augmentation of a similarly named Clippy
+/// function, `get_callee_generic_args_and_args`.
+fn get_callee_span_generic_args_and_args<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+) -> Option<(DefId, Span, GenericArgsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
+    if let ExprKind::Call(callee, args) = expr.kind
+        && let callee_ty = cx.typeck_results().expr_ty(callee)
+        && let ty::FnDef(callee_def_id, generic_args) = callee_ty.kind()
+    {
+        return Some((*callee_def_id, callee.span, generic_args, None, args));
+    }
+    if let ExprKind::MethodCall(segment, recv, args, _) = expr.kind
+        && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
+    {
+        let generic_args = cx.typeck_results().node_args(expr.hir_id);
+        return Some((method_def_id, segment.ident.span, generic_args, Some(recv), args));
+    }
+    None
+}
+
 declare_tool_lint! {
     /// The `usage_of_ty_tykind` lint detects usages of `ty::TyKind::<kind>`,
     /// where `ty::<kind>` would suffice.
@@ -411,22 +465,21 @@ declare_lint_pass!(LintPassImpl => [LINT_PASS_IMPL_WITHOUT_MACRO]);
 
 impl EarlyLintPass for LintPassImpl {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if let ast::ItemKind::Impl(box ast::Impl { of_trait: Some(lint_pass), .. }) = &item.kind {
-            if let Some(last) = lint_pass.path.segments.last() {
-                if last.ident.name == sym::LintPass {
-                    let expn_data = lint_pass.path.span.ctxt().outer_expn_data();
-                    let call_site = expn_data.call_site;
-                    if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
-                        && call_site.ctxt().outer_expn_data().kind
-                            != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
-                    {
-                        cx.emit_span_lint(
-                            LINT_PASS_IMPL_WITHOUT_MACRO,
-                            lint_pass.path.span,
-                            LintPassByHand,
-                        );
-                    }
-                }
+        if let ast::ItemKind::Impl(ast::Impl { of_trait: Some(of_trait), .. }) = &item.kind
+            && let Some(last) = of_trait.trait_ref.path.segments.last()
+            && last.ident.name == sym::LintPass
+        {
+            let expn_data = of_trait.trait_ref.path.span.ctxt().outer_expn_data();
+            let call_site = expn_data.call_site;
+            if expn_data.kind != ExpnKind::Macro(MacroKind::Bang, sym::impl_lint_pass)
+                && call_site.ctxt().outer_expn_data().kind
+                    != ExpnKind::Macro(MacroKind::Bang, sym::declare_lint_pass)
+            {
+                cx.emit_span_lint(
+                    LINT_PASS_IMPL_WITHOUT_MACRO,
+                    of_trait.trait_ref.path.span,
+                    LintPassByHand,
+                );
             }
         }
     }
@@ -462,33 +515,22 @@ declare_tool_lint! {
 declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);
 
 impl LateLintPass<'_> for Diagnostics {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
+    fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
         let collect_args_tys_and_spans = |args: &[hir::Expr<'_>], reserve_one_extra: bool| {
             let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
             result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
             result
         };
         // Only check function calls and method calls.
-        let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind {
-            hir::ExprKind::Call(callee, args) => {
-                match cx.typeck_results().node_type(callee.hir_id).kind() {
-                    &ty::FnDef(def_id, fn_gen_args) => {
-                        (callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
-                    }
-                    _ => return, // occurs for fns passed as args
-                }
-            }
-            hir::ExprKind::MethodCall(_segment, _recv, args, _span) => {
-                let Some((span, def_id, fn_gen_args)) = typeck_results_of_method_fn(cx, expr)
-                else {
-                    return;
-                };
-                let mut args = collect_args_tys_and_spans(args, true);
-                args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self`
-                (span, def_id, fn_gen_args, args)
-            }
-            _ => return,
+        let Some((def_id, span, fn_gen_args, recv, args)) =
+            get_callee_span_generic_args_and_args(cx, expr)
+        else {
+            return;
         };
+        let mut arg_tys_and_spans = collect_args_tys_and_spans(args, recv.is_some());
+        if let Some(recv) = recv {
+            arg_tys_and_spans.insert(0, (cx.tcx.types.self_param, recv.span)); // dummy inserted for `self`
+        }
 
         Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
         Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
@@ -497,7 +539,7 @@ impl LateLintPass<'_> for Diagnostics {
 
 impl Diagnostics {
     // Is the type `{D,Subd}iagMessage`?
-    fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
+    fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: Ty<'cx>) -> bool {
         if let Some(adt_def) = ty.ty_adt_def()
             && let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
             && matches!(name, sym::DiagMessage | sym::SubdiagMessage)
@@ -511,7 +553,7 @@ impl Diagnostics {
     fn untranslatable_diagnostic<'cx>(
         cx: &LateContext<'cx>,
         def_id: DefId,
-        arg_tys_and_spans: &[(MiddleTy<'cx>, Span)],
+        arg_tys_and_spans: &[(Ty<'cx>, Span)],
     ) {
         let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
         let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;
@@ -583,8 +625,8 @@ impl Diagnostics {
         for (_hir_id, parent) in cx.tcx.hir_parent_iter(current_id) {
             debug!(?parent);
             if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) = parent
-                && let hir::Impl { of_trait: Some(of_trait), .. } = impl_
-                && let Some(def_id) = of_trait.trait_def_id()
+                && let Some(of_trait) = impl_.of_trait
+                && let Some(def_id) = of_trait.trait_ref.trait_def_id()
                 && let Some(name) = cx.tcx.get_diagnostic_name(def_id)
                 && matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
             {
diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs
index c681deea779..ccfba715a1b 100644
--- a/compiler/rustc_lint/src/late.rs
+++ b/compiler/rustc_lint/src/late.rs
@@ -356,7 +356,16 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
     let store = unerased_lint_store(tcx.sess);
 
     if store.late_module_passes.is_empty() {
-        late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
+        // If all builtin lints can be skipped, there is no point in running `late_lint_mod_inner`
+        // at all. This happens often for dependencies built with `--cap-lints=allow`.
+        let dont_need_to_run = tcx.lints_that_dont_need_to_run(());
+        let can_skip_lints = builtin_lints
+            .get_lints()
+            .iter()
+            .all(|lint| dont_need_to_run.contains(&LintId::of(lint)));
+        if !can_skip_lints {
+            late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
+        }
     } else {
         let builtin_lints = Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>;
         let mut binding = store
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 16eeb89207b..ac47897b568 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -933,6 +933,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
     fn check_gated_lint(&self, lint_id: LintId, span: Span, lint_from_cli: bool) -> bool {
         let feature = if let Some(feature) = lint_id.lint.feature_gate
             && !self.features.enabled(feature)
+            && !span.allows_unstable(feature)
         {
             // Lint is behind a feature that is not enabled; eventually return false.
             feature
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index f06757b3c23..9bb53fea54a 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" }
 
@@ -195,8 +194,7 @@ late_lint_methods!(
             DefaultCouldBeDerived: DefaultCouldBeDerived::default(),
             DerefIntoDynSupertrait: DerefIntoDynSupertrait,
             DropForgetUseless: DropForgetUseless,
-            ImproperCTypesDeclarations: ImproperCTypesDeclarations,
-            ImproperCTypesDefinitions: ImproperCTypesDefinitions,
+            ImproperCTypesLint: ImproperCTypesLint,
             InvalidFromUtf8: InvalidFromUtf8,
             VariantSizeDifferences: VariantSizeDifferences,
             PathStatements: PathStatements,
diff --git a/compiler/rustc_lint/src/lifetime_syntax.rs b/compiler/rustc_lint/src/lifetime_syntax.rs
index 2a5a34cdc6e..413525eb6e5 100644
--- a/compiler/rustc_lint/src/lifetime_syntax.rs
+++ b/compiler/rustc_lint/src/lifetime_syntax.rs
@@ -214,9 +214,9 @@ impl<T> LifetimeSyntaxCategories<Vec<T>> {
         }
     }
 
-    pub fn flatten(&self) -> impl Iterator<Item = &T> {
-        let Self { hidden, elided, named } = self;
-        [hidden.iter(), elided.iter(), named.iter()].into_iter().flatten()
+    pub fn iter_unnamed(&self) -> impl Iterator<Item = &T> {
+        let Self { hidden, elided, named: _ } = self;
+        [hidden.iter(), elided.iter()].into_iter().flatten()
     }
 }
 
@@ -434,7 +434,7 @@ fn emit_mismatch_diagnostic<'tcx>(
         lints::MismatchedLifetimeSyntaxesSuggestion::Mixed {
             implicit_suggestions,
             explicit_anonymous_suggestions,
-            tool_only: false,
+            optional_alternative: false,
         }
     });
 
@@ -455,7 +455,10 @@ fn emit_mismatch_diagnostic<'tcx>(
     let implicit_suggestion = should_suggest_implicit.then(|| {
         let suggestions = make_implicit_suggestions(&suggest_change_to_implicit);
 
-        lints::MismatchedLifetimeSyntaxesSuggestion::Implicit { suggestions, tool_only: false }
+        lints::MismatchedLifetimeSyntaxesSuggestion::Implicit {
+            suggestions,
+            optional_alternative: false,
+        }
     });
 
     tracing::debug!(
@@ -492,7 +495,7 @@ fn emit_mismatch_diagnostic<'tcx>(
 
     cx.emit_span_lint(
         MISMATCHED_LIFETIME_SYNTAXES,
-        inputs.flatten().copied().collect::<Vec<_>>(),
+        inputs.iter_unnamed().chain(outputs.iter_unnamed()).copied().collect::<Vec<_>>(),
         lints::MismatchedLifetimeSyntaxes { inputs, outputs, suggestions },
     );
 }
@@ -508,7 +511,7 @@ fn build_mismatch_suggestion(
     lints::MismatchedLifetimeSyntaxesSuggestion::Explicit {
         lifetime_name,
         suggestions,
-        tool_only: false,
+        optional_alternative: false,
     }
 }
 
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index fd8d0f832aa..b7312484de5 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1,14 +1,14 @@
+// ignore-tidy-filelength
+
 #![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,
     EmissionGuarantee, LintDiagnostic, MultiSpan, Subdiagnostic, SuggestionStyle,
 };
 use rustc_hir as hir;
-use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::VisitorExt;
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
@@ -17,7 +17,7 @@ use rustc_middle::ty::{Clause, PolyExistentialTraitRef, Ty, TyCtxt};
 use rustc_session::Session;
 use rustc_session::lint::AmbiguityErrorDiag;
 use rustc_span::edition::Edition;
-use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
+use rustc_span::{Ident, Span, Symbol, sym};
 
 use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds};
 use crate::errors::{OverruledAttributeSub, RequestedLevel};
@@ -817,80 +817,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)]
@@ -902,9 +828,8 @@ pub(crate) struct MappingToUnit {
     pub argument_label: Span,
     #[label(lint_map_label)]
     pub map_label: Span,
-    #[suggestion(style = "verbose", code = "{replace}", applicability = "maybe-incorrect")]
+    #[suggestion(style = "verbose", code = "for_each", applicability = "maybe-incorrect")]
     pub suggestion: Span,
-    pub replace: String,
 }
 
 // internal.rs
@@ -1188,6 +1113,22 @@ pub(crate) struct DanglingPointersFromTemporaries<'tcx> {
     pub temporary_span: Span,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(lint_dangling_pointers_from_locals)]
+#[note]
+pub(crate) struct DanglingPointersFromLocals<'tcx> {
+    pub ret_ty: Ty<'tcx>,
+    #[label(lint_ret_ty)]
+    pub ret_ty_span: Span,
+    pub fn_kind: &'static str,
+    #[label(lint_local_var)]
+    pub local_var: Span,
+    pub local_var_name: Ident,
+    pub local_var_ty: Ty<'tcx>,
+    #[label(lint_created_at)]
+    pub created_at: Option<Span>,
+}
+
 // multiple_supertrait_upcastable.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_multiple_supertrait_upcastable)]
@@ -1603,6 +1544,48 @@ impl<'a> LintDiagnostic<'a, ()> for DropGlue<'_> {
     }
 }
 
+// transmute.rs
+#[derive(LintDiagnostic)]
+#[diag(lint_int_to_ptr_transmutes)]
+#[note]
+#[note(lint_note_exposed_provenance)]
+#[help(lint_suggestion_without_provenance_mut)]
+#[help(lint_help_transmute)]
+#[help(lint_help_exposed_provenance)]
+pub(crate) struct IntegerToPtrTransmutes<'tcx> {
+    #[subdiagnostic]
+    pub suggestion: Option<IntegerToPtrTransmutesSuggestion<'tcx>>,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum IntegerToPtrTransmutesSuggestion<'tcx> {
+    #[multipart_suggestion(
+        lint_suggestion_with_exposed_provenance,
+        applicability = "machine-applicable",
+        style = "verbose"
+    )]
+    ToPtr {
+        dst: Ty<'tcx>,
+        suffix: &'static str,
+        #[suggestion_part(code = "std::ptr::with_exposed_provenance{suffix}::<{dst}>(")]
+        start_call: Span,
+    },
+    #[multipart_suggestion(
+        lint_suggestion_with_exposed_provenance,
+        applicability = "machine-applicable",
+        style = "verbose"
+    )]
+    ToRef {
+        dst: Ty<'tcx>,
+        suffix: &'static str,
+        ref_mutbl: &'static str,
+        #[suggestion_part(
+            code = "&{ref_mutbl}*std::ptr::with_exposed_provenance{suffix}::<{dst}>("
+        )]
+        start_call: Span,
+    },
+}
+
 // types.rs
 #[derive(LintDiagnostic)]
 #[diag(lint_range_endpoint_out_of_range)]
@@ -2553,25 +2536,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;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_unused_macro_use)]
-pub(crate) struct UnusedMacroUse;
-
-#[derive(LintDiagnostic)]
 #[diag(lint_private_extern_crate_reexport, code = E0365)]
 pub(crate) struct PrivateExternCrateReexport {
     pub ident: Ident,
@@ -2580,10 +2544,6 @@ pub(crate) struct PrivateExternCrateReexport {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_unused_label)]
-pub(crate) struct UnusedLabel;
-
-#[derive(LintDiagnostic)]
 #[diag(lint_macro_is_private)]
 pub(crate) struct MacroIsPrivate {
     pub ident: Ident,
@@ -2613,50 +2573,6 @@ impl<'a> LintDiagnostic<'a, ()> for UnstableFeature {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_avoid_intel_syntax)]
-pub(crate) struct AvoidIntelSyntax;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_avoid_att_syntax)]
-pub(crate) struct AvoidAttSyntax;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_incomplete_include)]
-pub(crate) struct IncompleteInclude;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_unnameable_test_items)]
-pub(crate) struct UnnameableTestItems;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_duplicate_macro_attribute)]
-pub(crate) struct DuplicateMacroAttribute;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_cfg_attr_no_attributes)]
-pub(crate) struct CfgAttrNoAttributes;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_metavariable_still_repeating)]
-pub(crate) struct MetaVariableStillRepeating {
-    pub name: MacroRulesNormalizedIdent,
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_metavariable_wrong_operator)]
-pub(crate) struct MetaVariableWrongOperator;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_duplicate_matcher_binding)]
-pub(crate) struct DuplicateMatcherBinding;
-
-#[derive(LintDiagnostic)]
-#[diag(lint_unknown_macro_variable)]
-pub(crate) struct UnknownMacroVariable {
-    pub name: MacroRulesNormalizedIdent,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_unused_crate_dependency)]
 #[help]
 pub(crate) struct UnusedCrateDependency {
@@ -2670,14 +2586,9 @@ pub(crate) struct UnusedCrateDependency {
 pub(crate) struct IllFormedAttributeInput {
     pub num_suggestions: usize,
     pub suggestions: DiagArgValue,
-}
-
-#[derive(LintDiagnostic)]
-pub(crate) enum InnerAttributeUnstable {
-    #[diag(lint_inner_macro_attribute_unstable)]
-    InnerMacroAttribute,
-    #[diag(lint_custom_inner_attribute_unstable)]
-    CustomInnerAttribute,
+    #[note]
+    pub has_docs: bool,
+    pub docs: &'static str,
 }
 
 #[derive(LintDiagnostic)]
@@ -2747,22 +2658,6 @@ pub(crate) struct AbsPathWithModuleSugg {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_proc_macro_derive_resolution_fallback)]
-pub(crate) struct ProcMacroDeriveResolutionFallback {
-    #[label]
-    pub span: Span,
-    pub ns: Namespace,
-    pub ident: Ident,
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_macro_expanded_macro_exports_accessed_by_absolute_paths)]
-pub(crate) struct MacroExpandedMacroExportsAccessedByAbsolutePaths {
-    #[note]
-    pub definition: Span,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_hidden_lifetime_parameters)]
 pub(crate) struct ElidedLifetimesInPaths {
     #[subdiagnostic]
@@ -2770,21 +2665,6 @@ pub(crate) struct ElidedLifetimesInPaths {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_invalid_crate_type_value)]
-pub(crate) struct UnknownCrateTypes {
-    #[subdiagnostic]
-    pub sugg: Option<UnknownCrateTypesSub>,
-}
-
-#[derive(Subdiagnostic)]
-#[suggestion(lint_suggestion, code = r#""{candidate}""#, applicability = "maybe-incorrect")]
-pub(crate) struct UnknownCrateTypesSub {
-    #[primary_span]
-    pub span: Span,
-    pub candidate: Symbol,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_unused_imports)]
 pub(crate) struct UnusedImports {
     #[subdiagnostic]
@@ -2842,14 +2722,6 @@ pub(crate) enum RedundantImportSub {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_unused_doc_comment)]
-#[help]
-pub(crate) struct UnusedDocComment {
-    #[label]
-    pub span: Span,
-}
-
-#[derive(LintDiagnostic)]
 pub(crate) enum PatternsInFnsWithoutBody {
     #[diag(lint_pattern_in_foreign)]
     Foreign {
@@ -2873,29 +2745,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]
-    pub span: Span,
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_or_patterns_back_compat)]
-pub(crate) struct OrPatternsBackCompat {
-    #[suggestion(code = "{suggestion}", applicability = "machine-applicable")]
-    pub span: Span,
-    pub suggestion: String,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_reserved_prefix)]
 pub(crate) struct ReservedPrefix {
     #[label]
@@ -2916,26 +2765,6 @@ pub(crate) struct RawPrefix {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_unused_builtin_attribute)]
-pub(crate) struct UnusedBuiltinAttribute {
-    #[note]
-    pub invoc_span: Span,
-
-    pub attr_name: Symbol,
-    pub macro_name: String,
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_trailing_semi_macro)]
-pub(crate) struct TrailingMacro {
-    #[note(lint_note1)]
-    #[note(lint_note2)]
-    pub is_trailing: bool,
-
-    pub name: Ident,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_break_with_label_and_loop)]
 pub(crate) struct BreakWithLabelAndLoop {
     #[subdiagnostic]
@@ -2978,13 +2807,6 @@ pub(crate) enum DeprecatedWhereClauseLocationSugg {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_missing_unsafe_on_extern)]
-pub(crate) struct MissingUnsafeOnExtern {
-    #[suggestion(code = "unsafe ", applicability = "machine-applicable")]
-    pub suggestion: Span,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_single_use_lifetime)]
 pub(crate) struct SingleUseLifetime {
     #[label(lint_label_param)]
@@ -3032,23 +2854,6 @@ pub(crate) struct NamedArgumentUsedPositionally {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_byte_slice_in_packed_struct_with_derive)]
-#[help]
-pub(crate) struct ByteSliceInPackedStructWithDerive {
-    // FIXME: make this translatable
-    pub ty: String,
-}
-
-#[derive(LintDiagnostic)]
-#[diag(lint_unused_extern_crate)]
-pub(crate) struct UnusedExternCrate {
-    #[label]
-    pub span: Span,
-    #[suggestion(code = "", applicability = "machine-applicable", style = "verbose")]
-    pub removal_span: Span,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_extern_crate_not_idiomatic)]
 pub(crate) struct ExternCrateNotIdiomatic {
     #[suggestion(style = "verbose", code = "{code}", applicability = "machine-applicable")]
@@ -3096,14 +2901,6 @@ pub(crate) struct HiddenGlobReexports {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_reexport_private_dependency)]
-pub(crate) struct ReexportPrivateDependency {
-    pub name: String,
-    pub kind: String,
-    pub krate: Symbol,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_unnecessary_qualification)]
 pub(crate) struct UnusedQualifications {
     #[suggestion(style = "verbose", code = "", applicability = "machine-applicable")]
@@ -3276,7 +3073,7 @@ impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSynta
             diag.subdiagnostic(s);
 
             for mut s in suggestions {
-                s.make_tool_only();
+                s.make_optional_alternative();
                 diag.subdiagnostic(s);
             }
         }
@@ -3287,33 +3084,33 @@ impl<'a, G: EmissionGuarantee> LintDiagnostic<'a, G> for MismatchedLifetimeSynta
 pub(crate) enum MismatchedLifetimeSyntaxesSuggestion {
     Implicit {
         suggestions: Vec<Span>,
-        tool_only: bool,
+        optional_alternative: bool,
     },
 
     Mixed {
         implicit_suggestions: Vec<Span>,
         explicit_anonymous_suggestions: Vec<(Span, String)>,
-        tool_only: bool,
+        optional_alternative: bool,
     },
 
     Explicit {
         lifetime_name: String,
         suggestions: Vec<(Span, String)>,
-        tool_only: bool,
+        optional_alternative: bool,
     },
 }
 
 impl MismatchedLifetimeSyntaxesSuggestion {
-    fn make_tool_only(&mut self) {
+    fn make_optional_alternative(&mut self) {
         use MismatchedLifetimeSyntaxesSuggestion::*;
 
-        let tool_only = match self {
-            Implicit { tool_only, .. } | Mixed { tool_only, .. } | Explicit { tool_only, .. } => {
-                tool_only
-            }
+        let optional_alternative = match self {
+            Implicit { optional_alternative, .. }
+            | Mixed { optional_alternative, .. }
+            | Explicit { optional_alternative, .. } => optional_alternative,
         };
 
-        *tool_only = true;
+        *optional_alternative = true;
     }
 }
 
@@ -3321,22 +3118,40 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
     fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
         use MismatchedLifetimeSyntaxesSuggestion::*;
 
-        let style = |tool_only| {
-            if tool_only { SuggestionStyle::CompletelyHidden } else { SuggestionStyle::ShowAlways }
+        let style = |optional_alternative| {
+            if optional_alternative {
+                SuggestionStyle::CompletelyHidden
+            } else {
+                SuggestionStyle::ShowAlways
+            }
+        };
+
+        let applicability = |optional_alternative| {
+            // `cargo fix` can't handle more than one fix for the same issue,
+            // so hide alternative suggestions from it by marking them as maybe-incorrect
+            if optional_alternative {
+                Applicability::MaybeIncorrect
+            } else {
+                Applicability::MachineApplicable
+            }
         };
 
         match self {
-            Implicit { suggestions, tool_only } => {
+            Implicit { suggestions, optional_alternative } => {
                 let suggestions = suggestions.into_iter().map(|s| (s, String::new())).collect();
                 diag.multipart_suggestion_with_style(
                     fluent::lint_mismatched_lifetime_syntaxes_suggestion_implicit,
                     suggestions,
-                    Applicability::MaybeIncorrect,
-                    style(tool_only),
+                    applicability(optional_alternative),
+                    style(optional_alternative),
                 );
             }
 
-            Mixed { implicit_suggestions, explicit_anonymous_suggestions, tool_only } => {
+            Mixed {
+                implicit_suggestions,
+                explicit_anonymous_suggestions,
+                optional_alternative,
+            } => {
                 let message = if implicit_suggestions.is_empty() {
                     fluent::lint_mismatched_lifetime_syntaxes_suggestion_mixed_only_paths
                 } else {
@@ -3352,12 +3167,12 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
                 diag.multipart_suggestion_with_style(
                     message,
                     suggestions,
-                    Applicability::MaybeIncorrect,
-                    style(tool_only),
+                    applicability(optional_alternative),
+                    style(optional_alternative),
                 );
             }
 
-            Explicit { lifetime_name, suggestions, tool_only } => {
+            Explicit { lifetime_name, suggestions, optional_alternative } => {
                 diag.arg("lifetime_name", lifetime_name);
                 let msg = diag.eagerly_translate(
                     fluent::lint_mismatched_lifetime_syntaxes_suggestion_explicit,
@@ -3366,8 +3181,8 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion {
                 diag.multipart_suggestion_with_style(
                     msg,
                     suggestions,
-                    Applicability::MaybeIncorrect,
-                    style(tool_only),
+                    applicability(optional_alternative),
+                    style(optional_alternative),
                 );
             }
         }
diff --git a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
index ce280fef8b6..7de6fbd941b 100644
--- a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
+++ b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
@@ -65,7 +65,7 @@ declare_lint! {
     /// to ensure the macros implement the desired behavior.
     ///
     /// [editions]: https://doc.rust-lang.org/edition-guide/
-    /// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html
+    /// [macro matcher fragment specifiers]: https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html
     /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
     pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
     Allow,
@@ -73,7 +73,7 @@ declare_lint! {
     To keep the existing behavior, use the `expr_2021` fragment specifier.",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>",
+        reference: "Migration Guide <https://doc.rust-lang.org/edition-guide/rust-2024/macro-fragment-specifiers.html>",
     };
 }
 
diff --git a/compiler/rustc_lint/src/map_unit_fn.rs b/compiler/rustc_lint/src/map_unit_fn.rs
index af509cb786d..18a947dc1ee 100644
--- a/compiler/rustc_lint/src/map_unit_fn.rs
+++ b/compiler/rustc_lint/src/map_unit_fn.rs
@@ -1,6 +1,7 @@
-use rustc_hir::{Expr, ExprKind, HirId, Stmt, StmtKind};
-use rustc_middle::ty::{self, Ty};
+use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
+use rustc_middle::ty::{self};
 use rustc_session::{declare_lint, declare_lint_pass};
+use rustc_span::sym;
 
 use crate::lints::MappingToUnit;
 use crate::{LateContext, LateLintPass, LintContext};
@@ -39,87 +40,51 @@ declare_lint_pass!(MapUnitFn => [MAP_UNIT_FN]);
 
 impl<'tcx> LateLintPass<'tcx> for MapUnitFn {
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) {
-        if stmt.span.from_expansion() {
+        let StmtKind::Semi(expr) = stmt.kind else {
+            return;
+        };
+        let ExprKind::MethodCall(path, receiver, [arg], span) = expr.kind else {
+            return;
+        };
+        if path.ident.name != sym::map
+            || stmt.span.from_expansion()
+            || receiver.span.from_expansion()
+            || arg.span.from_expansion()
+            || !is_impl_slice(cx, receiver)
+            || !cx
+                .typeck_results()
+                .type_dependent_def_id(expr.hir_id)
+                .is_some_and(|id| cx.tcx.is_diagnostic_item(sym::IteratorMap, id))
+        {
             return;
         }
-
-        if let StmtKind::Semi(expr) = stmt.kind {
-            if let ExprKind::MethodCall(path, receiver, args, span) = expr.kind {
-                if path.ident.name.as_str() == "map" {
-                    if receiver.span.from_expansion()
-                        || args.iter().any(|e| e.span.from_expansion())
-                        || !is_impl_slice(cx, receiver)
-                        || !is_diagnostic_name(cx, expr.hir_id, "IteratorMap")
-                    {
-                        return;
-                    }
-                    let arg_ty = cx.typeck_results().expr_ty(&args[0]);
-                    let default_span = args[0].span;
-                    if let ty::FnDef(id, _) = arg_ty.kind() {
-                        let fn_ty = cx.tcx.fn_sig(id).skip_binder();
-                        let ret_ty = fn_ty.output().skip_binder();
-                        if is_unit_type(ret_ty) {
-                            cx.emit_span_lint(
-                                MAP_UNIT_FN,
-                                span,
-                                MappingToUnit {
-                                    function_label: cx
-                                        .tcx
-                                        .span_of_impl(*id)
-                                        .unwrap_or(default_span),
-                                    argument_label: args[0].span,
-                                    map_label: span,
-                                    suggestion: path.ident.span,
-                                    replace: "for_each".to_string(),
-                                },
-                            )
-                        }
-                    } else if let ty::Closure(id, subs) = arg_ty.kind() {
-                        let cl_ty = subs.as_closure().sig();
-                        let ret_ty = cl_ty.output().skip_binder();
-                        if is_unit_type(ret_ty) {
-                            cx.emit_span_lint(
-                                MAP_UNIT_FN,
-                                span,
-                                MappingToUnit {
-                                    function_label: cx
-                                        .tcx
-                                        .span_of_impl(*id)
-                                        .unwrap_or(default_span),
-                                    argument_label: args[0].span,
-                                    map_label: span,
-                                    suggestion: path.ident.span,
-                                    replace: "for_each".to_string(),
-                                },
-                            )
-                        }
-                    }
-                }
-            }
+        let (id, sig) = match *cx.typeck_results().expr_ty(arg).kind() {
+            ty::Closure(id, subs) => (id, subs.as_closure().sig()),
+            ty::FnDef(id, _) => (id, cx.tcx.fn_sig(id).skip_binder()),
+            _ => return,
+        };
+        let ret_ty = sig.output().skip_binder();
+        if !(ret_ty.is_unit() || ret_ty.is_never()) {
+            return;
         }
+        cx.emit_span_lint(
+            MAP_UNIT_FN,
+            span,
+            MappingToUnit {
+                function_label: cx.tcx.span_of_impl(id).unwrap_or(arg.span),
+                argument_label: arg.span,
+                map_label: span,
+                suggestion: path.ident.span,
+            },
+        );
     }
 }
 
 fn is_impl_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
-        if let Some(impl_id) = cx.tcx.impl_of_method(method_id) {
-            return cx.tcx.type_of(impl_id).skip_binder().is_slice();
-        }
-    }
-    false
-}
-
-fn is_unit_type(ty: Ty<'_>) -> bool {
-    ty.is_unit() || ty.is_never()
-}
-
-fn is_diagnostic_name(cx: &LateContext<'_>, id: HirId, name: &str) -> bool {
-    if let Some(def_id) = cx.typeck_results().type_dependent_def_id(id) {
-        if let Some(item) = cx.tcx.get_diagnostic_name(def_id) {
-            if item.as_str() == name {
-                return true;
-            }
-        }
+    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
+        && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id)
+    {
+        return cx.tcx.type_of(impl_id).skip_binder().is_slice();
     }
     false
 }
diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
index 5513c703f1d..93f067d0983 100644
--- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
+++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
@@ -47,7 +47,8 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
                 .explicit_super_predicates_of(def_id)
                 .iter_identity_copied()
                 .filter_map(|(pred, _)| pred.as_trait_clause())
-                .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized));
+                .filter(|pred| !cx.tcx.is_lang_item(pred.def_id(), hir::LangItem::MetaSized))
+                .filter(|pred| !cx.tcx.is_default_trait(pred.def_id()));
             if direct_super_traits_iter.count() > 1 {
                 cx.emit_span_lint(
                     MULTIPLE_SUPERTRAIT_UPCASTABLE,
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 16c06100808..2eabeeaa88f 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -48,39 +48,39 @@ declare_lint_pass!(NonPanicFmt => [NON_FMT_PANICS]);
 
 impl<'tcx> LateLintPass<'tcx> for NonPanicFmt {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
-        if let hir::ExprKind::Call(f, [arg]) = &expr.kind {
-            if let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind() {
-                let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id);
+        if let hir::ExprKind::Call(f, [arg]) = &expr.kind
+            && let &ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(f).kind()
+        {
+            let f_diagnostic_name = cx.tcx.get_diagnostic_name(def_id);
 
-                if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic)
-                    || cx.tcx.is_lang_item(def_id, LangItem::Panic)
-                    || f_diagnostic_name == Some(sym::panic_str_2015)
-                {
-                    if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
-                        if matches!(
-                            cx.tcx.get_diagnostic_name(id),
-                            Some(sym::core_panic_2015_macro | sym::std_panic_2015_macro)
-                        ) {
-                            check_panic(cx, f, arg);
-                        }
-                    }
-                } else if f_diagnostic_name == Some(sym::unreachable_display) {
-                    if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
-                        if cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id) {
-                            check_panic(
-                                cx,
-                                f,
-                                // This is safe because we checked above that the callee is indeed
-                                // unreachable_display
-                                match &arg.kind {
-                                    // Get the borrowed arg not the borrow
-                                    hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg,
-                                    _ => bug!("call to unreachable_display without borrow"),
-                                },
-                            );
-                        }
+            if cx.tcx.is_lang_item(def_id, LangItem::BeginPanic)
+                || cx.tcx.is_lang_item(def_id, LangItem::Panic)
+                || f_diagnostic_name == Some(sym::panic_str_2015)
+            {
+                if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id {
+                    if matches!(
+                        cx.tcx.get_diagnostic_name(id),
+                        Some(sym::core_panic_2015_macro | sym::std_panic_2015_macro)
+                    ) {
+                        check_panic(cx, f, arg);
                     }
                 }
+            } else if f_diagnostic_name == Some(sym::unreachable_display) {
+                if let Some(id) = f.span.ctxt().outer_expn_data().macro_def_id
+                    && cx.tcx.is_diagnostic_item(sym::unreachable_2015_macro, id)
+                {
+                    check_panic(
+                        cx,
+                        f,
+                        // This is safe because we checked above that the callee is indeed
+                        // unreachable_display
+                        match &arg.kind {
+                            // Get the borrowed arg not the borrow
+                            hir::ExprKind::AddrOf(ast::BorrowKind::Ref, _, arg) => arg,
+                            _ => bug!("call to unreachable_display without borrow"),
+                        },
+                    );
+                }
             }
         }
     }
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index b877f909fc0..dca22b986ff 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -5,7 +5,7 @@ use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
-use rustc_span::{ExpnKind, MacroKind, Span, kw, sym};
+use rustc_span::{ExpnKind, Span, kw, sym};
 
 use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
 use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
@@ -129,8 +129,8 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                 // of the `impl` definition
                 let mut collector = PathCollector { paths: Vec::new() };
                 collector.visit_ty_unambig(&impl_.self_ty);
-                if let Some(of_trait) = &impl_.of_trait {
-                    collector.visit_trait_ref(of_trait);
+                if let Some(of_trait) = impl_.of_trait {
+                    collector.visit_trait_ref(&of_trait.trait_ref);
                 }
 
                 // 1.5. Remove any path that doesn't resolve to a `DefId` or if it resolve to a
@@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions {
                     },
                 )
             }
-            ItemKind::Macro(_, _macro, MacroKind::Bang)
+            ItemKind::Macro(_, _macro, _kinds)
                 if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) =>
             {
                 cx.emit_span_lint(
diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs
index db89396d1dc..7f643a551bb 100644
--- a/compiler/rustc_lint/src/nonstandard_style.rs
+++ b/compiler/rustc_lint/src/nonstandard_style.rs
@@ -1,13 +1,13 @@
 use rustc_abi::ExternAbi;
-use rustc_attr_data_structures::{AttributeKind, ReprAttr, find_attr};
 use rustc_attr_parsing::AttributeParser;
 use rustc_errors::Applicability;
+use rustc_hir::attrs::{AttributeKind, ReprAttr};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{FnKind, Visitor};
-use rustc_hir::{AttrArgs, AttrItem, Attribute, GenericParamKind, PatExprKind, PatKind};
+use rustc_hir::{Attribute, GenericParamKind, PatExprKind, PatKind, find_attr};
 use rustc_middle::hir::nested_filter::All;
-use rustc_middle::ty;
+use rustc_middle::ty::AssocContainer;
 use rustc_session::config::CrateType;
 use rustc_session::{declare_lint, declare_lint_pass};
 use rustc_span::def_id::LocalDefId;
@@ -20,29 +20,6 @@ use crate::lints::{
 };
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 
-#[derive(PartialEq)]
-pub(crate) enum MethodLateContext {
-    TraitAutoImpl,
-    TraitImpl,
-    PlainImpl,
-}
-
-pub(crate) fn method_context(cx: &LateContext<'_>, id: LocalDefId) -> MethodLateContext {
-    let item = cx.tcx.associated_item(id);
-    match item.container {
-        ty::AssocItemContainer::Trait => MethodLateContext::TraitAutoImpl,
-        ty::AssocItemContainer::Impl => match cx.tcx.impl_trait_ref(item.container_id(cx.tcx)) {
-            Some(_) => MethodLateContext::TraitImpl,
-            None => MethodLateContext::PlainImpl,
-        },
-    }
-}
-
-fn assoc_item_in_trait_impl(cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) -> bool {
-    let item = cx.tcx.associated_item(ii.owner_id);
-    item.trait_item_def_id.is_some()
-}
-
 declare_lint! {
     /// The `non_camel_case_types` lint detects types, variants, traits and
     /// type parameters that don't have camel case names.
@@ -187,7 +164,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
 
             // N.B. This check is only for inherent associated types, so that we don't lint against
             // trait impls where we should have warned for the trait definition already.
-            ast::ItemKind::Impl(box ast::Impl { of_trait: None, items, .. }) => {
+            ast::ItemKind::Impl(ast::Impl { of_trait: None, items, .. }) => {
                 for it in items {
                     // FIXME: this doesn't respect `#[allow(..)]` on the item itself.
                     if let ast::AssocItemKind::Type(alias) = &it.kind {
@@ -343,35 +320,27 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
         let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name {
             Some(Ident::from_str(name))
         } else {
-            ast::attr::find_by_name(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), sym::crate_name).and_then(
-                |attr| {
-                    if let Attribute::Unparsed(n) = attr
-                        && let AttrItem { args: AttrArgs::Eq { eq_span: _, expr: lit }, .. } =
-                            n.as_ref()
-                        && let ast::LitKind::Str(name, ..) = lit.kind
-                    {
-                        // Discard the double quotes surrounding the literal.
-                        let sp = cx
-                            .sess()
-                            .source_map()
-                            .span_to_snippet(lit.span)
-                            .ok()
-                            .and_then(|snippet| {
-                                let left = snippet.find('"')?;
-                                let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?;
-
-                                Some(
-                                    lit.span
-                                        .with_lo(lit.span.lo() + BytePos(left as u32 + 1))
-                                        .with_hi(lit.span.hi() - BytePos(right as u32)),
-                                )
-                            })
-                            .unwrap_or(lit.span);
-
-                        Some(Ident::new(name, sp))
-                    } else {
-                        None
-                    }
+            find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::CrateName{name, name_span,..} => (name, name_span)).map(
+                |(&name, &span)| {
+                    // Discard the double quotes surrounding the literal.
+                    let sp = cx
+                        .sess()
+                        .source_map()
+                        .span_to_snippet(span)
+                        .ok()
+                        .and_then(|snippet| {
+                            let left = snippet.find('"')?;
+                            let right = snippet.rfind('"').map(|pos| snippet.len() - pos)?;
+
+                            Some(
+                                span
+                                    .with_lo(span.lo() + BytePos(left as u32 + 1))
+                                    .with_hi(span.hi() - BytePos(right as u32)),
+                            )
+                        })
+                        .unwrap_or(span);
+
+                    Ident::new(name, sp)
                 },
             )
         };
@@ -397,8 +366,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
         id: LocalDefId,
     ) {
         match &fk {
-            FnKind::Method(ident, sig, ..) => match method_context(cx, id) {
-                MethodLateContext::PlainImpl => {
+            FnKind::Method(ident, sig, ..) => match cx.tcx.associated_item(id).container {
+                AssocContainer::InherentImpl => {
                     if sig.header.abi != ExternAbi::Rust
                         && find_attr!(cx.tcx.get_all_attrs(id), AttributeKind::NoMangle(..))
                     {
@@ -406,10 +375,10 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
                     }
                     self.check_snake_case(cx, "method", ident);
                 }
-                MethodLateContext::TraitAutoImpl => {
+                AssocContainer::Trait => {
                     self.check_snake_case(cx, "trait method", ident);
                 }
-                _ => (),
+                AssocContainer::TraitImpl(_) => {}
             },
             FnKind::ItemFn(ident, _, header) => {
                 // Skip foreign-ABI #[no_mangle] functions (Issue #31924)
@@ -610,7 +579,7 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, ii: &hir::ImplItem<'_>) {
         if let hir::ImplItemKind::Const(..) = ii.kind
-            && !assoc_item_in_trait_impl(cx, ii)
+            && let hir::ImplItemImplKind::Inherent { .. } = ii.impl_kind
         {
             NonUpperCaseGlobals::check_upper_case(cx, "associated constant", None, &ii.ident);
         }
@@ -623,15 +592,15 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
             ..
         }) = p.kind
         {
-            if let Res::Def(DefKind::Const, _) = path.res {
-                if let [segment] = path.segments {
-                    NonUpperCaseGlobals::check_upper_case(
-                        cx,
-                        "constant in pattern",
-                        None,
-                        &segment.ident,
-                    );
-                }
+            if let Res::Def(DefKind::Const, _) = path.res
+                && let [segment] = path.segments
+            {
+                NonUpperCaseGlobals::check_upper_case(
+                    cx,
+                    "constant in pattern",
+                    None,
+                    &segment.ident,
+                );
             }
         }
     }
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index b7835e6c36a..24682c4562a 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
             return;
         };
 
-        let Some(trait_id) = cx.tcx.trait_of_item(did) else { return };
+        let Some(trait_id) = cx.tcx.trait_of_assoc(did) else { return };
 
         let Some(trait_) = cx.tcx.get_diagnostic_name(trait_id) else { return };
 
diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs
index d3b3b55dd4c..29006732aad 100644
--- a/compiler/rustc_lint/src/pass_by_value.rs
+++ b/compiler/rustc_lint/src/pass_by_value.rs
@@ -1,6 +1,6 @@
-use rustc_attr_data_structures::{AttributeKind, find_attr};
+use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::Res;
-use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind};
+use rustc_hir::{self as hir, AmbigArg, GenericArg, PathSegment, QPath, TyKind, find_attr};
 use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -24,10 +24,8 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue {
     fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) {
         match &ty.kind {
             TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => {
-                if let Some(impl_did) = cx.tcx.impl_of_method(ty.hir_id.owner.to_def_id()) {
-                    if cx.tcx.impl_trait_ref(impl_did).is_some() {
-                        return;
-                    }
+                if cx.tcx.trait_impl_of_assoc(ty.hir_id.owner.to_def_id()).is_some() {
+                    return;
                 }
                 if let Some(t) = path_for_pass_by_value(cx, inner_ty) {
                     cx.emit_span_lint(
diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs
index affea1b80ec..191eb721b34 100644
--- a/compiler/rustc_lint/src/passes.rs
+++ b/compiler/rustc_lint/src/passes.rs
@@ -92,7 +92,7 @@ macro_rules! expand_combined_late_lint_pass_methods {
 /// Combines multiple lints passes into a single lint pass, at compile time,
 /// for maximum speed. Each `check_foo` method in `$methods` within this pass
 /// simply calls `check_foo` once per `$pass`. Compare with
-/// `LateLintPassObjects`, which is similar, but combines lint passes at
+/// `RuntimeCombinedLateLintPass`, which is similar, but combines lint passes at
 /// runtime.
 #[macro_export]
 macro_rules! declare_combined_late_lint_pass {
@@ -123,10 +123,10 @@ macro_rules! declare_combined_late_lint_pass {
         #[allow(rustc::lint_pass_impl_without_macro)]
         impl $crate::LintPass for $name {
             fn name(&self) -> &'static str {
-                panic!()
+                stringify!($name)
             }
             fn get_lints(&self) -> LintVec {
-                panic!()
+                $name::get_lints()
             }
         }
     )
diff --git a/compiler/rustc_lint/src/ptr_nulls.rs b/compiler/rustc_lint/src/ptr_nulls.rs
index 826bce2c315..b2fa0fba76d 100644
--- a/compiler/rustc_lint/src/ptr_nulls.rs
+++ b/compiler/rustc_lint/src/ptr_nulls.rs
@@ -160,12 +160,10 @@ impl<'tcx> LateLintPass<'tcx> for PtrNullChecks {
                 let (arg_indices, are_zsts_allowed): (&[_], _) = match diag_name {
                     sym::ptr_read
                     | sym::ptr_read_unaligned
-                    | sym::ptr_read_volatile
                     | sym::ptr_replace
                     | sym::ptr_write
                     | sym::ptr_write_bytes
-                    | sym::ptr_write_unaligned
-                    | sym::ptr_write_volatile => (&[0], true),
+                    | sym::ptr_write_unaligned => (&[0], true),
                     sym::slice_from_raw_parts | sym::slice_from_raw_parts_mut => (&[0], false),
                     sym::ptr_copy
                     | sym::ptr_copy_nonoverlapping
diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs
index 00fa0499556..d296ae46f43 100644
--- a/compiler/rustc_lint/src/shadowed_into_iter.rs
+++ b/compiler/rustc_lint/src/shadowed_into_iter.rs
@@ -32,7 +32,7 @@ declare_lint! {
     "detects calling `into_iter` on arrays in Rust 2015 and 2018",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2021),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2021/IntoIterator-for-arrays.html>",
     };
 }
 
@@ -61,7 +61,7 @@ declare_lint! {
     "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/intoiterator-box-slice.html>"
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/intoiterator-box-slice.html>"
     };
 }
 
diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs
index 4dda3c7951b..16e1fb0192b 100644
--- a/compiler/rustc_lint/src/static_mut_refs.rs
+++ b/compiler/rustc_lint/src/static_mut_refs.rs
@@ -54,7 +54,7 @@ declare_lint! {
     "creating a shared reference to mutable static",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
-        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>",
+        reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>",
         explain_reason: false,
     };
     @edition Edition2024 => Deny;
diff --git a/compiler/rustc_lint/src/transmute.rs b/compiler/rustc_lint/src/transmute.rs
index bc1d4587d07..98510eea73b 100644
--- a/compiler/rustc_lint/src/transmute.rs
+++ b/compiler/rustc_lint/src/transmute.rs
@@ -1,3 +1,4 @@
+use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::LocalDefId;
@@ -7,6 +8,7 @@ use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::sym;
 
+use crate::lints::{IntegerToPtrTransmutes, IntegerToPtrTransmutesSuggestion};
 use crate::{LateContext, LateLintPass};
 
 declare_lint! {
@@ -67,9 +69,44 @@ declare_lint! {
     "detects transmutes that can also be achieved by other operations"
 }
 
+declare_lint! {
+    /// The `integer_to_ptr_transmutes` lint detects integer to pointer
+    /// transmutes where the resulting pointers are undefined behavior to dereference.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn foo(a: usize) -> *const u8 {
+    ///    unsafe {
+    ///        std::mem::transmute::<usize, *const u8>(a)
+    ///    }
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Any attempt to use the resulting pointers are undefined behavior as the resulting
+    /// pointers won't have any provenance.
+    ///
+    /// Alternatively, [`std::ptr::with_exposed_provenance`] should be used, as they do not
+    /// carry the provenance requirement. If wanting to create pointers without provenance
+    /// [`std::ptr::without_provenance`] should be used instead.
+    ///
+    /// See [`std::mem::transmute`] in the reference for more details.
+    ///
+    /// [`std::mem::transmute`]: https://doc.rust-lang.org/std/mem/fn.transmute.html
+    /// [`std::ptr::with_exposed_provenance`]: https://doc.rust-lang.org/std/ptr/fn.with_exposed_provenance.html
+    /// [`std::ptr::without_provenance`]: https://doc.rust-lang.org/std/ptr/fn.without_provenance.html
+    pub INTEGER_TO_PTR_TRANSMUTES,
+    Warn,
+    "detects integer to pointer transmutes",
+}
+
 pub(crate) struct CheckTransmutes;
 
-impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES]);
+impl_lint_pass!(CheckTransmutes => [PTR_TO_INTEGER_TRANSMUTE_IN_CONSTS, UNNECESSARY_TRANSMUTES, INTEGER_TO_PTR_TRANSMUTES]);
 
 impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
@@ -94,9 +131,68 @@ impl<'tcx> LateLintPass<'tcx> for CheckTransmutes {
 
         check_ptr_transmute_in_const(cx, expr, body_owner_def_id, const_context, src, dst);
         check_unnecessary_transmute(cx, expr, callee, arg, const_context, src, dst);
+        check_int_to_ptr_transmute(cx, expr, arg, src, dst);
     }
 }
 
+/// Check for transmutes from integer to pointers (*const/*mut and &/&mut).
+///
+/// Using the resulting pointers would be undefined behavior.
+fn check_int_to_ptr_transmute<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    arg: &'tcx hir::Expr<'tcx>,
+    src: Ty<'tcx>,
+    dst: Ty<'tcx>,
+) {
+    if !matches!(src.kind(), ty::Uint(_) | ty::Int(_)) {
+        return;
+    }
+    let (ty::Ref(_, inner_ty, mutbl) | ty::RawPtr(inner_ty, mutbl)) = dst.kind() else {
+        return;
+    };
+    // bail-out if the argument is literal 0 as we have other lints for those cases
+    if matches!(arg.kind, hir::ExprKind::Lit(hir::Lit { node: LitKind::Int(v, _), .. }) if v == 0) {
+        return;
+    }
+    // bail-out if the inner type is a ZST
+    let Ok(layout_inner_ty) = cx.tcx.layout_of(cx.typing_env().as_query_input(*inner_ty)) else {
+        return;
+    };
+    if layout_inner_ty.is_1zst() {
+        return;
+    }
+
+    let suffix = if mutbl.is_mut() { "_mut" } else { "" };
+    cx.tcx.emit_node_span_lint(
+        INTEGER_TO_PTR_TRANSMUTES,
+        expr.hir_id,
+        expr.span,
+        IntegerToPtrTransmutes {
+            suggestion: if layout_inner_ty.is_sized() {
+                Some(if dst.is_ref() {
+                    IntegerToPtrTransmutesSuggestion::ToRef {
+                        dst: *inner_ty,
+                        suffix,
+                        ref_mutbl: mutbl.prefix_str(),
+                        start_call: expr.span.shrink_to_lo().until(arg.span),
+                    }
+                } else {
+                    IntegerToPtrTransmutesSuggestion::ToPtr {
+                        dst: *inner_ty,
+                        suffix,
+                        start_call: expr.span.shrink_to_lo().until(arg.span),
+                    }
+                })
+            } else {
+                // We can't suggest using `with_exposed_provenance` for unsized type
+                // so don't suggest anything.
+                None
+            },
+        },
+    );
+}
+
 /// Check for transmutes that exhibit undefined behavior.
 /// For example, transmuting pointers to integers in a const context.
 ///
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index fc9d795cb23..eaec0c9857d 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -1,35 +1,28 @@
 use std::iter;
-use std::ops::ControlFlow;
 
-use rustc_abi::{BackendRepr, TagEncoding, VariantIdx, Variants, WrappingRange};
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::DiagMessage;
-use rustc_hir::intravisit::VisitorExt;
-use rustc_hir::{AmbigArg, Expr, ExprKind, HirId, LangItem};
+use rustc_abi::{BackendRepr, TagEncoding, Variants, WrappingRange};
+use rustc_hir::{Expr, ExprKind, HirId, LangItem};
 use rustc_middle::bug;
 use rustc_middle::ty::layout::{LayoutOf, SizeSkeleton};
-use rustc_middle::ty::{
-    self, Adt, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
-    TypeVisitableExt,
-};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
-use rustc_span::def_id::LocalDefId;
 use rustc_span::{Span, Symbol, sym};
 use tracing::debug;
 use {rustc_ast as ast, rustc_hir as hir};
 
-mod improper_ctypes;
+mod improper_ctypes; // these filed do the implementation for ImproperCTypesDefinitions,ImproperCTypesDeclarations
+pub(crate) use improper_ctypes::ImproperCTypesLint;
 
 use crate::lints::{
     AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
     AmbiguousWidePointerComparisonsAddrSuggestion, AmbiguousWidePointerComparisonsCastSuggestion,
     AmbiguousWidePointerComparisonsExpectSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
-    AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons,
+    AtomicOrderingStore, InvalidAtomicOrderingDiag, InvalidNanComparisons,
     InvalidNanComparisonsSuggestion, UnpredictableFunctionPointerComparisons,
-    UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons, UsesPowerAlignment,
+    UnpredictableFunctionPointerComparisonsSuggestion, UnusedComparisons,
     VariantSizeDifferencesDiag,
 };
-use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
+use crate::{LateContext, LateLintPass, LintContext};
 
 mod literal;
 
@@ -327,7 +320,7 @@ fn lint_wide_pointer<'tcx>(
         };
 
         (!ty.is_sized(cx.tcx, cx.typing_env()))
-            .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn))))
+            .then(|| (refs, modifiers, matches!(ty.kind(), ty::Dynamic(_, _))))
     };
 
     // the left and right operands can have references, remove any explicit references
@@ -690,144 +683,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits {
     }
 }
 
-declare_lint! {
-    /// The `improper_ctypes` lint detects incorrect use of types in foreign
-    /// modules.
-    ///
-    /// ### Example
-    ///
-    /// ```rust
-    /// unsafe extern "C" {
-    ///     static STATIC: String;
-    /// }
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// The compiler has several checks to verify that types used in `extern`
-    /// blocks are safe and follow certain rules to ensure proper
-    /// compatibility with the foreign interfaces. This lint is issued when it
-    /// detects a probable mistake in a definition. The lint usually should
-    /// provide a description of the issue, along with possibly a hint on how
-    /// to resolve it.
-    IMPROPER_CTYPES,
-    Warn,
-    "proper use of libc types in foreign modules"
-}
-
-declare_lint_pass!(ImproperCTypesDeclarations => [IMPROPER_CTYPES]);
-
-declare_lint! {
-    /// The `improper_ctypes_definitions` lint detects incorrect use of
-    /// [`extern` function] definitions.
-    ///
-    /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
-    ///
-    /// ### Example
-    ///
-    /// ```rust
-    /// # #![allow(unused)]
-    /// pub extern "C" fn str_type(p: &str) { }
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// There are many parameter and return types that may be specified in an
-    /// `extern` function that are not compatible with the given ABI. This
-    /// lint is an alert that these types should not be used. The lint usually
-    /// should provide a description of the issue, along with possibly a hint
-    /// on how to resolve it.
-    IMPROPER_CTYPES_DEFINITIONS,
-    Warn,
-    "proper use of libc types in foreign item definitions"
-}
-
-declare_lint! {
-    /// The `uses_power_alignment` lint detects specific `repr(C)`
-    /// aggregates on AIX.
-    /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment
-    /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment),
-    /// which can also be set for XLC by `#pragma align(power)` or
-    /// `-qalign=power`. Aggregates with a floating-point type as the
-    /// recursively first field (as in "at offset 0") modify the layout of
-    /// *subsequent* fields of the associated structs to use an alignment value
-    /// where the floating-point type is aligned on a 4-byte boundary.
-    ///
-    /// Effectively, subsequent floating-point fields act as-if they are `repr(packed(4))`. This
-    /// would be unsound to do in a `repr(C)` type without all the restrictions that come with
-    /// `repr(packed)`. Rust instead chooses a layout that maintains soundness of Rust code, at the
-    /// expense of incompatibility with C code.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,ignore (fails on non-powerpc64-ibm-aix)
-    /// #[repr(C)]
-    /// pub struct Floats {
-    ///     a: f64,
-    ///     b: u8,
-    ///     c: f64,
-    /// }
-    /// ```
-    ///
-    /// This will produce:
-    ///
-    /// ```text
-    /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type
-    ///  --> <source>:5:3
-    ///   |
-    /// 5 |   c: f64,
-    ///   |   ^^^^^^
-    ///   |
-    ///   = note: `#[warn(uses_power_alignment)]` on by default
-    /// ```
-    ///
-    /// ### Explanation
-    ///
-    /// The power alignment rule specifies that the above struct has the
-    /// following alignment:
-    ///  - offset_of!(Floats, a) == 0
-    ///  - offset_of!(Floats, b) == 8
-    ///  - offset_of!(Floats, c) == 12
-    ///
-    /// However, Rust currently aligns `c` at `offset_of!(Floats, c) == 16`.
-    /// Using offset 12 would be unsound since `f64` generally must be 8-aligned on this target.
-    /// Thus, a warning is produced for the above struct.
-    USES_POWER_ALIGNMENT,
-    Warn,
-    "Structs do not follow the power alignment rule under repr(C)"
-}
-
-declare_lint_pass!(ImproperCTypesDefinitions => [IMPROPER_CTYPES_DEFINITIONS, USES_POWER_ALIGNMENT]);
-
-#[derive(Clone, Copy)]
-pub(crate) enum CItemKind {
-    Declaration,
-    Definition,
-}
-
-struct ImproperCTypesVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-    mode: CItemKind,
-}
-
-/// Accumulator for recursive ffi type checking
-struct CTypesVisitorState<'tcx> {
-    cache: FxHashSet<Ty<'tcx>>,
-    /// The original type being checked, before we recursed
-    /// to any other types it contains.
-    base_ty: Ty<'tcx>,
-}
-
-enum FfiResult<'tcx> {
-    FfiSafe,
-    FfiPhantom(Ty<'tcx>),
-    FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
-}
-
 pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
     tcx: TyCtxt<'tcx>,
     def: ty::AdtDef<'tcx>,
@@ -855,14 +710,13 @@ fn ty_is_known_nonnull<'tcx>(
     tcx: TyCtxt<'tcx>,
     typing_env: ty::TypingEnv<'tcx>,
     ty: Ty<'tcx>,
-    mode: CItemKind,
 ) -> bool {
     let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
 
     match ty.kind() {
         ty::FnPtr(..) => true,
         ty::Ref(..) => true,
-        ty::Adt(def, _) if def.is_box() && matches!(mode, CItemKind::Definition) => true,
+        ty::Adt(def, _) if def.is_box() => true,
         ty::Adt(def, args) if def.repr().transparent() && !def.is_union() => {
             let marked_non_null = nonnull_optimization_guaranteed(tcx, *def);
 
@@ -878,10 +732,10 @@ fn ty_is_known_nonnull<'tcx>(
             def.variants()
                 .iter()
                 .filter_map(|variant| transparent_newtype_field(tcx, variant))
-                .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args), mode))
+                .any(|field| ty_is_known_nonnull(tcx, typing_env, field.ty(tcx, args)))
         }
         ty::Pat(base, pat) => {
-            ty_is_known_nonnull(tcx, typing_env, *base, mode)
+            ty_is_known_nonnull(tcx, typing_env, *base)
                 || pat_ty_is_known_nonnull(tcx, typing_env, *pat)
         }
         _ => false,
@@ -992,7 +846,6 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
     tcx: TyCtxt<'tcx>,
     typing_env: ty::TypingEnv<'tcx>,
     ty: Ty<'tcx>,
-    ckind: CItemKind,
 ) -> Option<Ty<'tcx>> {
     debug!("is_repr_nullable_ptr(tcx, ty = {:?})", ty);
     match ty.kind() {
@@ -1017,7 +870,7 @@ pub(crate) fn repr_nullable_ptr<'tcx>(
                 _ => return None,
             };
 
-            if !ty_is_known_nonnull(tcx, typing_env, field_ty, ckind) {
+            if !ty_is_known_nonnull(tcx, typing_env, field_ty) {
                 return None;
             }
 
@@ -1076,717 +929,13 @@ fn get_nullable_type_from_pat<'tcx>(
     }
 }
 
-impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
-    /// Check if the type is array and emit an unsafe type lint.
-    fn check_for_array_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
-        if let ty::Array(..) = ty.kind() {
-            self.emit_ffi_unsafe_type_lint(
-                ty,
-                sp,
-                fluent::lint_improper_ctypes_array_reason,
-                Some(fluent::lint_improper_ctypes_array_help),
-            );
-            true
-        } else {
-            false
-        }
-    }
-
-    /// Checks if the given field's type is "ffi-safe".
-    fn check_field_type_for_ffi(
-        &self,
-        acc: &mut CTypesVisitorState<'tcx>,
-        field: &ty::FieldDef,
-        args: GenericArgsRef<'tcx>,
-    ) -> FfiResult<'tcx> {
-        let field_ty = field.ty(self.cx.tcx, args);
-        let field_ty = self
-            .cx
-            .tcx
-            .try_normalize_erasing_regions(self.cx.typing_env(), field_ty)
-            .unwrap_or(field_ty);
-        self.check_type_for_ffi(acc, field_ty)
-    }
-
-    /// Checks if the given `VariantDef`'s field types are "ffi-safe".
-    fn check_variant_for_ffi(
-        &self,
-        acc: &mut CTypesVisitorState<'tcx>,
-        ty: Ty<'tcx>,
-        def: ty::AdtDef<'tcx>,
-        variant: &ty::VariantDef,
-        args: GenericArgsRef<'tcx>,
-    ) -> FfiResult<'tcx> {
-        use FfiResult::*;
-        let transparent_with_all_zst_fields = if def.repr().transparent() {
-            if let Some(field) = transparent_newtype_field(self.cx.tcx, variant) {
-                // Transparent newtypes have at most one non-ZST field which needs to be checked..
-                match self.check_field_type_for_ffi(acc, field, args) {
-                    FfiUnsafe { ty, .. } if ty.is_unit() => (),
-                    r => return r,
-                }
-
-                false
-            } else {
-                // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
-                // `PhantomData`).
-                true
-            }
-        } else {
-            false
-        };
-
-        // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
-        let mut all_phantom = !variant.fields.is_empty();
-        for field in &variant.fields {
-            all_phantom &= match self.check_field_type_for_ffi(acc, field, args) {
-                FfiSafe => false,
-                // `()` fields are FFI-safe!
-                FfiUnsafe { ty, .. } if ty.is_unit() => false,
-                FfiPhantom(..) => true,
-                r @ FfiUnsafe { .. } => return r,
-            }
-        }
-
-        if all_phantom {
-            FfiPhantom(ty)
-        } else if transparent_with_all_zst_fields {
-            FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
-        } else {
-            FfiSafe
-        }
-    }
-
-    /// Checks if the given type is "ffi-safe" (has a stable, well-defined
-    /// representation which can be exported to C code).
-    fn check_type_for_ffi(
-        &self,
-        acc: &mut CTypesVisitorState<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> FfiResult<'tcx> {
-        use FfiResult::*;
-
-        let tcx = self.cx.tcx;
-
-        // Protect against infinite recursion, for example
-        // `struct S(*mut S);`.
-        // FIXME: A recursion limit is necessary as well, for irregular
-        // recursive types.
-        if !acc.cache.insert(ty) {
-            return FfiSafe;
-        }
-
-        match *ty.kind() {
-            ty::Adt(def, args) => {
-                if let Some(boxed) = ty.boxed_ty()
-                    && matches!(self.mode, CItemKind::Definition)
-                {
-                    if boxed.is_sized(tcx, self.cx.typing_env()) {
-                        return FfiSafe;
-                    } else {
-                        return FfiUnsafe {
-                            ty,
-                            reason: fluent::lint_improper_ctypes_box,
-                            help: None,
-                        };
-                    }
-                }
-                if def.is_phantom_data() {
-                    return FfiPhantom(ty);
-                }
-                match def.adt_kind() {
-                    AdtKind::Struct | AdtKind::Union => {
-                        if let Some(sym::cstring_type | sym::cstr_type) =
-                            tcx.get_diagnostic_name(def.did())
-                            && !acc.base_ty.is_mutable_ptr()
-                        {
-                            return FfiUnsafe {
-                                ty,
-                                reason: fluent::lint_improper_ctypes_cstr_reason,
-                                help: Some(fluent::lint_improper_ctypes_cstr_help),
-                            };
-                        }
-
-                        if !def.repr().c() && !def.repr().transparent() {
-                            return FfiUnsafe {
-                                ty,
-                                reason: if def.is_struct() {
-                                    fluent::lint_improper_ctypes_struct_layout_reason
-                                } else {
-                                    fluent::lint_improper_ctypes_union_layout_reason
-                                },
-                                help: if def.is_struct() {
-                                    Some(fluent::lint_improper_ctypes_struct_layout_help)
-                                } else {
-                                    Some(fluent::lint_improper_ctypes_union_layout_help)
-                                },
-                            };
-                        }
-
-                        if def.non_enum_variant().field_list_has_applicable_non_exhaustive() {
-                            return FfiUnsafe {
-                                ty,
-                                reason: if def.is_struct() {
-                                    fluent::lint_improper_ctypes_struct_non_exhaustive
-                                } else {
-                                    fluent::lint_improper_ctypes_union_non_exhaustive
-                                },
-                                help: None,
-                            };
-                        }
-
-                        if def.non_enum_variant().fields.is_empty() {
-                            return FfiUnsafe {
-                                ty,
-                                reason: if def.is_struct() {
-                                    fluent::lint_improper_ctypes_struct_fieldless_reason
-                                } else {
-                                    fluent::lint_improper_ctypes_union_fieldless_reason
-                                },
-                                help: if def.is_struct() {
-                                    Some(fluent::lint_improper_ctypes_struct_fieldless_help)
-                                } else {
-                                    Some(fluent::lint_improper_ctypes_union_fieldless_help)
-                                },
-                            };
-                        }
-
-                        self.check_variant_for_ffi(acc, ty, def, def.non_enum_variant(), args)
-                    }
-                    AdtKind::Enum => {
-                        if def.variants().is_empty() {
-                            // Empty enums are okay... although sort of useless.
-                            return FfiSafe;
-                        }
-                        // Check for a repr() attribute to specify the size of the
-                        // discriminant.
-                        if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
-                        {
-                            // Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
-                            if let Some(ty) =
-                                repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty, self.mode)
-                            {
-                                return self.check_type_for_ffi(acc, ty);
-                            }
-
-                            return FfiUnsafe {
-                                ty,
-                                reason: fluent::lint_improper_ctypes_enum_repr_reason,
-                                help: Some(fluent::lint_improper_ctypes_enum_repr_help),
-                            };
-                        }
-
-                        use improper_ctypes::check_non_exhaustive_variant;
-
-                        let non_exhaustive = def.variant_list_has_applicable_non_exhaustive();
-                        // Check the contained variants.
-                        let ret = def.variants().iter().try_for_each(|variant| {
-                            check_non_exhaustive_variant(non_exhaustive, variant)
-                                .map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
-
-                            match self.check_variant_for_ffi(acc, ty, def, variant, args) {
-                                FfiSafe => ControlFlow::Continue(()),
-                                r => ControlFlow::Break(r),
-                            }
-                        });
-                        if let ControlFlow::Break(result) = ret {
-                            return result;
-                        }
-
-                        FfiSafe
-                    }
-                }
-            }
-
-            ty::Char => FfiUnsafe {
-                ty,
-                reason: fluent::lint_improper_ctypes_char_reason,
-                help: Some(fluent::lint_improper_ctypes_char_help),
-            },
-
-            // It's just extra invariants on the type that you need to uphold,
-            // but only the base type is relevant for being representable in FFI.
-            ty::Pat(base, ..) => self.check_type_for_ffi(acc, base),
-
-            // Primitive types with a stable representation.
-            ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
-
-            ty::Slice(_) => FfiUnsafe {
-                ty,
-                reason: fluent::lint_improper_ctypes_slice_reason,
-                help: Some(fluent::lint_improper_ctypes_slice_help),
-            },
-
-            ty::Dynamic(..) => {
-                FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
-            }
-
-            ty::Str => FfiUnsafe {
-                ty,
-                reason: fluent::lint_improper_ctypes_str_reason,
-                help: Some(fluent::lint_improper_ctypes_str_help),
-            },
-
-            ty::Tuple(..) => FfiUnsafe {
-                ty,
-                reason: fluent::lint_improper_ctypes_tuple_reason,
-                help: Some(fluent::lint_improper_ctypes_tuple_help),
-            },
-
-            ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
-                if {
-                    matches!(self.mode, CItemKind::Definition)
-                        && ty.is_sized(self.cx.tcx, self.cx.typing_env())
-                } =>
-            {
-                FfiSafe
-            }
-
-            ty::RawPtr(ty, _)
-                if match ty.kind() {
-                    ty::Tuple(tuple) => tuple.is_empty(),
-                    _ => false,
-                } =>
-            {
-                FfiSafe
-            }
-
-            ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.check_type_for_ffi(acc, ty),
-
-            ty::Array(inner_ty, _) => self.check_type_for_ffi(acc, inner_ty),
-
-            ty::FnPtr(sig_tys, hdr) => {
-                let sig = sig_tys.with(hdr);
-                if sig.abi().is_rustic_abi() {
-                    return FfiUnsafe {
-                        ty,
-                        reason: fluent::lint_improper_ctypes_fnptr_reason,
-                        help: Some(fluent::lint_improper_ctypes_fnptr_help),
-                    };
-                }
-
-                let sig = tcx.instantiate_bound_regions_with_erased(sig);
-                for arg in sig.inputs() {
-                    match self.check_type_for_ffi(acc, *arg) {
-                        FfiSafe => {}
-                        r => return r,
-                    }
-                }
-
-                let ret_ty = sig.output();
-                if ret_ty.is_unit() {
-                    return FfiSafe;
-                }
-
-                self.check_type_for_ffi(acc, ret_ty)
-            }
-
-            ty::Foreign(..) => FfiSafe,
-
-            // While opaque types are checked for earlier, if a projection in a struct field
-            // normalizes to an opaque type, then it will reach this branch.
-            ty::Alias(ty::Opaque, ..) => {
-                FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
-            }
-
-            // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
-            //  so they are currently ignored for the purposes of this lint.
-            ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
-                if matches!(self.mode, CItemKind::Definition) =>
-            {
-                FfiSafe
-            }
-
-            ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
-
-            ty::Param(..)
-            | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
-            | ty::Infer(..)
-            | ty::Bound(..)
-            | ty::Error(_)
-            | ty::Closure(..)
-            | ty::CoroutineClosure(..)
-            | ty::Coroutine(..)
-            | ty::CoroutineWitness(..)
-            | ty::Placeholder(..)
-            | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
-        }
-    }
-
-    fn emit_ffi_unsafe_type_lint(
-        &mut self,
-        ty: Ty<'tcx>,
-        sp: Span,
-        note: DiagMessage,
-        help: Option<DiagMessage>,
-    ) {
-        let lint = match self.mode {
-            CItemKind::Declaration => IMPROPER_CTYPES,
-            CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
-        };
-        let desc = match self.mode {
-            CItemKind::Declaration => "block",
-            CItemKind::Definition => "fn",
-        };
-        let span_note = if let ty::Adt(def, _) = ty.kind()
-            && let Some(sp) = self.cx.tcx.hir_span_if_local(def.did())
-        {
-            Some(sp)
-        } else {
-            None
-        };
-        self.cx.emit_span_lint(
-            lint,
-            sp,
-            ImproperCTypes { ty, desc, label: sp, help, note, span_note },
-        );
-    }
-
-    fn check_for_opaque_ty(&mut self, sp: Span, ty: Ty<'tcx>) -> bool {
-        struct ProhibitOpaqueTypes;
-        impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
-            type Result = ControlFlow<Ty<'tcx>>;
-
-            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
-                if !ty.has_opaque_types() {
-                    return ControlFlow::Continue(());
-                }
-
-                if let ty::Alias(ty::Opaque, ..) = ty.kind() {
-                    ControlFlow::Break(ty)
-                } else {
-                    ty.super_visit_with(self)
-                }
-            }
-        }
-
-        if let Some(ty) = self
-            .cx
-            .tcx
-            .try_normalize_erasing_regions(self.cx.typing_env(), ty)
-            .unwrap_or(ty)
-            .visit_with(&mut ProhibitOpaqueTypes)
-            .break_value()
-        {
-            self.emit_ffi_unsafe_type_lint(ty, sp, fluent::lint_improper_ctypes_opaque, None);
-            true
-        } else {
-            false
-        }
-    }
-
-    fn check_type_for_ffi_and_report_errors(
-        &mut self,
-        sp: Span,
-        ty: Ty<'tcx>,
-        is_static: bool,
-        is_return_type: bool,
-    ) {
-        if self.check_for_opaque_ty(sp, ty) {
-            // We've already emitted an error due to an opaque type.
-            return;
-        }
-
-        let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
-
-        // C doesn't really support passing arrays by value - the only way to pass an array by value
-        // is through a struct. So, first test that the top level isn't an array, and then
-        // recursively check the types inside.
-        if !is_static && self.check_for_array_ty(sp, ty) {
-            return;
-        }
-
-        // Don't report FFI errors for unit return types. This check exists here, and not in
-        // the caller (where it would make more sense) so that normalization has definitely
-        // happened.
-        if is_return_type && ty.is_unit() {
-            return;
-        }
-
-        let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
-        match self.check_type_for_ffi(&mut acc, ty) {
-            FfiResult::FfiSafe => {}
-            FfiResult::FfiPhantom(ty) => {
-                self.emit_ffi_unsafe_type_lint(
-                    ty,
-                    sp,
-                    fluent::lint_improper_ctypes_only_phantomdata,
-                    None,
-                );
-            }
-            FfiResult::FfiUnsafe { ty, reason, help } => {
-                self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
-            }
-        }
-    }
-
-    /// Check if a function's argument types and result type are "ffi-safe".
-    ///
-    /// For a external ABI function, argument types and the result type are walked to find fn-ptr
-    /// types that have external ABIs, as these still need checked.
-    fn check_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
-        let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity();
-        let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
-
-        for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
-            for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(input_hir, *input_ty) {
-                self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, false);
-            }
-        }
-
-        if let hir::FnRetTy::Return(ret_hir) = decl.output {
-            for (fn_ptr_ty, span) in self.find_fn_ptr_ty_with_external_abi(ret_hir, sig.output()) {
-                self.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, true);
-            }
-        }
-    }
-
-    /// Check if a function's argument types and result type are "ffi-safe".
-    fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &'tcx hir::FnDecl<'_>) {
-        let sig = self.cx.tcx.fn_sig(def_id).instantiate_identity();
-        let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
-
-        for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
-            self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
-        }
-
-        if let hir::FnRetTy::Return(ret_hir) = decl.output {
-            self.check_type_for_ffi_and_report_errors(ret_hir.span, sig.output(), false, true);
-        }
-    }
-
-    fn check_foreign_static(&mut self, id: hir::OwnerId, span: Span) {
-        let ty = self.cx.tcx.type_of(id).instantiate_identity();
-        self.check_type_for_ffi_and_report_errors(span, ty, true, false);
-    }
-
-    /// Find any fn-ptr types with external ABIs in `ty`.
-    ///
-    /// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
-    fn find_fn_ptr_ty_with_external_abi(
-        &self,
-        hir_ty: &hir::Ty<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Vec<(Ty<'tcx>, Span)> {
-        struct FnPtrFinder<'tcx> {
-            spans: Vec<Span>,
-            tys: Vec<Ty<'tcx>>,
-        }
-
-        impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> {
-            fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
-                debug!(?ty);
-                if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind
-                    && !abi.is_rustic_abi()
-                {
-                    self.spans.push(ty.span);
-                }
-
-                hir::intravisit::walk_ty(self, ty)
-            }
-        }
-
-        impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'tcx> {
-            type Result = ();
-
-            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
-                if let ty::FnPtr(_, hdr) = ty.kind()
-                    && !hdr.abi.is_rustic_abi()
-                {
-                    self.tys.push(ty);
-                }
-
-                ty.super_visit_with(self)
-            }
-        }
-
-        let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() };
-        ty.visit_with(&mut visitor);
-        visitor.visit_ty_unambig(hir_ty);
-
-        iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
-    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
-        let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
-        let abi = cx.tcx.hir_get_foreign_abi(it.hir_id());
-
-        match it.kind {
-            hir::ForeignItemKind::Fn(sig, _, _) => {
-                if abi.is_rustic_abi() {
-                    vis.check_fn(it.owner_id.def_id, sig.decl)
-                } else {
-                    vis.check_foreign_fn(it.owner_id.def_id, sig.decl);
-                }
-            }
-            hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => {
-                vis.check_foreign_static(it.owner_id, ty.span);
-            }
-            hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
-        }
-    }
-}
-
-impl ImproperCTypesDefinitions {
-    fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        hir_ty: &'tcx hir::Ty<'_>,
-        ty: Ty<'tcx>,
-    ) {
-        let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
-        for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
-            vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
-        }
-    }
-
-    fn check_arg_for_power_alignment<'tcx>(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> bool {
-        assert!(cx.tcx.sess.target.os == "aix");
-        // Structs (under repr(C)) follow the power alignment rule if:
-        //   - the first field of the struct is a floating-point type that
-        //     is greater than 4-bytes, or
-        //   - the first field of the struct is an aggregate whose
-        //     recursively first field is a floating-point type greater than
-        //     4 bytes.
-        if ty.is_floating_point() && ty.primitive_size(cx.tcx).bytes() > 4 {
-            return true;
-        } else if let Adt(adt_def, _) = ty.kind()
-            && adt_def.is_struct()
-            && adt_def.repr().c()
-            && !adt_def.repr().packed()
-            && adt_def.repr().align.is_none()
-        {
-            let struct_variant = adt_def.variant(VariantIdx::ZERO);
-            // Within a nested struct, all fields are examined to correctly
-            // report if any fields after the nested struct within the
-            // original struct are misaligned.
-            for struct_field in &struct_variant.fields {
-                let field_ty = cx.tcx.type_of(struct_field.did).instantiate_identity();
-                if self.check_arg_for_power_alignment(cx, field_ty) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
-    fn check_struct_for_power_alignment<'tcx>(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        item: &'tcx hir::Item<'tcx>,
-    ) {
-        let adt_def = cx.tcx.adt_def(item.owner_id.to_def_id());
-        // repr(C) structs also with packed or aligned representation
-        // should be ignored.
-        if adt_def.repr().c()
-            && !adt_def.repr().packed()
-            && adt_def.repr().align.is_none()
-            && cx.tcx.sess.target.os == "aix"
-            && !adt_def.all_fields().next().is_none()
-        {
-            let struct_variant_data = item.expect_struct().2;
-            for field_def in struct_variant_data.fields().iter().skip(1) {
-                // Struct fields (after the first field) are checked for the
-                // power alignment rule, as fields after the first are likely
-                // to be the fields that are misaligned.
-                let def_id = field_def.def_id;
-                let ty = cx.tcx.type_of(def_id).instantiate_identity();
-                if self.check_arg_for_power_alignment(cx, ty) {
-                    cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment);
-                }
-            }
-        }
-    }
-}
-
-/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
-/// `extern "C" { }` blocks):
-///
-/// - `extern "<abi>" fn` definitions are checked in the same way as the
-///   `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
-/// - All other items which contain types (e.g. other functions, struct definitions, etc) are
-///   checked for extern fn-ptrs with external ABIs.
-impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
-        match item.kind {
-            hir::ItemKind::Static(_, _, ty, _)
-            | hir::ItemKind::Const(_, _, ty, _)
-            | hir::ItemKind::TyAlias(_, _, ty) => {
-                self.check_ty_maybe_containing_foreign_fnptr(
-                    cx,
-                    ty,
-                    cx.tcx.type_of(item.owner_id).instantiate_identity(),
-                );
-            }
-            // See `check_fn`..
-            hir::ItemKind::Fn { .. } => {}
-            // Structs are checked based on if they follow the power alignment
-            // rule (under repr(C)).
-            hir::ItemKind::Struct(..) => {
-                self.check_struct_for_power_alignment(cx, item);
-            }
-            // See `check_field_def`..
-            hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {}
-            // Doesn't define something that can contain a external type to be checked.
-            hir::ItemKind::Impl(..)
-            | hir::ItemKind::TraitAlias(..)
-            | hir::ItemKind::Trait(..)
-            | hir::ItemKind::GlobalAsm { .. }
-            | hir::ItemKind::ForeignMod { .. }
-            | hir::ItemKind::Mod(..)
-            | hir::ItemKind::Macro(..)
-            | hir::ItemKind::Use(..)
-            | hir::ItemKind::ExternCrate(..) => {}
-        }
-    }
-
-    fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
-        self.check_ty_maybe_containing_foreign_fnptr(
-            cx,
-            field.ty,
-            cx.tcx.type_of(field.def_id).instantiate_identity(),
-        );
-    }
-
-    fn check_fn(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        kind: hir::intravisit::FnKind<'tcx>,
-        decl: &'tcx hir::FnDecl<'_>,
-        _: &'tcx hir::Body<'_>,
-        _: Span,
-        id: LocalDefId,
-    ) {
-        use hir::intravisit::FnKind;
-
-        let abi = match kind {
-            FnKind::ItemFn(_, _, header, ..) => header.abi,
-            FnKind::Method(_, sig, ..) => sig.header.abi,
-            _ => return,
-        };
-
-        let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
-        if abi.is_rustic_abi() {
-            vis.check_fn(id, decl);
-        } else {
-            vis.check_foreign_fn(id, decl);
-        }
-    }
-}
-
 declare_lint_pass!(VariantSizeDifferences => [VARIANT_SIZE_DIFFERENCES]);
 
 impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         if let hir::ItemKind::Enum(_, _, ref enum_definition) = it.kind {
             let t = cx.tcx.type_of(it.owner_id).instantiate_identity();
-            let ty = cx.tcx.erase_regions(t);
+            let ty = cx.tcx.erase_and_anonymize_regions(t);
             let Ok(layout) = cx.layout_of(ty) else { return };
             let Variants::Multiple { tag_encoding: TagEncoding::Direct, tag, variants, .. } =
                 &layout.variants
@@ -1904,10 +1053,9 @@ impl InvalidAtomicOrdering {
         if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind
             && recognized_names.contains(&method_path.ident.name)
             && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
-            && let Some(impl_did) = cx.tcx.impl_of_method(m_def_id)
-            && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
             // skip extension traits, only lint functions from the standard library
-            && cx.tcx.trait_id_of_impl(impl_did).is_none()
+            && let Some(impl_did) = cx.tcx.inherent_impl_of_assoc(m_def_id)
+            && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
             && let parent = cx.tcx.parent(adt.did())
             && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent)
             && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did()))
diff --git a/compiler/rustc_lint/src/types/improper_ctypes.rs b/compiler/rustc_lint/src/types/improper_ctypes.rs
index 13afa540afc..7ca57b0094e 100644
--- a/compiler/rustc_lint/src/types/improper_ctypes.rs
+++ b/compiler/rustc_lint/src/types/improper_ctypes.rs
@@ -1,10 +1,141 @@
+use std::iter;
 use std::ops::ControlFlow;
 
+use bitflags::bitflags;
+use rustc_abi::VariantIdx;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::DiagMessage;
 use rustc_hir::def::CtorKind;
-use rustc_middle::ty;
+use rustc_hir::intravisit::VisitorExt;
+use rustc_hir::{self as hir, AmbigArg};
+use rustc_middle::bug;
+use rustc_middle::ty::{
+    self, Adt, AdtDef, AdtKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+    TypeVisitableExt,
+};
+use rustc_session::{declare_lint, declare_lint_pass};
+use rustc_span::def_id::LocalDefId;
+use rustc_span::{Span, sym};
+use tracing::debug;
 
-use crate::fluent_generated as fluent;
+use super::repr_nullable_ptr;
+use crate::lints::{ImproperCTypes, UsesPowerAlignment};
+use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent};
+
+declare_lint! {
+    /// The `improper_ctypes` lint detects incorrect use of types in foreign
+    /// modules.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// unsafe extern "C" {
+    ///     static STATIC: String;
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// The compiler has several checks to verify that types used in `extern`
+    /// blocks are safe and follow certain rules to ensure proper
+    /// compatibility with the foreign interfaces. This lint is issued when it
+    /// detects a probable mistake in a definition. The lint usually should
+    /// provide a description of the issue, along with possibly a hint on how
+    /// to resolve it.
+    IMPROPER_CTYPES,
+    Warn,
+    "proper use of libc types in foreign modules"
+}
+
+declare_lint! {
+    /// The `improper_ctypes_definitions` lint detects incorrect use of
+    /// [`extern` function] definitions.
+    ///
+    /// [`extern` function]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// # #![allow(unused)]
+    /// pub extern "C" fn str_type(p: &str) { }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// There are many parameter and return types that may be specified in an
+    /// `extern` function that are not compatible with the given ABI. This
+    /// lint is an alert that these types should not be used. The lint usually
+    /// should provide a description of the issue, along with possibly a hint
+    /// on how to resolve it.
+    IMPROPER_CTYPES_DEFINITIONS,
+    Warn,
+    "proper use of libc types in foreign item definitions"
+}
+
+declare_lint! {
+    /// The `uses_power_alignment` lint detects specific `repr(C)`
+    /// aggregates on AIX.
+    /// In its platform C ABI, AIX uses the "power" (as in PowerPC) alignment
+    /// rule (detailed in https://www.ibm.com/docs/en/xl-c-and-cpp-aix/16.1?topic=data-using-alignment-modes#alignment),
+    /// which can also be set for XLC by `#pragma align(power)` or
+    /// `-qalign=power`. Aggregates with a floating-point type as the
+    /// recursively first field (as in "at offset 0") modify the layout of
+    /// *subsequent* fields of the associated structs to use an alignment value
+    /// where the floating-point type is aligned on a 4-byte boundary.
+    ///
+    /// Effectively, subsequent floating-point fields act as-if they are `repr(packed(4))`. This
+    /// would be unsound to do in a `repr(C)` type without all the restrictions that come with
+    /// `repr(packed)`. Rust instead chooses a layout that maintains soundness of Rust code, at the
+    /// expense of incompatibility with C code.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,ignore (fails on non-powerpc64-ibm-aix)
+    /// #[repr(C)]
+    /// pub struct Floats {
+    ///     a: f64,
+    ///     b: u8,
+    ///     c: f64,
+    /// }
+    /// ```
+    ///
+    /// This will produce:
+    ///
+    /// ```text
+    /// warning: repr(C) does not follow the power alignment rule. This may affect platform C ABI compatibility for this type
+    ///  --> <source>:5:3
+    ///   |
+    /// 5 |   c: f64,
+    ///   |   ^^^^^^
+    ///   |
+    ///   = note: `#[warn(uses_power_alignment)]` on by default
+    /// ```
+    ///
+    /// ### Explanation
+    ///
+    /// The power alignment rule specifies that the above struct has the
+    /// following alignment:
+    ///  - offset_of!(Floats, a) == 0
+    ///  - offset_of!(Floats, b) == 8
+    ///  - offset_of!(Floats, c) == 12
+    ///
+    /// However, Rust currently aligns `c` at `offset_of!(Floats, c) == 16`.
+    /// Using offset 12 would be unsound since `f64` generally must be 8-aligned on this target.
+    /// Thus, a warning is produced for the above struct.
+    USES_POWER_ALIGNMENT,
+    Warn,
+    "Structs do not follow the power alignment rule under repr(C)"
+}
+
+declare_lint_pass!(ImproperCTypesLint => [
+    IMPROPER_CTYPES,
+    IMPROPER_CTYPES_DEFINITIONS,
+    USES_POWER_ALIGNMENT
+]);
 
 /// Check a variant of a non-exhaustive enum for improper ctypes
 ///
@@ -41,3 +172,884 @@ fn variant_has_complex_ctor(variant: &ty::VariantDef) -> bool {
     // CtorKind::Const means a "unit" ctor
     !matches!(variant.ctor_kind(), Some(CtorKind::Const))
 }
+
+/// Per-struct-field function that checks if a struct definition follows
+/// the Power alignment Rule (see the `check_struct_for_power_alignment` function).
+fn check_arg_for_power_alignment<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    let tcx = cx.tcx;
+    assert!(tcx.sess.target.os == "aix");
+    // Structs (under repr(C)) follow the power alignment rule if:
+    //   - the first field of the struct is a floating-point type that
+    //     is greater than 4-bytes, or
+    //   - the first field of the struct is an aggregate whose
+    //     recursively first field is a floating-point type greater than
+    //     4 bytes.
+    if ty.is_floating_point() && ty.primitive_size(tcx).bytes() > 4 {
+        return true;
+    } else if let Adt(adt_def, _) = ty.kind()
+        && adt_def.is_struct()
+        && adt_def.repr().c()
+        && !adt_def.repr().packed()
+        && adt_def.repr().align.is_none()
+    {
+        let struct_variant = adt_def.variant(VariantIdx::ZERO);
+        // Within a nested struct, all fields are examined to correctly
+        // report if any fields after the nested struct within the
+        // original struct are misaligned.
+        for struct_field in &struct_variant.fields {
+            let field_ty = tcx.type_of(struct_field.did).instantiate_identity();
+            if check_arg_for_power_alignment(cx, field_ty) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+/// Check a struct definition for respect of the Power alignment Rule (as in PowerPC),
+/// which should be respected in the "aix" target OS.
+/// To do so, we must follow one of the two following conditions:
+/// - The first field of the struct must be floating-point type that
+///    is greater than 4-bytes.
+///  - The first field of the struct must be an aggregate whose
+///    recursively first field is a floating-point type greater than
+///    4 bytes.
+fn check_struct_for_power_alignment<'tcx>(
+    cx: &LateContext<'tcx>,
+    item: &'tcx hir::Item<'tcx>,
+    adt_def: AdtDef<'tcx>,
+) {
+    let tcx = cx.tcx;
+    // repr(C) structs also with packed or aligned representation
+    // should be ignored.
+    if adt_def.repr().c()
+        && !adt_def.repr().packed()
+        && adt_def.repr().align.is_none()
+        && tcx.sess.target.os == "aix"
+        && !adt_def.all_fields().next().is_none()
+    {
+        let struct_variant_data = item.expect_struct().2;
+        for field_def in struct_variant_data.fields().iter().skip(1) {
+            // Struct fields (after the first field) are checked for the
+            // power alignment rule, as fields after the first are likely
+            // to be the fields that are misaligned.
+            let def_id = field_def.def_id;
+            let ty = tcx.type_of(def_id).instantiate_identity();
+            if check_arg_for_power_alignment(cx, ty) {
+                cx.emit_span_lint(USES_POWER_ALIGNMENT, field_def.span, UsesPowerAlignment);
+            }
+        }
+    }
+}
+
+#[derive(Clone, Copy)]
+enum CItemKind {
+    Declaration,
+    Definition,
+}
+
+enum FfiResult<'tcx> {
+    FfiSafe,
+    FfiPhantom(Ty<'tcx>),
+    FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
+}
+
+/// The result when a type has been checked but perhaps not completely. `None` indicates that
+/// FFI safety/unsafety has not yet been determined, `Some(res)` indicates that the safety/unsafety
+/// in the `FfiResult` is final.
+type PartialFfiResult<'tcx> = Option<FfiResult<'tcx>>;
+
+bitflags! {
+    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
+    struct VisitorState: u8 {
+        /// For use in (externally-linked) static variables.
+        const STATIC = 0b000001;
+        /// For use in functions in general.
+        const FUNC = 0b000010;
+        /// For variables in function returns (implicitly: not for static variables).
+        const FN_RETURN = 0b000100;
+        /// For variables in functions/variables which are defined in rust.
+        const DEFINED = 0b001000;
+        /// For times where we are only defining the type of something
+        /// (struct/enum/union definitions, FnPtrs).
+        const THEORETICAL = 0b010000;
+    }
+}
+
+impl VisitorState {
+    // The values that can be set.
+    const STATIC_TY: Self = Self::STATIC;
+    const ARGUMENT_TY_IN_DEFINITION: Self =
+        Self::from_bits(Self::FUNC.bits() | Self::DEFINED.bits()).unwrap();
+    const RETURN_TY_IN_DEFINITION: Self =
+        Self::from_bits(Self::FUNC.bits() | Self::FN_RETURN.bits() | Self::DEFINED.bits()).unwrap();
+    const ARGUMENT_TY_IN_DECLARATION: Self = Self::FUNC;
+    const RETURN_TY_IN_DECLARATION: Self =
+        Self::from_bits(Self::FUNC.bits() | Self::FN_RETURN.bits()).unwrap();
+    const ARGUMENT_TY_IN_FNPTR: Self =
+        Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits()).unwrap();
+    const RETURN_TY_IN_FNPTR: Self =
+        Self::from_bits(Self::FUNC.bits() | Self::THEORETICAL.bits() | Self::FN_RETURN.bits())
+            .unwrap();
+
+    /// Get the proper visitor state for a given function's arguments.
+    fn argument_from_fnmode(fn_mode: CItemKind) -> Self {
+        match fn_mode {
+            CItemKind::Definition => VisitorState::ARGUMENT_TY_IN_DEFINITION,
+            CItemKind::Declaration => VisitorState::ARGUMENT_TY_IN_DECLARATION,
+        }
+    }
+
+    /// Get the proper visitor state for a given function's return type.
+    fn return_from_fnmode(fn_mode: CItemKind) -> Self {
+        match fn_mode {
+            CItemKind::Definition => VisitorState::RETURN_TY_IN_DEFINITION,
+            CItemKind::Declaration => VisitorState::RETURN_TY_IN_DECLARATION,
+        }
+    }
+
+    /// Whether the type is used in a function.
+    fn is_in_function(self) -> bool {
+        let ret = self.contains(Self::FUNC);
+        if ret {
+            debug_assert!(!self.contains(Self::STATIC));
+        }
+        ret
+    }
+    /// Whether the type is used (directly or not) in a function, in return position.
+    fn is_in_function_return(self) -> bool {
+        let ret = self.contains(Self::FN_RETURN);
+        if ret {
+            debug_assert!(self.is_in_function());
+        }
+        ret
+    }
+    /// Whether the type is used (directly or not) in a defined function.
+    /// In other words, whether or not we allow non-FFI-safe types behind a C pointer,
+    /// to be treated as an opaque type on the other side of the FFI boundary.
+    fn is_in_defined_function(self) -> bool {
+        self.contains(Self::DEFINED) && self.is_in_function()
+    }
+
+    /// Whether the type is used (directly or not) in a function pointer type.
+    /// Here, we also allow non-FFI-safe types behind a C pointer,
+    /// to be treated as an opaque type on the other side of the FFI boundary.
+    fn is_in_fnptr(self) -> bool {
+        self.contains(Self::THEORETICAL) && self.is_in_function()
+    }
+
+    /// Whether we can expect type parameters and co in a given type.
+    fn can_expect_ty_params(self) -> bool {
+        // rust-defined functions, as well as FnPtrs
+        self.contains(Self::THEORETICAL) || self.is_in_defined_function()
+    }
+}
+
+/// Visitor used to recursively traverse MIR types and evaluate FFI-safety.
+/// It uses ``check_*`` methods as entrypoints to be called elsewhere,
+/// and ``visit_*`` methods to recurse.
+struct ImproperCTypesVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+    /// To prevent problems with recursive types,
+    /// add a types-in-check cache.
+    cache: FxHashSet<Ty<'tcx>>,
+    /// The original type being checked, before we recursed
+    /// to any other types it contains.
+    base_ty: Ty<'tcx>,
+    base_fn_mode: CItemKind,
+}
+
+impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self {
+        Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() }
+    }
+
+    /// Checks if the given field's type is "ffi-safe".
+    fn check_field_type_for_ffi(
+        &mut self,
+        state: VisitorState,
+        field: &ty::FieldDef,
+        args: GenericArgsRef<'tcx>,
+    ) -> FfiResult<'tcx> {
+        let field_ty = field.ty(self.cx.tcx, args);
+        let field_ty = self
+            .cx
+            .tcx
+            .try_normalize_erasing_regions(self.cx.typing_env(), field_ty)
+            .unwrap_or(field_ty);
+        self.visit_type(state, field_ty)
+    }
+
+    /// Checks if the given `VariantDef`'s field types are "ffi-safe".
+    fn check_variant_for_ffi(
+        &mut self,
+        state: VisitorState,
+        ty: Ty<'tcx>,
+        def: ty::AdtDef<'tcx>,
+        variant: &ty::VariantDef,
+        args: GenericArgsRef<'tcx>,
+    ) -> FfiResult<'tcx> {
+        use FfiResult::*;
+        let transparent_with_all_zst_fields = if def.repr().transparent() {
+            if let Some(field) = super::transparent_newtype_field(self.cx.tcx, variant) {
+                // Transparent newtypes have at most one non-ZST field which needs to be checked..
+                match self.check_field_type_for_ffi(state, field, args) {
+                    FfiUnsafe { ty, .. } if ty.is_unit() => (),
+                    r => return r,
+                }
+
+                false
+            } else {
+                // ..or have only ZST fields, which is FFI-unsafe (unless those fields are all
+                // `PhantomData`).
+                true
+            }
+        } else {
+            false
+        };
+
+        // We can't completely trust `repr(C)` markings, so make sure the fields are actually safe.
+        let mut all_phantom = !variant.fields.is_empty();
+        for field in &variant.fields {
+            all_phantom &= match self.check_field_type_for_ffi(state, field, args) {
+                FfiSafe => false,
+                // `()` fields are FFI-safe!
+                FfiUnsafe { ty, .. } if ty.is_unit() => false,
+                FfiPhantom(..) => true,
+                r @ FfiUnsafe { .. } => return r,
+            }
+        }
+
+        if all_phantom {
+            FfiPhantom(ty)
+        } else if transparent_with_all_zst_fields {
+            FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_struct_zst, help: None }
+        } else {
+            FfiSafe
+        }
+    }
+
+    /// Checks if the given type is "ffi-safe" (has a stable, well-defined
+    /// representation which can be exported to C code).
+    fn visit_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
+        use FfiResult::*;
+
+        let tcx = self.cx.tcx;
+
+        // Protect against infinite recursion, for example
+        // `struct S(*mut S);`.
+        // FIXME: A recursion limit is necessary as well, for irregular
+        // recursive types.
+        if !self.cache.insert(ty) {
+            return FfiSafe;
+        }
+
+        match *ty.kind() {
+            ty::Adt(def, args) => {
+                if let Some(boxed) = ty.boxed_ty()
+                    && (
+                        // FIXME(ctypes): this logic is broken, but it still fits the current tests
+                        state.is_in_defined_function()
+                            || (state.is_in_fnptr()
+                                && matches!(self.base_fn_mode, CItemKind::Definition))
+                    )
+                {
+                    if boxed.is_sized(tcx, self.cx.typing_env()) {
+                        return FfiSafe;
+                    } else {
+                        return FfiUnsafe {
+                            ty,
+                            reason: fluent::lint_improper_ctypes_box,
+                            help: None,
+                        };
+                    }
+                }
+                if def.is_phantom_data() {
+                    return FfiPhantom(ty);
+                }
+                match def.adt_kind() {
+                    AdtKind::Struct | AdtKind::Union => {
+                        if let Some(sym::cstring_type | sym::cstr_type) =
+                            tcx.get_diagnostic_name(def.did())
+                            && !self.base_ty.is_mutable_ptr()
+                        {
+                            return FfiUnsafe {
+                                ty,
+                                reason: fluent::lint_improper_ctypes_cstr_reason,
+                                help: Some(fluent::lint_improper_ctypes_cstr_help),
+                            };
+                        }
+
+                        if !def.repr().c() && !def.repr().transparent() {
+                            return FfiUnsafe {
+                                ty,
+                                reason: if def.is_struct() {
+                                    fluent::lint_improper_ctypes_struct_layout_reason
+                                } else {
+                                    fluent::lint_improper_ctypes_union_layout_reason
+                                },
+                                help: if def.is_struct() {
+                                    Some(fluent::lint_improper_ctypes_struct_layout_help)
+                                } else {
+                                    Some(fluent::lint_improper_ctypes_union_layout_help)
+                                },
+                            };
+                        }
+
+                        if def.non_enum_variant().field_list_has_applicable_non_exhaustive() {
+                            return FfiUnsafe {
+                                ty,
+                                reason: if def.is_struct() {
+                                    fluent::lint_improper_ctypes_struct_non_exhaustive
+                                } else {
+                                    fluent::lint_improper_ctypes_union_non_exhaustive
+                                },
+                                help: None,
+                            };
+                        }
+
+                        if def.non_enum_variant().fields.is_empty() {
+                            return FfiUnsafe {
+                                ty,
+                                reason: if def.is_struct() {
+                                    fluent::lint_improper_ctypes_struct_fieldless_reason
+                                } else {
+                                    fluent::lint_improper_ctypes_union_fieldless_reason
+                                },
+                                help: if def.is_struct() {
+                                    Some(fluent::lint_improper_ctypes_struct_fieldless_help)
+                                } else {
+                                    Some(fluent::lint_improper_ctypes_union_fieldless_help)
+                                },
+                            };
+                        }
+
+                        self.check_variant_for_ffi(state, ty, def, def.non_enum_variant(), args)
+                    }
+                    AdtKind::Enum => {
+                        if def.variants().is_empty() {
+                            // Empty enums are okay... although sort of useless.
+                            return FfiSafe;
+                        }
+                        // Check for a repr() attribute to specify the size of the
+                        // discriminant.
+                        if !def.repr().c() && !def.repr().transparent() && def.repr().int.is_none()
+                        {
+                            // Special-case types like `Option<extern fn()>` and `Result<extern fn(), ()>`
+                            if let Some(ty) =
+                                repr_nullable_ptr(self.cx.tcx, self.cx.typing_env(), ty)
+                            {
+                                return self.visit_type(state, ty);
+                            }
+
+                            return FfiUnsafe {
+                                ty,
+                                reason: fluent::lint_improper_ctypes_enum_repr_reason,
+                                help: Some(fluent::lint_improper_ctypes_enum_repr_help),
+                            };
+                        }
+
+                        let non_exhaustive = def.variant_list_has_applicable_non_exhaustive();
+                        // Check the contained variants.
+                        let ret = def.variants().iter().try_for_each(|variant| {
+                            check_non_exhaustive_variant(non_exhaustive, variant)
+                                .map_break(|reason| FfiUnsafe { ty, reason, help: None })?;
+
+                            match self.check_variant_for_ffi(state, ty, def, variant, args) {
+                                FfiSafe => ControlFlow::Continue(()),
+                                r => ControlFlow::Break(r),
+                            }
+                        });
+                        if let ControlFlow::Break(result) = ret {
+                            return result;
+                        }
+
+                        FfiSafe
+                    }
+                }
+            }
+
+            ty::Char => FfiUnsafe {
+                ty,
+                reason: fluent::lint_improper_ctypes_char_reason,
+                help: Some(fluent::lint_improper_ctypes_char_help),
+            },
+
+            // It's just extra invariants on the type that you need to uphold,
+            // but only the base type is relevant for being representable in FFI.
+            ty::Pat(base, ..) => self.visit_type(state, base),
+
+            // Primitive types with a stable representation.
+            ty::Bool | ty::Int(..) | ty::Uint(..) | ty::Float(..) | ty::Never => FfiSafe,
+
+            ty::Slice(_) => FfiUnsafe {
+                ty,
+                reason: fluent::lint_improper_ctypes_slice_reason,
+                help: Some(fluent::lint_improper_ctypes_slice_help),
+            },
+
+            ty::Dynamic(..) => {
+                FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_dyn, help: None }
+            }
+
+            ty::Str => FfiUnsafe {
+                ty,
+                reason: fluent::lint_improper_ctypes_str_reason,
+                help: Some(fluent::lint_improper_ctypes_str_help),
+            },
+
+            ty::Tuple(..) => FfiUnsafe {
+                ty,
+                reason: fluent::lint_improper_ctypes_tuple_reason,
+                help: Some(fluent::lint_improper_ctypes_tuple_help),
+            },
+
+            ty::RawPtr(ty, _) | ty::Ref(_, ty, _)
+                if {
+                    (state.is_in_defined_function() || state.is_in_fnptr())
+                        && ty.is_sized(self.cx.tcx, self.cx.typing_env())
+                } =>
+            {
+                FfiSafe
+            }
+
+            ty::RawPtr(ty, _)
+                if match ty.kind() {
+                    ty::Tuple(tuple) => tuple.is_empty(),
+                    _ => false,
+                } =>
+            {
+                FfiSafe
+            }
+
+            ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => self.visit_type(state, ty),
+
+            ty::Array(inner_ty, _) => self.visit_type(state, inner_ty),
+
+            ty::FnPtr(sig_tys, hdr) => {
+                let sig = sig_tys.with(hdr);
+                if sig.abi().is_rustic_abi() {
+                    return FfiUnsafe {
+                        ty,
+                        reason: fluent::lint_improper_ctypes_fnptr_reason,
+                        help: Some(fluent::lint_improper_ctypes_fnptr_help),
+                    };
+                }
+
+                let sig = tcx.instantiate_bound_regions_with_erased(sig);
+                for arg in sig.inputs() {
+                    match self.visit_type(VisitorState::ARGUMENT_TY_IN_FNPTR, *arg) {
+                        FfiSafe => {}
+                        r => return r,
+                    }
+                }
+
+                let ret_ty = sig.output();
+                if ret_ty.is_unit() {
+                    return FfiSafe;
+                }
+
+                self.visit_type(VisitorState::RETURN_TY_IN_FNPTR, ret_ty)
+            }
+
+            ty::Foreign(..) => FfiSafe,
+
+            // While opaque types are checked for earlier, if a projection in a struct field
+            // normalizes to an opaque type, then it will reach this branch.
+            ty::Alias(ty::Opaque, ..) => {
+                FfiUnsafe { ty, reason: fluent::lint_improper_ctypes_opaque, help: None }
+            }
+
+            // `extern "C" fn` functions can have type parameters, which may or may not be FFI-safe,
+            //  so they are currently ignored for the purposes of this lint.
+            ty::Param(..) | ty::Alias(ty::Projection | ty::Inherent, ..)
+                if state.can_expect_ty_params() =>
+            {
+                FfiSafe
+            }
+
+            ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
+
+            ty::Param(..)
+            | ty::Alias(ty::Projection | ty::Inherent | ty::Free, ..)
+            | ty::Infer(..)
+            | ty::Bound(..)
+            | ty::Error(_)
+            | ty::Closure(..)
+            | ty::CoroutineClosure(..)
+            | ty::Coroutine(..)
+            | ty::CoroutineWitness(..)
+            | ty::Placeholder(..)
+            | ty::FnDef(..) => bug!("unexpected type in foreign function: {:?}", ty),
+        }
+    }
+
+    fn visit_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> {
+        struct ProhibitOpaqueTypes;
+        impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
+            type Result = ControlFlow<Ty<'tcx>>;
+
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+                if !ty.has_opaque_types() {
+                    return ControlFlow::Continue(());
+                }
+
+                if let ty::Alias(ty::Opaque, ..) = ty.kind() {
+                    ControlFlow::Break(ty)
+                } else {
+                    ty.super_visit_with(self)
+                }
+            }
+        }
+
+        if let Some(ty) = self
+            .cx
+            .tcx
+            .try_normalize_erasing_regions(self.cx.typing_env(), ty)
+            .unwrap_or(ty)
+            .visit_with(&mut ProhibitOpaqueTypes)
+            .break_value()
+        {
+            Some(FfiResult::FfiUnsafe {
+                ty,
+                reason: fluent::lint_improper_ctypes_opaque,
+                help: None,
+            })
+        } else {
+            None
+        }
+    }
+
+    /// Check if the type is array and emit an unsafe type lint.
+    fn check_for_array_ty(&mut self, ty: Ty<'tcx>) -> PartialFfiResult<'tcx> {
+        if let ty::Array(..) = ty.kind() {
+            Some(FfiResult::FfiUnsafe {
+                ty,
+                reason: fluent::lint_improper_ctypes_array_reason,
+                help: Some(fluent::lint_improper_ctypes_array_help),
+            })
+        } else {
+            None
+        }
+    }
+
+    /// Determine the FFI-safety of a single (MIR) type, given the context of how it is used.
+    fn check_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
+        if let Some(res) = self.visit_for_opaque_ty(ty) {
+            return res;
+        }
+
+        let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
+
+        // C doesn't really support passing arrays by value - the only way to pass an array by value
+        // is through a struct. So, first test that the top level isn't an array, and then
+        // recursively check the types inside.
+        if state.is_in_function() {
+            if let Some(res) = self.check_for_array_ty(ty) {
+                return res;
+            }
+        }
+
+        // Don't report FFI errors for unit return types. This check exists here, and not in
+        // the caller (where it would make more sense) so that normalization has definitely
+        // happened.
+        if state.is_in_function_return() && ty.is_unit() {
+            return FfiResult::FfiSafe;
+        }
+
+        self.visit_type(state, ty)
+    }
+}
+
+impl<'tcx> ImproperCTypesLint {
+    /// Find any fn-ptr types with external ABIs in `ty`, and FFI-checks them.
+    /// For example, `Option<extern "C" fn()>` FFI-checks `extern "C" fn()`.
+    fn check_type_for_external_abi_fnptr(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        state: VisitorState,
+        hir_ty: &hir::Ty<'tcx>,
+        ty: Ty<'tcx>,
+        fn_mode: CItemKind,
+    ) {
+        struct FnPtrFinder<'tcx> {
+            spans: Vec<Span>,
+            tys: Vec<Ty<'tcx>>,
+        }
+
+        impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> {
+            fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
+                debug!(?ty);
+                if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind
+                    && !abi.is_rustic_abi()
+                {
+                    self.spans.push(ty.span);
+                }
+
+                hir::intravisit::walk_ty(self, ty)
+            }
+        }
+
+        impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'tcx> {
+            type Result = ();
+
+            fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+                if let ty::FnPtr(_, hdr) = ty.kind()
+                    && !hdr.abi.is_rustic_abi()
+                {
+                    self.tys.push(ty);
+                }
+
+                ty.super_visit_with(self)
+            }
+        }
+
+        let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() };
+        ty.visit_with(&mut visitor);
+        visitor.visit_ty_unambig(hir_ty);
+
+        let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
+        for (fn_ptr_ty, span) in all_types {
+            let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
+            // FIXME(ctypes): make a check_for_fnptr
+            let ffi_res = visitor.check_type(state, fn_ptr_ty);
+
+            self.process_ffi_result(cx, span, ffi_res, fn_mode);
+        }
+    }
+
+    /// Regardless of a function's need to be "ffi-safe", look for fn-ptr argument/return types
+    /// that need to be checked for ffi-safety.
+    fn check_fn_for_external_abi_fnptr(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        fn_mode: CItemKind,
+        def_id: LocalDefId,
+        decl: &'tcx hir::FnDecl<'_>,
+    ) {
+        let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
+        let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
+
+        for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
+            let state = VisitorState::argument_from_fnmode(fn_mode);
+            self.check_type_for_external_abi_fnptr(cx, state, input_hir, *input_ty, fn_mode);
+        }
+
+        if let hir::FnRetTy::Return(ret_hir) = decl.output {
+            let state = VisitorState::return_from_fnmode(fn_mode);
+            self.check_type_for_external_abi_fnptr(cx, state, ret_hir, sig.output(), fn_mode);
+        }
+    }
+
+    /// For a local definition of a #[repr(C)] struct/enum/union, check that it is indeed FFI-safe.
+    fn check_reprc_adt(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        item: &'tcx hir::Item<'tcx>,
+        adt_def: AdtDef<'tcx>,
+    ) {
+        debug_assert!(
+            adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none()
+        );
+
+        // FIXME(ctypes): this following call is awkward.
+        // is there a way to perform its logic in MIR space rather than HIR space?
+        // (so that its logic can be absorbed into visitor.visit_struct_or_union)
+        check_struct_for_power_alignment(cx, item, adt_def);
+    }
+
+    fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
+        let ty = cx.tcx.type_of(id).instantiate_identity();
+        let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration);
+        let ffi_res = visitor.check_type(VisitorState::STATIC_TY, ty);
+        self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration);
+    }
+
+    /// Check if a function's argument types and result type are "ffi-safe".
+    fn check_foreign_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        fn_mode: CItemKind,
+        def_id: LocalDefId,
+        decl: &'tcx hir::FnDecl<'_>,
+    ) {
+        let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
+        let sig = cx.tcx.instantiate_bound_regions_with_erased(sig);
+
+        for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
+            let state = VisitorState::argument_from_fnmode(fn_mode);
+            let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode);
+            let ffi_res = visitor.check_type(state, *input_ty);
+            self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode);
+        }
+
+        if let hir::FnRetTy::Return(ret_hir) = decl.output {
+            let state = VisitorState::return_from_fnmode(fn_mode);
+            let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode);
+            let ffi_res = visitor.check_type(state, sig.output());
+            self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode);
+        }
+    }
+
+    fn process_ffi_result(
+        &self,
+        cx: &LateContext<'tcx>,
+        sp: Span,
+        res: FfiResult<'tcx>,
+        fn_mode: CItemKind,
+    ) {
+        match res {
+            FfiResult::FfiSafe => {}
+            FfiResult::FfiPhantom(ty) => {
+                self.emit_ffi_unsafe_type_lint(
+                    cx,
+                    ty,
+                    sp,
+                    fluent::lint_improper_ctypes_only_phantomdata,
+                    None,
+                    fn_mode,
+                );
+            }
+            FfiResult::FfiUnsafe { ty, reason, help } => {
+                self.emit_ffi_unsafe_type_lint(cx, ty, sp, reason, help, fn_mode);
+            }
+        }
+    }
+
+    fn emit_ffi_unsafe_type_lint(
+        &self,
+        cx: &LateContext<'tcx>,
+        ty: Ty<'tcx>,
+        sp: Span,
+        note: DiagMessage,
+        help: Option<DiagMessage>,
+        fn_mode: CItemKind,
+    ) {
+        let lint = match fn_mode {
+            CItemKind::Declaration => IMPROPER_CTYPES,
+            CItemKind::Definition => IMPROPER_CTYPES_DEFINITIONS,
+        };
+        let desc = match fn_mode {
+            CItemKind::Declaration => "block",
+            CItemKind::Definition => "fn",
+        };
+        let span_note = if let ty::Adt(def, _) = ty.kind()
+            && let Some(sp) = cx.tcx.hir_span_if_local(def.did())
+        {
+            Some(sp)
+        } else {
+            None
+        };
+        cx.emit_span_lint(lint, sp, ImproperCTypes { ty, desc, label: sp, help, note, span_note });
+    }
+}
+
+/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
+/// `extern "C" { }` blocks):
+///
+/// - `extern "<abi>" fn` definitions are checked in the same way as the
+///   `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
+/// - All other items which contain types (e.g. other functions, struct definitions, etc) are
+///   checked for extern fn-ptrs with external ABIs.
+impl<'tcx> LateLintPass<'tcx> for ImproperCTypesLint {
+    fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
+        let abi = cx.tcx.hir_get_foreign_abi(it.hir_id());
+
+        match it.kind {
+            hir::ForeignItemKind::Fn(sig, _, _) => {
+                // fnptrs are a special case, they always need to be treated as
+                // "the element rendered unsafe" because their unsafety doesn't affect
+                // their surroundings, and their type is often declared inline
+                if !abi.is_rustic_abi() {
+                    self.check_foreign_fn(cx, CItemKind::Declaration, it.owner_id.def_id, sig.decl);
+                } else {
+                    self.check_fn_for_external_abi_fnptr(
+                        cx,
+                        CItemKind::Declaration,
+                        it.owner_id.def_id,
+                        sig.decl,
+                    );
+                }
+            }
+            hir::ForeignItemKind::Static(ty, _, _) if !abi.is_rustic_abi() => {
+                self.check_foreign_static(cx, it.owner_id, ty.span);
+            }
+            hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
+        }
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
+        match item.kind {
+            hir::ItemKind::Static(_, _, ty, _)
+            | hir::ItemKind::Const(_, _, ty, _)
+            | hir::ItemKind::TyAlias(_, _, ty) => {
+                self.check_type_for_external_abi_fnptr(
+                    cx,
+                    VisitorState::STATIC_TY,
+                    ty,
+                    cx.tcx.type_of(item.owner_id).instantiate_identity(),
+                    CItemKind::Definition,
+                );
+            }
+            // See `check_fn` for declarations, `check_foreign_items` for definitions in extern blocks
+            hir::ItemKind::Fn { .. } => {}
+            hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {
+                // looking for extern FnPtr:s is delegated to `check_field_def`.
+                let adt_def: AdtDef<'tcx> = cx.tcx.adt_def(item.owner_id.to_def_id());
+
+                if adt_def.repr().c() && !adt_def.repr().packed() && adt_def.repr().align.is_none()
+                {
+                    self.check_reprc_adt(cx, item, adt_def);
+                }
+            }
+
+            // Doesn't define something that can contain a external type to be checked.
+            hir::ItemKind::Impl(..)
+            | hir::ItemKind::TraitAlias(..)
+            | hir::ItemKind::Trait(..)
+            | hir::ItemKind::GlobalAsm { .. }
+            | hir::ItemKind::ForeignMod { .. }
+            | hir::ItemKind::Mod(..)
+            | hir::ItemKind::Macro(..)
+            | hir::ItemKind::Use(..)
+            | hir::ItemKind::ExternCrate(..) => {}
+        }
+    }
+
+    fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
+        self.check_type_for_external_abi_fnptr(
+            cx,
+            VisitorState::STATIC_TY,
+            field.ty,
+            cx.tcx.type_of(field.def_id).instantiate_identity(),
+            CItemKind::Definition,
+        );
+    }
+
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        kind: hir::intravisit::FnKind<'tcx>,
+        decl: &'tcx hir::FnDecl<'_>,
+        _: &'tcx hir::Body<'_>,
+        _: Span,
+        id: LocalDefId,
+    ) {
+        use hir::intravisit::FnKind;
+
+        let abi = match kind {
+            FnKind::ItemFn(_, _, header, ..) => header.abi,
+            FnKind::Method(_, sig, ..) => sig.header.abi,
+            _ => return,
+        };
+
+        // fnptrs are a special case, they always need to be treated as
+        // "the element rendered unsafe" because their unsafety doesn't affect
+        // their surroundings, and their type is often declared inline
+        if !abi.is_rustic_abi() {
+            self.check_foreign_fn(cx, CItemKind::Definition, id, decl);
+        } else {
+            self.check_fn_for_external_abi_fnptr(cx, CItemKind::Definition, id, decl);
+        }
+    }
+}
diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs
index 2bac58ba23d..1febbff4bbf 100644
--- a/compiler/rustc_lint/src/types/literal.rs
+++ b/compiler/rustc_lint/src/types/literal.rs
@@ -1,11 +1,11 @@
 use hir::{ExprKind, Node, is_range_literal};
 use rustc_abi::{Integer, Size};
-use rustc_hir::HirId;
+use rustc_hir::{HirId, attrs};
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::{bug, ty};
 use rustc_span::Span;
-use {rustc_ast as ast, rustc_attr_data_structures as attrs, rustc_hir as hir};
+use {rustc_ast as ast, rustc_hir as hir};
 
 use crate::LateContext;
 use crate::context::LintContext;
@@ -272,7 +272,7 @@ fn lint_int_literal<'tcx>(
                 cx,
                 hir_id,
                 span,
-                attrs::IntType::SignedInt(ty::ast_int_ty(t)),
+                attrs::IntType::SignedInt(t),
                 Integer::from_int_ty(cx, t).size(),
                 repr_str,
                 v,
@@ -358,7 +358,7 @@ fn lint_uint_literal<'tcx>(
                 cx,
                 hir_id,
                 span,
-                attrs::IntType::UnsignedInt(ty::ast_uint_ty(t)),
+                attrs::IntType::UnsignedInt(t),
                 Integer::from_uint_ty(cx, t).size(),
                 repr_str,
                 lit_val,
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index a9eb1739f7f..874c4354029 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -2,12 +2,12 @@ use std::iter;
 
 use rustc_ast::util::{classify, parser};
 use rustc_ast::{self as ast, ExprKind, FnRetTy, HasAttrs as _, StmtKind};
-use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{MultiSpan, pluralize};
+use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::{self as hir, LangItem};
+use rustc_hir::{self as hir, LangItem, find_attr};
 use rustc_infer::traits::util::elaborate;
 use rustc_middle::ty::{self, Ty, adjustment};
 use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
@@ -185,7 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
         let mut op_warned = false;
 
         if let Some(must_use_op) = must_use_op {
-            let span = expr.span.find_oldest_ancestor_in_same_ctxt();
+            let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span);
             cx.emit_span_lint(
                 UNUSED_MUST_USE,
                 expr.span,
@@ -312,7 +312,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                         })
                         .map(|inner| MustUsePath::Opaque(Box::new(inner)))
                 }
-                ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
+                ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| {
                     if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
                     {
                         let def_id = trait_ref.def_id;
@@ -511,7 +511,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                     );
                 }
                 MustUsePath::Def(span, def_id, reason) => {
-                    let span = span.find_oldest_ancestor_in_same_ctxt();
+                    let span = span.find_ancestor_not_from_macro().unwrap_or(*span);
                     cx.emit_span_lint(
                         UNUSED_MUST_USE,
                         span,
@@ -562,20 +562,19 @@ declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
 
 impl<'tcx> LateLintPass<'tcx> for PathStatements {
     fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
-        if let hir::StmtKind::Semi(expr) = s.kind {
-            if let hir::ExprKind::Path(_) = expr.kind {
-                let ty = cx.typeck_results().expr_ty(expr);
-                if ty.needs_drop(cx.tcx, cx.typing_env()) {
-                    let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
-                    {
-                        PathStatementDropSub::Suggestion { span: s.span, snippet }
-                    } else {
-                        PathStatementDropSub::Help { span: s.span }
-                    };
-                    cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
+        if let hir::StmtKind::Semi(expr) = s.kind
+            && let hir::ExprKind::Path(_) = expr.kind
+        {
+            let ty = cx.typeck_results().expr_ty(expr);
+            if ty.needs_drop(cx.tcx, cx.typing_env()) {
+                let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
+                    PathStatementDropSub::Suggestion { span: s.span, snippet }
                 } else {
-                    cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
-                }
+                    PathStatementDropSub::Help { span: s.span }
+                };
+                cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
+            } else {
+                cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
             }
         }
     }
@@ -844,6 +843,10 @@ trait UnusedDelimLint {
                 && !snip.ends_with(' ')
             {
                 " "
+            } else if let Ok(snip) = sm.span_to_prev_source(value_span)
+                && snip.ends_with(|c: char| c.is_alphanumeric())
+            {
+                " "
             } else {
                 ""
             };
@@ -853,6 +856,10 @@ trait UnusedDelimLint {
                 && !snip.starts_with(' ')
             {
                 " "
+            } else if let Ok(snip) = sm.span_to_prev_source(value_span)
+                && snip.starts_with(|c: char| c.is_alphanumeric())
+            {
+                " "
             } else {
                 ""
             };
@@ -1340,7 +1347,15 @@ impl EarlyLintPass for UnusedParens {
                 self.with_self_ty_parens = false;
             }
             ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
-                self.in_no_bounds_pos.insert(mut_ty.ty.id, NoBoundsException::OneBound);
+                // If this type itself appears in no-bounds position, we propagate its
+                // potentially tighter constraint or risk a false posive (issue 143653).
+                let own_constraint = self.in_no_bounds_pos.get(&ty.id);
+                let constraint = match own_constraint {
+                    Some(NoBoundsException::None) => NoBoundsException::None,
+                    Some(NoBoundsException::OneBound) => NoBoundsException::OneBound,
+                    None => NoBoundsException::OneBound,
+                };
+                self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint);
             }
             ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
                 for i in 0..bounds.len() {
@@ -1509,21 +1524,19 @@ impl UnusedDelimLint for UnusedBraces {
                 //      let _: A<{produces_literal!()}>;
                 //      ```
                 // FIXME(const_generics): handle paths when #67075 is fixed.
-                if let [stmt] = inner.stmts.as_slice() {
-                    if let ast::StmtKind::Expr(ref expr) = stmt.kind {
-                        if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
-                            && (ctx != UnusedDelimsCtx::AnonConst
-                                || (matches!(expr.kind, ast::ExprKind::Lit(_))
-                                    && !expr.span.from_expansion()))
-                            && ctx != UnusedDelimsCtx::ClosureBody
-                            && !cx.sess().source_map().is_multiline(value.span)
-                            && value.attrs.is_empty()
-                            && !value.span.from_expansion()
-                            && !inner.span.from_expansion()
-                        {
-                            self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
-                        }
-                    }
+                if let [stmt] = inner.stmts.as_slice()
+                    && let ast::StmtKind::Expr(ref expr) = stmt.kind
+                    && !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
+                    && (ctx != UnusedDelimsCtx::AnonConst
+                        || (matches!(expr.kind, ast::ExprKind::Lit(_))
+                            && !expr.span.from_expansion()))
+                    && ctx != UnusedDelimsCtx::ClosureBody
+                    && !cx.sess().source_map().is_multiline(value.span)
+                    && value.attrs.is_empty()
+                    && !value.span.from_expansion()
+                    && !inner.span.from_expansion()
+                {
+                    self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
                 }
             }
             ast::ExprKind::Let(_, ref expr, _, _) => {