1919
2020use clippy_config:: Conf ;
2121use clippy_utils:: consts:: { ConstEvalCtxt , Constant } ;
22- use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help , span_lint_and_then} ;
22+ use clippy_utils:: diagnostics:: { span_lint, span_lint_and_then} ;
2323use clippy_utils:: macros:: macro_backtrace;
2424use clippy_utils:: ty:: { get_field_idx_by_name, implements_trait} ;
2525use clippy_utils:: { def_path_def_ids, in_constant} ;
@@ -41,35 +41,36 @@ use std::collections::hash_map::Entry;
4141
4242declare_clippy_lint ! {
4343 /// ### What it does
44- /// Checks for declaration of `const` items which is interior
45- /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
44+ /// Checks for the declaration of named constant which contain interior mutability.
4645 ///
4746 /// ### Why is this bad?
48- /// Consts are copied everywhere they are referenced, i.e.,
49- /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
50- /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
51- /// these types in the first place.
47+ /// Named constants are copied at every use site which means any change to their value
48+ /// will be lost after the newly created value is dropped. e.g.
5249 ///
53- /// The `const` should better be replaced by a `static` item if a global
54- /// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
50+ /// ```rust
51+ /// use core::sync::atomic::{AtomicUsize, Ordering};
52+ /// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
53+ /// fn add_one() -> usize {
54+ /// // This will always return `0` since `ATOMIC` is copied before it's used.
55+ /// ATOMIC.fetch_add(1, Ordering::AcqRel)
56+ /// }
57+ /// ```
5558 ///
56- /// ### Known problems
57- /// A "non-constant" const item is a legacy way to supply an
58- /// initialized value to downstream `static` items (e.g., the
59- /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
60- /// and this lint should be suppressed.
59+ /// If shared modification of the value is desired, a `static` item is needed instead.
60+ /// If that is not desired, a `const fn` constructor should be used to make it obvious
61+ /// at the use site that a new value is created.
6162 ///
62- /// Even though the lint avoids triggering on a constant whose type has enums that have variants
63- /// with interior mutability, and its value uses non interior mutable variants (see
64- /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and
65- /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);
66- /// it complains about associated constants without default values only based on its types;
67- /// which might not be preferable.
68- /// There're other enums plus associated constants cases that the lint cannot handle.
63+ /// ### Known problems
64+ /// Prior to `const fn` stabilization this was the only way to provide a value which
65+ /// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In
66+ /// this case the use of `const` is required and this lint should be suppressed.
6967 ///
70- /// Types that have underlying or potential interior mutability trigger the lint whether
71- /// the interior mutable field is used or not. See issue
72- /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812)
68+ /// There also exists types which contain private fields with interior mutability, but
69+ /// no way to both create a value as a constant and modify any mutable field using the
70+ /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
71+ /// scan a crate's interface to see if this is the case, all such types will be linted.
72+ /// If this happens use the `ignore-interior-mutability` configuration option to allow
73+ /// the type.
7374 ///
7475 /// ### Example
7576 /// ```no_run
@@ -95,26 +96,42 @@ declare_clippy_lint! {
9596
9697declare_clippy_lint ! {
9798 /// ### What it does
98- /// Checks if `const` items which is interior mutable (e.g.,
99- /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
99+ /// Checks for a borrow of a named constant with interior mutability.
100100 ///
101101 /// ### Why is this bad?
102- /// Consts are copied everywhere they are referenced, i.e.,
103- /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
104- /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
105- /// these types in the first place.
102+ /// Named constants are copied at every use site which means any change to their value
103+ /// will be lost after the newly created value is dropped. e.g.
106104 ///
107- /// The `const` value should be stored inside a `static` item.
105+ /// ```rust
106+ /// use core::sync::atomic::{AtomicUsize, Ordering};
107+ /// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
108+ /// fn add_one() -> usize {
109+ /// // This will always return `0` since `ATOMIC` is copied before it's borrowed
110+ /// // for use by `fetch_add`.
111+ /// ATOMIC.fetch_add(1, Ordering::AcqRel)
112+ /// }
113+ /// ```
108114 ///
109115 /// ### Known problems
110- /// When an enum has variants with interior mutability, use of its non
111- /// interior mutable variants can generate false positives. See issue
112- /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
116+ /// This lint does not, and cannot in general, determine if the borrow of the constant
117+ /// is used in a way which causes a mutation. e.g.
118+ ///
119+ /// ```rust
120+ /// use core::cell::Cell;
121+ /// const CELL: Cell<usize> = Cell::new(0);
122+ /// fn get_cell() -> Cell<usize> {
123+ /// // This is fine. It borrows a copy of `CELL`, but never mutates it through the
124+ /// // borrow.
125+ /// CELL.clone()
126+ /// }
127+ /// ```
113128 ///
114- /// Types that have underlying or potential interior mutability trigger the lint whether
115- /// the interior mutable field is used or not. See issues
116- /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
117- /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
129+ /// There also exists types which contain private fields with interior mutability, but
130+ /// no way to both create a value as a constant and modify any mutable field using the
131+ /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
132+ /// scan a crate's interface to see if this is the case, all such types will be linted.
133+ /// If this happens use the `ignore-interior-mutability` configuration option to allow
134+ /// the type.
118135 ///
119136 /// ### Example
120137 /// ```no_run
@@ -697,17 +714,15 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
697714 cx,
698715 DECLARE_INTERIOR_MUTABLE_CONST ,
699716 item. ident . span ,
700- "a `const` item should not be interior mutable " ,
717+ "named constant with interior mutability " ,
701718 |diag| {
702719 let Some ( sync_trait) = cx. tcx . lang_items ( ) . sync_trait ( ) else {
703720 return ;
704721 } ;
705722 if implements_trait ( cx, ty, sync_trait, & [ ] ) {
706- diag. help ( "consider making this a static item" ) ;
723+ diag. help ( "did you mean to make this a ` static` item" ) ;
707724 } else {
708- diag. help (
709- "consider making this `Sync` so that it can go in a static item or using a `thread_local`" ,
710- ) ;
725+ diag. help ( "did you mean to make this a `thread_local!` item" ) ;
711726 }
712727 } ,
713728 ) ;
@@ -741,7 +756,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
741756 cx,
742757 DECLARE_INTERIOR_MUTABLE_CONST ,
743758 item. ident . span ,
744- "a `const` item should not be interior mutable " ,
759+ "named constant with interior mutability " ,
745760 ) ;
746761 }
747762 }
@@ -793,11 +808,12 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
793808 cx,
794809 DECLARE_INTERIOR_MUTABLE_CONST ,
795810 item. ident . span ,
796- "a `const` item should not be interior mutable " ,
811+ "named constant with interior mutability " ,
797812 ) ;
798813 }
799814 }
800815
816+ #[ expect( irrefutable_let_patterns) ]
801817 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , e : & ' tcx Expr < ' _ > ) {
802818 if let ExprKind :: Path ( qpath) = & e. kind
803819 && let typeck = cx. typeck_results ( )
@@ -824,13 +840,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
824840 }
825841 && !in_external_macro ( cx. sess ( ) , borrow_src. expr . span )
826842 {
827- span_lint_and_help (
843+ span_lint_and_then (
828844 cx,
829845 BORROW_INTERIOR_MUTABLE_CONST ,
830846 borrow_src. expr . span ,
831- "a `const` item with interior mutability should not be borrowed" ,
832- None ,
833- "assign this const to a local or static variable, and use the variable here" ,
847+ "borrow of a named constant with interior mutability" ,
848+ |diag| {
849+ diag. help ( "this lint can be silenced by assigning the value to a local variable before borrowing" ) ;
850+ if let Some ( note) = borrow_src. cause . note ( ) {
851+ diag. note ( note) ;
852+ }
853+ } ,
834854 ) ;
835855 }
836856 }
0 commit comments