19
19
20
20
use clippy_config:: Conf ;
21
21
use 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} ;
23
23
use clippy_utils:: macros:: macro_backtrace;
24
24
use clippy_utils:: ty:: { get_field_idx_by_name, implements_trait} ;
25
25
use clippy_utils:: { def_path_def_ids, is_in_const_context} ;
@@ -41,35 +41,36 @@ use std::collections::hash_map::Entry;
41
41
42
42
declare_clippy_lint ! {
43
43
/// ### 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.
46
45
///
47
46
/// ### 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.
52
49
///
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
+ /// ```
55
58
///
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.
61
62
///
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.
69
67
///
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.
73
74
///
74
75
/// ### Example
75
76
/// ```no_run
@@ -95,26 +96,42 @@ declare_clippy_lint! {
95
96
96
97
declare_clippy_lint ! {
97
98
/// ### 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.
100
100
///
101
101
/// ### 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.
106
104
///
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
+ /// ```
108
114
///
109
115
/// ### 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
+ /// ```
113
128
///
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.
118
135
///
119
136
/// ### Example
120
137
/// ```no_run
@@ -697,17 +714,15 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
697
714
cx,
698
715
DECLARE_INTERIOR_MUTABLE_CONST ,
699
716
item. ident . span ,
700
- "a `const` item should not be interior mutable " ,
717
+ "named constant with interior mutability " ,
701
718
|diag| {
702
719
let Some ( sync_trait) = cx. tcx . lang_items ( ) . sync_trait ( ) else {
703
720
return ;
704
721
} ;
705
722
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" ) ;
707
724
} 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" ) ;
711
726
}
712
727
} ,
713
728
) ;
@@ -741,7 +756,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
741
756
cx,
742
757
DECLARE_INTERIOR_MUTABLE_CONST ,
743
758
item. ident . span ,
744
- "a `const` item should not be interior mutable " ,
759
+ "named constant with interior mutability " ,
745
760
) ;
746
761
}
747
762
}
@@ -793,7 +808,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
793
808
cx,
794
809
DECLARE_INTERIOR_MUTABLE_CONST ,
795
810
item. ident . span ,
796
- "a `const` item should not be interior mutable " ,
811
+ "named constant with interior mutability " ,
797
812
) ;
798
813
}
799
814
}
@@ -825,13 +840,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
825
840
}
826
841
&& !in_external_macro ( cx. sess ( ) , borrow_src. expr . span )
827
842
{
828
- span_lint_and_help (
843
+ span_lint_and_then (
829
844
cx,
830
845
BORROW_INTERIOR_MUTABLE_CONST ,
831
846
borrow_src. expr . span ,
832
- "a `const` item with interior mutability should not be borrowed" ,
833
- None ,
834
- "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
+ } ,
835
854
) ;
836
855
}
837
856
}
0 commit comments