about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/non_copy_const.rs1153
-rw-r--r--clippy_utils/src/ty/mod.rs11
-rw-r--r--tests/ui/auxiliary/interior_mutable_const.rs (renamed from tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs)0
-rw-r--r--tests/ui/borrow_interior_mutable_const.rs221
-rw-r--r--tests/ui/borrow_interior_mutable_const.stderr247
-rw-r--r--tests/ui/borrow_interior_mutable_const/enums.rs101
-rw-r--r--tests/ui/borrow_interior_mutable_const/enums.stderr79
-rw-r--r--tests/ui/borrow_interior_mutable_const/others.rs128
-rw-r--r--tests/ui/borrow_interior_mutable_const/others.stderr119
-rw-r--r--tests/ui/borrow_interior_mutable_const/projections.rs42
-rw-r--r--tests/ui/borrow_interior_mutable_const/projections.stderr44
-rw-r--r--tests/ui/borrow_interior_mutable_const/traits.rs219
-rw-r--r--tests/ui/borrow_interior_mutable_const/traits.stderr143
-rw-r--r--tests/ui/crashes/ice-12979.1.fixed2
-rw-r--r--tests/ui/crashes/ice-12979.2.fixed3
-rw-r--r--tests/ui/crashes/ice-12979.rs3
-rw-r--r--tests/ui/crashes/ice-12979.stderr19
-rw-r--r--tests/ui/crashes/ice-9445.rs4
-rw-r--r--tests/ui/crashes/ice-9445.stderr12
-rw-r--r--tests/ui/declare_interior_mutable_const.rs200
-rw-r--r--tests/ui/declare_interior_mutable_const.stderr197
-rw-r--r--tests/ui/declare_interior_mutable_const/enums.rs135
-rw-r--r--tests/ui/declare_interior_mutable_const/enums.stderr89
-rw-r--r--tests/ui/declare_interior_mutable_const/others.rs76
-rw-r--r--tests/ui/declare_interior_mutable_const/others.stderr50
-rw-r--r--tests/ui/declare_interior_mutable_const/traits.rs162
-rw-r--r--tests/ui/declare_interior_mutable_const/traits.stderr88
27 files changed, 1691 insertions, 1856 deletions
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index 6d3e77b6b6e..a27c6aa75e3 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -1,56 +1,78 @@
-use std::ptr;
+// Implementation for lints detecting interior mutability in constants.
+//
+// For `declare_interior_mutable_const` there are three strategies used to
+// determine if a value has interior mutability:
+// * A type-based check. This is the least accurate, but can always run.
+// * A const-eval based check. This is the most accurate, but this requires that the value is
+//   defined and does not work with generics.
+// * A HIR-tree based check. This is less accurate than const-eval, but it can be applied to generic
+//   values.
+//
+// For `borrow_interior_mutable_const` the same three strategies are applied
+// when checking a constant's value, but field and array index projections at
+// the borrow site are taken into account as well. As an example: `FOO.bar` may
+// have interior mutability, but `FOO.baz` may not. When borrowing `FOO.baz` no
+// warning will be issued.
+//
+// No matter the lint or strategy, a warning should only be issued if a value
+// definitely contains interior mutability.
 
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::is_in_const_context;
 use clippy_utils::macros::macro_backtrace;
-use clippy_utils::ty::{InteriorMut, implements_trait};
-use rustc_abi::VariantIdx;
+use clippy_utils::paths::{PathNS, lookup_path_str};
+use clippy_utils::ty::{get_field_idx_by_name, implements_trait};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::{
-    BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
+    Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, StructTailExpr, TraitItem, TraitItemKind, UnOp,
+};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::mir::{ConstValue, UnevaluatedConst};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment};
+use rustc_middle::ty::{
+    self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable,
+    TypeckResults, TypingEnv,
 };
-use rustc_lint::{LateContext, LateLintPass, Lint};
-use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId, ReportedErrorInfo};
-use rustc_middle::ty::adjustment::Adjust;
-use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
-use rustc_span::{DUMMY_SP, Span, sym};
+use rustc_span::{DUMMY_SP, sym};
+use std::collections::hash_map::Entry;
 
-// FIXME: this is a correctness problem but there's no suitable
-// warn-by-default category.
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for declaration of `const` items which is interior
-    /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
+    /// Checks for the declaration of named constant which contain interior mutability.
     ///
     /// ### Why is this bad?
-    /// Consts are copied everywhere they are referenced, i.e.,
-    /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
-    /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
-    /// these types in the first place.
+    /// Named constants are copied at every use site which means any change to their value
+    /// will be lost after the newly created value is dropped. e.g.
     ///
-    /// The `const` should better be replaced by a `static` item if a global
-    /// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
+    /// ```rust
+    /// use core::sync::atomic::{AtomicUsize, Ordering};
+    /// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
+    /// fn add_one() -> usize {
+    ///     // This will always return `0` since `ATOMIC` is copied before it's used.
+    ///     ATOMIC.fetch_add(1, Ordering::AcqRel)
+    /// }
+    /// ```
     ///
-    /// ### Known problems
-    /// A "non-constant" const item is a legacy way to supply an
-    /// initialized value to downstream `static` items (e.g., the
-    /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
-    /// and this lint should be suppressed.
+    /// If shared modification of the value is desired, a `static` item is needed instead.
+    /// If that is not desired, a `const fn` constructor should be used to make it obvious
+    /// at the use site that a new value is created.
     ///
-    /// Even though the lint avoids triggering on a constant whose type has enums that have variants
-    /// with interior mutability, and its value uses non interior mutable variants (see
-    /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and
-    /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);
-    /// it complains about associated constants without default values only based on its types;
-    /// which might not be preferable.
-    /// There're other enums plus associated constants cases that the lint cannot handle.
+    /// ### Known problems
+    /// Prior to `const fn` stabilization this was the only way to provide a value which
+    /// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In
+    /// this case the use of `const` is required and this lint should be suppressed.
     ///
-    /// Types that have underlying or potential interior mutability trigger the lint whether
-    /// the interior mutable field is used or not. See issue
-    /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812)
+    /// There also exists types which contain private fields with interior mutability, but
+    /// no way to both create a value as a constant and modify any mutable field using the
+    /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
+    /// scan a crate's interface to see if this is the case, all such types will be linted.
+    /// If this happens use the `ignore-interior-mutability` configuration option to allow
+    /// the type.
     ///
     /// ### Example
     /// ```no_run
@@ -74,20 +96,44 @@ declare_clippy_lint! {
     "declaring `const` with interior mutability"
 }
 
-// FIXME: this is a correctness problem but there's no suitable
-// warn-by-default category.
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks if `const` items which is interior mutable (e.g.,
-    /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
+    /// Checks for a borrow of a named constant with interior mutability.
     ///
     /// ### Why is this bad?
-    /// Consts are copied everywhere they are referenced, i.e.,
-    /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
-    /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
-    /// these types in the first place.
+    /// Named constants are copied at every use site which means any change to their value
+    /// will be lost after the newly created value is dropped. e.g.
+    ///
+    /// ```rust
+    /// use core::sync::atomic::{AtomicUsize, Ordering};
+    /// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
+    /// fn add_one() -> usize {
+    ///     // This will always return `0` since `ATOMIC` is copied before it's borrowed
+    ///     // for use by `fetch_add`.
+    ///     ATOMIC.fetch_add(1, Ordering::AcqRel)
+    /// }
+    /// ```
     ///
-    /// The `const` value should be stored inside a `static` item.
+    /// ### Known problems
+    /// This lint does not, and cannot in general, determine if the borrow of the constant
+    /// is used in a way which causes a mutation. e.g.
+    ///
+    /// ```rust
+    /// use core::cell::Cell;
+    /// const CELL: Cell<usize> = Cell::new(0);
+    /// fn get_cell() -> Cell<usize> {
+    ///     // This is fine. It borrows a copy of `CELL`, but never mutates it through the
+    ///     // borrow.
+    ///     CELL.clone()
+    /// }
+    /// ```
+    ///
+    /// There also exists types which contain private fields with interior mutability, but
+    /// no way to both create a value as a constant and modify any mutable field using the
+    /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
+    /// scan a crate's interface to see if this is the case, all such types will be linted.
+    /// If this happens use the `ignore-interior-mutability` configuration option to allow
+    /// the type.
     ///
     /// ### Example
     /// ```no_run
@@ -113,60 +159,101 @@ declare_clippy_lint! {
     "referencing `const` with interior mutability"
 }
 
-#[derive(Copy, Clone)]
-enum Source<'tcx> {
-    Item { item: Span, ty: Ty<'tcx> },
-    Assoc { item: Span },
-    Expr { expr: Span },
+#[derive(Clone, Copy)]
+enum IsFreeze {
+    /// The type and all possible values are `Freeze`
+    Yes,
+    /// The type itself is non-`Freeze`, but not all values are.
+    Maybe,
+    /// The type and all possible values are non-`Freeze`
+    No,
 }
+impl IsFreeze {
+    /// Merges the variants of a sum type (i.e. an enum).
+    fn from_variants(iter: impl Iterator<Item = Self>) -> Self {
+        iter.fold(Self::Yes, |x, y| match (x, y) {
+            (Self::Maybe, _) | (_, Self::Maybe) | (Self::No, Self::Yes) | (Self::Yes, Self::No) => Self::Maybe,
+            (Self::No, Self::No) => Self::No,
+            (Self::Yes, Self::Yes) => Self::Yes,
+        })
+    }
 
-impl Source<'_> {
-    #[must_use]
-    fn lint(&self) -> (&'static Lint, &'static str, Span) {
-        match self {
-            Self::Item { item, .. } | Self::Assoc { item, .. } => (
-                DECLARE_INTERIOR_MUTABLE_CONST,
-                "a `const` item should not be interior mutable",
-                *item,
-            ),
-            Self::Expr { expr } => (
-                BORROW_INTERIOR_MUTABLE_CONST,
-                "a `const` item with interior mutability should not be borrowed",
-                *expr,
-            ),
-        }
+    /// Merges the fields of a product type (e.g. a struct or tuple).
+    fn from_fields(mut iter: impl Iterator<Item = Self>) -> Self {
+        iter.try_fold(Self::Yes, |x, y| match (x, y) {
+            (Self::No, _) | (_, Self::No) => None,
+            (Self::Maybe, _) | (_, Self::Maybe) => Some(Self::Maybe),
+            (Self::Yes, Self::Yes) => Some(Self::Yes),
+        })
+        .unwrap_or(Self::No)
+    }
+
+    /// Checks if this is definitely `Freeze`.
+    fn is_freeze(self) -> bool {
+        matches!(self, Self::Yes)
+    }
+
+    /// Checks if this is definitely not `Freeze`.
+    fn is_not_freeze(self) -> bool {
+        matches!(self, Self::No)
     }
 }
 
-fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) {
-    let (lint, msg, span) = source.lint();
-    span_lint_and_then(cx, lint, span, msg, |diag| {
-        if span.from_expansion() {
-            return; // Don't give suggestions into macros.
-        }
-        match source {
-            Source::Item { ty, .. } => {
-                let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else {
-                    return;
-                };
-                if implements_trait(cx, ty, sync_trait, &[]) {
-                    diag.help("consider making this a static item");
-                } else {
-                    diag.help(
-                        "consider making this `Sync` so that it can go in a static item or using a `thread_local`",
-                    );
-                }
-            },
-            Source::Assoc { .. } => (),
-            Source::Expr { .. } => {
-                diag.help("assign this const to a local or static variable, and use the variable here");
+/// What operation caused a borrow to occur.
+#[derive(Clone, Copy)]
+enum BorrowCause {
+    Borrow,
+    Deref,
+    Index,
+    AutoDeref,
+    AutoBorrow,
+    AutoDerefField,
+}
+impl BorrowCause {
+    fn note(self) -> Option<&'static str> {
+        match self {
+            Self::Borrow => None,
+            Self::Deref => Some("this deref expression is a call to `Deref::deref`"),
+            Self::Index => Some("this index expression is a call to `Index::index`"),
+            Self::AutoDeref => Some("there is a compiler inserted call to `Deref::deref` here"),
+            Self::AutoBorrow => Some("there is a compiler inserted borrow here"),
+            Self::AutoDerefField => {
+                Some("there is a compiler inserted call to `Deref::deref` when accessing this field")
             },
         }
-    });
+    }
+}
+
+/// The source of a borrow. Both what caused it and where.
+struct BorrowSource<'tcx> {
+    expr: &'tcx Expr<'tcx>,
+    cause: BorrowCause,
+}
+impl<'tcx> BorrowSource<'tcx> {
+    fn new(tcx: TyCtxt<'tcx>, expr: &'tcx Expr<'tcx>, cause: BorrowCause) -> Self {
+        // Custom deref and index impls will always have an auto-borrow inserted since we
+        // never work with reference types.
+        let (expr, cause) = if matches!(cause, BorrowCause::AutoBorrow)
+            && let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id)
+        {
+            match parent.kind {
+                ExprKind::Unary(UnOp::Deref, _) => (parent, BorrowCause::Deref),
+                ExprKind::Index(..) => (parent, BorrowCause::Index),
+                ExprKind::Field(..) => (parent, BorrowCause::AutoDerefField),
+                _ => (expr, cause),
+            }
+        } else {
+            (expr, cause)
+        };
+        Self { expr, cause }
+    }
 }
 
 pub struct NonCopyConst<'tcx> {
-    interior_mut: InteriorMut<'tcx>,
+    ignore_tys: DefIdSet,
+    // Cache checked types. We can recurse through a type multiple times so this
+    // can be hit quite frequently.
+    freeze_tys: FxHashMap<Ty<'tcx>, IsFreeze>,
 }
 
 impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
@@ -174,332 +261,629 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE
 impl<'tcx> NonCopyConst<'tcx> {
     pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self {
         Self {
-            interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability),
+            ignore_tys: conf
+                .ignore_interior_mutability
+                .iter()
+                .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty))
+                .collect(),
+            freeze_tys: FxHashMap::default(),
         }
     }
 
