@@ -43,35 +43,36 @@ use std::collections::hash_map::Entry;
4343
4444declare_clippy_lint ! {
4545 /// ### What it does
46- /// Checks for declaration of `const` items which is interior
47- /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
46+ /// Checks for the declaration of named constant which contain interior mutability.
4847 ///
4948 /// ### Why is this bad?
50- /// Consts are copied everywhere they are referenced, i.e.,
51- /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
52- /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
53- /// these types in the first place.
49+ /// Named constants are copied at every use site which means any change to their value
50+ /// will be lost after the newly created value is dropped. e.g.
5451 ///
55- /// The `const` should better be replaced by a `static` item if a global
56- /// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
52+ /// ```rust
53+ /// use core::sync::atomic::{AtomicUsize, Ordering};
54+ /// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
55+ /// fn add_one() -> usize {
56+ /// // This will always return `0` since `ATOMIC` is copied before it's used.
57+ /// ATOMIC.fetch_add(1, Ordering::AcqRel)
58+ /// }
59+ /// ```
5760 ///
58- /// ### Known problems
59- /// A "non-constant" const item is a legacy way to supply an
60- /// initialized value to downstream `static` items (e.g., the
61- /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
62- /// and this lint should be suppressed.
61+ /// If shared modification of the value is desired, a `static` item is needed instead.
62+ /// If that is not desired, a `const fn` constructor should be used to make it obvious
63+ /// at the use site that a new value is created.
6364 ///
64- /// Even though the lint avoids triggering on a constant whose type has enums that have variants
65- /// with interior mutability, and its value uses non interior mutable variants (see
66- /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and
67- /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);
68- /// it complains about associated constants without default values only based on its types;
69- /// which might not be preferable.
70- /// There're other enums plus associated constants cases that the lint cannot handle.
65+ /// ### Known problems
66+ /// Prior to `const fn` stabilization this was the only way to provide a value which
67+ /// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In
68+ /// this case the use of `const` is required and this lint should be suppressed.
7169 ///
72- /// Types that have underlying or potential interior mutability trigger the lint whether
73- /// the interior mutable field is used or not. See issue
74- /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812)
70+ /// There also exists types which contain private fields with interior mutability, but
71+ /// no way to both create a value as a constant and modify any mutable field using the
72+ /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
73+ /// scan a crate's interface to see if this is the case, all such types will be linted.
74+ /// If this happens use the `ignore-interior-mutability` configuration option to allow
75+ /// the type.
7576 ///
7677 /// ### Example
7778 /// ```no_run
@@ -97,16 +98,42 @@ declare_clippy_lint! {
9798
9899declare_clippy_lint ! {
99100 /// ### What it does
100- /// Checks if `const` items which is interior mutable (e.g.,
101- /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
101+ /// Checks for a borrow of a named constant with interior mutability.
102102 ///
103103 /// ### Why is this bad?
104- /// Consts are copied everywhere they are referenced, i.e.,
105- /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
106- /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
107- /// these types in the first place.
104+ /// Named constants are copied at every use site which means any change to their value
105+ /// will be lost after the newly created value is dropped. e.g.
106+ ///
107+ /// ```rust
108+ /// use core::sync::atomic::{AtomicUsize, Ordering};
109+ /// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
110+ /// fn add_one() -> usize {
111+ /// // This will always return `0` since `ATOMIC` is copied before it's borrowed
112+ /// // for use by `fetch_add`.
113+ /// ATOMIC.fetch_add(1, Ordering::AcqRel)
114+ /// }
115+ /// ```
108116 ///
109- /// The `const` value should be stored inside a `static` item.
117+ /// ### Known problems
118+ /// This lint does not, and cannot in general, determine if the borrow of the constant
119+ /// is used in a way which causes a mutation. e.g.
120+ ///
121+ /// ```rust
122+ /// use core::cell::Cell;
123+ /// const CELL: Cell<usize> = Cell::new(0);
124+ /// fn get_cell() -> Cell<usize> {
125+ /// // This is fine. It borrows a copy of `CELL`, but never mutates it through the
126+ /// // borrow.
127+ /// CELL.clone()
128+ /// }
129+ /// ```
130+ ///
131+ /// There also exists types which contain private fields with interior mutability, but
132+ /// no way to both create a value as a constant and modify any mutable field using the
133+ /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
134+ /// scan a crate's interface to see if this is the case, all such types will be linted.
135+ /// If this happens use the `ignore-interior-mutability` configuration option to allow
136+ /// the type.
110137 ///
111138 /// ### Example
112139 /// ```no_run
@@ -180,6 +207,7 @@ enum BorrowCause {
180207 Index ,
181208 AutoDeref ,
182209 AutoBorrow ,
210+ AutoDerefField ,
183211}
184212impl BorrowCause {
185213 fn note ( self ) -> Option < & ' static str > {
@@ -189,6 +217,9 @@ impl BorrowCause {
189217 Self :: Index => Some ( "this index expression is a call to `Index::index`" ) ,
190218 Self :: AutoDeref => Some ( "there is a compiler inserted call to `Deref::deref` here" ) ,
191219 Self :: AutoBorrow => Some ( "there is a compiler inserted borrow here" ) ,
220+ Self :: AutoDerefField => {
221+ Some ( "there is a compiler inserted call to `Deref::deref` when accessing this field" )
222+ } ,
192223 }
193224 }
194225}
@@ -208,6 +239,7 @@ impl<'tcx> BorrowSource<'tcx> {
208239 match parent. kind {
209240 ExprKind :: Unary ( UnOp :: Deref , _) => ( parent, BorrowCause :: Deref ) ,
210241 ExprKind :: Index ( ..) => ( parent, BorrowCause :: Index ) ,
242+ ExprKind :: Field ( ..) => ( parent, BorrowCause :: AutoDerefField ) ,
211243 _ => ( expr, cause) ,
212244 }
213245 } else {
@@ -688,17 +720,15 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
688720 cx,
689721 DECLARE_INTERIOR_MUTABLE_CONST ,
690722 ident. span ,
691- "a `const` item should not be interior mutable " ,
723+ "named constant with interior mutability " ,
692724 |diag| {
693725 let Some ( sync_trait) = cx. tcx . lang_items ( ) . sync_trait ( ) else {
694726 return ;
695727 } ;
696728 if implements_trait ( cx, ty, sync_trait, & [ ] ) {
697- diag. help ( "consider making this a static item" ) ;
729+ diag. help ( "did you mean to make this a ` static` item" ) ;
698730 } else {
699- diag. help (
700- "consider making this `Sync` so that it can go in a static item or using a `thread_local`" ,
701- ) ;
731+ diag. help ( "did you mean to make this a `thread_local!` item" ) ;
702732 }
703733 } ,
704734 ) ;
@@ -732,7 +762,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
732762 cx,
733763 DECLARE_INTERIOR_MUTABLE_CONST ,
734764 item. ident . span ,
735- "a `const` item should not be interior mutable " ,
765+ "named constant with interior mutability " ,
736766 ) ;
737767 }
738768 }
@@ -784,7 +814,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
784814 cx,
785815 DECLARE_INTERIOR_MUTABLE_CONST ,
786816 item. ident . span ,
787- "a `const` item should not be interior mutable " ,
817+ "named constant with interior mutability " ,
788818 ) ;
789819 }
790820 }
@@ -819,12 +849,12 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
819849 cx,
820850 BORROW_INTERIOR_MUTABLE_CONST ,
821851 borrow_src. expr . span ,
822- "a `const` item with interior mutability should not be borrowed " ,
852+ "borrow of a named constant with interior mutability" ,
823853 |diag| {
824- if let Some ( msg ) = borrow_src. cause . note ( ) {
825- diag. note ( msg ) ;
854+ if let Some ( note ) = borrow_src. cause . note ( ) {
855+ diag. note ( note ) ;
826856 }
827- diag. help ( "assign this const to a local or static variable, and use the variable here " ) ;
857+ diag. help ( "this lint can be silenced by assigning the value to a local variable before borrowing " ) ;
828858 } ,
829859 ) ;
830860 }
0 commit comments