11use clippy_config:: Conf ;
22use clippy_utils:: consts:: { ConstEvalCtxt , Constant } ;
33use 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} ;
55use clippy_utils:: macros:: macro_backtrace;
66use clippy_utils:: ty:: { get_field_idx_by_name, implements_trait} ;
77use rustc_data_structures:: fx:: FxHashMap ;
@@ -21,35 +21,36 @@ use std::collections::hash_map::Entry;
2121
2222declare_clippy_lint ! {
2323 /// ### 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.
2625 ///
2726 /// ### 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.
3229 ///
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::atomic::{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+ /// ```
3538 ///
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.
4142 ///
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.
4947 ///
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.
5354 ///
5455 /// ### Example
5556 /// ```no_run
@@ -75,26 +76,42 @@ declare_clippy_lint! {
7576
7677declare_clippy_lint ! {
7778 /// ### 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.
8080 ///
8181 /// ### 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.
8684 ///
87- /// The `const` value should be stored inside a `static` item.
85+ /// ```rust
86+ /// use core::sync::atomic::{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+ /// ```
8894 ///
8995 /// ### 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+ /// ```
93108 ///
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.
98115 ///
99116 /// ### Example
100117 /// ```no_run
@@ -659,17 +676,15 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
659676 cx,
660677 DECLARE_INTERIOR_MUTABLE_CONST ,
661678 item. ident . span ,
662- "a `const` item should not be interior mutable " ,
679+ "named constant with interior mutability " ,
663680 |diag| {
664681 let Some ( sync_trait) = cx. tcx . lang_items ( ) . sync_trait ( ) else {
665682 return ;
666683 } ;
667684 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" ) ;
669686 } 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" ) ;
673688 }
674689 } ,
675690 ) ;
@@ -702,7 +717,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
702717 cx,
703718 DECLARE_INTERIOR_MUTABLE_CONST ,
704719 item. ident . span ,
705- "a `const` item should not be interior mutable " ,
720+ "named constant with interior mutability " ,
706721 ) ;
707722 }
708723 }
@@ -753,7 +768,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
753768 cx,
754769 DECLARE_INTERIOR_MUTABLE_CONST ,
755770 item. ident . span ,
756- "a `const` item should not be interior mutable " ,
771+ "named constant with interior mutability " ,
757772 ) ;
758773 }
759774 }
@@ -790,13 +805,17 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
790805 }
791806 && !in_external_macro ( cx. sess ( ) , borrow_src. expr . span )
792807 {
793- span_lint_and_help (
808+ span_lint_and_then (
794809 cx,
795810 BORROW_INTERIOR_MUTABLE_CONST ,
796811 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+ } ,
800819 ) ;
801820 }
802821 }
0 commit comments