-    fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
-        // No branch that we check (yet) should continue if val isn't a branch
-        let Some(branched_val) = val.try_to_branch() else {
-            return false;
-        };
-        match *ty.kind() {
-            // the fact that we have to dig into every structs to search enums
-            // leads us to the point checking `UnsafeCell` directly is the only option.
-            ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true,
-            // As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
-            // contained value.
-            ty::Adt(def, ..) if def.is_union() => false,
-            ty::Array(ty, _) => branched_val
-                .iter()
-                .any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
-            ty::Adt(def, args) if def.is_enum() => {
-                let Some((&variant_valtree, fields)) = branched_val.split_first() else {
-                    return false;
-                };
-                let variant_index = variant_valtree.unwrap_leaf();
-                let variant_index = VariantIdx::from_u32(variant_index.to_u32());
-                fields
-                    .iter()
-                    .copied()
-                    .zip(
-                        def.variants()[variant_index]
+    /// Checks if a value of the given type is `Freeze`, or may be depending on the value.
+    fn is_ty_freeze(&mut self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>) -> IsFreeze {
+        let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
+        match self.freeze_tys.entry(ty) {
+            Entry::Occupied(e) => *e.get(),
+            Entry::Vacant(e) => {
+                let e = e.insert(IsFreeze::Yes);
+                if ty.is_freeze(tcx, typing_env) {
+                    return IsFreeze::Yes;
+                }
+                let is_freeze = match *ty.kind() {
+                    ty::Adt(adt, _) if adt.is_unsafe_cell() => {
+                        *e = IsFreeze::No;
+                        return IsFreeze::No;
+                    },
+                    ty::Adt(adt, _) if self.ignore_tys.contains(&adt.did()) => return IsFreeze::Yes,
+                    ty::Adt(adt, args) if adt.is_enum() => IsFreeze::from_variants(adt.variants().iter().map(|v| {
+                        IsFreeze::from_fields(
+                            v.fields
+                                .iter()
+                                .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))),
+                        )
+                    })),
+                    // Workaround for `ManuallyDrop`-like unions.
+                    ty::Adt(adt, args)
+                        if adt.is_union()
+                            && adt.non_enum_variant().fields.iter().any(|f| {
+                                tcx.layout_of(typing_env.as_query_input(f.ty(tcx, args)))
+                                    .is_ok_and(|l| l.layout.size().bytes() == 0)
+                            }) =>
+                    {
+                        return IsFreeze::Yes;
+                    },
+                    // Rust doesn't have the concept of an active union field so we have
+                    // to treat all fields as active.
+                    ty::Adt(adt, args) => IsFreeze::from_fields(
+                        adt.non_enum_variant()
                             .fields
                             .iter()
-                            .map(|field| field.ty(cx.tcx, args)),
-                    )
-                    .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty))
-            },
-            ty::Adt(def, args) => branched_val
-                .iter()
-                .zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
-                .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
-            ty::Tuple(tys) => branched_val
-                .iter()
-                .zip(tys)
-                .any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
-            ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) {
-                Ok(normalized_ty) if ty != normalized_ty => Self::is_value_unfrozen_raw_inner(cx, val, normalized_ty),
-                _ => false,
+                            .map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))),
+                    ),
+                    ty::Array(ty, _) | ty::Pat(ty, _) => self.is_ty_freeze(tcx, typing_env, ty),
+                    ty::Tuple(tys) => {
+                        IsFreeze::from_fields(tys.iter().map(|ty| self.is_ty_freeze(tcx, typing_env, ty)))
+                    },
+                    // Treat type parameters as though they were `Freeze`.
+                    ty::Param(_) | ty::Alias(..) => return IsFreeze::Yes,
+                    // TODO: check other types.
+                    _ => {
+                        *e = IsFreeze::No;
+                        return IsFreeze::No;
+                    },
+                };
+                if !is_freeze.is_freeze() {
+                    self.freeze_tys.insert(ty, is_freeze);
+                }
+                is_freeze
             },
-            _ => false,
         }
     }
 
-    fn is_value_unfrozen_raw(
-        cx: &LateContext<'tcx>,
-        result: Result<Result<ty::ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>,
+    /// Checks if the given constant value is `Freeze`. Returns `Err` if the constant
+    /// cannot be read, but the result depends on the value.
+    fn is_value_freeze(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        typing_env: TypingEnv<'tcx>,
         ty: Ty<'tcx>,
-    ) -> bool {
-        result.map_or_else(
-            |err| {
-                // Consider `TooGeneric` cases as being unfrozen.
-                // This causes a false positive where an assoc const whose type is unfrozen
-                // have a value that is a frozen variant with a generic param (an example is
-                // `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`).
-                // However, it prevents a number of false negatives that is, I think, important:
-                // 1. assoc consts in trait defs referring to consts of themselves (an example is
-                //    `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`).
-                // 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait
-                //    defs (i.e. without substitute for `Self`). (e.g. borrowing
-                //    `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`)
-                // 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no
-                //    enums. (An example is
-                //    `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and
-                //    `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`).
-                // One might be able to prevent these FNs correctly, and replace this with `false`;
-                // e.g. implementing `has_frozen_variant` described above, and not running this function
-                // when the type doesn't have any frozen variants would be the 'correct' way for the 2nd
-                // case (that actually removes another suboptimal behavior (I won't say 'false positive') where,
-                // similar to 2., but with a frozen variant) (e.g. borrowing
-                // `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`).
-                // I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
-                matches!(err, ErrorHandled::TooGeneric(..))
+        val: ConstValue<'tcx>,
+    ) -> Result<bool, ()> {
+        let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
+        match self.is_ty_freeze(tcx, typing_env, ty) {
+            IsFreeze::Yes => Ok(true),
+            IsFreeze::Maybe if matches!(ty.kind(), ty::Adt(..) | ty::Array(..) | ty::Tuple(..)) => {
+                for &(val, ty) in tcx
+                    .try_destructure_mir_constant_for_user_output(val, ty)
+                    .ok_or(())?
+                    .fields
+                {
+                    if !self.is_value_freeze(tcx, typing_env, ty, val)? {
+                        return Ok(false);
+                    }
+                }
+                Ok(true)
             },
-            |val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)),
-        )
-    }
-
-    fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
-        let def_id = body_id.hir_id.owner.to_def_id();
-        let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
-        let instance = ty::Instance::new_raw(def_id, args);
-        let cid = GlobalId {
-            instance,
-            promoted: None,
-        };
-        let typing_env = ty::TypingEnv::post_analysis(cx.tcx, def_id);
-        let result = cx.tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP);
-        Self::is_value_unfrozen_raw(cx, result, ty)
-    }
-
-    fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
-        let args = cx.typeck_results().node_args(hir_id);
-
-        let result = Self::const_eval_resolve(
-            cx.tcx,
-            cx.typing_env(),
-            ty::UnevaluatedConst::new(def_id, args),
-            DUMMY_SP,
-        );
-        Self::is_value_unfrozen_raw(cx, result, ty)
+            IsFreeze::Maybe | IsFreeze::No => Ok(false),
+        }
     }
 
-    pub fn const_eval_resolve(
+    /// Checks if the given expression creates a value which is `Freeze`.
+    ///
+    /// This will return `true` if the type is maybe `Freeze`, but it cannot be
+    /// determined for certain from the value.
+    ///
+    /// `typing_env` and `gen_args` are from the constant's use site.
+    /// `typeck` and `e` are from the constant's definition site.
+    fn is_init_expr_freeze(
+        &mut self,
         tcx: TyCtxt<'tcx>,
-        typing_env: ty::TypingEnv<'tcx>,
-        ct: ty::UnevaluatedConst<'tcx>,
-        span: Span,
-    ) -> EvalToValTreeResult<'tcx> {
-        match ty::Instance::try_resolve(tcx, typing_env, ct.def, ct.args) {
-            Ok(Some(instance)) => {
-                let cid = GlobalId {
-                    instance,
-                    promoted: None,
-                };
-                tcx.const_eval_global_id_for_typeck(typing_env, cid, span)
+        typing_env: TypingEnv<'tcx>,
+        typeck: &'tcx TypeckResults<'tcx>,
+        gen_args: GenericArgsRef<'tcx>,
+        e: &'tcx Expr<'tcx>,
+    ) -> bool {
+        // Make sure to instantiate all types coming from `typeck` with `gen_args`.
+        let ty = EarlyBinder::bind(typeck.expr_ty(e)).instantiate(tcx, gen_args);
+        let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
+        match self.is_ty_freeze(tcx, typing_env, ty) {
+            IsFreeze::Yes => true,
+            IsFreeze::No => false,
+            IsFreeze::Maybe => match e.kind {
+                ExprKind::Block(b, _)
+                    if !b.targeted_by_break
+                        && b.stmts.is_empty()
+                        && let Some(e) = b.expr =>
+                {
+                    self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)
+                },
+                ExprKind::Path(ref p) => {
+                    let res = typeck.qpath_res(p, e.hir_id);
+                    let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)).instantiate(tcx, gen_args);
+                    match res {
+                        Res::Def(DefKind::Const | DefKind::AssocConst, did)
+                            if let Ok(val) =
+                                tcx.const_eval_resolve(typing_env, UnevaluatedConst::new(did, gen_args), DUMMY_SP)
+                                && let Ok(is_freeze) = self.is_value_freeze(tcx, typing_env, ty, val) =>
+                        {
+                            is_freeze
+                        },
+                        Res::Def(DefKind::Const | DefKind::AssocConst, did)
+                            if let Some((typeck, init)) = get_const_hir_value(tcx, typing_env, did, gen_args) =>
+                        {
+                            self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, init)
+                        },
+                        // Either this is a unit constructor, or some unknown value.
+                        // In either case we consider the value to be `Freeze`.
+                        _ => true,
+                    }
+                },
+                ExprKind::Call(callee, args)
+                    if let ExprKind::Path(p) = &callee.kind
+                        && let res = typeck.qpath_res(p, callee.hir_id)
+                        && matches!(res, Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_)) =>
+                {
+                    args.iter()
+                        .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e))
+                },
+                ExprKind::Struct(_, fields, StructTailExpr::None) => fields
+                    .iter()
+                    .all(|f| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, f.expr)),
+                ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs
+                    .iter()
+                    .all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)),
+                ExprKind::Repeat(e, _) => self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e),
+                _ => true,
             },
-            Ok(None) => Err(ErrorHandled::TooGeneric(span)),
-            Err(err) => Err(ErrorHandled::Reported(
-                ReportedErrorInfo::non_const_eval_error(err),
-                span,
-            )),
         }
     }
-}
 
-impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
-    fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
-        if let ItemKind::Const(.., body_id) = it.kind {
-            let ty = cx.tcx.type_of(it.owner_id).instantiate_identity();
-            if !ignored_macro(cx, it)
-                && self.interior_mut.is_interior_mut_ty(cx, ty)
-                && Self::is_value_unfrozen_poly(cx, body_id, ty)
-            {
-                lint(cx, Source::Item { item: it.span, ty });
+    /// Checks if the given expression (or a local projection of it) is both borrowed and
+    /// definitely a non-`Freeze` type.
+    fn is_non_freeze_expr_borrowed(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        typing_env: TypingEnv<'tcx>,
+        typeck: &'tcx TypeckResults<'tcx>,
+        mut src_expr: &'tcx Expr<'tcx>,
+    ) -> Option<BorrowSource<'tcx>> {
+        let mut parents = tcx.hir_parent_iter(src_expr.hir_id);
+        loop {
+            let ty = typeck.expr_ty(src_expr);
+            // Normalized as we need to check if this is an array later.
+            let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
+            let is_freeze = self.is_ty_freeze(tcx, typing_env, ty);
+            if is_freeze.is_freeze() {
+                return None;
             }
-        }
-    }
-
-    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
-        if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind {
-            let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity();
-
-            // Normalize assoc types because ones originated from generic params
-            // bounded other traits could have their bound.
-            let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty);
-            if self.interior_mut.is_interior_mut_ty(cx, normalized)
-                // When there's no default value, lint it only according to its type;
-                // in other words, lint consts whose value *could* be unfrozen, not definitely is.
-                // This feels inconsistent with how the lint treats generic types,
-                // which avoids linting types which potentially become unfrozen.
-                // One could check whether an unfrozen type have a *frozen variant*
-                // (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`),
-                // and do the same as the case of generic types at impl items.
-                // Note that it isn't sufficient to check if it has an enum
-                // since all of that enum's variants can be unfrozen:
-                // i.e. having an enum doesn't necessary mean a type has a frozen variant.
-                // And, implementing it isn't a trivial task; it'll probably end up
-                // re-implementing the trait predicate evaluation specific to `Freeze`.
-                && body_id_opt.is_none_or(|body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized))
-            {
-                lint(cx, Source::Assoc { item: trait_item.span });
+            if let [adjust, ..] = typeck.expr_adjustments(src_expr) {
+                return does_adjust_borrow(adjust)
+                    .filter(|_| is_freeze.is_not_freeze())
+                    .map(|cause| BorrowSource::new(tcx, src_expr, cause));
+            }
+            let Some((_, Node::Expr(use_expr))) = parents.next() else {
+                return None;
+            };
+            match use_expr.kind {
+                ExprKind::Field(..) => {},
+                ExprKind::Index(..) if ty.is_array() => {},
+                ExprKind::AddrOf(..) if is_freeze.is_not_freeze() => {
+                    return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow));
+                },
+                // All other expressions use the value.
+                _ => return None,
             }
+            src_expr = use_expr;
         }
     }
 
