@@ -43,35 +43,36 @@ use std::collections::hash_map::Entry;
43
43
44
44
declare_clippy_lint ! {
45
45
/// ### 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.
48
47
///
49
48
/// ### 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.
54
51
///
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
+ /// ```
57
60
///
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.
63
64
///
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.
71
69
///
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.
75
76
///
76
77
/// ### Example
77
78
/// ```no_run
@@ -97,16 +98,42 @@ declare_clippy_lint! {
97
98
98
99
declare_clippy_lint ! {
99
100
/// ### 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.
102
102
///
103
103
/// ### 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
+ /// ```
108
116
///
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.
110
137
///
111
138
/// ### Example
112
139
/// ```no_run
@@ -180,6 +207,7 @@ enum BorrowCause {
180
207
Index ,
181
208
AutoDeref ,
182
209
AutoBorrow ,
210
+ AutoDerefField ,
183
211
}
184
212
impl BorrowCause {
185
213
fn note ( self ) -> Option < & ' static str > {
@@ -189,6 +217,9 @@ impl BorrowCause {
189
217
Self :: Index => Some ( "this index expression is a call to `Index::index`" ) ,
190
218
Self :: AutoDeref => Some ( "there is a compiler inserted call to `Deref::deref` here" ) ,
191
219
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
+ } ,
192
223
}
193
224
}
194
225
}
@@ -208,6 +239,7 @@ impl<'tcx> BorrowSource<'tcx> {
208
239
match parent. kind {
209
240
ExprKind :: Unary ( UnOp :: Deref , _) => ( parent, BorrowCause :: Deref ) ,
210
241
ExprKind :: Index ( ..) => ( parent, BorrowCause :: Index ) ,
242
+ ExprKind :: Field ( ..) => ( parent, BorrowCause :: AutoDerefField ) ,
211
243
_ => ( expr, cause) ,
212
244
}
213
245
} else {
@@ -688,17 +720,15 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
688
720
cx,
689
721
DECLARE_INTERIOR_MUTABLE_CONST ,
690
722
ident. span ,
691
- "a `const` item should not be interior mutable " ,
723
+ "named constant with interior mutability " ,
692
724
|diag| {
693
725
let Some ( sync_trait) = cx. tcx . lang_items ( ) . sync_trait ( ) else {
694
726
return ;
695
727
} ;
696
728
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" ) ;
698
730
} 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" ) ;
702
732
}
703
733
} ,
704
734
) ;
@@ -732,7 +762,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
732
762
cx,
733
763
DECLARE_INTERIOR_MUTABLE_CONST ,
734
764
item. ident . span ,
735
- "a `const` item should not be interior mutable " ,
765
+ "named constant with interior mutability " ,
736
766
) ;
737
767
}
738
768
}
@@ -784,7 +814,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
784
814
cx,
785
815
DECLARE_INTERIOR_MUTABLE_CONST ,
786
816
item. ident . span ,
787
- "a `const` item should not be interior mutable " ,
817
+ "named constant with interior mutability " ,
788
818
) ;
789
819
}
790
820
}
@@ -819,12 +849,12 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
819
849
cx,
820
850
BORROW_INTERIOR_MUTABLE_CONST ,
821
851
borrow_src. expr . span ,
822
- "a `const` item with interior mutability should not be borrowed " ,
852
+ "borrow of a named constant with interior mutability" ,
823
853
|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 ) ;
826
856
}
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 " ) ;
828
858
} ,
829
859
) ;
830
860
}
0 commit comments