1
1
use clippy_config:: Conf ;
2
2
use clippy_utils:: consts:: { ConstEvalCtxt , Constant } ;
3
3
use clippy_utils:: def_path_def_ids;
4
- use clippy_utils:: diagnostics:: { span_lint, span_lint_and_help , span_lint_and_then} ;
4
+ use clippy_utils:: diagnostics:: { span_lint, span_lint_and_then} ;
5
5
use clippy_utils:: macros:: macro_backtrace;
6
6
use clippy_utils:: ty:: { get_field_idx_by_name, implements_trait} ;
7
7
use rustc_data_structures:: fx:: FxHashMap ;
@@ -21,35 +21,36 @@ use std::collections::hash_map::Entry;
21
21
22
22
declare_clippy_lint ! {
23
23
/// ### What it does
24
- /// Checks for declaration of `const` items which is interior
25
- /// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
24
+ /// Checks for the declaration of named constant which contain interior mutability.
26
25
///
27
26
/// ### Why is this bad?
28
- /// Consts are copied everywhere they are referenced, i.e.,
29
- /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
30
- /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
31
- /// these types in the first place.
27
+ /// Named constants are copied at every use site which means any change to their value
28
+ /// will be lost after the newly created value is dropped. e.g.
32
29
///
33
- /// The `const` should better be replaced by a `static` item if a global
34
- /// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
30
+ /// ```rust
31
+ /// use core::sync::{AtomicUsize, Ordering};
32
+ /// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
33
+ /// fn add_one() -> usize {
34
+ /// // This will always return `0` since `ATOMIC` is copied before it's used.
35
+ /// ATOMIC.fetch_add(1, Ordering::AcqRel)
36
+ /// }
37
+ /// ```
35
38
///
36
- /// ### Known problems
37
- /// A "non-constant" const item is a legacy way to supply an
38
- /// initialized value to downstream `static` items (e.g., the
39
- /// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
40
- /// and this lint should be suppressed.
39
+ /// If shared modification of the value is desired, a `static` item is needed instead.
40
+ /// If that is not desired, a `const fn` constructor should be used to make it obvious
41
+ /// at the use site that a new value is created.
41
42
///
42
- /// Even though the lint avoids triggering on a constant whose type has enums that have variants
43
- /// with interior mutability, and its value uses non interior mutable variants (see
44
- /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and
45
- /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);
46
- /// it complains about associated constants without default values only based on its types;
47
- /// which might not be preferable.
48
- /// There're other enums plus associated constants cases that the lint cannot handle.
43
+ /// ### Known problems
44
+ /// Prior to `const fn` stabilization this was the only way to provide a value which
45
+ /// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In
46
+ /// this case the use of `const` is required and this lint should be suppressed.
49
47
///
50
- /// Types that have underlying or potential interior mutability trigger the lint whether
51
- /// the interior mutable field is used or not. See issue
52
- /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812)
48
+ /// There also exists types which contain private fields with interior mutability, but
49
+ /// no way to both create a value as a constant and modify any mutable field using the
50
+ /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
51
+ /// scan a crate's interface to see if this is the case, all such types will be linted.
52
+ /// If this happens use the `ignore-interior-mutability` configuration option to allow
53
+ /// the type.
53
54
///
54
55
/// ### Example
55
56
/// ```no_run
@@ -75,26 +76,42 @@ declare_clippy_lint! {
75
76
76
77
declare_clippy_lint ! {
77
78
/// ### What it does
78
- /// Checks if `const` items which is interior mutable (e.g.,
79
- /// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
79
+ /// Checks for a borrow of a named constant with interior mutability.
80
80
///
81
81
/// ### Why is this bad?
82
- /// Consts are copied everywhere they are referenced, i.e.,
83
- /// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
84
- /// or `AtomicXxxx` will be created, which defeats the whole purpose of using
85
- /// these types in the first place.
82
+ /// Named constants are copied at every use site which means any change to their value
83
+ /// will be lost after the newly created value is dropped. e.g.
86
84
///
87
- /// The `const` value should be stored inside a `static` item.
85
+ /// ```rust
86
+ /// use core::sync::{AtomicUsize, Ordering};
87
+ /// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
88
+ /// fn add_one() -> usize {
89
+ /// // This will always return `0` since `ATOMIC` is copied before it's borrowed
90
+ /// // for use by `fetch_add`.
91
+ /// ATOMIC.fetch_add(1, Ordering::AcqRel)
92
+ /// }
93
+ /// ```
88
94
///
89
95
/// ### Known problems
90
- /// When an enum has variants with interior mutability, use of its non
91
- /// interior mutable variants can generate false positives. See issue
92
- /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962)
96
+ /// This lint does not, and cannot in general, determine if the borrow of the constant
97
+ /// is used in a way which causes a mutation. e.g.
98
+ ///
99
+ /// ```rust
100
+ /// use core::cell::Cell;
101
+ /// const CELL: Cell<usize> = Cell::new(0);
102
+ /// fn get_cell() -> Cell<usize> {
103
+ /// // This is fine. It borrows a copy of `CELL`, but never mutates it through the
104
+ /// // borrow.
105
+ /// CELL.clone()
106
+ /// }
107
+ /// ```
93
108
///
94
- /// Types that have underlying or potential interior mutability trigger the lint whether
95
- /// the interior mutable field is used or not. See issues
96
- /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and
97
- /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825)
109
+ /// There also exists types which contain private fields with interior mutability, but
110
+ /// no way to both create a value as a constant and modify any mutable field using the
111
+ /// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
112
+ /// scan a crate's interface to see if this is the case, all such types will be linted.
113
+ /// If this happens use the `ignore-interior-mutability` configuration option to allow
114
+ /// the type.
98
115
///
99
116
/// ### Example
100
117
/// ```no_run
@@ -659,17 +676,15 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
659
676
cx,
660
677
DECLARE_INTERIOR_MUTABLE_CONST ,
661
678
item. ident . span ,
662
- "a `const` item should not be interior mutable " ,
679
+ "named constant with interior mutability " ,
663
680
|diag| {
664
681
let Some ( sync_trait) = cx. tcx . lang_items ( ) . sync_trait ( ) else {
665
682
return ;
666
683
} ;
667
684
if implements_trait ( cx, ty, sync_trait, & [ ] ) {
668
- diag. help ( "consider making this a static item" ) ;
685
+ diag. help ( "did you mean to make this a ` static` item" ) ;
669
686
} else {
670
- diag. help (
671
- "consider making this `Sync` so that it can go in a static item or using a `thread_local`" ,
672
- ) ;
687
+ diag. help ( "did you mean to make this a `thread_local!` item" ) ;
673
688
}
674
689
} ,
675
690
) ;
@@ -702,7 +717,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
702
717
cx,
703
718
DECLARE_INTERIOR_MUTABLE_CONST ,
704
719
item. ident . span ,
705
- "a `const` item should not be interior mutable " ,
720
+ "named constant with interior mutability " ,
706
721
) ;
707
722
}
708
723
}
@@ -753,7 +768,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
753
768
cx,
754
769
DECLARE_INTERIOR_MUTABLE_CONST ,
755
770
item. ident . span ,
756
- "a `const` item should not be interior mutable " ,
771
+ "named constant with interior mutability " ,
757
772
) ;
758
773
}
759
774
}
@@ -790,13 +805,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
790
805
}
791
806
&& !in_external_macro ( cx. sess ( ) , borrow_src. expr . span )
792
807
{
793
- span_lint_and_help (
808
+ span_lint_and_then (
794
809
cx,
795
810
BORROW_INTERIOR_MUTABLE_CONST ,
796
811
borrow_src. expr . span ,
797
- "a `const` item with interior mutability should not be borrowed" ,
798
- None ,
799
- "assign this const to a local or static variable, and use the variable here" ,
812
+ "borrow of a named constant with interior mutability" ,
813
+ |diag| {
814
+ diag. help ( "this lint can be silenced by assigning the value to a local variable before borrowing" ) ;
815
+ if let Some ( note) = borrow_src. cause . note ( ) {
816
+ diag. note ( note) ;
817
+ }
818
+ } ,
800
819
) ;
801
820
}
802
821
}
0 commit comments