-    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
-        if let ImplItemKind::Const(_, body_id) = &impl_item.kind {
-            let item_def_id = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
-            let item = cx.tcx.hir_expect_item(item_def_id);
-
-            match &item.kind {
-                ItemKind::Impl(Impl {
-                    of_trait: Some(of_trait_ref),
-                    ..
-                }) => {
-                    if let Some(of_trait_def_id) = of_trait_ref.trait_def_id()
-                        // Lint a trait impl item only when the definition is a generic type,
-                        // assuming an assoc const is not meant to be an interior mutable type.
-                        && let Some(of_assoc_item) = cx
-                            .tcx
-                            .associated_item(impl_item.owner_id)
-                            .trait_item_def_id
-                        && cx
-                            .tcx
-                            .layout_of(ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id).as_query_input(
-                                // Normalize assoc types because ones originated from generic params
-                                // bounded other traits could have their bound at the trait defs;
-                                // and, in that case, the definition is *not* generic.
-                                cx.tcx.normalize_erasing_regions(
-                                    ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id),
-                                    cx.tcx.type_of(of_assoc_item).instantiate_identity(),
-                                ),
-                            ))
-                            .is_err()
-                            // If there were a function like `has_frozen_variant` described above,
-                            // we should use here as a frozen variant is a potential to be frozen
-                            // similar to unknown layouts.
-                            // e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
-                        && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity()
-                        && let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty)
-                        && self.interior_mut.is_interior_mut_ty(cx, normalized)
-                        && Self::is_value_unfrozen_poly(cx, *body_id, normalized)
-                    {
-                        lint(cx, Source::Assoc { item: impl_item.span });
+    /// Checks if the given value (or a local projection of it) is both borrowed and
+    /// definitely non-`Freeze`. Returns `Err` if the constant cannot be read, but the
+    /// result depends on the value.
+    fn is_non_freeze_val_borrowed(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        typing_env: TypingEnv<'tcx>,
+        typeck: &'tcx TypeckResults<'tcx>,
+        mut src_expr: &'tcx Expr<'tcx>,
+        mut val: ConstValue<'tcx>,
+    ) -> Result<Option<BorrowSource<'tcx>>, ()> {
+        let mut parents = tcx.hir_parent_iter(src_expr.hir_id);
+        let mut ty = typeck.expr_ty(src_expr);
+        loop {
+            // Normalized as we need to check if this is an array later.
+            ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
+            if let [adjust, ..] = typeck.expr_adjustments(src_expr) {
+                let res = if let Some(cause) = does_adjust_borrow(adjust)
+                    && !self.is_value_freeze(tcx, typing_env, ty, val)?
+                {
+                    Some(BorrowSource::new(tcx, src_expr, cause))
+                } else {
+                    None
+                };
+                return Ok(res);
+            }
+            // Check only the type here as the result gets cached for each type.
+            if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() {
+                return Ok(None);
+            }
+            let Some((_, Node::Expr(use_expr))) = parents.next() else {
+                return Ok(None);
+            };
+            let next_val = match use_expr.kind {
+                ExprKind::Field(_, name) => {
+                    if let Some(idx) = get_field_idx_by_name(ty, name.name) {
+                        tcx.try_destructure_mir_constant_for_user_output(val, ty)
+                            .ok_or(())?
+                            .fields
+                            .get(idx)
+                    } else {
+                        return Ok(None);
                     }
                 },
-                ItemKind::Impl(Impl { of_trait: None, .. }) => {
-                    let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity();
-                    // Normalize assoc types originated from generic params.
-                    let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty);
-
-                    if self.interior_mut.is_interior_mut_ty(cx, normalized)
-                        && Self::is_value_unfrozen_poly(cx, *body_id, normalized)
-                    {
-                        lint(cx, Source::Assoc { item: impl_item.span });
+                ExprKind::Index(_, idx, _) if ty.is_array() => {
+                    let val = tcx.try_destructure_mir_constant_for_user_output(val, ty).ok_or(())?;
+                    if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) {
+                        val.fields.get(idx as usize)
+                    } else {
+                        // It's some value in the array so check all of them.
+                        for &(val, _) in val.fields {
+                            if let Some(src) =
+                                self.is_non_freeze_val_borrowed(tcx, typing_env, typeck, use_expr, val)?
+                            {
+                                return Ok(Some(src));
+                            }
+                        }
+                        return Ok(None);
                     }
                 },
-                _ => (),
+                ExprKind::AddrOf(..) if !self.is_value_freeze(tcx, typing_env, ty, val)? => {
+                    return Ok(Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)));
+                },
+                // All other expressions use the value.
+                _ => return Ok(None),
+            };
+            src_expr = use_expr;
+            if let Some(&(next_val, next_ty)) = next_val {
+                ty = next_ty;
+                val = next_val;
+            } else {
+                return Ok(None);
             }
         }
     }
 
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Path(qpath) = &expr.kind {
-            // Only lint if we use the const item inside a function.
-            if is_in_const_context(cx) {
-                return;
+    /// Checks if the given value (or a local projection of it) is both borrowed and
+    /// definitely non-`Freeze`.
+    ///
+    /// `typing_env` and `init_args` are from the constant's use site.
+    /// `init_typeck` and `init_expr` are from the constant's definition site.
+    #[expect(clippy::too_many_arguments, clippy::too_many_lines)]
+    fn is_non_freeze_init_borrowed(
+        &mut self,
+        tcx: TyCtxt<'tcx>,
+        typing_env: TypingEnv<'tcx>,
+        typeck: &'tcx TypeckResults<'tcx>,
+        mut src_expr: &'tcx Expr<'tcx>,
+        mut init_typeck: &'tcx TypeckResults<'tcx>,
+        mut init_args: GenericArgsRef<'tcx>,
+        mut init_expr: &'tcx Expr<'tcx>,
+    ) -> Option<BorrowSource<'tcx>> {
+        // Make sure to instantiate all types coming from `init_typeck` with `init_args`.
+        let mut parents = tcx.hir_parent_iter(src_expr.hir_id);
+        loop {
+            // First handle any adjustments since they are cheap to check.
+            if let [adjust, ..] = typeck.expr_adjustments(src_expr) {
+                return does_adjust_borrow(adjust)
+                    .filter(|_| !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr))
+                    .map(|cause| BorrowSource::new(tcx, src_expr, cause));
             }
 
-            // Make sure it is a const item.
-            let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else {
-                return;
-            };
-
-            // Climb up to resolve any field access and explicit referencing.
-            let mut cur_expr = expr;
-            let mut dereferenced_expr = expr;
-            let mut needs_check_adjustment = true;
+            // Then read through constants and blocks on the init expression before
+            // applying the next use expression.
             loop {
-                let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id);
-                if parent_id == cur_expr.hir_id {
-                    break;
+                match init_expr.kind {
+                    ExprKind::Block(b, _)
+                        if !b.targeted_by_break
+                            && b.stmts.is_empty()
+                            && let Some(next_init) = b.expr =>
+                    {
+                        init_expr = next_init;
+                    },
+                    ExprKind::Path(ref init_path) => {
+                        let next_init_args =
+                            EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)).instantiate(tcx, init_args);
+                        match init_typeck.qpath_res(init_path, init_expr.hir_id) {
+                            Res::Def(DefKind::Ctor(..), _) => return None,
+                            Res::Def(DefKind::Const | DefKind::AssocConst, did)
+                                if let Ok(val) = tcx.const_eval_resolve(
+                                    typing_env,
+                                    UnevaluatedConst::new(did, next_init_args),
+                                    DUMMY_SP,
+                                ) && let Ok(res) =
+                                    self.is_non_freeze_val_borrowed(tcx, typing_env, init_typeck, src_expr, val) =>
+                            {
+                                return res;
+                            },
+                            Res::Def(DefKind::Const | DefKind::AssocConst, did)
+                                if let Some((next_typeck, value)) =
+                                    get_const_hir_value(tcx, typing_env, did, next_init_args) =>
+                            {
+                                init_typeck = next_typeck;
+                                init_args = next_init_args;
+                                init_expr = value;
+                            },
+                            // There's no more that we can read from the init expression. Switch to a
+                            // type based check.
+                            _ => {
+                                return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, src_expr);
+                            },
+                        }
+                    },
+                    _ => break,
                 }
-                if let Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) {
-                    match &parent_expr.kind {
-                        ExprKind::AddrOf(..) => {
-                            // `&e` => `e` must be referenced.
-                            needs_check_adjustment = false;
-                        },
-                        ExprKind::Field(..) => {
-                            needs_check_adjustment = true;
+            }
 
-                            // Check whether implicit dereferences happened;
-                            // if so, no need to go further up
-                            // because of the same reason as the `ExprKind::Unary` case.
-                            if cx
-                                .typeck_results()
-                                .expr_adjustments(dereferenced_expr)
-                                .iter()
-                                .any(|adj| matches!(adj.kind, Adjust::Deref(_)))
-                            {
-                                break;
-                            }
+            // Then a type check. Note we only check the type here as the result
+            // gets cached.
+            let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args);
+            // Normalized as we need to check if this is an array later.
+            let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
+            if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() {
+                return None;
+            }
 
-                            dereferenced_expr = parent_expr;
-                        },
-                        ExprKind::Index(e, _, _) if ptr::eq(&raw const **e, cur_expr) => {
-                            // `e[i]` => desugared to `*Index::index(&e, i)`,
-                            // meaning `e` must be referenced.
-                            // no need to go further up since a method call is involved now.
-                            needs_check_adjustment = false;
-                            break;
-                        },
-                        ExprKind::Unary(UnOp::Deref, _) => {
-                            // `*e` => desugared to `*Deref::deref(&e)`,
-                            // meaning `e` must be referenced.
-                            // no need to go further up since a method call is involved now.
-                            needs_check_adjustment = false;
-                            break;
+            // Finally reduce the init expression using the next use expression.
+            let Some((_, Node::Expr(use_expr))) = parents.next() else {
+                return None;
+            };
+            init_expr = match &use_expr.kind {
+                ExprKind::Field(_, name) => match init_expr.kind {
+                    ExprKind::Struct(_, fields, _)
+                        if let Some(field) = fields.iter().find(|f| f.ident.name == name.name) =>
+                    {
+                        field.expr
+                    },
+                    ExprKind::Tup(fields)
+                        if let Ok(idx) = name.as_str().parse::<usize>()
+                            && let Some(field) = fields.get(idx) =>
+                    {
+                        field
+                    },
+                    ExprKind::Call(callee, args)
+                        if let ExprKind::Path(callee_path) = &callee.kind
+                            && matches!(
+                                init_typeck.qpath_res(callee_path, callee.hir_id),
+                                Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_)
+                            )
+                            && let Ok(idx) = name.as_str().parse::<usize>()
+                            && let Some(arg) = args.get(idx) =>
+                    {
+                        arg
+                    },
+                    // Revert to a type based check as we don't know the field's value.
+                    _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr),
+                },
+                ExprKind::Index(_, idx, _) if ty.is_array() => match init_expr.kind {
+                    ExprKind::Array(fields) => {
+                        if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) {
+                            // If the index is out of bounds it means the code
+                            // unconditionally panics. In that case there is no borrow.
+                            fields.get(idx as usize)?
+                        } else {
+                            // Unknown index, just run the check for all values.
+                            return fields.iter().find_map(|f| {
+                                self.is_non_freeze_init_borrowed(
+                                    tcx,
+                                    typing_env,
+                                    typeck,
+                                    use_expr,
+                                    init_typeck,
+                                    init_args,
+                                    f,
+                                )
+                            });
+                        }
+                    },
+                    // Just assume the index expression doesn't panic here.
+                    ExprKind::Repeat(field, _) => field,
+                    _ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr),
+                },
+                ExprKind::AddrOf(..)
+                    if !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr) =>
+                {
+                    return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow));
+                },
+                // All other expressions use the value.
+                _ => return None,
+            };
+            src_expr = use_expr;
+        }
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+        if let ItemKind::Const(ident, .., body_id) = item.kind
+            && !ident.is_special()
+            && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
+            && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
+                IsFreeze::No => true,
+                IsFreeze::Yes => false,
+                IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
+                    Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
+                    _ => !self.is_init_expr_freeze(
+                        cx.tcx,
+                        cx.typing_env(),
+                        cx.tcx.typeck(item.owner_id),
+                        GenericArgs::identity_for_item(cx.tcx, item.owner_id),
+                        cx.tcx.hir_body(body_id).value,
+                    ),
+                },
+            }
+            && !item.span.in_external_macro(cx.sess().source_map())
+            // Only needed when compiling `std`
+            && !is_thread_local(cx, item)
+        {
+            span_lint_and_then(
+                cx,
+                DECLARE_INTERIOR_MUTABLE_CONST,
+                ident.span,
+                "named constant with interior mutability",
+                |diag| {
+                    let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else {
+                        return;
+                    };
+                    if implements_trait(cx, ty, sync_trait, &[]) {
+                        diag.help("did you mean to make this a `static` item");
+                    } else {
+                        diag.help("did you mean to make this a `thread_local!` item");
+                    }
+                },
+            );
+        }
+    }
+
+    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
+        if let TraitItemKind::Const(_, body_id_opt) = item.kind
+            && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
+            && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
+                IsFreeze::No => true,
+                IsFreeze::Maybe if let Some(body_id) = body_id_opt => {
+                    match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
+                        Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => {
+                            !is_freeze
                         },
-                        _ => break,
+                        _ => !self.is_init_expr_freeze(
+                            cx.tcx,
+                            cx.typing_env(),
+                            cx.tcx.typeck(item.owner_id),
+                            GenericArgs::identity_for_item(cx.tcx, item.owner_id),
+                            cx.tcx.hir_body(body_id).value,
+                        ),
                     }
-                    cur_expr = parent_expr;
-                } else {
-                    break;
-                }
+                },
+                IsFreeze::Yes | IsFreeze::Maybe => false,
             }
+            && !item.span.in_external_macro(cx.sess().source_map())
+        {
+            span_lint(
+                cx,
+                DECLARE_INTERIOR_MUTABLE_CONST,
+                item.ident.span,
+                "named constant with interior mutability",
+            );
+        }
+    }
 
-            let ty = if needs_check_adjustment {
-                let adjustments = cx.typeck_results().expr_adjustments(dereferenced_expr);
-                if let Some(i) = adjustments
-                    .iter()
-                    .position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_)))
-                {
-                    if i == 0 {
-                        cx.typeck_results().expr_ty(dereferenced_expr)
+    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
+        if let ImplItemKind::Const(_, body_id) = item.kind
+            && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
+            && match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
+                IsFreeze::Yes => false,
+                IsFreeze::No => {
+                    // If this is a trait impl, check if the trait definition is the source
+                    // of the cell.
+                    if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id())
+                        && let ItemKind::Impl(impl_block) = parent_item.kind
+                        && let Some(of_trait) = impl_block.of_trait
+                        && let Some(trait_id) = of_trait.trait_def_id()
+                    {
+                        // Replace all instances of `<Self as Trait>::AssocType` with the
+                        // unit type and check again. If the result is the same then the
+                        // trait definition is the cause.
+                        let ty = (ReplaceAssocFolder {
+                            tcx: cx.tcx,
+                            trait_id,
+                            self_ty: cx.tcx.type_of(parent_item.owner_id).instantiate_identity(),
+                        })
+                        .fold_ty(cx.tcx.type_of(item.owner_id).instantiate_identity());
+                        // `ty` may not be normalizable, but that should be fine.
+                        !self.is_ty_freeze(cx.tcx, cx.typing_env(), ty).is_not_freeze()
                     } else {
-                        adjustments[i - 1].target
+                        true
                     }
+                },
+                // Even if this is from a trait, there are values which don't have
+                // interior mutability.
+                IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
+                    Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
+                    _ => !self.is_init_expr_freeze(
+                        cx.tcx,
+                        cx.typing_env(),
+                        cx.tcx.typeck(item.owner_id),
+                        GenericArgs::identity_for_item(cx.tcx, item.owner_id),
+                        cx.tcx.hir_body(body_id).value,
+                    ),
+                },
+            }
+            && !item.span.in_external_macro(cx.sess().source_map())
+        {
+            span_lint(
+                cx,
+                DECLARE_INTERIOR_MUTABLE_CONST,
+                item.ident.span,
+                "named constant with interior mutability",
+            );
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        if let ExprKind::Path(qpath) = &e.kind
+            && let typeck = cx.typeck_results()
+            && let Res::Def(DefKind::Const | DefKind::AssocConst, did) = typeck.qpath_res(qpath, e.hir_id)
+            // As of `1.80` constant contexts can't borrow any type with interior mutability
+            && !is_in_const_context(cx)
+            && !self.is_ty_freeze(cx.tcx, cx.typing_env(), typeck.expr_ty(e)).is_freeze()
+            && let Some(borrow_src) = {
+                // The extra block helps formatting a lot.
+                if let Ok(val) = cx.tcx.const_eval_resolve(
+                    cx.typing_env(),
+                    UnevaluatedConst::new(did, typeck.node_args(e.hir_id)),
+                    DUMMY_SP,
+                ) && let Ok(src) = self.is_non_freeze_val_borrowed(cx.tcx, cx.typing_env(), typeck, e, val)
+                {
+                    src
+                } else if let init_args = typeck.node_args(e.hir_id)
+                    && let Some((init_typeck, init)) = get_const_hir_value(cx.tcx, cx.typing_env(), did, init_args)
+                {
+                    self.is_non_freeze_init_borrowed(cx.tcx, cx.typing_env(), typeck, e, init_typeck, init_args, init)
                 } else {
-                    // No borrow adjustments means the entire const is moved.
-                    return;
+                    self.is_non_freeze_expr_borrowed(cx.tcx, cx.typing_env(), typeck, e)
                 }
-            } else {
-                cx.typeck_results().expr_ty(dereferenced_expr)
-            };
-
-            if self.interior_mut.is_interior_mut_ty(cx, ty)
-                && Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
-            {
-                lint(cx, Source::Expr { expr: expr.span });
             }
+            && !borrow_src.expr.span.in_external_macro(cx.sess().source_map())
+        {
+            span_lint_and_then(
+                cx,
+                BORROW_INTERIOR_MUTABLE_CONST,
+                borrow_src.expr.span,
+                "borrow of a named constant with interior mutability",
+                |diag| {
+                    if let Some(note) = borrow_src.cause.note() {
+                        diag.note(note);
+                    }
+                    diag.help("this lint can be silenced by assigning the value to a local variable before borrowing");
+                },
+            );
+        }
+    }
+}
+
+struct ReplaceAssocFolder<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    trait_id: DefId,
+    self_ty: Ty<'tcx>,
+}
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAssocFolder<'tcx> {
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if let ty::Alias(AliasTyKind::Projection, ty) = ty.kind()
+            && ty.trait_def_id(self.tcx) == self.trait_id
+            && ty.self_ty() == self.self_ty
+        {
+            self.tcx.types.unit
+        } else {
+            ty.super_fold_with(self)
         }
     }
 }
 
-fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool {
+fn is_thread_local(cx: &LateContext<'_>, it: &Item<'_>) -> bool {
     macro_backtrace(it.span).any(|macro_call| {
         matches!(
             cx.tcx.get_diagnostic_name(macro_call.def_id),
@@ -507,3 +891,42 @@ fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool {
         )
     })
 }
+
+/// Checks if the adjustment causes a borrow of the original value. Returns
+/// `None` if the value is consumed instead of borrowed.
+fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option<BorrowCause> {
+    match adjust.kind {
+        Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow),
+        // Custom deref calls `<T as Deref>::deref(&x)` resulting in a borrow.
+        Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref),
+        // All other adjustments read the value.
+        _ => None,
+    }
+}
+
+/// Attempts to get the value of a constant as a HIR expression. Also gets the
+/// `TypeckResults` associated with the constant's body.
+fn get_const_hir_value<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    typing_env: TypingEnv<'tcx>,
+    did: DefId,
+    args: GenericArgsRef<'tcx>,
+) -> Option<(&'tcx TypeckResults<'tcx>, &'tcx Expr<'tcx>)> {
+    let did = did.as_local()?;
+    let (did, body_id) = match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) {
+        Node::Item(item) if let ItemKind::Const(.., body_id) = item.kind => (did, body_id),
+        Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id),
+        Node::TraitItem(_)
+            if let Ok(Some(inst)) = Instance::try_resolve(tcx, typing_env, did.into(), args)
+                && let Some(did) = inst.def_id().as_local() =>
+        {
+            match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) {
+                Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id),
+                Node::TraitItem(item) if let TraitItemKind::Const(.., Some(body_id)) = item.kind => (did, body_id),
+                _ => return None,
+            }
+        },
+        _ => return None,
+    };
+    Some((tcx.typeck(did), tcx.hir_body(body_id).value))
+}
diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs
index 26d41cfb497..c50ad17bfad 100644
--- a/clippy_utils/src/ty/mod.rs
+++ b/clippy_utils/src/ty/mod.rs
@@ -1361,3 +1361,14 @@ pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
         || ty.is_array()
         || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did()))
 }
+
+/// Gets the index of a field by name.
+pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option<usize> {
+    match *ty.kind() {
+        ty::Adt(def, _) if def.is_union() || def.is_struct() => {
+            def.non_enum_variant().fields.iter().position(|f| f.name == name)
+        },
+        ty::Tuple(_) => name.as_str().parse::<usize>().ok(),
+        _ => None,
+    }
+}
diff --git a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs b/tests/ui/auxiliary/interior_mutable_const.rs
index 96e037d4fcd..96e037d4fcd 100644
--- a/tests/ui/borrow_interior_mutable_const/auxiliary/helper.rs
+++ b/tests/ui/auxiliary/interior_mutable_const.rs
diff --git a/tests/ui/borrow_interior_mutable_const.rs b/tests/ui/borrow_interior_mutable_const.rs
new file mode 100644
index 00000000000..0f439f78915
--- /dev/null
+++ b/tests/ui/borrow_interior_mutable_const.rs
@@ -0,0 +1,221 @@
+//@aux-build:interior_mutable_const.rs
+
+#![deny(clippy::borrow_interior_mutable_const)]
+#![allow(
+    clippy::declare_interior_mutable_const,
+    clippy::out_of_bounds_indexing,
+    const_item_mutation,
+    unconditional_panic
+)]
+
+use core::cell::{Cell, UnsafeCell};
+use core::ops::{Deref, Index};
+
+trait ConstDefault {
+    const DEFAULT: Self;
+}
+impl ConstDefault for u32 {
+    const DEFAULT: Self = 0;
+}
+impl<T: ConstDefault> ConstDefault for Cell<T> {
+    const DEFAULT: Self = Cell::new(T::DEFAULT);
+}
+
+fn main() {
+    {
+        const C: String = String::new();
+        let _ = C;
+        let _ = &C;
+        let _ = C.len();
+        let _ = &*C;
+    }
+    {
+        const C: UnsafeCell<u32> = UnsafeCell::new(0);
+        let _ = C;
+        let _ = &C; //~ borrow_interior_mutable_const
+        let _ = C.into_inner();
+        let _ = C.get(); //~ borrow_interior_mutable_const
+    }
+    {
+        const C: Cell<u32> = Cell::new(0);
+        let _ = C;
+        let _ = &C; //~ borrow_interior_mutable_const
+        let _ = &mut C; //~ borrow_interior_mutable_const
+        let _ = C.into_inner();
+
+        let local = C;
+        C.swap(&local) //~ borrow_interior_mutable_const
+    }
+    {
+        const C: [(Cell<u32>,); 1] = [(Cell::new(0),)];
+        let _ = C;
+        let _ = &C; //~ borrow_interior_mutable_const
+        let _ = &C[0]; //~ borrow_interior_mutable_const
+        let _ = &C[0].0; //~ borrow_interior_mutable_const
+        C[0].0.set(1); //~ borrow_interior_mutable_const
+    }
+    {
+        struct S(Cell<u32>);
+        impl S {
+            const C: Self = Self(Cell::new(0));
+        }
+        impl Deref for S {
+            type Target = Cell<u32>;
+            fn deref(&self) -> &Self::Target {
+                &self.0
+            }
+        }
+        let _ = S::C;
+        let _ = S::C.0;
+        let _ = &S::C; //~ borrow_interior_mutable_const
+        let _ = &S::C.0; //~ borrow_interior_mutable_const
+        S::C.set(1); //~ borrow_interior_mutable_const
+        let _ = &*S::C; //~ borrow_interior_mutable_const
+        (*S::C).set(1); //~ borrow_interior_mutable_const
+    }
+    {
+        enum E {
+            Cell(Cell<u32>),
+            Other,
+        }
+        const CELL: E = E::Cell(Cell::new(0));
+        const OTHER: E = E::Other;
+
+        let _ = CELL;
+        let _ = &CELL; //~ borrow_interior_mutable_const
+        let E::Cell(_) = CELL else {
+            return;
+        };
+
+        let _ = OTHER;
+        let _ = &OTHER;
+        let E::Cell(ref _x) = OTHER else {
+            return;
+        };
+    }
+    {
+        struct S<T> {
+            cell: (Cell<T>, u32),
+            other: Option<T>,
+        }
+        impl<T: ConstDefault + Copy> S<T> {
+            const C: Self = Self {
+                cell: (Cell::<T>::DEFAULT, 0),
+                other: Some(T::DEFAULT),
+            };
+
+            fn f() {
+                let _ = Self::C;
+                let _ = &Self::C; //~ borrow_interior_mutable_const
+                let _ = Self::C.other;
+                let _ = &Self::C.other;
+                let _ = &Self::C.cell; //~ borrow_interior_mutable_const
+                let _ = &Self::C.cell.0; //~ borrow_interior_mutable_const
+                Self::C.cell.0.set(T::DEFAULT); //~ borrow_interior_mutable_const
+                let _ = &Self::C.cell.1;
+            }
+        }
+    }
+    {
+        trait T {
+            const VALUE: Option<Cell<u32>> = Some(Cell::new(0));
+        }
+        impl T for u32 {}
+        impl T for i32 {
+            const VALUE: Option<Cell<u32>> = None;
+        }
+
+        let _ = &u32::VALUE; //~ borrow_interior_mutable_const
+        let _ = &i32::VALUE;
+    }
+    {
+        trait Trait<T: ConstDefault> {
+            type T<U: ConstDefault>: ConstDefault;
+            const VALUE: Option<Self::T<T>> = Some(Self::T::<T>::DEFAULT);
+        }
+        impl<T: ConstDefault> Trait<T> for u32 {
+            type T<U: ConstDefault> = Cell<U>;
+        }
+        impl<T: ConstDefault> Trait<T> for i32 {
+            type T<U: ConstDefault> = Cell<U>;
+            const VALUE: Option<Cell<T>> = None;
+        }
+
+        fn f<T: ConstDefault>() {
+            let _ = &<u32 as Trait<T>>::VALUE; //~ borrow_interior_mutable_const
+            let _ = &<i32 as Trait<T>>::VALUE;
+        }
+    }
+    {
+        trait Trait {
+            const UNFROZEN: Option<Cell<u32>> = Some(Cell::new(0));
+            const FROZEN: Option<Cell<u32>> = None;
+            const NON_FREEZE: u32 = 0;
+        }
+        fn f<T: Trait>() {
+            // None of these are guaranteed to be frozen, so don't lint.
+            let _ = &T::UNFROZEN;
+            let _ = &T::FROZEN;
+            let _ = &T::NON_FREEZE;
+        }
+    }
+    {
+        struct S([Option<Cell<u32>>; 2]);
+        impl Index<usize> for S {
+            type Output = Option<Cell<u32>>;
+            fn index(&self, idx: usize) -> &Self::Output {
+                &self.0[idx]
+            }
+        }
+
+        const C: S = S([Some(Cell::new(0)), None]);
+        let _ = &C; //~ borrow_interior_mutable_const
+        let _ = &C[0]; //~ borrow_interior_mutable_const
+        let _ = &C.0[0]; //~ borrow_interior_mutable_const
+        let _ = &C.0[1];
+    }
+    {
+        const C: [Option<Cell<u32>>; 2] = [None, None];
+        let _ = &C[0];
+        let _ = &C[1];
+        let _ = &C[2];
+
+        fn f(i: usize) {
+            let _ = &C[i];
+        }
+    }
+    {
+        const C: [Option<Cell<u32>>; 2] = [None, Some(Cell::new(0))];
+        let _ = &C[0];
+        let _ = &C[1]; //~ borrow_interior_mutable_const
+        let _ = &C[2];
+
+        fn f(i: usize) {
+            let _ = &C[i]; //~ borrow_interior_mutable_const
+        }
+    }
+    {
+        let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const
+        let _ = &interior_mutable_const::WRAPPED_PRIVATE_FROZEN_VARIANT;
+    }
+    {
+        type Cell2<T> = Cell<T>;
+        type MyCell = Cell2<u32>;
+        struct S(Option<MyCell>);
+        trait T {
+            type Assoc;
+        }
+        struct S2<T>(T, T, u32);
+        impl T for S {
+            type Assoc = S2<Self>;
+        }
+        type Assoc<X> = <X as T>::Assoc;
+        impl S {
+            const VALUE: Assoc<Self> = S2(Self(None), Self(Some(Cell::new(0))), 0);
+        }
+        let _ = &S::VALUE; //~ borrow_interior_mutable_const
+        let _ = &S::VALUE.0;
+        let _ = &S::VALUE.1; //~ borrow_interior_mutable_const
+        let _ = &S::VALUE.2;
+    }
+}
diff --git a/tests/ui/borrow_interior_mutable_const.stderr b/tests/ui/borrow_interior_mutable_const.stderr
new file mode 100644
index 00000000000..e7c3f879b05
--- /dev/null
+++ b/tests/ui/borrow_interior_mutable_const.stderr
@@ -0,0 +1,247 @@
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:35:17
+   |
+LL |         let _ = &C;
+   |                 ^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+note: the lint level is defined here
+  --> tests/ui/borrow_interior_mutable_const.rs:3:9
+   |
+LL | #![deny(clippy::borrow_interior_mutable_const)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:37:17
+   |
+LL |         let _ = C.get();
+   |                 ^
+   |
+   = note: there is a compiler inserted borrow here
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:42:17
+   |
+LL |         let _ = &C;
+   |                 ^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:43:17
+   |
+LL |         let _ = &mut C;
+   |                 ^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:47:9
+   |
+LL |         C.swap(&local)
+   |         ^
+   |
+   = note: there is a compiler inserted borrow here
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:52:17
+   |
+LL |         let _ = &C;
+   |                 ^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:53:17
+   |
+LL |         let _ = &C[0];
+   |                 ^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:54:17
+   |
+LL |         let _ = &C[0].0;
+   |                 ^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:55:9
+   |
+LL |         C[0].0.set(1);
+   |         ^^^^^^
+   |
+   = note: there is a compiler inserted borrow here
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:70:17
+   |
+LL |         let _ = &S::C;
+   |                 ^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:71:17
+   |
+LL |         let _ = &S::C.0;
+   |                 ^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:72:9
+   |
+LL |         S::C.set(1);
+   |         ^^^^
+   |
+   = note: there is a compiler inserted call to `Deref::deref` here
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:73:18
+   |
+LL |         let _ = &*S::C;
+   |                  ^^^^^
+   |
+   = note: this deref expression is a call to `Deref::deref`
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:74:9
+   |
+LL |         (*S::C).set(1);
+   |         ^^^^^^^
+   |
+   = note: this deref expression is a call to `Deref::deref`
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:85:17
+   |
+LL |         let _ = &CELL;
+   |                 ^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:109:25
+   |
+LL |                 let _ = &Self::C;
+   |                         ^^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:112:25
+   |
+LL |                 let _ = &Self::C.cell;
+   |                         ^^^^^^^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:113:25
+   |
+LL |                 let _ = &Self::C.cell.0;
+   |                         ^^^^^^^^^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:114:17
+   |
+LL |                 Self::C.cell.0.set(T::DEFAULT);
+   |                 ^^^^^^^^^^^^^^
+   |
+   = note: there is a compiler inserted borrow here
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:128:17
+   |
+LL |         let _ = &u32::VALUE;
+   |                 ^^^^^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:145:21
+   |
+LL |             let _ = &<u32 as Trait<T>>::VALUE;
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:172:17
+   |
+LL |         let _ = &C;
+   |                 ^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:173:18
+   |
+LL |         let _ = &C[0];
+   |                  ^^^^
+   |
+   = note: this index expression is a call to `Index::index`
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:174:17
+   |
+LL |         let _ = &C.0[0];
+   |                 ^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:190:17
+   |
+LL |         let _ = &C[1];
+   |                 ^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:194:21
+   |
+LL |             let _ = &C[i];
+   |                     ^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:198:17
+   |
+LL |         let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:216:17
+   |
+LL |         let _ = &S::VALUE;
+   |                 ^^^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: borrow of a named constant with interior mutability
+  --> tests/ui/borrow_interior_mutable_const.rs:218:17
+   |
+LL |         let _ = &S::VALUE.1;
+   |                 ^^^^^^^^^^^
+   |
+   = help: this lint can be silenced by assigning the value to a local variable before borrowing
+
+error: aborting due to 29 previous errors
+
diff --git a/tests/ui/borrow_interior_mutable_const/enums.rs b/tests/ui/borrow_interior_mutable_const/enums.rs
deleted file mode 100644
index da940a4cfb5..00000000000
--- a/tests/ui/borrow_interior_mutable_const/enums.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-//@aux-build:helper.rs
-
-#![deny(clippy::borrow_interior_mutable_const)]
-#![allow(clippy::declare_interior_mutable_const)]
-
-// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions.
-
-extern crate helper;
-
-use std::cell::Cell;
-use std::sync::atomic::AtomicUsize;
-
-enum OptionalCell {
-    Unfrozen(Cell<bool>),
-    Frozen,
-}
-
-const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
-const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
-
-fn borrow_optional_cell() {
-    let _ = &UNFROZEN_VARIANT; //~ ERROR: interior mutability
-    let _ = &FROZEN_VARIANT;
-}
-
-trait AssocConsts {
-    const TO_BE_UNFROZEN_VARIANT: OptionalCell;
-    const TO_BE_FROZEN_VARIANT: OptionalCell;
-
-    const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
-    const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
-
-    fn function() {
-        // This is the "suboptimal behavior" mentioned in `is_value_unfrozen`
-        // caused by a similar reason to unfrozen types without any default values
-        // get linted even if it has frozen variants'.
-        let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR: interior mutability
-
-        // The lint ignores default values because an impl of this trait can set
-        // an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`.
-        let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR: interior mutability
-    }
-}
-
-impl AssocConsts for u64 {
-    const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
-    const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
-
-    fn function() {
-        let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
-        let _ = &<Self as AssocConsts>::TO_BE_FROZEN_VARIANT;
-        let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR: interior mutability
-        let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT;
-    }
-}
-
-trait AssocTypes {
-    type ToBeUnfrozen;
-
-    const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
-    const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
-
-    // there's no need to test here because it's the exactly same as `trait::AssocTypes`
-    fn function();
-}
-
-impl AssocTypes for u64 {
-    type ToBeUnfrozen = AtomicUsize;
-
-    const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4));
-    const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
-
-    fn function() {
-        let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
-        let _ = &<Self as AssocTypes>::TO_BE_FROZEN_VARIANT;
-    }
-}
-
-enum BothOfCellAndGeneric<T> {
-    Unfrozen(Cell<*const T>),
-    Generic(*const T),
-    Frozen(usize),
-}
-
-impl<T> BothOfCellAndGeneric<T> {
-    const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
-    const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null());
-    const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
-
-    fn function() {
-        let _ = &Self::UNFROZEN_VARIANT; //~ ERROR: interior mutability
-        let _ = &Self::GENERIC_VARIANT; //~ ERROR: interior mutability
-        let _ = &Self::FROZEN_VARIANT;
-    }
-}
-
-fn main() {
-    // constants defined in foreign crates
-    let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
-    let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT;
-}
diff --git a/tests/ui/borrow_interior_mutable_const/enums.stderr b/tests/ui/borrow_interior_mutable_const/enums.stderr
deleted file mode 100644
index 43850384b90..00000000000
--- a/tests/ui/borrow_interior_mutable_const/enums.stderr
+++ /dev/null
@@ -1,79 +0,0 @@
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:22:14
-   |
-LL |     let _ = &UNFROZEN_VARIANT;
-   |              ^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-note: the lint level is defined here
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:3:9
-   |
-LL | #![deny(clippy::borrow_interior_mutable_const)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:37:18
-   |
-LL |         let _ = &Self::TO_BE_FROZEN_VARIANT;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:41:18
-   |
-LL |         let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:50:18
-   |
-LL |         let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:52:18
-   |
-LL |         let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:74:18
-   |
-LL |         let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:91:18
-   |
-LL |         let _ = &Self::UNFROZEN_VARIANT;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:92:18
-   |
-LL |         let _ = &Self::GENERIC_VARIANT;
-   |                  ^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/enums.rs:99:14
-   |
-LL |     let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT;
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: aborting due to 9 previous errors
-
diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs
deleted file mode 100644
index fa729b62d7f..00000000000
--- a/tests/ui/borrow_interior_mutable_const/others.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-#![deny(clippy::borrow_interior_mutable_const)]
-#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)]
-#![allow(const_item_mutation)]
-
-use std::borrow::Cow;
-use std::cell::{Cell, UnsafeCell};
-use std::fmt::Display;
-use std::sync::Once;
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-const ATOMIC: AtomicUsize = AtomicUsize::new(5);
-const CELL: Cell<usize> = Cell::new(6);
-const ATOMIC_TUPLE: ([AtomicUsize; 1], Option<Box<AtomicUsize>>, u8) = ([ATOMIC], None, 7);
-const INTEGER: u8 = 8;
-const STRING: String = String::new();
-const STR: &str = "012345";
-const COW: Cow<str> = Cow::Borrowed("abcdef");
-const NO_ANN: &dyn Display = &70;
-static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
-const ONCE_INIT: Once = Once::new();
-
-// This is just a pointer that can be safely dereferenced,
-// it's semantically the same as `&'static T`;
-// but it isn't allowed to make a static reference from an arbitrary integer value at the moment.
-// For more information, please see the issue #5918.
-pub struct StaticRef<T> {
-    ptr: *const T,
-}
-
-impl<T> StaticRef<T> {
-    /// Create a new `StaticRef` from a raw pointer
-    ///
-    /// ## Safety
-    ///
-    /// Callers must pass in a reference to statically allocated memory which
-    /// does not overlap with other values.
-    pub const unsafe fn new(ptr: *const T) -> StaticRef<T> {
-        StaticRef { ptr }
-    }
-}
-
-impl<T> std::ops::Deref for StaticRef<T> {
-    type Target = T;
-
-    fn deref(&self) -> &T {
-        unsafe { &*self.ptr }
-    }
-}
-
-// ICE regression test
-mod issue12979 {
-    use std::cell::UnsafeCell;
-
-    const ATOMIC_TUPLE: (Vec<UnsafeCell<u8>>, ()) = (Vec::new(), ());
-
-    fn main() {
-        let _x = &ATOMIC_TUPLE.0;
-    }
-}
-
-// use a tuple to make sure referencing a field behind a pointer isn't linted.
-const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
-
-fn main() {
-    ATOMIC.store(1, Ordering::SeqCst);
-    //~^ borrow_interior_mutable_const
-    assert_eq!(ATOMIC.load(Ordering::SeqCst), 5);
-    //~^ borrow_interior_mutable_const
-
-    let _once = ONCE_INIT;
-    let _once_ref = &ONCE_INIT;
-    //~^ borrow_interior_mutable_const
-    let _once_ref_2 = &&ONCE_INIT;
-    //~^ borrow_interior_mutable_const
-    let _once_ref_4 = &&&&ONCE_INIT;
-    //~^ borrow_interior_mutable_const
-    let _once_mut = &mut ONCE_INIT;
-    //~^ borrow_interior_mutable_const
-    let _atomic_into_inner = ATOMIC.into_inner();
-    // these should be all fine.
-    let _twice = (ONCE_INIT, ONCE_INIT);
-    let _ref_twice = &(ONCE_INIT, ONCE_INIT);
-    let _ref_once = &(ONCE_INIT, ONCE_INIT).0;
-    let _array_twice = [ONCE_INIT, ONCE_INIT];
-    let _ref_array_twice = &[ONCE_INIT, ONCE_INIT];
-    let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0];
-
-    // referencing projection is still bad.
-    let _ = &ATOMIC_TUPLE;
-    //~^ borrow_interior_mutable_const
-    let _ = &ATOMIC_TUPLE.0;
-    //~^ borrow_interior_mutable_const
-    let _ = &(&&&&ATOMIC_TUPLE).0;
-    //~^ borrow_interior_mutable_const
-    let _ = &ATOMIC_TUPLE.0[0];
-    //~^ borrow_interior_mutable_const
-    let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst);
-    //~^ borrow_interior_mutable_const
-    let _ = &ATOMIC_TUPLE.2;
-    let _ = (&&&&ATOMIC_TUPLE).0;
-    let _ = (&&&&ATOMIC_TUPLE).2;
-    let _ = ATOMIC_TUPLE.0;
-    let _ = ATOMIC_TUPLE.0[0];
-    //~^ borrow_interior_mutable_const
-    let _ = ATOMIC_TUPLE.1.into_iter();
-    let _ = ATOMIC_TUPLE.2;
-    let _ = &{ ATOMIC_TUPLE };
-
-    CELL.set(2);
-    //~^ borrow_interior_mutable_const
-    assert_eq!(CELL.get(), 6);
-    //~^ borrow_interior_mutable_const
-
-    assert_eq!(INTEGER, 8);
-    assert!(STRING.is_empty());
-
-    let a = ATOMIC;
-    a.store(4, Ordering::SeqCst);
-    assert_eq!(a.load(Ordering::SeqCst), 4);
-
-    STATIC_TUPLE.0.store(3, Ordering::SeqCst);
-    assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3);
-    assert!(STATIC_TUPLE.1.is_empty());
-
-    assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
-
-    let _ = &CELL_REF.0;
-}
diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr
deleted file mode 100644
index decea153f71..00000000000
--- a/tests/ui/borrow_interior_mutable_const/others.stderr
+++ /dev/null
@@ -1,119 +0,0 @@
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:65:5
-   |
-LL |     ATOMIC.store(1, Ordering::SeqCst);
-   |     ^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-note: the lint level is defined here
-  --> tests/ui/borrow_interior_mutable_const/others.rs:1:9
-   |
-LL | #![deny(clippy::borrow_interior_mutable_const)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:67:16
-   |
-LL |     assert_eq!(ATOMIC.load(Ordering::SeqCst), 5);
-   |                ^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:71:22
-   |
-LL |     let _once_ref = &ONCE_INIT;
-   |                      ^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:73:25
-   |
-LL |     let _once_ref_2 = &&ONCE_INIT;
-   |                         ^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:75:27
-   |
-LL |     let _once_ref_4 = &&&&ONCE_INIT;
-   |                           ^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:77:26
-   |
-LL |     let _once_mut = &mut ONCE_INIT;
-   |                          ^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:89:14
-   |
-LL |     let _ = &ATOMIC_TUPLE;
-   |              ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:91:14
-   |
-LL |     let _ = &ATOMIC_TUPLE.0;
-   |              ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:93:19
-   |
-LL |     let _ = &(&&&&ATOMIC_TUPLE).0;
-   |                   ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:95:14
-   |
-LL |     let _ = &ATOMIC_TUPLE.0[0];
-   |              ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:97:13
-   |
-LL |     let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst);
-   |             ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:103:13
-   |
-LL |     let _ = ATOMIC_TUPLE.0[0];
-   |             ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:109:5
-   |
-LL |     CELL.set(2);
-   |     ^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:111:16
-   |
-LL |     assert_eq!(CELL.get(), 6);
-   |                ^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: aborting due to 14 previous errors
-
diff --git a/tests/ui/borrow_interior_mutable_const/projections.rs b/tests/ui/borrow_interior_mutable_const/projections.rs
deleted file mode 100644
index bbe5538fbe1..00000000000
--- a/tests/ui/borrow_interior_mutable_const/projections.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-#![deny(clippy::borrow_interior_mutable_const)]
-#![deny(clippy::declare_interior_mutable_const)]
-
-// Inspired by https://github.com/rust-lang/rust/pull/130543#issuecomment-2364828139
-
-use std::cell::UnsafeCell;
-
-trait Trait {
-    type Assoc;
-}
-
-type Assoc<T> = <T as Trait>::Assoc;
-
-impl Trait for u8 {
-    type Assoc = UnsafeCell<u8>;
-}
-
-impl Trait for () {
-    type Assoc = ();
-}
-
-enum MaybeMutable {
-    Mutable(Assoc<u8>),
-    Immutable(Assoc<()>),
-}
-
-const CELL: Assoc<u8> = UnsafeCell::new(0); //~ ERROR: interior mutable
-const UNIT: Assoc<()> = ();
-const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); //~ ERROR: interior mutable
-const IMMUTABLE: MaybeMutable = MaybeMutable::Immutable(UNIT);
-
-fn print_ref<T>(t: &T) {
-    let p: *const T = t;
-    println!("{p:p}")
-}
-
-fn main() {
-    print_ref(&CELL); //~ ERROR: interior mutability
-    print_ref(&UNIT);
-    print_ref(&MUTABLE); //~ ERROR: interior mutability
-    print_ref(&IMMUTABLE);
-}
diff --git a/tests/ui/borrow_interior_mutable_const/projections.stderr b/tests/ui/borrow_interior_mutable_const/projections.stderr
deleted file mode 100644
index eabaf66560a..00000000000
--- a/tests/ui/borrow_interior_mutable_const/projections.stderr
+++ /dev/null
@@ -1,44 +0,0 @@
-error: a `const` item should not be interior mutable
-  --> tests/ui/borrow_interior_mutable_const/projections.rs:27:1
-   |
-LL | const CELL: Assoc<u8> = UnsafeCell::new(0);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
-note: the lint level is defined here
-  --> tests/ui/borrow_interior_mutable_const/projections.rs:2:9
-   |
-LL | #![deny(clippy::declare_interior_mutable_const)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/borrow_interior_mutable_const/projections.rs:29:1
-   |
-LL | const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/projections.rs:38:16
-   |
-LL |     print_ref(&CELL);
-   |                ^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-note: the lint level is defined here
-  --> tests/ui/borrow_interior_mutable_const/projections.rs:1:9
-   |
-LL | #![deny(clippy::borrow_interior_mutable_const)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/projections.rs:40:16
-   |
-LL |     print_ref(&MUTABLE);
-   |                ^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: aborting due to 4 previous errors
-
diff --git a/tests/ui/borrow_interior_mutable_const/traits.rs b/tests/ui/borrow_interior_mutable_const/traits.rs
deleted file mode 100644
index c4878dbe57b..00000000000
--- a/tests/ui/borrow_interior_mutable_const/traits.rs
+++ /dev/null
@@ -1,219 +0,0 @@
-#![deny(clippy::borrow_interior_mutable_const)]
-#![allow(clippy::declare_interior_mutable_const)]
-
-// this file replicates its `declare` counterpart. Please see it for more discussions.
-
-use std::borrow::Cow;
-use std::cell::Cell;
-use std::sync::atomic::{AtomicUsize, Ordering};
-
-trait ConcreteTypes {
-    const ATOMIC: AtomicUsize;
-    const STRING: String;
-
-    fn function() {
-        let _ = &Self::ATOMIC;
-        //~^ borrow_interior_mutable_const
-        let _ = &Self::STRING;
-    }
-}
-
-impl ConcreteTypes for u64 {
-    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
-    const STRING: String = String::new();
-
-    fn function() {
-        // Lint this again since implementers can choose not to borrow it.
-        let _ = &Self::ATOMIC;
-        //~^ borrow_interior_mutable_const
-        let _ = &Self::STRING;
-    }
-}
-
-// a helper trait used below
-trait ConstDefault {
-    const DEFAULT: Self;
-}
-
-trait GenericTypes<T, U> {
-    const TO_REMAIN_GENERIC: T;
-    const TO_BE_CONCRETE: U;
-
-    fn function() {
-        let _ = &Self::TO_REMAIN_GENERIC;
-    }
-}
-
-impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for Vec<T> {
-    const TO_REMAIN_GENERIC: T = T::DEFAULT;
-    const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
-
-    fn function() {
-        let _ = &Self::TO_REMAIN_GENERIC;
-        let _ = &Self::TO_BE_CONCRETE;
-        //~^ borrow_interior_mutable_const
-    }
-}
-
-// a helper type used below
-pub struct Wrapper<T>(T);
-
-trait AssocTypes {
-    type ToBeFrozen;
-    type ToBeUnfrozen;
-    type ToBeGenericParam;
-
-    const TO_BE_FROZEN: Self::ToBeFrozen;
-    const TO_BE_UNFROZEN: Self::ToBeUnfrozen;
-    const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>;
-    const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
-
-    fn function() {
-        let _ = &Self::TO_BE_FROZEN;
-        let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
-    }
-}
-
-impl<T: ConstDefault> AssocTypes for Vec<T> {
-    type ToBeFrozen = u16;
-    type ToBeUnfrozen = AtomicUsize;
-    type ToBeGenericParam = T;
-
-    const TO_BE_FROZEN: Self::ToBeFrozen = 12;
-    const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13);
-    const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
-    const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
-
-    fn function() {
-        let _ = &Self::TO_BE_FROZEN;
-        let _ = &Self::TO_BE_UNFROZEN;
-        //~^ borrow_interior_mutable_const
-        let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
-        //~^ borrow_interior_mutable_const
-        let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM;
-    }
-}
-
-// a helper trait used below
-trait AssocTypesHelper {
-    type NotToBeBounded;
-    type ToBeBounded;
-
-    const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
-}
-
-trait AssocTypesFromGenericParam<T>
-where
-    T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
-{
-    const NOT_BOUNDED: T::NotToBeBounded;
-    const BOUNDED: T::ToBeBounded;
-
-    fn function() {
-        let _ = &Self::NOT_BOUNDED;
-        let _ = &Self::BOUNDED;
-        //~^ borrow_interior_mutable_const
-    }
-}
-
-impl<T> AssocTypesFromGenericParam<T> for Vec<T>
-where
-    T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
-{
-    const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
-    const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
-
-    fn function() {
-        let _ = &Self::NOT_BOUNDED;
-        let _ = &Self::BOUNDED;
-        //~^ borrow_interior_mutable_const
-    }
-}
-
-trait SelfType: Sized {
-    const SELF: Self;
-    const WRAPPED_SELF: Option<Self>;
-
-    fn function() {
-        let _ = &Self::SELF;
-        let _ = &Self::WRAPPED_SELF;
-    }
-}
-
-impl SelfType for u64 {
-    const SELF: Self = 16;
-    const WRAPPED_SELF: Option<Self> = Some(20);
-
-    fn function() {
-        let _ = &Self::SELF;
-        let _ = &Self::WRAPPED_SELF;
-    }
-}
-
-impl SelfType for AtomicUsize {
-    const SELF: Self = AtomicUsize::new(17);
-    const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
-
-    fn function() {
-        let _ = &Self::SELF;
-        //~^ borrow_interior_mutable_const
-        let _ = &Self::WRAPPED_SELF;
-        //~^ borrow_interior_mutable_const
-    }
-}
-
-trait BothOfCellAndGeneric<T> {
-    const DIRECT: Cell<T>;
-    const INDIRECT: Cell<*const T>;
-
-    fn function() {
-        let _ = &Self::DIRECT;
-        //~^ borrow_interior_mutable_const
-        let _ = &Self::INDIRECT;
-        //~^ borrow_interior_mutable_const
-    }
-}
-
-impl<T: ConstDefault> BothOfCellAndGeneric<T> for Vec<T> {
-    const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
-    const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
-
-    fn function() {
-        let _ = &Self::DIRECT;
-        //~^ borrow_interior_mutable_const
-        let _ = &Self::INDIRECT;
-        //~^ borrow_interior_mutable_const
-    }
-}
-
-struct Local<T>(T);
-
-impl<T> Local<T>
-where
-    T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
-{
-    const ATOMIC: AtomicUsize = AtomicUsize::new(18);
-    const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
-
-    const GENERIC_TYPE: T = T::DEFAULT;
-
-    const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
-    const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
-
-    fn function() {
-        let _ = &Self::ATOMIC;
-        //~^ borrow_interior_mutable_const
-        let _ = &Self::COW;
-        let _ = &Self::GENERIC_TYPE;
-        let _ = &Self::ASSOC_TYPE;
-        let _ = &Self::BOUNDED_ASSOC_TYPE;
-        //~^ borrow_interior_mutable_const
-    }
-}
-
-fn main() {
-    u64::ATOMIC.store(5, Ordering::SeqCst);
-    //~^ borrow_interior_mutable_const
-    assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9);
-    //~^ borrow_interior_mutable_const
-}
diff --git a/tests/ui/borrow_interior_mutable_const/traits.stderr b/tests/ui/borrow_interior_mutable_const/traits.stderr
deleted file mode 100644
index cad68ca9260..00000000000
--- a/tests/ui/borrow_interior_mutable_const/traits.stderr
+++ /dev/null
@@ -1,143 +0,0 @@
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:15:18
-   |
-LL |         let _ = &Self::ATOMIC;
-   |                  ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-note: the lint level is defined here
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:1:9
-   |
-LL | #![deny(clippy::borrow_interior_mutable_const)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:27:18
-   |
-LL |         let _ = &Self::ATOMIC;
-   |                  ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:53:18
-   |
-LL |         let _ = &Self::TO_BE_CONCRETE;
-   |                  ^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:89:18
-   |
-LL |         let _ = &Self::TO_BE_UNFROZEN;
-   |                  ^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:91:18
-   |
-LL |         let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:114:18
-   |
-LL |         let _ = &Self::BOUNDED;
-   |                  ^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:128:18
-   |
-LL |         let _ = &Self::BOUNDED;
-   |                  ^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:158:18
-   |
-LL |         let _ = &Self::SELF;
-   |                  ^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:160:18
-   |
-LL |         let _ = &Self::WRAPPED_SELF;
-   |                  ^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:170:18
-   |
-LL |         let _ = &Self::DIRECT;
-   |                  ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:172:18
-   |
-LL |         let _ = &Self::INDIRECT;
-   |                  ^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:182:18
-   |
-LL |         let _ = &Self::DIRECT;
-   |                  ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:184:18
-   |
-LL |         let _ = &Self::INDIRECT;
-   |                  ^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:204:18
-   |
-LL |         let _ = &Self::ATOMIC;
-   |                  ^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:209:18
-   |
-LL |         let _ = &Self::BOUNDED_ASSOC_TYPE;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:215:5
-   |
-LL |     u64::ATOMIC.store(5, Ordering::SeqCst);
-   |     ^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/traits.rs:217:16
-   |
-LL |     assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9);
-   |                ^^^^^^^^^^^
-   |
-   = help: assign this const to a local or static variable, and use the variable here
-
-error: aborting due to 17 previous errors
-
diff --git a/tests/ui/crashes/ice-12979.1.fixed b/tests/ui/crashes/ice-12979.1.fixed
new file mode 100644
index 00000000000..e68f1c20a8e
--- /dev/null
+++ b/tests/ui/crashes/ice-12979.1.fixed
@@ -0,0 +1,2 @@
+#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr
+const FOO: u8 = 0;
diff --git a/tests/ui/crashes/ice-12979.2.fixed b/tests/ui/crashes/ice-12979.2.fixed
new file mode 100644
index 00000000000..e89fa636d4b
--- /dev/null
+++ b/tests/ui/crashes/ice-12979.2.fixed
@@ -0,0 +1,3 @@
+#![deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr
+
+const FOO: u8 = 0;
diff --git a/tests/ui/crashes/ice-12979.rs b/tests/ui/crashes/ice-12979.rs
new file mode 100644
index 00000000000..a2787291d9e
--- /dev/null
+++ b/tests/ui/crashes/ice-12979.rs
@@ -0,0 +1,3 @@
+#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr
+
+const FOO: u8 = 0;
diff --git a/tests/ui/crashes/ice-12979.stderr b/tests/ui/crashes/ice-12979.stderr
new file mode 100644
index 00000000000..5e760816164
--- /dev/null
+++ b/tests/ui/crashes/ice-12979.stderr
@@ -0,0 +1,19 @@
+error: empty line after outer attribute
+  --> tests/ui/crashes/ice-12979.rs:1:1
+   |
+LL | / #[deny(clippy::declare_interior_mutable_const)]
+LL | |
+   | |_^
+LL |   const FOO: u8 = 0;
+   |   --------- the attribute applies to this constant item
+   |
+   = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
+   = help: if the empty line is unintentional, remove it
+help: if the attribute should apply to the crate use an inner attribute
+   |
+LL | #![deny(clippy::declare_interior_mutable_const)]
+   |  +
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/crashes/ice-9445.rs b/tests/ui/crashes/ice-9445.rs
deleted file mode 100644
index 232b8e4a795..00000000000
--- a/tests/ui/crashes/ice-9445.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit();
-//~^ declare_interior_mutable_const
-
-fn main() {}
diff --git a/tests/ui/crashes/ice-9445.stderr b/tests/ui/crashes/ice-9445.stderr
deleted file mode 100644
index 76689cd6f5c..00000000000
--- a/tests/ui/crashes/ice-9445.stderr
+++ /dev/null
@@ -1,12 +0,0 @@
-error: a `const` item should not be interior mutable
-  --> tests/ui/crashes/ice-9445.rs:1:1
-   |
-LL | const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit();
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
-   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/declare_interior_mutable_const.rs b/tests/ui/declare_interior_mutable_const.rs
new file mode 100644
index 00000000000..c65df275038
--- /dev/null
+++ b/tests/ui/declare_interior_mutable_const.rs
@@ -0,0 +1,200 @@
+#![deny(clippy::declare_interior_mutable_const)]
+#![allow(clippy::missing_const_for_thread_local)]
+
+use core::cell::{Cell, RefCell, UnsafeCell};
+use core::mem::{ManuallyDrop, MaybeUninit};
+use core::ptr;
+use core::sync::atomic::AtomicUsize;
+
+fn main() {}
+
+const _: Cell<u32> = Cell::new(0);
+const UNSAFE_CELL: UnsafeCell<u32> = UnsafeCell::new(0); //~ declare_interior_mutable_const
+const REF_CELL: RefCell<u32> = RefCell::new(0); //~ declare_interior_mutable_const
+const CELL: Cell<u32> = Cell::new(0); //~ declare_interior_mutable_const
+
+// Constants can't contain pointers or references to type with interior mutability.
+const fn make_ptr() -> *const Cell<u32> {
+    ptr::null()
+}
+const PTR: *const Cell<u32> = make_ptr();
+
+const fn casted_to_cell_ptr() -> *const Cell<u32> {
+    const VALUE: u32 = 0;
+    &VALUE as *const _ as *const Cell<u32>
+}
+const TRANSMUTED_PTR: *const Cell<u32> = casted_to_cell_ptr();
+
+const CELL_TUPLE: (bool, Cell<u32>) = (true, Cell::new(0)); //~ declare_interior_mutable_const
+const CELL_ARRAY: [Cell<u32>; 2] = [Cell::new(0), Cell::new(0)]; //~ declare_interior_mutable_const
+
+const UNINIT_CELL: MaybeUninit<Cell<&'static ()>> = MaybeUninit::uninit();
+
+struct CellStruct {
+    x: u32,
+    cell: Cell<u32>,
+}
+//~v declare_interior_mutable_const
+const CELL_STRUCT: CellStruct = CellStruct {
+    x: 0,
+    cell: Cell::new(0),
+};
+
+enum CellEnum {
+    Cell(Cell<u32>),
+}
+const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); //~ declare_interior_mutable_const
+
+const NONE_CELL: Option<Cell<u32>> = None;
+const SOME_CELL: Option<Cell<u32>> = Some(Cell::new(0)); //~ declare_interior_mutable_const
+
+struct NestedCell([(Option<Cell<u32>>,); 1]);
+const NONE_NESTED_CELL: NestedCell = NestedCell([(None,)]);
+const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); //~ declare_interior_mutable_const
+
+union UnionCell {
+    cell: ManuallyDrop<Cell<u32>>,
+    x: u32,
+}
+//~v declare_interior_mutable_const
+const UNION_CELL: UnionCell = UnionCell {
+    cell: ManuallyDrop::new(Cell::new(0)),
+};
+// Access to either union field is valid so we have to be conservative here.
+const UNION_U32: UnionCell = UnionCell { x: 0 }; //~ declare_interior_mutable_const
+
+struct Assoc;
+impl Assoc {
+    const SELF: Self = Self;
+    const CELL: Cell<u32> = Cell::new(0); //~ declare_interior_mutable_const
+}
+
+struct AssocCell(Cell<u32>);
+impl AssocCell {
+    const SELF: Self = Self(Cell::new(0)); //~ declare_interior_mutable_const
+    const NONE_SELF: Option<Self> = None;
+    const SOME_SELF: Option<Self> = Some(Self(Cell::new(0))); //~ declare_interior_mutable_const
+}
+
+trait ConstDefault {
+    // May or may not be `Freeze`
+    const DEFAULT: Self;
+}
+impl ConstDefault for u32 {
+    const DEFAULT: Self = 0;
+}
+impl<T: ConstDefault> ConstDefault for Cell<T> {
+    // Interior mutability is forced by the trait.
+    const DEFAULT: Self = Cell::new(T::DEFAULT);
+}
+impl<T: ConstDefault> ConstDefault for Option<Cell<T>> {
+    // Could have been `None`
+    const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const
+}
+
+enum GenericEnumCell<T> {
+    Cell(Cell<T>),
+    Other(T),
+}
+impl<T: ConstDefault> ConstDefault for GenericEnumCell<T> {
+    const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const
+}
+impl<T: ConstDefault> GenericEnumCell<T> {
+    const CELL: Self = Self::DEFAULT; //~ declare_interior_mutable_const
+    const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); //~ declare_interior_mutable_const
+    const OTHER: Self = Self::Other(T::DEFAULT);
+    const FROM_OTHER: Self = Self::OTHER;
+}
+
+enum GenericNestedEnumCell<T> {
+    GenericEnumCell(GenericEnumCell<T>),
+    EnumCell(GenericEnumCell<u32>),
+    Other(T),
+}
+impl<T: ConstDefault> GenericNestedEnumCell<T> {
+    const GENERIC_OTHER: Self = Self::GenericEnumCell(GenericEnumCell::<T>::FROM_OTHER);
+    const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::<T>::CELL); //~ declare_interior_mutable_const
+    const ENUM_OTHER: Self = Self::EnumCell(GenericEnumCell::<u32>::FROM_OTHER);
+    const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::<u32>::CELL); //~ declare_interior_mutable_const
+}
+
+trait CellTrait: ConstDefault + Sized {
+    // Must be non-`Freeze` due to the type
+    const CELL: Cell<Self>; //~ declare_interior_mutable_const
+    // May be non-`Freeze`, but may not be
+    const OPTION_CELL: Option<Cell<Self>>;
+    // May get redefined by the impl, but the default is non-`Freeze`.
+    const SOME_CELL: Option<Cell<Self>> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const
+    // May get redefined by the impl, but the default is `Freeze`.
+    const NONE_CELL: Option<Cell<Self>> = None;
+}
+
+trait CellWithAssoc {
+    type T;
+    const DEFAULT: Self::T;
+    // Must be non-`Freeze` due to the type
+    const CELL: Cell<Self::T>; //~ declare_interior_mutable_const
+    // May be non-`Freeze`, but may not be
+    const OPTION_CELL: Option<Cell<Self::T>>;
+    // May get redefined by the impl, but the default is non-`Freeze`.
+    const SOME_CELL: Option<Cell<Self::T>> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const
+    // May get redefined by the impl, but the default is `Freeze`.
+    const NONE_CELL: Option<Cell<Self::T>> = None;
+}
+
+impl CellWithAssoc for () {
+    type T = u32;
+    const DEFAULT: Self::T = 0;
+    const CELL: Cell<Self::T> = Cell::new(0);
+    const OPTION_CELL: Option<Cell<Self::T>> = None;
+}
+
+trait WithAssoc {
+    type T;
+    const VALUE: Self::T;
+}
+
+impl WithAssoc for u32 {
+    type T = Cell<u32>;
+    // The cell comes from the impl block, not the trait.
+    const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const
+}
+
+trait WithLayeredAssoc {
+    type T: WithAssoc;
+    const VALUE: <Self::T as WithAssoc>::T;
+}
+
+impl WithLayeredAssoc for u32 {
+    type T = u32;
+    // The cell comes from the impl block, not the trait.
+    const VALUE: <Self::T as WithAssoc>::T = Cell::new(0); //~ declare_interior_mutable_const
+}
+
+trait WithGenericAssoc {
+    type T<U>;
+    const VALUE: Self::T<u32>;
+}
+
+impl WithGenericAssoc for u32 {
+    type T<U> = Cell<U>;
+    const VALUE: Self::T<u32> = Cell::new(0); //~ declare_interior_mutable_const
+}
+
+trait WithGenericAssocCell {
+    type T<U>;
+    const VALUE: Self::T<Cell<u32>>;
+}
+
+impl WithGenericAssocCell for u32 {
+    type T<U> = Option<U>;
+    const VALUE: Self::T<Cell<u32>> = None;
+}
+
+impl WithGenericAssocCell for i32 {
+    type T<U> = Option<U>;
+    const VALUE: Self::T<Cell<u32>> = Some(Cell::new(0)); //~ declare_interior_mutable_const
+}
+
+thread_local!(static THREAD_LOCAL_CELL: Cell<u32> = const { Cell::new(0) });
+thread_local!(static THREAD_LOCAL_CELL2: Cell<u32> = Cell::new(0));
diff --git a/tests/ui/declare_interior_mutable_const.stderr b/tests/ui/declare_interior_mutable_const.stderr
new file mode 100644
index 00000000000..9742c17486c
--- /dev/null
+++ b/tests/ui/declare_interior_mutable_const.stderr
@@ -0,0 +1,197 @@
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:12:7
+   |
+LL | const UNSAFE_CELL: UnsafeCell<u32> = UnsafeCell::new(0);
+   |       ^^^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+note: the lint level is defined here
+  --> tests/ui/declare_interior_mutable_const.rs:1:9
+   |
+LL | #![deny(clippy::declare_interior_mutable_const)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:13:7
+   |
+LL | const REF_CELL: RefCell<u32> = RefCell::new(0);
+   |       ^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:14:7
+   |
+LL | const CELL: Cell<u32> = Cell::new(0);
+   |       ^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:28:7
+   |
+LL | const CELL_TUPLE: (bool, Cell<u32>) = (true, Cell::new(0));
+   |       ^^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:29:7
+   |
+LL | const CELL_ARRAY: [Cell<u32>; 2] = [Cell::new(0), Cell::new(0)];
+   |       ^^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:38:7
+   |
+LL | const CELL_STRUCT: CellStruct = CellStruct {
+   |       ^^^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:46:7
+   |
+LL | const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0));
+   |       ^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:49:7
+   |
+LL | const SOME_CELL: Option<Cell<u32>> = Some(Cell::new(0));
+   |       ^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:53:7
+   |
+LL | const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]);
+   |       ^^^^^^^^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:60:7
+   |
+LL | const UNION_CELL: UnionCell = UnionCell {
+   |       ^^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:64:7
+   |
+LL | const UNION_U32: UnionCell = UnionCell { x: 0 };
+   |       ^^^^^^^^^
+   |
+   = help: did you mean to make this a `thread_local!` item
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:69:11
+   |
+LL |     const CELL: Cell<u32> = Cell::new(0);
+   |           ^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:74:11
+   |
+LL |     const SELF: Self = Self(Cell::new(0));
+   |           ^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:76:11
+   |
+LL |     const SOME_SELF: Option<Self> = Some(Self(Cell::new(0)));
+   |           ^^^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:92:11
+   |
+LL |     const DEFAULT: Self = Some(Cell::new(T::DEFAULT));
+   |           ^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:100:11
+   |
+LL |     const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT));
+   |           ^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:103:11
+   |
+LL |     const CELL: Self = Self::DEFAULT;
+   |           ^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:104:11
+   |
+LL |     const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT);
+   |           ^^^^^^^^^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:116:11
+   |
+LL |     const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::<T>::CELL);
+   |           ^^^^^^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:118:11
+   |
+LL |     const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::<u32>::CELL);
+   |           ^^^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:123:11
+   |
+LL |     const CELL: Cell<Self>;
+   |           ^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:127:11
+   |
+LL |     const SOME_CELL: Option<Cell<Self>> = Some(Cell::new(Self::DEFAULT));
+   |           ^^^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:136:11
+   |
+LL |     const CELL: Cell<Self::T>;
+   |           ^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:140:11
+   |
+LL |     const SOME_CELL: Option<Cell<Self::T>> = Some(Cell::new(Self::DEFAULT));
+   |           ^^^^^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:160:11
+   |
+LL |     const VALUE: Self::T = Cell::new(0);
+   |           ^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:171:11
+   |
+LL |     const VALUE: <Self::T as WithAssoc>::T = Cell::new(0);
+   |           ^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:181:11
+   |
+LL |     const VALUE: Self::T<u32> = Cell::new(0);
+   |           ^^^^^
+
+error: named constant with interior mutability
+  --> tests/ui/declare_interior_mutable_const.rs:196:11
+   |
+LL |     const VALUE: Self::T<Cell<u32>> = Some(Cell::new(0));
+   |           ^^^^^
+
+error: aborting due to 28 previous errors
+
diff --git a/tests/ui/declare_interior_mutable_const/enums.rs b/tests/ui/declare_interior_mutable_const/enums.rs
deleted file mode 100644
index c87468277fb..00000000000
--- a/tests/ui/declare_interior_mutable_const/enums.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-#![warn(clippy::declare_interior_mutable_const)]
-
-use std::cell::Cell;
-use std::sync::atomic::AtomicUsize;
-
-enum OptionalCell {
-    Unfrozen(Cell<bool>),
-    Frozen,
-}
-
-// a constant with enums should be linted only when the used variant is unfrozen (#3962).
-const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
-//~^ declare_interior_mutable_const
-const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
-
-const fn unfrozen_variant() -> OptionalCell {
-    OptionalCell::Unfrozen(Cell::new(false))
-}
-
-const fn frozen_variant() -> OptionalCell {
-    OptionalCell::Frozen
-}
-
-const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant();
-//~^ declare_interior_mutable_const
-const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant();
-
-enum NestedInnermost {
-    Unfrozen(AtomicUsize),
-    Frozen,
-}
-
-struct NestedInner {
-    inner: NestedInnermost,
-}
-
-enum NestedOuter {
-    NestedInner(NestedInner),
-    NotNested(usize),
-}
-
-struct NestedOutermost {
-    outer: NestedOuter,
-}
-
-// a constant with enums should be linted according to its value, no matter how structs involve.
-const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
-    //~^ declare_interior_mutable_const
-    outer: NestedOuter::NestedInner(NestedInner {
-        inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
-    }),
-};
-const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost {
-    outer: NestedOuter::NestedInner(NestedInner {
-        inner: NestedInnermost::Frozen,
-    }),
-};
-
-trait AssocConsts {
-    // When there's no default value, lint it only according to its type.
-    // Further details are on the corresponding code (`NonCopyConst::check_trait_item`).
-    const TO_BE_UNFROZEN_VARIANT: OptionalCell;
-    //~^ declare_interior_mutable_const
-    const TO_BE_FROZEN_VARIANT: OptionalCell;
-    //~^ declare_interior_mutable_const
-
-    // Lint default values accordingly.
-    const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
-    //~^ declare_interior_mutable_const
-    const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
-}
-
-// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it
-// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'.
-impl AssocConsts for u64 {
-    const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
-    const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
-
-    // even if this sets an unfrozen variant, the lint ignores it.
-    const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
-}
-
-// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters
-// here are values; and I think substituted generics at definitions won't appear in MIR.
-trait AssocTypes {
-    type ToBeUnfrozen;
-
-    const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
-    const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
-}
-
-impl AssocTypes for u64 {
-    type ToBeUnfrozen = AtomicUsize;
-
-    const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4));
-    //~^ declare_interior_mutable_const
-    const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
-}
-
-// Use raw pointers since direct generics have a false negative at the type level.
-enum BothOfCellAndGeneric<T> {
-    Unfrozen(Cell<*const T>),
-    Generic(*const T),
-    Frozen(usize),
-}
-
-impl<T> BothOfCellAndGeneric<T> {
-    const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
-    //~^ declare_interior_mutable_const
-
-    // This is a false positive. The argument about this is on `is_value_unfrozen_raw`
-    const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null());
-    //~^ declare_interior_mutable_const
-
-    const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
-
-    // This is what is likely to be a false negative when one tries to fix
-    // the `GENERIC_VARIANT` false positive.
-    const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null());
-    //~^ declare_interior_mutable_const
-}
-
-// associated types here is basically the same as the one above.
-trait BothOfCellAndGenericWithAssocType {
-    type AssocType;
-
-    const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
-        //~^ declare_interior_mutable_const
-        BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
-    const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null());
-    //~^ declare_interior_mutable_const
-    const FROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Frozen(5);
-}
-
-fn main() {}
diff --git a/tests/ui/declare_interior_mutable_const/enums.stderr b/tests/ui/declare_interior_mutable_const/enums.stderr
deleted file mode 100644
index 32839d14f0e..00000000000
--- a/tests/ui/declare_interior_mutable_const/enums.stderr
+++ /dev/null
@@ -1,89 +0,0 @@
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:12:1
-   |
-LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
-   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:24:1
-   |
-LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant();
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:47:1
-   |
-LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
-LL | |
-LL | |     outer: NestedOuter::NestedInner(NestedInner {
-LL | |         inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
-LL | |     }),
-LL | | };
-   | |__^
-   |
-   = help: consider making this a static item
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:62:5
-   |
-LL |     const TO_BE_UNFROZEN_VARIANT: OptionalCell;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:64:5
-   |
-LL |     const TO_BE_FROZEN_VARIANT: OptionalCell;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:68:5
-   |
-LL |     const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:95:5
-   |
-LL |     const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:108:5
-   |
-LL |     const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:112:5
-   |
-LL |     const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:119:5
-   |
-LL |     const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:127:5
-   |
-LL | /     const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
-LL | |
-LL | |         BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
-   | |____________________________________________________________________^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/enums.rs:130:5
-   |
-LL |     const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 12 previous errors
-
diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs
deleted file mode 100644
index 7ce04a3f2c3..00000000000
--- a/tests/ui/declare_interior_mutable_const/others.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-#![warn(clippy::declare_interior_mutable_const)]
-
-use std::borrow::Cow;
-use std::cell::Cell;
-use std::fmt::Display;
-use std::ptr;
-use std::sync::Once;
-use std::sync::atomic::AtomicUsize;
-
-const ATOMIC: AtomicUsize = AtomicUsize::new(5);
-//~^ declare_interior_mutable_const
-const CELL: Cell<usize> = Cell::new(6);
-//~^ declare_interior_mutable_const
-const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
-//~^ declare_interior_mutable_const
-
-macro_rules! declare_const {
-    ($name:ident: $ty:ty = $e:expr) => {
-        const $name: $ty = $e;
-        //~^ declare_interior_mutable_const
-    };
-}
-declare_const!(_ONCE: Once = Once::new());
-
-// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492.
-
-const INTEGER: u8 = 8;
-const STRING: String = String::new();
-const STR: &str = "012345";
-const COW: Cow<str> = Cow::Borrowed("abcdef");
-// note: a const item of Cow is used in the `postgres` package.
-
-const NO_ANN: &dyn Display = &70;
-
-static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
-// there should be no lints on the line above line
-
-mod issue_8493 {
-    use std::cell::Cell;
-
-    thread_local! {
-        static _BAR: Cell<i32> = const { Cell::new(0) };
-    }
-
-    macro_rules! issue_8493 {
-        () => {
-            const _BAZ: Cell<usize> = Cell::new(0);
-            //~^ declare_interior_mutable_const
-            static _FOOBAR: () = {
-                thread_local! {
-                    static _VAR: Cell<i32> = const { Cell::new(0) };
-                }
-            };
-        };
-    }
-
-    issue_8493!();
-}
-
-#[repr(C, align(8))]
-struct NoAtomic(usize);
-#[repr(C, align(8))]
-struct WithAtomic(AtomicUsize);
-
-const fn with_non_null() -> *const WithAtomic {
-    const NO_ATOMIC: NoAtomic = NoAtomic(0);
-    (&NO_ATOMIC as *const NoAtomic).cast()
-}
-const WITH_ATOMIC: *const WithAtomic = with_non_null();
-
-struct Generic<T>(T);
-impl<T> Generic<T> {
-    const RAW_POINTER: *const Cell<T> = ptr::null();
-}
-
-fn main() {}
diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr
deleted file mode 100644
index 09299b29041..00000000000
--- a/tests/ui/declare_interior_mutable_const/others.stderr
+++ /dev/null
@@ -1,50 +0,0 @@
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:10:1
-   |
-LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider making this a static item
-   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:12:1
-   |
-LL | const CELL: Cell<usize> = Cell::new(6);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:14:1
-   |
-LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider making this a static item
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:19:9
-   |
-LL |         const $name: $ty = $e;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | declare_const!(_ONCE: Once = Once::new());
-   | ----------------------------------------- in this macro invocation
-   |
-   = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:47:13
-   |
-LL |             const _BAZ: Cell<usize> = Cell::new(0);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL |     issue_8493!();
-   |     ------------- in this macro invocation
-   |
-   = note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 5 previous errors
-
diff --git a/tests/ui/declare_interior_mutable_const/traits.rs b/tests/ui/declare_interior_mutable_const/traits.rs
deleted file mode 100644
index d3139be6859..00000000000
--- a/tests/ui/declare_interior_mutable_const/traits.rs
+++ /dev/null
@@ -1,162 +0,0 @@
-#![warn(clippy::declare_interior_mutable_const)]
-
-use std::borrow::Cow;
-use std::cell::Cell;
-use std::sync::atomic::AtomicUsize;
-
-macro_rules! declare_const {
-    ($name:ident: $ty:ty = $e:expr) => {
-        const $name: $ty = $e;
-        //~^ declare_interior_mutable_const
-    };
-}
-
-// a constant whose type is a concrete type should be linted at the definition site.
-trait ConcreteTypes {
-    const ATOMIC: AtomicUsize;
-    //~^ declare_interior_mutable_const
-    const INTEGER: u64;
-    const STRING: String;
-    declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC);
-}
-
-impl ConcreteTypes for u64 {
-    const ATOMIC: AtomicUsize = AtomicUsize::new(9);
-    const INTEGER: u64 = 10;
-    const STRING: String = String::new();
-}
-
-// a helper trait used below
-trait ConstDefault {
-    const DEFAULT: Self;
-}
-
-// a constant whose type is a generic type should be linted at the implementation site.
-trait GenericTypes<T, U> {
-    const TO_REMAIN_GENERIC: T;
-    const TO_BE_CONCRETE: U;
-
-    const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC;
-    declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC);
-}
-
-impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for u64 {
-    const TO_REMAIN_GENERIC: T = T::DEFAULT;
-    const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
-    //~^ declare_interior_mutable_const
-}
-
-// a helper type used below
-struct Wrapper<T>(T);
-
-// a constant whose type is an associated type should be linted at the implementation site, too.
-trait AssocTypes {
-    type ToBeFrozen;
-    type ToBeUnfrozen;
-    type ToBeGenericParam;
-
-    const TO_BE_FROZEN: Self::ToBeFrozen;
-    const TO_BE_UNFROZEN: Self::ToBeUnfrozen;
-    const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>;
-    // to ensure it can handle things when a generic type remains after normalization.
-    const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
-}
-
-impl<T: ConstDefault> AssocTypes for Vec<T> {
-    type ToBeFrozen = u16;
-    type ToBeUnfrozen = AtomicUsize;
-    type ToBeGenericParam = T;
-
-    const TO_BE_FROZEN: Self::ToBeFrozen = 12;
-    const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13);
-    //~^ declare_interior_mutable_const
-    const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
-    //~^ declare_interior_mutable_const
-    const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
-}
-
-// a helper trait used below
-trait AssocTypesHelper {
-    type NotToBeBounded;
-    type ToBeBounded;
-
-    const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
-}
-
-// a constant whose type is an assoc type originated from a generic param bounded at the definition
-// site should be linted at there.
-trait AssocTypesFromGenericParam<T>
-where
-    T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
-{
-    const NOT_BOUNDED: T::NotToBeBounded;
-    const BOUNDED: T::ToBeBounded;
-    //~^ declare_interior_mutable_const
-}
-
-impl<T> AssocTypesFromGenericParam<T> for u64
-where
-    T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
-{
-    // an associated type could remain unknown in a trait impl.
-    const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
-    const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
-}
-
-// a constant whose type is `Self` should be linted at the implementation site as well.
-// (`Option` requires `Sized` bound.)
-trait SelfType: Sized {
-    const SELF: Self;
-    // this was the one in the original issue (#5050).
-    const WRAPPED_SELF: Option<Self>;
-}
-
-impl SelfType for u64 {
-    const SELF: Self = 16;
-    const WRAPPED_SELF: Option<Self> = Some(20);
-}
-
-impl SelfType for AtomicUsize {
-    // this (interior mutable `Self` const) exists in `parking_lot`.
-    // `const_trait_impl` will replace it in the future, hopefully.
-    const SELF: Self = AtomicUsize::new(17);
-    //~^ declare_interior_mutable_const
-    const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
-    //~^ declare_interior_mutable_const
-}
-
-// Even though a constant contains a generic type, if it also have an interior mutable type,
-// it should be linted at the definition site.
-trait BothOfCellAndGeneric<T> {
-    const DIRECT: Cell<T>;
-    //~^ declare_interior_mutable_const
-    const INDIRECT: Cell<*const T>;
-    //~^ declare_interior_mutable_const
-}
-
-impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 {
-    const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
-    //~^ declare_interior_mutable_const
-    const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
-}
-
-struct Local<T>(T);
-
-// a constant in an inherent impl are essentially the same as a normal const item
-// except there can be a generic or associated type.
-impl<T> Local<T>
-where
-    T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
-{
-    const ATOMIC: AtomicUsize = AtomicUsize::new(18);
-    //~^ declare_interior_mutable_const
-    const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
-
-    const GENERIC_TYPE: T = T::DEFAULT;
-
-    const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
-    const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
-    //~^ declare_interior_mutable_const
-}
-
-fn main() {}
diff --git a/tests/ui/declare_interior_mutable_const/traits.stderr b/tests/ui/declare_interior_mutable_const/traits.stderr
deleted file mode 100644
index b03dd7a0840..00000000000
--- a/tests/ui/declare_interior_mutable_const/traits.stderr
+++ /dev/null
@@ -1,88 +0,0 @@
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:16:5
-   |
-LL |     const ATOMIC: AtomicUsize;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:9:9
-   |
-LL |         const $name: $ty = $e;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
-...
-LL |     declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC);
-   |     ---------------------------------------------------------- in this macro invocation
-   |
-   = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:45:5
-   |
-LL |     const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:71:5
-   |
-LL |     const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:73:5
-   |
-LL |     const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:93:5
-   |
-LL |     const BOUNDED: T::ToBeBounded;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:122:5
-   |
-LL |     const SELF: Self = AtomicUsize::new(17);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:124:5
-   |
-LL |     const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:131:5
-   |
-LL |     const DIRECT: Cell<T>;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:133:5
-   |
-LL |     const INDIRECT: Cell<*const T>;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:138:5
-   |
-LL |     const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:151:5
-   |
-LL |     const ATOMIC: AtomicUsize = AtomicUsize::new(18);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/traits.rs:158:5
-   |
-LL |     const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 13 previous errors
-