mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-15 20:45:45 +03:00
Rewrite non_copy_const (#13207)
fixes #12979 fixes #12951 fixes #13233 Tests still need to be finished and the docs still need to be updated, but this should otherwise ready. changelog: Lint `declare_interior_mutable_const` and `borrow_interior_mutable_const` more precisely
This commit is contained in:
+812
-389
@@ -1,56 +1,78 @@
|
||||
use std::ptr;
|
||||
// Implementation for lints detecting interior mutability in constants.
|
||||
//
|
||||
// For `declare_interior_mutable_const` there are three strategies used to
|
||||
// determine if a value has interior mutability:
|
||||
// * A type-based check. This is the least accurate, but can always run.
|
||||
// * A const-eval based check. This is the most accurate, but this requires that the value is
|
||||
// defined and does not work with generics.
|
||||
// * A HIR-tree based check. This is less accurate than const-eval, but it can be applied to generic
|
||||
// values.
|
||||
//
|
||||
// For `borrow_interior_mutable_const` the same three strategies are applied
|
||||
// when checking a constant's value, but field and array index projections at
|
||||
// the borrow site are taken into account as well. As an example: `FOO.bar` may
|
||||
// have interior mutability, but `FOO.baz` may not. When borrowing `FOO.baz` no
|
||||
// warning will be issued.
|
||||
//
|
||||
// No matter the lint or strategy, a warning should only be issued if a value
|
||||
// definitely contains interior mutability.
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::is_in_const_context;
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
use clippy_utils::ty::{InteriorMut, implements_trait};
|
||||
use rustc_abi::VariantIdx;
|
||||
use clippy_utils::paths::{PathNS, lookup_path_str};
|
||||
use clippy_utils::ty::{get_field_idx_by_name, implements_trait};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::def_id::{DefId, DefIdSet};
|
||||
use rustc_hir::{
|
||||
BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
|
||||
Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, Node, StructTailExpr, TraitItem, TraitItemKind, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::mir::{ConstValue, UnevaluatedConst};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_middle::ty::{
|
||||
self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable,
|
||||
TypeckResults, TypingEnv,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId, ReportedErrorInfo};
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{DUMMY_SP, Span, sym};
|
||||
use rustc_span::{DUMMY_SP, sym};
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
// FIXME: this is a correctness problem but there's no suitable
|
||||
// warn-by-default category.
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for declaration of `const` items which is interior
|
||||
/// mutable (e.g., contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.).
|
||||
/// Checks for the declaration of named constant which contain interior mutability.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Consts are copied everywhere they are referenced, i.e.,
|
||||
/// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
|
||||
/// or `AtomicXxxx` will be created, which defeats the whole purpose of using
|
||||
/// these types in the first place.
|
||||
/// Named constants are copied at every use site which means any change to their value
|
||||
/// will be lost after the newly created value is dropped. e.g.
|
||||
///
|
||||
/// The `const` should better be replaced by a `static` item if a global
|
||||
/// variable is wanted, or replaced by a `const fn` if a constructor is wanted.
|
||||
/// ```rust
|
||||
/// use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
/// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
|
||||
/// fn add_one() -> usize {
|
||||
/// // This will always return `0` since `ATOMIC` is copied before it's used.
|
||||
/// ATOMIC.fetch_add(1, Ordering::AcqRel)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// If shared modification of the value is desired, a `static` item is needed instead.
|
||||
/// If that is not desired, a `const fn` constructor should be used to make it obvious
|
||||
/// at the use site that a new value is created.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// A "non-constant" const item is a legacy way to supply an
|
||||
/// initialized value to downstream `static` items (e.g., the
|
||||
/// `std::sync::ONCE_INIT` constant). In this case the use of `const` is legit,
|
||||
/// and this lint should be suppressed.
|
||||
/// Prior to `const fn` stabilization this was the only way to provide a value which
|
||||
/// could initialize a `static` item (e.g. the `std::sync::ONCE_INIT` constant). In
|
||||
/// this case the use of `const` is required and this lint should be suppressed.
|
||||
///
|
||||
/// Even though the lint avoids triggering on a constant whose type has enums that have variants
|
||||
/// with interior mutability, and its value uses non interior mutable variants (see
|
||||
/// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) and
|
||||
/// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) for examples);
|
||||
/// it complains about associated constants without default values only based on its types;
|
||||
/// which might not be preferable.
|
||||
/// There're other enums plus associated constants cases that the lint cannot handle.
|
||||
///
|
||||
/// Types that have underlying or potential interior mutability trigger the lint whether
|
||||
/// the interior mutable field is used or not. See issue
|
||||
/// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812)
|
||||
/// There also exists types which contain private fields with interior mutability, but
|
||||
/// no way to both create a value as a constant and modify any mutable field using the
|
||||
/// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
|
||||
/// scan a crate's interface to see if this is the case, all such types will be linted.
|
||||
/// If this happens use the `ignore-interior-mutability` configuration option to allow
|
||||
/// the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
@@ -74,20 +96,44 @@
|
||||
"declaring `const` with interior mutability"
|
||||
}
|
||||
|
||||
// FIXME: this is a correctness problem but there's no suitable
|
||||
// warn-by-default category.
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if `const` items which is interior mutable (e.g.,
|
||||
/// contains a `Cell`, `Mutex`, `AtomicXxxx`, etc.) has been borrowed directly.
|
||||
/// Checks for a borrow of a named constant with interior mutability.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Consts are copied everywhere they are referenced, i.e.,
|
||||
/// every time you refer to the const a fresh instance of the `Cell` or `Mutex`
|
||||
/// or `AtomicXxxx` will be created, which defeats the whole purpose of using
|
||||
/// these types in the first place.
|
||||
/// Named constants are copied at every use site which means any change to their value
|
||||
/// will be lost after the newly created value is dropped. e.g.
|
||||
///
|
||||
/// The `const` value should be stored inside a `static` item.
|
||||
/// ```rust
|
||||
/// use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
/// const ATOMIC: AtomicUsize = AtomicUsize::new(0);
|
||||
/// fn add_one() -> usize {
|
||||
/// // This will always return `0` since `ATOMIC` is copied before it's borrowed
|
||||
/// // for use by `fetch_add`.
|
||||
/// ATOMIC.fetch_add(1, Ordering::AcqRel)
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint does not, and cannot in general, determine if the borrow of the constant
|
||||
/// is used in a way which causes a mutation. e.g.
|
||||
///
|
||||
/// ```rust
|
||||
/// use core::cell::Cell;
|
||||
/// const CELL: Cell<usize> = Cell::new(0);
|
||||
/// fn get_cell() -> Cell<usize> {
|
||||
/// // This is fine. It borrows a copy of `CELL`, but never mutates it through the
|
||||
/// // borrow.
|
||||
/// CELL.clone()
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// There also exists types which contain private fields with interior mutability, but
|
||||
/// no way to both create a value as a constant and modify any mutable field using the
|
||||
/// type's public interface (e.g. `bytes::Bytes`). As there is no reasonable way to
|
||||
/// scan a crate's interface to see if this is the case, all such types will be linted.
|
||||
/// If this happens use the `ignore-interior-mutability` configuration option to allow
|
||||
/// the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
@@ -113,60 +159,101 @@
|
||||
"referencing `const` with interior mutability"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Source<'tcx> {
|
||||
Item { item: Span, ty: Ty<'tcx> },
|
||||
Assoc { item: Span },
|
||||
Expr { expr: Span },
|
||||
#[derive(Clone, Copy)]
|
||||
enum IsFreeze {
|
||||
/// The type and all possible values are `Freeze`
|
||||
Yes,
|
||||
/// The type itself is non-`Freeze`, but not all values are.
|
||||
Maybe,
|
||||
/// The type and all possible values are non-`Freeze`
|
||||
No,
|
||||
}
|
||||
impl IsFreeze {
|
||||
/// Merges the variants of a sum type (i.e. an enum).
|
||||
fn from_variants(iter: impl Iterator<Item = Self>) -> Self {
|
||||
iter.fold(Self::Yes, |x, y| match (x, y) {
|
||||
(Self::Maybe, _) | (_, Self::Maybe) | (Self::No, Self::Yes) | (Self::Yes, Self::No) => Self::Maybe,
|
||||
(Self::No, Self::No) => Self::No,
|
||||
(Self::Yes, Self::Yes) => Self::Yes,
|
||||
})
|
||||
}
|
||||
|
||||
/// Merges the fields of a product type (e.g. a struct or tuple).
|
||||
fn from_fields(mut iter: impl Iterator<Item = Self>) -> Self {
|
||||
iter.try_fold(Self::Yes, |x, y| match (x, y) {
|
||||
(Self::No, _) | (_, Self::No) => None,
|
||||
(Self::Maybe, _) | (_, Self::Maybe) => Some(Self::Maybe),
|
||||
(Self::Yes, Self::Yes) => Some(Self::Yes),
|
||||
})
|
||||
.unwrap_or(Self::No)
|
||||
}
|
||||
|
||||
/// Checks if this is definitely `Freeze`.
|
||||
fn is_freeze(self) -> bool {
|
||||
matches!(self, Self::Yes)
|
||||
}
|
||||
|
||||
/// Checks if this is definitely not `Freeze`.
|
||||
fn is_not_freeze(self) -> bool {
|
||||
matches!(self, Self::No)
|
||||
}
|
||||
}
|
||||
|
||||
impl Source<'_> {
|
||||
#[must_use]
|
||||
fn lint(&self) -> (&'static Lint, &'static str, Span) {
|
||||
/// What operation caused a borrow to occur.
|
||||
#[derive(Clone, Copy)]
|
||||
enum BorrowCause {
|
||||
Borrow,
|
||||
Deref,
|
||||
Index,
|
||||
AutoDeref,
|
||||
AutoBorrow,
|
||||
AutoDerefField,
|
||||
}
|
||||
impl BorrowCause {
|
||||
fn note(self) -> Option<&'static str> {
|
||||
match self {
|
||||
Self::Item { item, .. } | Self::Assoc { item, .. } => (
|
||||
DECLARE_INTERIOR_MUTABLE_CONST,
|
||||
"a `const` item should not be interior mutable",
|
||||
*item,
|
||||
),
|
||||
Self::Expr { expr } => (
|
||||
BORROW_INTERIOR_MUTABLE_CONST,
|
||||
"a `const` item with interior mutability should not be borrowed",
|
||||
*expr,
|
||||
),
|
||||
Self::Borrow => None,
|
||||
Self::Deref => Some("this deref expression is a call to `Deref::deref`"),
|
||||
Self::Index => Some("this index expression is a call to `Index::index`"),
|
||||
Self::AutoDeref => Some("there is a compiler inserted call to `Deref::deref` here"),
|
||||
Self::AutoBorrow => Some("there is a compiler inserted borrow here"),
|
||||
Self::AutoDerefField => {
|
||||
Some("there is a compiler inserted call to `Deref::deref` when accessing this field")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lint<'tcx>(cx: &LateContext<'tcx>, source: Source<'tcx>) {
|
||||
let (lint, msg, span) = source.lint();
|
||||
span_lint_and_then(cx, lint, span, msg, |diag| {
|
||||
if span.from_expansion() {
|
||||
return; // Don't give suggestions into macros.
|
||||
}
|
||||
match source {
|
||||
Source::Item { ty, .. } => {
|
||||
let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else {
|
||||
return;
|
||||
};
|
||||
if implements_trait(cx, ty, sync_trait, &[]) {
|
||||
diag.help("consider making this a static item");
|
||||
} else {
|
||||
diag.help(
|
||||
"consider making this `Sync` so that it can go in a static item or using a `thread_local`",
|
||||
);
|
||||
}
|
||||
},
|
||||
Source::Assoc { .. } => (),
|
||||
Source::Expr { .. } => {
|
||||
diag.help("assign this const to a local or static variable, and use the variable here");
|
||||
},
|
||||
}
|
||||
});
|
||||
/// The source of a borrow. Both what caused it and where.
|
||||
struct BorrowSource<'tcx> {
|
||||
expr: &'tcx Expr<'tcx>,
|
||||
cause: BorrowCause,
|
||||
}
|
||||
impl<'tcx> BorrowSource<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, expr: &'tcx Expr<'tcx>, cause: BorrowCause) -> Self {
|
||||
// Custom deref and index impls will always have an auto-borrow inserted since we
|
||||
// never work with reference types.
|
||||
let (expr, cause) = if matches!(cause, BorrowCause::AutoBorrow)
|
||||
&& let Node::Expr(parent) = tcx.parent_hir_node(expr.hir_id)
|
||||
{
|
||||
match parent.kind {
|
||||
ExprKind::Unary(UnOp::Deref, _) => (parent, BorrowCause::Deref),
|
||||
ExprKind::Index(..) => (parent, BorrowCause::Index),
|
||||
ExprKind::Field(..) => (parent, BorrowCause::AutoDerefField),
|
||||
_ => (expr, cause),
|
||||
}
|
||||
} else {
|
||||
(expr, cause)
|
||||
};
|
||||
Self { expr, cause }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NonCopyConst<'tcx> {
|
||||
interior_mut: InteriorMut<'tcx>,
|
||||
ignore_tys: DefIdSet,
|
||||
// Cache checked types. We can recurse through a type multiple times so this
|
||||
// can be hit quite frequently.
|
||||
freeze_tys: FxHashMap<Ty<'tcx>, IsFreeze>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTERIOR_MUTABLE_CONST]);
|
||||
@@ -174,332 +261,629 @@ pub struct NonCopyConst<'tcx> {
|
||||
impl<'tcx> NonCopyConst<'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability),
|
||||
ignore_tys: conf
|
||||
.ignore_interior_mutability
|
||||
.iter()
|
||||
.flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty))
|
||||
.collect(),
|
||||
freeze_tys: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_value_unfrozen_raw_inner(cx: &LateContext<'tcx>, val: ty::ValTree<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
// No branch that we check (yet) should continue if val isn't a branch
|
||||
let Some(branched_val) = val.try_to_branch() else {
|
||||
return false;
|
||||
};
|
||||
match *ty.kind() {
|
||||
// the fact that we have to dig into every structs to search enums
|
||||
// leads us to the point checking `UnsafeCell` directly is the only option.
|
||||
ty::Adt(ty_def, ..) if ty_def.is_unsafe_cell() => true,
|
||||
// As of 2022-09-08 miri doesn't track which union field is active so there's no safe way to check the
|
||||
// contained value.
|
||||
ty::Adt(def, ..) if def.is_union() => false,
|
||||
ty::Array(ty, _) => branched_val
|
||||
.iter()
|
||||
.any(|field| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
|
||||
ty::Adt(def, args) if def.is_enum() => {
|
||||
let Some((&variant_valtree, fields)) = branched_val.split_first() else {
|
||||
return false;
|
||||
};
|
||||
let variant_index = variant_valtree.unwrap_leaf();
|
||||
let variant_index = VariantIdx::from_u32(variant_index.to_u32());
|
||||
fields
|
||||
.iter()
|
||||
.copied()
|
||||
.zip(
|
||||
def.variants()[variant_index]
|
||||
/// Checks if a value of the given type is `Freeze`, or may be depending on the value.
|
||||
fn is_ty_freeze(&mut self, tcx: TyCtxt<'tcx>, typing_env: TypingEnv<'tcx>, ty: Ty<'tcx>) -> IsFreeze {
|
||||
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
match self.freeze_tys.entry(ty) {
|
||||
Entry::Occupied(e) => *e.get(),
|
||||
Entry::Vacant(e) => {
|
||||
let e = e.insert(IsFreeze::Yes);
|
||||
if ty.is_freeze(tcx, typing_env) {
|
||||
return IsFreeze::Yes;
|
||||
}
|
||||
let is_freeze = match *ty.kind() {
|
||||
ty::Adt(adt, _) if adt.is_unsafe_cell() => {
|
||||
*e = IsFreeze::No;
|
||||
return IsFreeze::No;
|
||||
},
|
||||
ty::Adt(adt, _) if self.ignore_tys.contains(&adt.did()) => return IsFreeze::Yes,
|
||||
ty::Adt(adt, args) if adt.is_enum() => IsFreeze::from_variants(adt.variants().iter().map(|v| {
|
||||
IsFreeze::from_fields(
|
||||
v.fields
|
||||
.iter()
|
||||
.map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))),
|
||||
)
|
||||
})),
|
||||
// Workaround for `ManuallyDrop`-like unions.
|
||||
ty::Adt(adt, args)
|
||||
if adt.is_union()
|
||||
&& adt.non_enum_variant().fields.iter().any(|f| {
|
||||
tcx.layout_of(typing_env.as_query_input(f.ty(tcx, args)))
|
||||
.is_ok_and(|l| l.layout.size().bytes() == 0)
|
||||
}) =>
|
||||
{
|
||||
return IsFreeze::Yes;
|
||||
},
|
||||
// Rust doesn't have the concept of an active union field so we have
|
||||
// to treat all fields as active.
|
||||
ty::Adt(adt, args) => IsFreeze::from_fields(
|
||||
adt.non_enum_variant()
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ty(cx.tcx, args)),
|
||||
)
|
||||
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, field, ty))
|
||||
.map(|f| self.is_ty_freeze(tcx, typing_env, f.ty(tcx, args))),
|
||||
),
|
||||
ty::Array(ty, _) | ty::Pat(ty, _) => self.is_ty_freeze(tcx, typing_env, ty),
|
||||
ty::Tuple(tys) => {
|
||||
IsFreeze::from_fields(tys.iter().map(|ty| self.is_ty_freeze(tcx, typing_env, ty)))
|
||||
},
|
||||
// Treat type parameters as though they were `Freeze`.
|
||||
ty::Param(_) | ty::Alias(..) => return IsFreeze::Yes,
|
||||
// TODO: check other types.
|
||||
_ => {
|
||||
*e = IsFreeze::No;
|
||||
return IsFreeze::No;
|
||||
},
|
||||
};
|
||||
if !is_freeze.is_freeze() {
|
||||
self.freeze_tys.insert(ty, is_freeze);
|
||||
}
|
||||
is_freeze
|
||||
},
|
||||
ty::Adt(def, args) => branched_val
|
||||
.iter()
|
||||
.zip(def.non_enum_variant().fields.iter().map(|field| field.ty(cx.tcx, args)))
|
||||
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
|
||||
ty::Tuple(tys) => branched_val
|
||||
.iter()
|
||||
.zip(tys)
|
||||
.any(|(field, ty)| Self::is_value_unfrozen_raw_inner(cx, *field, ty)),
|
||||
ty::Alias(ty::Projection, _) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) {
|
||||
Ok(normalized_ty) if ty != normalized_ty => Self::is_value_unfrozen_raw_inner(cx, val, normalized_ty),
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_value_unfrozen_raw(
|
||||
cx: &LateContext<'tcx>,
|
||||
result: Result<Result<ty::ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
result.map_or_else(
|
||||
|err| {
|
||||
// Consider `TooGeneric` cases as being unfrozen.
|
||||
// This causes a false positive where an assoc const whose type is unfrozen
|
||||
// have a value that is a frozen variant with a generic param (an example is
|
||||
// `declare_interior_mutable_const::enums::BothOfCellAndGeneric::GENERIC_VARIANT`).
|
||||
// However, it prevents a number of false negatives that is, I think, important:
|
||||
// 1. assoc consts in trait defs referring to consts of themselves (an example is
|
||||
// `declare_interior_mutable_const::traits::ConcreteTypes::ANOTHER_ATOMIC`).
|
||||
// 2. a path expr referring to assoc consts whose type is doesn't have any frozen variants in trait
|
||||
// defs (i.e. without substitute for `Self`). (e.g. borrowing
|
||||
// `borrow_interior_mutable_const::trait::ConcreteTypes::ATOMIC`)
|
||||
// 3. similar to the false positive above; but the value is an unfrozen variant, or the type has no
|
||||
// enums. (An example is
|
||||
// `declare_interior_mutable_const::enums::BothOfCellAndGeneric::UNFROZEN_VARIANT` and
|
||||
// `declare_interior_mutable_const::enums::BothOfCellAndGeneric::NO_ENUM`).
|
||||
// One might be able to prevent these FNs correctly, and replace this with `false`;
|
||||
// e.g. implementing `has_frozen_variant` described above, and not running this function
|
||||
// when the type doesn't have any frozen variants would be the 'correct' way for the 2nd
|
||||
// case (that actually removes another suboptimal behavior (I won't say 'false positive') where,
|
||||
// similar to 2., but with a frozen variant) (e.g. borrowing
|
||||
// `borrow_interior_mutable_const::enums::AssocConsts::TO_BE_FROZEN_VARIANT`).
|
||||
// I chose this way because unfrozen enums as assoc consts are rare (or, hopefully, none).
|
||||
matches!(err, ErrorHandled::TooGeneric(..))
|
||||
},
|
||||
|val| val.map_or(true, |val| Self::is_value_unfrozen_raw_inner(cx, val, ty)),
|
||||
)
|
||||
}
|
||||
|
||||
fn is_value_unfrozen_poly(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<'tcx>) -> bool {
|
||||
let def_id = body_id.hir_id.owner.to_def_id();
|
||||
let args = ty::GenericArgs::identity_for_item(cx.tcx, def_id);
|
||||
let instance = ty::Instance::new_raw(def_id, args);
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
let typing_env = ty::TypingEnv::post_analysis(cx.tcx, def_id);
|
||||
let result = cx.tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP);
|
||||
Self::is_value_unfrozen_raw(cx, result, ty)
|
||||
}
|
||||
|
||||
fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
|
||||
let args = cx.typeck_results().node_args(hir_id);
|
||||
|
||||
let result = Self::const_eval_resolve(
|
||||
cx.tcx,
|
||||
cx.typing_env(),
|
||||
ty::UnevaluatedConst::new(def_id, args),
|
||||
DUMMY_SP,
|
||||
);
|
||||
Self::is_value_unfrozen_raw(cx, result, ty)
|
||||
}
|
||||
|
||||
pub fn const_eval_resolve(
|
||||
/// Checks if the given constant value is `Freeze`. Returns `Err` if the constant
|
||||
/// cannot be read, but the result depends on the value.
|
||||
fn is_value_freeze(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: ty::TypingEnv<'tcx>,
|
||||
ct: ty::UnevaluatedConst<'tcx>,
|
||||
span: Span,
|
||||
) -> EvalToValTreeResult<'tcx> {
|
||||
match ty::Instance::try_resolve(tcx, typing_env, ct.def, ct.args) {
|
||||
Ok(Some(instance)) => {
|
||||
let cid = GlobalId {
|
||||
instance,
|
||||
promoted: None,
|
||||
};
|
||||
tcx.const_eval_global_id_for_typeck(typing_env, cid, span)
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
val: ConstValue<'tcx>,
|
||||
) -> Result<bool, ()> {
|
||||
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
match self.is_ty_freeze(tcx, typing_env, ty) {
|
||||
IsFreeze::Yes => Ok(true),
|
||||
IsFreeze::Maybe if matches!(ty.kind(), ty::Adt(..) | ty::Array(..) | ty::Tuple(..)) => {
|
||||
for &(val, ty) in tcx
|
||||
.try_destructure_mir_constant_for_user_output(val, ty)
|
||||
.ok_or(())?
|
||||
.fields
|
||||
{
|
||||
if !self.is_value_freeze(tcx, typing_env, ty, val)? {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
},
|
||||
Ok(None) => Err(ErrorHandled::TooGeneric(span)),
|
||||
Err(err) => Err(ErrorHandled::Reported(
|
||||
ReportedErrorInfo::non_const_eval_error(err),
|
||||
span,
|
||||
)),
|
||||
IsFreeze::Maybe | IsFreeze::No => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given expression creates a value which is `Freeze`.
|
||||
///
|
||||
/// This will return `true` if the type is maybe `Freeze`, but it cannot be
|
||||
/// determined for certain from the value.
|
||||
///
|
||||
/// `typing_env` and `gen_args` are from the constant's use site.
|
||||
/// `typeck` and `e` are from the constant's definition site.
|
||||
fn is_init_expr_freeze(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
gen_args: GenericArgsRef<'tcx>,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
) -> bool {
|
||||
// Make sure to instantiate all types coming from `typeck` with `gen_args`.
|
||||
let ty = EarlyBinder::bind(typeck.expr_ty(e)).instantiate(tcx, gen_args);
|
||||
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
match self.is_ty_freeze(tcx, typing_env, ty) {
|
||||
IsFreeze::Yes => true,
|
||||
IsFreeze::No => false,
|
||||
IsFreeze::Maybe => match e.kind {
|
||||
ExprKind::Block(b, _)
|
||||
if !b.targeted_by_break
|
||||
&& b.stmts.is_empty()
|
||||
&& let Some(e) = b.expr =>
|
||||
{
|
||||
self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)
|
||||
},
|
||||
ExprKind::Path(ref p) => {
|
||||
let res = typeck.qpath_res(p, e.hir_id);
|
||||
let gen_args = EarlyBinder::bind(typeck.node_args(e.hir_id)).instantiate(tcx, gen_args);
|
||||
match res {
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst, did)
|
||||
if let Ok(val) =
|
||||
tcx.const_eval_resolve(typing_env, UnevaluatedConst::new(did, gen_args), DUMMY_SP)
|
||||
&& let Ok(is_freeze) = self.is_value_freeze(tcx, typing_env, ty, val) =>
|
||||
{
|
||||
is_freeze
|
||||
},
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst, did)
|
||||
if let Some((typeck, init)) = get_const_hir_value(tcx, typing_env, did, gen_args) =>
|
||||
{
|
||||
self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, init)
|
||||
},
|
||||
// Either this is a unit constructor, or some unknown value.
|
||||
// In either case we consider the value to be `Freeze`.
|
||||
_ => true,
|
||||
}
|
||||
},
|
||||
ExprKind::Call(callee, args)
|
||||
if let ExprKind::Path(p) = &callee.kind
|
||||
&& let res = typeck.qpath_res(p, callee.hir_id)
|
||||
&& matches!(res, Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_)) =>
|
||||
{
|
||||
args.iter()
|
||||
.all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e))
|
||||
},
|
||||
ExprKind::Struct(_, fields, StructTailExpr::None) => fields
|
||||
.iter()
|
||||
.all(|f| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, f.expr)),
|
||||
ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs
|
||||
.iter()
|
||||
.all(|e| self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e)),
|
||||
ExprKind::Repeat(e, _) => self.is_init_expr_freeze(tcx, typing_env, typeck, gen_args, e),
|
||||
_ => true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given expression (or a local projection of it) is both borrowed and
|
||||
/// definitely a non-`Freeze` type.
|
||||
fn is_non_freeze_expr_borrowed(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
mut src_expr: &'tcx Expr<'tcx>,
|
||||
) -> Option<BorrowSource<'tcx>> {
|
||||
let mut parents = tcx.hir_parent_iter(src_expr.hir_id);
|
||||
loop {
|
||||
let ty = typeck.expr_ty(src_expr);
|
||||
// Normalized as we need to check if this is an array later.
|
||||
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
let is_freeze = self.is_ty_freeze(tcx, typing_env, ty);
|
||||
if is_freeze.is_freeze() {
|
||||
return None;
|
||||
}
|
||||
if let [adjust, ..] = typeck.expr_adjustments(src_expr) {
|
||||
return does_adjust_borrow(adjust)
|
||||
.filter(|_| is_freeze.is_not_freeze())
|
||||
.map(|cause| BorrowSource::new(tcx, src_expr, cause));
|
||||
}
|
||||
let Some((_, Node::Expr(use_expr))) = parents.next() else {
|
||||
return None;
|
||||
};
|
||||
match use_expr.kind {
|
||||
ExprKind::Field(..) => {},
|
||||
ExprKind::Index(..) if ty.is_array() => {},
|
||||
ExprKind::AddrOf(..) if is_freeze.is_not_freeze() => {
|
||||
return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow));
|
||||
},
|
||||
// All other expressions use the value.
|
||||
_ => return None,
|
||||
}
|
||||
src_expr = use_expr;
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given value (or a local projection of it) is both borrowed and
|
||||
/// definitely non-`Freeze`. Returns `Err` if the constant cannot be read, but the
|
||||
/// result depends on the value.
|
||||
fn is_non_freeze_val_borrowed(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
mut src_expr: &'tcx Expr<'tcx>,
|
||||
mut val: ConstValue<'tcx>,
|
||||
) -> Result<Option<BorrowSource<'tcx>>, ()> {
|
||||
let mut parents = tcx.hir_parent_iter(src_expr.hir_id);
|
||||
let mut ty = typeck.expr_ty(src_expr);
|
||||
loop {
|
||||
// Normalized as we need to check if this is an array later.
|
||||
ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
if let [adjust, ..] = typeck.expr_adjustments(src_expr) {
|
||||
let res = if let Some(cause) = does_adjust_borrow(adjust)
|
||||
&& !self.is_value_freeze(tcx, typing_env, ty, val)?
|
||||
{
|
||||
Some(BorrowSource::new(tcx, src_expr, cause))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return Ok(res);
|
||||
}
|
||||
// Check only the type here as the result gets cached for each type.
|
||||
if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() {
|
||||
return Ok(None);
|
||||
}
|
||||
let Some((_, Node::Expr(use_expr))) = parents.next() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let next_val = match use_expr.kind {
|
||||
ExprKind::Field(_, name) => {
|
||||
if let Some(idx) = get_field_idx_by_name(ty, name.name) {
|
||||
tcx.try_destructure_mir_constant_for_user_output(val, ty)
|
||||
.ok_or(())?
|
||||
.fields
|
||||
.get(idx)
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
},
|
||||
ExprKind::Index(_, idx, _) if ty.is_array() => {
|
||||
let val = tcx.try_destructure_mir_constant_for_user_output(val, ty).ok_or(())?;
|
||||
if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) {
|
||||
val.fields.get(idx as usize)
|
||||
} else {
|
||||
// It's some value in the array so check all of them.
|
||||
for &(val, _) in val.fields {
|
||||
if let Some(src) =
|
||||
self.is_non_freeze_val_borrowed(tcx, typing_env, typeck, use_expr, val)?
|
||||
{
|
||||
return Ok(Some(src));
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
},
|
||||
ExprKind::AddrOf(..) if !self.is_value_freeze(tcx, typing_env, ty, val)? => {
|
||||
return Ok(Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow)));
|
||||
},
|
||||
// All other expressions use the value.
|
||||
_ => return Ok(None),
|
||||
};
|
||||
src_expr = use_expr;
|
||||
if let Some(&(next_val, next_ty)) = next_val {
|
||||
ty = next_ty;
|
||||
val = next_val;
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks if the given value (or a local projection of it) is both borrowed and
|
||||
/// definitely non-`Freeze`.
|
||||
///
|
||||
/// `typing_env` and `init_args` are from the constant's use site.
|
||||
/// `init_typeck` and `init_expr` are from the constant's definition site.
|
||||
#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
|
||||
fn is_non_freeze_init_borrowed(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
mut src_expr: &'tcx Expr<'tcx>,
|
||||
mut init_typeck: &'tcx TypeckResults<'tcx>,
|
||||
mut init_args: GenericArgsRef<'tcx>,
|
||||
mut init_expr: &'tcx Expr<'tcx>,
|
||||
) -> Option<BorrowSource<'tcx>> {
|
||||
// Make sure to instantiate all types coming from `init_typeck` with `init_args`.
|
||||
let mut parents = tcx.hir_parent_iter(src_expr.hir_id);
|
||||
loop {
|
||||
// First handle any adjustments since they are cheap to check.
|
||||
if let [adjust, ..] = typeck.expr_adjustments(src_expr) {
|
||||
return does_adjust_borrow(adjust)
|
||||
.filter(|_| !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr))
|
||||
.map(|cause| BorrowSource::new(tcx, src_expr, cause));
|
||||
}
|
||||
|
||||
// Then read through constants and blocks on the init expression before
|
||||
// applying the next use expression.
|
||||
loop {
|
||||
match init_expr.kind {
|
||||
ExprKind::Block(b, _)
|
||||
if !b.targeted_by_break
|
||||
&& b.stmts.is_empty()
|
||||
&& let Some(next_init) = b.expr =>
|
||||
{
|
||||
init_expr = next_init;
|
||||
},
|
||||
ExprKind::Path(ref init_path) => {
|
||||
let next_init_args =
|
||||
EarlyBinder::bind(init_typeck.node_args(init_expr.hir_id)).instantiate(tcx, init_args);
|
||||
match init_typeck.qpath_res(init_path, init_expr.hir_id) {
|
||||
Res::Def(DefKind::Ctor(..), _) => return None,
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst, did)
|
||||
if let Ok(val) = tcx.const_eval_resolve(
|
||||
typing_env,
|
||||
UnevaluatedConst::new(did, next_init_args),
|
||||
DUMMY_SP,
|
||||
) && let Ok(res) =
|
||||
self.is_non_freeze_val_borrowed(tcx, typing_env, init_typeck, src_expr, val) =>
|
||||
{
|
||||
return res;
|
||||
},
|
||||
Res::Def(DefKind::Const | DefKind::AssocConst, did)
|
||||
if let Some((next_typeck, value)) =
|
||||
get_const_hir_value(tcx, typing_env, did, next_init_args) =>
|
||||
{
|
||||
init_typeck = next_typeck;
|
||||
init_args = next_init_args;
|
||||
init_expr = value;
|
||||
},
|
||||
// There's no more that we can read from the init expression. Switch to a
|
||||
// type based check.
|
||||
_ => {
|
||||
return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, src_expr);
|
||||
},
|
||||
}
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
// Then a type check. Note we only check the type here as the result
|
||||
// gets cached.
|
||||
let ty = EarlyBinder::bind(typeck.expr_ty(src_expr)).instantiate(tcx, init_args);
|
||||
// Normalized as we need to check if this is an array later.
|
||||
let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
|
||||
if self.is_ty_freeze(tcx, typing_env, ty).is_freeze() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Finally reduce the init expression using the next use expression.
|
||||
let Some((_, Node::Expr(use_expr))) = parents.next() else {
|
||||
return None;
|
||||
};
|
||||
init_expr = match &use_expr.kind {
|
||||
ExprKind::Field(_, name) => match init_expr.kind {
|
||||
ExprKind::Struct(_, fields, _)
|
||||
if let Some(field) = fields.iter().find(|f| f.ident.name == name.name) =>
|
||||
{
|
||||
field.expr
|
||||
},
|
||||
ExprKind::Tup(fields)
|
||||
if let Ok(idx) = name.as_str().parse::<usize>()
|
||||
&& let Some(field) = fields.get(idx) =>
|
||||
{
|
||||
field
|
||||
},
|
||||
ExprKind::Call(callee, args)
|
||||
if let ExprKind::Path(callee_path) = &callee.kind
|
||||
&& matches!(
|
||||
init_typeck.qpath_res(callee_path, callee.hir_id),
|
||||
Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(_)
|
||||
)
|
||||
&& let Ok(idx) = name.as_str().parse::<usize>()
|
||||
&& let Some(arg) = args.get(idx) =>
|
||||
{
|
||||
arg
|
||||
},
|
||||
// Revert to a type based check as we don't know the field's value.
|
||||
_ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr),
|
||||
},
|
||||
ExprKind::Index(_, idx, _) if ty.is_array() => match init_expr.kind {
|
||||
ExprKind::Array(fields) => {
|
||||
if let Some(Constant::Int(idx)) = ConstEvalCtxt::with_env(tcx, typing_env, typeck).eval(idx) {
|
||||
// If the index is out of bounds it means the code
|
||||
// unconditionally panics. In that case there is no borrow.
|
||||
fields.get(idx as usize)?
|
||||
} else {
|
||||
// Unknown index, just run the check for all values.
|
||||
return fields.iter().find_map(|f| {
|
||||
self.is_non_freeze_init_borrowed(
|
||||
tcx,
|
||||
typing_env,
|
||||
typeck,
|
||||
use_expr,
|
||||
init_typeck,
|
||||
init_args,
|
||||
f,
|
||||
)
|
||||
});
|
||||
}
|
||||
},
|
||||
// Just assume the index expression doesn't panic here.
|
||||
ExprKind::Repeat(field, _) => field,
|
||||
_ => return self.is_non_freeze_expr_borrowed(tcx, typing_env, typeck, use_expr),
|
||||
},
|
||||
ExprKind::AddrOf(..)
|
||||
if !self.is_init_expr_freeze(tcx, typing_env, init_typeck, init_args, init_expr) =>
|
||||
{
|
||||
return Some(BorrowSource::new(tcx, use_expr, BorrowCause::Borrow));
|
||||
},
|
||||
// All other expressions use the value.
|
||||
_ => return None,
|
||||
};
|
||||
src_expr = use_expr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
|
||||
if let ItemKind::Const(.., body_id) = it.kind {
|
||||
let ty = cx.tcx.type_of(it.owner_id).instantiate_identity();
|
||||
if !ignored_macro(cx, it)
|
||||
&& self.interior_mut.is_interior_mut_ty(cx, ty)
|
||||
&& Self::is_value_unfrozen_poly(cx, body_id, ty)
|
||||
{
|
||||
lint(cx, Source::Item { item: it.span, ty });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'_>) {
|
||||
if let TraitItemKind::Const(_, body_id_opt) = &trait_item.kind {
|
||||
let ty = cx.tcx.type_of(trait_item.owner_id).instantiate_identity();
|
||||
|
||||
// Normalize assoc types because ones originated from generic params
|
||||
// bounded other traits could have their bound.
|
||||
let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty);
|
||||
if self.interior_mut.is_interior_mut_ty(cx, normalized)
|
||||
// When there's no default value, lint it only according to its type;
|
||||
// in other words, lint consts whose value *could* be unfrozen, not definitely is.
|
||||
// This feels inconsistent with how the lint treats generic types,
|
||||
// which avoids linting types which potentially become unfrozen.
|
||||
// One could check whether an unfrozen type have a *frozen variant*
|
||||
// (like `body_id_opt.map_or_else(|| !has_frozen_variant(...), ...)`),
|
||||
// and do the same as the case of generic types at impl items.
|
||||
// Note that it isn't sufficient to check if it has an enum
|
||||
// since all of that enum's variants can be unfrozen:
|
||||
// i.e. having an enum doesn't necessary mean a type has a frozen variant.
|
||||
// And, implementing it isn't a trivial task; it'll probably end up
|
||||
// re-implementing the trait predicate evaluation specific to `Freeze`.
|
||||
&& body_id_opt.is_none_or(|body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized))
|
||||
{
|
||||
lint(cx, Source::Assoc { item: trait_item.span });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
|
||||
if let ImplItemKind::Const(_, body_id) = &impl_item.kind {
|
||||
let item_def_id = cx.tcx.hir_get_parent_item(impl_item.hir_id()).def_id;
|
||||
let item = cx.tcx.hir_expect_item(item_def_id);
|
||||
|
||||
match &item.kind {
|
||||
ItemKind::Impl(Impl {
|
||||
of_trait: Some(of_trait_ref),
|
||||
..
|
||||
}) => {
|
||||
if let Some(of_trait_def_id) = of_trait_ref.trait_def_id()
|
||||
// Lint a trait impl item only when the definition is a generic type,
|
||||
// assuming an assoc const is not meant to be an interior mutable type.
|
||||
&& let Some(of_assoc_item) = cx
|
||||
.tcx
|
||||
.associated_item(impl_item.owner_id)
|
||||
.trait_item_def_id
|
||||
&& cx
|
||||
.tcx
|
||||
.layout_of(ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id).as_query_input(
|
||||
// Normalize assoc types because ones originated from generic params
|
||||
// bounded other traits could have their bound at the trait defs;
|
||||
// and, in that case, the definition is *not* generic.
|
||||
cx.tcx.normalize_erasing_regions(
|
||||
ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id),
|
||||
cx.tcx.type_of(of_assoc_item).instantiate_identity(),
|
||||
),
|
||||
))
|
||||
.is_err()
|
||||
// If there were a function like `has_frozen_variant` described above,
|
||||
// we should use here as a frozen variant is a potential to be frozen
|
||||
// similar to unknown layouts.
|
||||
// e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
|
||||
&& let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity()
|
||||
&& let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty)
|
||||
&& self.interior_mut.is_interior_mut_ty(cx, normalized)
|
||||
&& Self::is_value_unfrozen_poly(cx, *body_id, normalized)
|
||||
{
|
||||
lint(cx, Source::Assoc { item: impl_item.span });
|
||||
}
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Const(ident, .., body_id) = item.kind
|
||||
&& !ident.is_special()
|
||||
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||
&& match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
|
||||
IsFreeze::No => true,
|
||||
IsFreeze::Yes => false,
|
||||
IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
|
||||
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
|
||||
_ => !self.is_init_expr_freeze(
|
||||
cx.tcx,
|
||||
cx.typing_env(),
|
||||
cx.tcx.typeck(item.owner_id),
|
||||
GenericArgs::identity_for_item(cx.tcx, item.owner_id),
|
||||
cx.tcx.hir_body(body_id).value,
|
||||
),
|
||||
},
|
||||
ItemKind::Impl(Impl { of_trait: None, .. }) => {
|
||||
let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity();
|
||||
// Normalize assoc types originated from generic params.
|
||||
let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty);
|
||||
|
||||
if self.interior_mut.is_interior_mut_ty(cx, normalized)
|
||||
&& Self::is_value_unfrozen_poly(cx, *body_id, normalized)
|
||||
{
|
||||
lint(cx, Source::Assoc { item: impl_item.span });
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Path(qpath) = &expr.kind {
|
||||
// Only lint if we use the const item inside a function.
|
||||
if is_in_const_context(cx) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure it is a const item.
|
||||
let Res::Def(DefKind::Const | DefKind::AssocConst, item_def_id) = cx.qpath_res(qpath, expr.hir_id) else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Climb up to resolve any field access and explicit referencing.
|
||||
let mut cur_expr = expr;
|
||||
let mut dereferenced_expr = expr;
|
||||
let mut needs_check_adjustment = true;
|
||||
loop {
|
||||
let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id);
|
||||
if parent_id == cur_expr.hir_id {
|
||||
break;
|
||||
}
|
||||
if let Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id) {
|
||||
match &parent_expr.kind {
|
||||
ExprKind::AddrOf(..) => {
|
||||
// `&e` => `e` must be referenced.
|
||||
needs_check_adjustment = false;
|
||||
},
|
||||
ExprKind::Field(..) => {
|
||||
needs_check_adjustment = true;
|
||||
|
||||
// Check whether implicit dereferences happened;
|
||||
// if so, no need to go further up
|
||||
// because of the same reason as the `ExprKind::Unary` case.
|
||||
if cx
|
||||
.typeck_results()
|
||||
.expr_adjustments(dereferenced_expr)
|
||||
.iter()
|
||||
.any(|adj| matches!(adj.kind, Adjust::Deref(_)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
dereferenced_expr = parent_expr;
|
||||
},
|
||||
ExprKind::Index(e, _, _) if ptr::eq(&raw const **e, cur_expr) => {
|
||||
// `e[i]` => desugared to `*Index::index(&e, i)`,
|
||||
// meaning `e` must be referenced.
|
||||
// no need to go further up since a method call is involved now.
|
||||
needs_check_adjustment = false;
|
||||
break;
|
||||
},
|
||||
ExprKind::Unary(UnOp::Deref, _) => {
|
||||
// `*e` => desugared to `*Deref::deref(&e)`,
|
||||
// meaning `e` must be referenced.
|
||||
// no need to go further up since a method call is involved now.
|
||||
needs_check_adjustment = false;
|
||||
break;
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
cur_expr = parent_expr;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let ty = if needs_check_adjustment {
|
||||
let adjustments = cx.typeck_results().expr_adjustments(dereferenced_expr);
|
||||
if let Some(i) = adjustments
|
||||
.iter()
|
||||
.position(|adj| matches!(adj.kind, Adjust::Borrow(_) | Adjust::Deref(_)))
|
||||
{
|
||||
if i == 0 {
|
||||
cx.typeck_results().expr_ty(dereferenced_expr)
|
||||
&& !item.span.in_external_macro(cx.sess().source_map())
|
||||
// Only needed when compiling `std`
|
||||
&& !is_thread_local(cx, item)
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
DECLARE_INTERIOR_MUTABLE_CONST,
|
||||
ident.span,
|
||||
"named constant with interior mutability",
|
||||
|diag| {
|
||||
let Some(sync_trait) = cx.tcx.lang_items().sync_trait() else {
|
||||
return;
|
||||
};
|
||||
if implements_trait(cx, ty, sync_trait, &[]) {
|
||||
diag.help("did you mean to make this a `static` item");
|
||||
} else {
|
||||
adjustments[i - 1].target
|
||||
diag.help("did you mean to make this a `thread_local!` item");
|
||||
}
|
||||
} else {
|
||||
// No borrow adjustments means the entire const is moved.
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
cx.typeck_results().expr_ty(dereferenced_expr)
|
||||
};
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if self.interior_mut.is_interior_mut_ty(cx, ty)
|
||||
&& Self::is_value_unfrozen_expr(cx, expr.hir_id, item_def_id, ty)
|
||||
{
|
||||
lint(cx, Source::Expr { expr: expr.span });
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
|
||||
if let TraitItemKind::Const(_, body_id_opt) = item.kind
|
||||
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||
&& match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
|
||||
IsFreeze::No => true,
|
||||
IsFreeze::Maybe if let Some(body_id) = body_id_opt => {
|
||||
match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
|
||||
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => {
|
||||
!is_freeze
|
||||
},
|
||||
_ => !self.is_init_expr_freeze(
|
||||
cx.tcx,
|
||||
cx.typing_env(),
|
||||
cx.tcx.typeck(item.owner_id),
|
||||
GenericArgs::identity_for_item(cx.tcx, item.owner_id),
|
||||
cx.tcx.hir_body(body_id).value,
|
||||
),
|
||||
}
|
||||
},
|
||||
IsFreeze::Yes | IsFreeze::Maybe => false,
|
||||
}
|
||||
&& !item.span.in_external_macro(cx.sess().source_map())
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
DECLARE_INTERIOR_MUTABLE_CONST,
|
||||
item.ident.span,
|
||||
"named constant with interior mutability",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
|
||||
if let ImplItemKind::Const(_, body_id) = item.kind
|
||||
&& let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
|
||||
&& match self.is_ty_freeze(cx.tcx, cx.typing_env(), ty) {
|
||||
IsFreeze::Yes => false,
|
||||
IsFreeze::No => {
|
||||
// If this is a trait impl, check if the trait definition is the source
|
||||
// of the cell.
|
||||
if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id())
|
||||
&& let ItemKind::Impl(impl_block) = parent_item.kind
|
||||
&& let Some(of_trait) = impl_block.of_trait
|
||||
&& let Some(trait_id) = of_trait.trait_def_id()
|
||||
{
|
||||
// Replace all instances of `<Self as Trait>::AssocType` with the
|
||||
// unit type and check again. If the result is the same then the
|
||||
// trait definition is the cause.
|
||||
let ty = (ReplaceAssocFolder {
|
||||
tcx: cx.tcx,
|
||||
trait_id,
|
||||
self_ty: cx.tcx.type_of(parent_item.owner_id).instantiate_identity(),
|
||||
})
|
||||
.fold_ty(cx.tcx.type_of(item.owner_id).instantiate_identity());
|
||||
// `ty` may not be normalizable, but that should be fine.
|
||||
!self.is_ty_freeze(cx.tcx, cx.typing_env(), ty).is_not_freeze()
|
||||
} else {
|
||||
true
|
||||
}
|
||||
},
|
||||
// Even if this is from a trait, there are values which don't have
|
||||
// interior mutability.
|
||||
IsFreeze::Maybe => match cx.tcx.const_eval_poly(item.owner_id.to_def_id()) {
|
||||
Ok(val) if let Ok(is_freeze) = self.is_value_freeze(cx.tcx, cx.typing_env(), ty, val) => !is_freeze,
|
||||
_ => !self.is_init_expr_freeze(
|
||||
cx.tcx,
|
||||
cx.typing_env(),
|
||||
cx.tcx.typeck(item.owner_id),
|
||||
GenericArgs::identity_for_item(cx.tcx, item.owner_id),
|
||||
cx.tcx.hir_body(body_id).value,
|
||||
),
|
||||
},
|
||||
}
|
||||
&& !item.span.in_external_macro(cx.sess().source_map())
|
||||
{
|
||||
span_lint(
|
||||
cx,
|
||||
DECLARE_INTERIOR_MUTABLE_CONST,
|
||||
item.ident.span,
|
||||
"named constant with interior mutability",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Path(qpath) = &e.kind
|
||||
&& let typeck = cx.typeck_results()
|
||||
&& let Res::Def(DefKind::Const | DefKind::AssocConst, did) = typeck.qpath_res(qpath, e.hir_id)
|
||||
// As of `1.80` constant contexts can't borrow any type with interior mutability
|
||||
&& !is_in_const_context(cx)
|
||||
&& !self.is_ty_freeze(cx.tcx, cx.typing_env(), typeck.expr_ty(e)).is_freeze()
|
||||
&& let Some(borrow_src) = {
|
||||
// The extra block helps formatting a lot.
|
||||
if let Ok(val) = cx.tcx.const_eval_resolve(
|
||||
cx.typing_env(),
|
||||
UnevaluatedConst::new(did, typeck.node_args(e.hir_id)),
|
||||
DUMMY_SP,
|
||||
) && let Ok(src) = self.is_non_freeze_val_borrowed(cx.tcx, cx.typing_env(), typeck, e, val)
|
||||
{
|
||||
src
|
||||
} else if let init_args = typeck.node_args(e.hir_id)
|
||||
&& let Some((init_typeck, init)) = get_const_hir_value(cx.tcx, cx.typing_env(), did, init_args)
|
||||
{
|
||||
self.is_non_freeze_init_borrowed(cx.tcx, cx.typing_env(), typeck, e, init_typeck, init_args, init)
|
||||
} else {
|
||||
self.is_non_freeze_expr_borrowed(cx.tcx, cx.typing_env(), typeck, e)
|
||||
}
|
||||
}
|
||||
&& !borrow_src.expr.span.in_external_macro(cx.sess().source_map())
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
BORROW_INTERIOR_MUTABLE_CONST,
|
||||
borrow_src.expr.span,
|
||||
"borrow of a named constant with interior mutability",
|
||||
|diag| {
|
||||
if let Some(note) = borrow_src.cause.note() {
|
||||
diag.note(note);
|
||||
}
|
||||
diag.help("this lint can be silenced by assigning the value to a local variable before borrowing");
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool {
|
||||
struct ReplaceAssocFolder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
}
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceAssocFolder<'tcx> {
|
||||
fn cx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if let ty::Alias(AliasTyKind::Projection, ty) = ty.kind()
|
||||
&& ty.trait_def_id(self.tcx) == self.trait_id
|
||||
&& ty.self_ty() == self.self_ty
|
||||
{
|
||||
self.tcx.types.unit
|
||||
} else {
|
||||
ty.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_thread_local(cx: &LateContext<'_>, it: &Item<'_>) -> bool {
|
||||
macro_backtrace(it.span).any(|macro_call| {
|
||||
matches!(
|
||||
cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||
@@ -507,3 +891,42 @@ fn ignored_macro(cx: &LateContext<'_>, it: &Item<'_>) -> bool {
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if the adjustment causes a borrow of the original value. Returns
|
||||
/// `None` if the value is consumed instead of borrowed.
|
||||
fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option<BorrowCause> {
|
||||
match adjust.kind {
|
||||
Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow),
|
||||
// Custom deref calls `<T as Deref>::deref(&x)` resulting in a borrow.
|
||||
Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref),
|
||||
// All other adjustments read the value.
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to get the value of a constant as a HIR expression. Also gets the
|
||||
/// `TypeckResults` associated with the constant's body.
|
||||
fn get_const_hir_value<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typing_env: TypingEnv<'tcx>,
|
||||
did: DefId,
|
||||
args: GenericArgsRef<'tcx>,
|
||||
) -> Option<(&'tcx TypeckResults<'tcx>, &'tcx Expr<'tcx>)> {
|
||||
let did = did.as_local()?;
|
||||
let (did, body_id) = match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) {
|
||||
Node::Item(item) if let ItemKind::Const(.., body_id) = item.kind => (did, body_id),
|
||||
Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id),
|
||||
Node::TraitItem(_)
|
||||
if let Ok(Some(inst)) = Instance::try_resolve(tcx, typing_env, did.into(), args)
|
||||
&& let Some(did) = inst.def_id().as_local() =>
|
||||
{
|
||||
match tcx.hir_node(tcx.local_def_id_to_hir_id(did)) {
|
||||
Node::ImplItem(item) if let ImplItemKind::Const(.., body_id) = item.kind => (did, body_id),
|
||||
Node::TraitItem(item) if let TraitItemKind::Const(.., Some(body_id)) = item.kind => (did, body_id),
|
||||
_ => return None,
|
||||
}
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
Some((tcx.typeck(did), tcx.hir_body(body_id).value))
|
||||
}
|
||||
|
||||
@@ -1361,3 +1361,14 @@ pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
|| ty.is_array()
|
||||
|| matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did()))
|
||||
}
|
||||
|
||||
/// Gets the index of a field by name.
|
||||
pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option<usize> {
|
||||
match *ty.kind() {
|
||||
ty::Adt(def, _) if def.is_union() || def.is_struct() => {
|
||||
def.non_enum_variant().fields.iter().position(|f| f.name == name)
|
||||
},
|
||||
ty::Tuple(_) => name.as_str().parse::<usize>().ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
//@aux-build:interior_mutable_const.rs
|
||||
|
||||
#![deny(clippy::borrow_interior_mutable_const)]
|
||||
#![allow(
|
||||
clippy::declare_interior_mutable_const,
|
||||
clippy::out_of_bounds_indexing,
|
||||
const_item_mutation,
|
||||
unconditional_panic
|
||||
)]
|
||||
|
||||
use core::cell::{Cell, UnsafeCell};
|
||||
use core::ops::{Deref, Index};
|
||||
|
||||
trait ConstDefault {
|
||||
const DEFAULT: Self;
|
||||
}
|
||||
impl ConstDefault for u32 {
|
||||
const DEFAULT: Self = 0;
|
||||
}
|
||||
impl<T: ConstDefault> ConstDefault for Cell<T> {
|
||||
const DEFAULT: Self = Cell::new(T::DEFAULT);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
{
|
||||
const C: String = String::new();
|
||||
let _ = C;
|
||||
let _ = &C;
|
||||
let _ = C.len();
|
||||
let _ = &*C;
|
||||
}
|
||||
{
|
||||
const C: UnsafeCell<u32> = UnsafeCell::new(0);
|
||||
let _ = C;
|
||||
let _ = &C; //~ borrow_interior_mutable_const
|
||||
let _ = C.into_inner();
|
||||
let _ = C.get(); //~ borrow_interior_mutable_const
|
||||
}
|
||||
{
|
||||
const C: Cell<u32> = Cell::new(0);
|
||||
let _ = C;
|
||||
let _ = &C; //~ borrow_interior_mutable_const
|
||||
let _ = &mut C; //~ borrow_interior_mutable_const
|
||||
let _ = C.into_inner();
|
||||
|
||||
let local = C;
|
||||
C.swap(&local) //~ borrow_interior_mutable_const
|
||||
}
|
||||
{
|
||||
const C: [(Cell<u32>,); 1] = [(Cell::new(0),)];
|
||||
let _ = C;
|
||||
let _ = &C; //~ borrow_interior_mutable_const
|
||||
let _ = &C[0]; //~ borrow_interior_mutable_const
|
||||
let _ = &C[0].0; //~ borrow_interior_mutable_const
|
||||
C[0].0.set(1); //~ borrow_interior_mutable_const
|
||||
}
|
||||
{
|
||||
struct S(Cell<u32>);
|
||||
impl S {
|
||||
const C: Self = Self(Cell::new(0));
|
||||
}
|
||||
impl Deref for S {
|
||||
type Target = Cell<u32>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
let _ = S::C;
|
||||
let _ = S::C.0;
|
||||
let _ = &S::C; //~ borrow_interior_mutable_const
|
||||
let _ = &S::C.0; //~ borrow_interior_mutable_const
|
||||
S::C.set(1); //~ borrow_interior_mutable_const
|
||||
let _ = &*S::C; //~ borrow_interior_mutable_const
|
||||
(*S::C).set(1); //~ borrow_interior_mutable_const
|
||||
}
|
||||
{
|
||||
enum E {
|
||||
Cell(Cell<u32>),
|
||||
Other,
|
||||
}
|
||||
const CELL: E = E::Cell(Cell::new(0));
|
||||
const OTHER: E = E::Other;
|
||||
|
||||
let _ = CELL;
|
||||
let _ = &CELL; //~ borrow_interior_mutable_const
|
||||
let E::Cell(_) = CELL else {
|
||||
return;
|
||||
};
|
||||
|
||||
let _ = OTHER;
|
||||
let _ = &OTHER;
|
||||
let E::Cell(ref _x) = OTHER else {
|
||||
return;
|
||||
};
|
||||
}
|
||||
{
|
||||
struct S<T> {
|
||||
cell: (Cell<T>, u32),
|
||||
other: Option<T>,
|
||||
}
|
||||
impl<T: ConstDefault + Copy> S<T> {
|
||||
const C: Self = Self {
|
||||
cell: (Cell::<T>::DEFAULT, 0),
|
||||
other: Some(T::DEFAULT),
|
||||
};
|
||||
|
||||
fn f() {
|
||||
let _ = Self::C;
|
||||
let _ = &Self::C; //~ borrow_interior_mutable_const
|
||||
let _ = Self::C.other;
|
||||
let _ = &Self::C.other;
|
||||
let _ = &Self::C.cell; //~ borrow_interior_mutable_const
|
||||
let _ = &Self::C.cell.0; //~ borrow_interior_mutable_const
|
||||
Self::C.cell.0.set(T::DEFAULT); //~ borrow_interior_mutable_const
|
||||
let _ = &Self::C.cell.1;
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
trait T {
|
||||
const VALUE: Option<Cell<u32>> = Some(Cell::new(0));
|
||||
}
|
||||
impl T for u32 {}
|
||||
impl T for i32 {
|
||||
const VALUE: Option<Cell<u32>> = None;
|
||||
}
|
||||
|
||||
let _ = &u32::VALUE; //~ borrow_interior_mutable_const
|
||||
let _ = &i32::VALUE;
|
||||
}
|
||||
{
|
||||
trait Trait<T: ConstDefault> {
|
||||
type T<U: ConstDefault>: ConstDefault;
|
||||
const VALUE: Option<Self::T<T>> = Some(Self::T::<T>::DEFAULT);
|
||||
}
|
||||
impl<T: ConstDefault> Trait<T> for u32 {
|
||||
type T<U: ConstDefault> = Cell<U>;
|
||||
}
|
||||
impl<T: ConstDefault> Trait<T> for i32 {
|
||||
type T<U: ConstDefault> = Cell<U>;
|
||||
const VALUE: Option<Cell<T>> = None;
|
||||
}
|
||||
|
||||
fn f<T: ConstDefault>() {
|
||||
let _ = &<u32 as Trait<T>>::VALUE; //~ borrow_interior_mutable_const
|
||||
let _ = &<i32 as Trait<T>>::VALUE;
|
||||
}
|
||||
}
|
||||
{
|
||||
trait Trait {
|
||||
const UNFROZEN: Option<Cell<u32>> = Some(Cell::new(0));
|
||||
const FROZEN: Option<Cell<u32>> = None;
|
||||
const NON_FREEZE: u32 = 0;
|
||||
}
|
||||
fn f<T: Trait>() {
|
||||
// None of these are guaranteed to be frozen, so don't lint.
|
||||
let _ = &T::UNFROZEN;
|
||||
let _ = &T::FROZEN;
|
||||
let _ = &T::NON_FREEZE;
|
||||
}
|
||||
}
|
||||
{
|
||||
struct S([Option<Cell<u32>>; 2]);
|
||||
impl Index<usize> for S {
|
||||
type Output = Option<Cell<u32>>;
|
||||
fn index(&self, idx: usize) -> &Self::Output {
|
||||
&self.0[idx]
|
||||
}
|
||||
}
|
||||
|
||||
const C: S = S([Some(Cell::new(0)), None]);
|
||||
let _ = &C; //~ borrow_interior_mutable_const
|
||||
let _ = &C[0]; //~ borrow_interior_mutable_const
|
||||
let _ = &C.0[0]; //~ borrow_interior_mutable_const
|
||||
let _ = &C.0[1];
|
||||
}
|
||||
{
|
||||
const C: [Option<Cell<u32>>; 2] = [None, None];
|
||||
let _ = &C[0];
|
||||
let _ = &C[1];
|
||||
let _ = &C[2];
|
||||
|
||||
fn f(i: usize) {
|
||||
let _ = &C[i];
|
||||
}
|
||||
}
|
||||
{
|
||||
const C: [Option<Cell<u32>>; 2] = [None, Some(Cell::new(0))];
|
||||
let _ = &C[0];
|
||||
let _ = &C[1]; //~ borrow_interior_mutable_const
|
||||
let _ = &C[2];
|
||||
|
||||
fn f(i: usize) {
|
||||
let _ = &C[i]; //~ borrow_interior_mutable_const
|
||||
}
|
||||
}
|
||||
{
|
||||
let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const
|
||||
let _ = &interior_mutable_const::WRAPPED_PRIVATE_FROZEN_VARIANT;
|
||||
}
|
||||
{
|
||||
type Cell2<T> = Cell<T>;
|
||||
type MyCell = Cell2<u32>;
|
||||
struct S(Option<MyCell>);
|
||||
trait T {
|
||||
type Assoc;
|
||||
}
|
||||
struct S2<T>(T, T, u32);
|
||||
impl T for S {
|
||||
type Assoc = S2<Self>;
|
||||
}
|
||||
type Assoc<X> = <X as T>::Assoc;
|
||||
impl S {
|
||||
const VALUE: Assoc<Self> = S2(Self(None), Self(Some(Cell::new(0))), 0);
|
||||
}
|
||||
let _ = &S::VALUE; //~ borrow_interior_mutable_const
|
||||
let _ = &S::VALUE.0;
|
||||
let _ = &S::VALUE.1; //~ borrow_interior_mutable_const
|
||||
let _ = &S::VALUE.2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:35:17
|
||||
|
|
||||
LL | let _ = &C;
|
||||
| ^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:3:9
|
||||
|
|
||||
LL | #![deny(clippy::borrow_interior_mutable_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:37:17
|
||||
|
|
||||
LL | let _ = C.get();
|
||||
| ^
|
||||
|
|
||||
= note: there is a compiler inserted borrow here
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:42:17
|
||||
|
|
||||
LL | let _ = &C;
|
||||
| ^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:43:17
|
||||
|
|
||||
LL | let _ = &mut C;
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:47:9
|
||||
|
|
||||
LL | C.swap(&local)
|
||||
| ^
|
||||
|
|
||||
= note: there is a compiler inserted borrow here
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:52:17
|
||||
|
|
||||
LL | let _ = &C;
|
||||
| ^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:53:17
|
||||
|
|
||||
LL | let _ = &C[0];
|
||||
| ^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:54:17
|
||||
|
|
||||
LL | let _ = &C[0].0;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:55:9
|
||||
|
|
||||
LL | C[0].0.set(1);
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: there is a compiler inserted borrow here
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:70:17
|
||||
|
|
||||
LL | let _ = &S::C;
|
||||
| ^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:71:17
|
||||
|
|
||||
LL | let _ = &S::C.0;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:72:9
|
||||
|
|
||||
LL | S::C.set(1);
|
||||
| ^^^^
|
||||
|
|
||||
= note: there is a compiler inserted call to `Deref::deref` here
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:73:18
|
||||
|
|
||||
LL | let _ = &*S::C;
|
||||
| ^^^^^
|
||||
|
|
||||
= note: this deref expression is a call to `Deref::deref`
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:74:9
|
||||
|
|
||||
LL | (*S::C).set(1);
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: this deref expression is a call to `Deref::deref`
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:85:17
|
||||
|
|
||||
LL | let _ = &CELL;
|
||||
| ^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:109:25
|
||||
|
|
||||
LL | let _ = &Self::C;
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:112:25
|
||||
|
|
||||
LL | let _ = &Self::C.cell;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:113:25
|
||||
|
|
||||
LL | let _ = &Self::C.cell.0;
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:114:17
|
||||
|
|
||||
LL | Self::C.cell.0.set(T::DEFAULT);
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: there is a compiler inserted borrow here
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:128:17
|
||||
|
|
||||
LL | let _ = &u32::VALUE;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:145:21
|
||||
|
|
||||
LL | let _ = &<u32 as Trait<T>>::VALUE;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:172:17
|
||||
|
|
||||
LL | let _ = &C;
|
||||
| ^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:173:18
|
||||
|
|
||||
LL | let _ = &C[0];
|
||||
| ^^^^
|
||||
|
|
||||
= note: this index expression is a call to `Index::index`
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:174:17
|
||||
|
|
||||
LL | let _ = &C.0[0];
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:190:17
|
||||
|
|
||||
LL | let _ = &C[1];
|
||||
| ^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:194:21
|
||||
|
|
||||
LL | let _ = &C[i];
|
||||
| ^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:198:17
|
||||
|
|
||||
LL | let _ = &interior_mutable_const::WRAPPED_PRIVATE_UNFROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:216:17
|
||||
|
|
||||
LL | let _ = &S::VALUE;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: borrow of a named constant with interior mutability
|
||||
--> tests/ui/borrow_interior_mutable_const.rs:218:17
|
||||
|
|
||||
LL | let _ = &S::VALUE.1;
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: this lint can be silenced by assigning the value to a local variable before borrowing
|
||||
|
||||
error: aborting due to 29 previous errors
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
//@aux-build:helper.rs
|
||||
|
||||
#![deny(clippy::borrow_interior_mutable_const)]
|
||||
#![allow(clippy::declare_interior_mutable_const)]
|
||||
|
||||
// this file (mostly) replicates its `declare` counterpart. Please see it for more discussions.
|
||||
|
||||
extern crate helper;
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
enum OptionalCell {
|
||||
Unfrozen(Cell<bool>),
|
||||
Frozen,
|
||||
}
|
||||
|
||||
const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
|
||||
const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
|
||||
|
||||
fn borrow_optional_cell() {
|
||||
let _ = &UNFROZEN_VARIANT; //~ ERROR: interior mutability
|
||||
let _ = &FROZEN_VARIANT;
|
||||
}
|
||||
|
||||
trait AssocConsts {
|
||||
const TO_BE_UNFROZEN_VARIANT: OptionalCell;
|
||||
const TO_BE_FROZEN_VARIANT: OptionalCell;
|
||||
|
||||
const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
|
||||
const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
|
||||
|
||||
fn function() {
|
||||
// This is the "suboptimal behavior" mentioned in `is_value_unfrozen`
|
||||
// caused by a similar reason to unfrozen types without any default values
|
||||
// get linted even if it has frozen variants'.
|
||||
let _ = &Self::TO_BE_FROZEN_VARIANT; //~ ERROR: interior mutability
|
||||
|
||||
// The lint ignores default values because an impl of this trait can set
|
||||
// an unfrozen variant to `DEFAULTED_ON_FROZEN_VARIANT` and use the default impl for `function`.
|
||||
let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; //~ ERROR: interior mutability
|
||||
}
|
||||
}
|
||||
|
||||
impl AssocConsts for u64 {
|
||||
const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
|
||||
const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
|
||||
|
||||
fn function() {
|
||||
let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
|
||||
let _ = &<Self as AssocConsts>::TO_BE_FROZEN_VARIANT;
|
||||
let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR: interior mutability
|
||||
let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT;
|
||||
}
|
||||
}
|
||||
|
||||
trait AssocTypes {
|
||||
type ToBeUnfrozen;
|
||||
|
||||
const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
|
||||
const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
|
||||
|
||||
// there's no need to test here because it's the exactly same as `trait::AssocTypes`
|
||||
fn function();
|
||||
}
|
||||
|
||||
impl AssocTypes for u64 {
|
||||
type ToBeUnfrozen = AtomicUsize;
|
||||
|
||||
const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4));
|
||||
const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
|
||||
|
||||
fn function() {
|
||||
let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
|
||||
let _ = &<Self as AssocTypes>::TO_BE_FROZEN_VARIANT;
|
||||
}
|
||||
}
|
||||
|
||||
enum BothOfCellAndGeneric<T> {
|
||||
Unfrozen(Cell<*const T>),
|
||||
Generic(*const T),
|
||||
Frozen(usize),
|
||||
}
|
||||
|
||||
impl<T> BothOfCellAndGeneric<T> {
|
||||
const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
|
||||
const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null());
|
||||
const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::UNFROZEN_VARIANT; //~ ERROR: interior mutability
|
||||
let _ = &Self::GENERIC_VARIANT; //~ ERROR: interior mutability
|
||||
let _ = &Self::FROZEN_VARIANT;
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// constants defined in foreign crates
|
||||
let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR: interior mutability
|
||||
let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:22:14
|
||||
|
|
||||
LL | let _ = &UNFROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:3:9
|
||||
|
|
||||
LL | #![deny(clippy::borrow_interior_mutable_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:37:18
|
||||
|
|
||||
LL | let _ = &Self::TO_BE_FROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:41:18
|
||||
|
|
||||
LL | let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:50:18
|
||||
|
|
||||
LL | let _ = &<Self as AssocConsts>::TO_BE_UNFROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:52:18
|
||||
|
|
||||
LL | let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:74:18
|
||||
|
|
||||
LL | let _ = &<Self as AssocTypes>::TO_BE_UNFROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:91:18
|
||||
|
|
||||
LL | let _ = &Self::UNFROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:92:18
|
||||
|
|
||||
LL | let _ = &Self::GENERIC_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/enums.rs:99:14
|
||||
|
|
||||
LL | let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: aborting due to 9 previous errors
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
#![deny(clippy::borrow_interior_mutable_const)]
|
||||
#![allow(clippy::declare_interior_mutable_const, clippy::needless_borrow)]
|
||||
#![allow(const_item_mutation)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::{Cell, UnsafeCell};
|
||||
use std::fmt::Display;
|
||||
use std::sync::Once;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
const ATOMIC: AtomicUsize = AtomicUsize::new(5);
|
||||
const CELL: Cell<usize> = Cell::new(6);
|
||||
const ATOMIC_TUPLE: ([AtomicUsize; 1], Option<Box<AtomicUsize>>, u8) = ([ATOMIC], None, 7);
|
||||
const INTEGER: u8 = 8;
|
||||
const STRING: String = String::new();
|
||||
const STR: &str = "012345";
|
||||
const COW: Cow<str> = Cow::Borrowed("abcdef");
|
||||
const NO_ANN: &dyn Display = &70;
|
||||
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
|
||||
const ONCE_INIT: Once = Once::new();
|
||||
|
||||
// This is just a pointer that can be safely dereferenced,
|
||||
// it's semantically the same as `&'static T`;
|
||||
// but it isn't allowed to make a static reference from an arbitrary integer value at the moment.
|
||||
// For more information, please see the issue #5918.
|
||||
pub struct StaticRef<T> {
|
||||
ptr: *const T,
|
||||
}
|
||||
|
||||
impl<T> StaticRef<T> {
|
||||
/// Create a new `StaticRef` from a raw pointer
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// Callers must pass in a reference to statically allocated memory which
|
||||
/// does not overlap with other values.
|
||||
pub const unsafe fn new(ptr: *const T) -> StaticRef<T> {
|
||||
StaticRef { ptr }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for StaticRef<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
// ICE regression test
|
||||
mod issue12979 {
|
||||
use std::cell::UnsafeCell;
|
||||
|
||||
const ATOMIC_TUPLE: (Vec<UnsafeCell<u8>>, ()) = (Vec::new(), ());
|
||||
|
||||
fn main() {
|
||||
let _x = &ATOMIC_TUPLE.0;
|
||||
}
|
||||
}
|
||||
|
||||
// use a tuple to make sure referencing a field behind a pointer isn't linted.
|
||||
const CELL_REF: StaticRef<(UnsafeCell<u32>,)> = unsafe { StaticRef::new(std::ptr::null()) };
|
||||
|
||||
fn main() {
|
||||
ATOMIC.store(1, Ordering::SeqCst);
|
||||
//~^ borrow_interior_mutable_const
|
||||
assert_eq!(ATOMIC.load(Ordering::SeqCst), 5);
|
||||
//~^ borrow_interior_mutable_const
|
||||
|
||||
let _once = ONCE_INIT;
|
||||
let _once_ref = &ONCE_INIT;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _once_ref_2 = &&ONCE_INIT;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _once_ref_4 = &&&&ONCE_INIT;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _once_mut = &mut ONCE_INIT;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _atomic_into_inner = ATOMIC.into_inner();
|
||||
// these should be all fine.
|
||||
let _twice = (ONCE_INIT, ONCE_INIT);
|
||||
let _ref_twice = &(ONCE_INIT, ONCE_INIT);
|
||||
let _ref_once = &(ONCE_INIT, ONCE_INIT).0;
|
||||
let _array_twice = [ONCE_INIT, ONCE_INIT];
|
||||
let _ref_array_twice = &[ONCE_INIT, ONCE_INIT];
|
||||
let _ref_array_once = &[ONCE_INIT, ONCE_INIT][0];
|
||||
|
||||
// referencing projection is still bad.
|
||||
let _ = &ATOMIC_TUPLE;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &ATOMIC_TUPLE.0;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &(&&&&ATOMIC_TUPLE).0;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &ATOMIC_TUPLE.0[0];
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst);
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &ATOMIC_TUPLE.2;
|
||||
let _ = (&&&&ATOMIC_TUPLE).0;
|
||||
let _ = (&&&&ATOMIC_TUPLE).2;
|
||||
let _ = ATOMIC_TUPLE.0;
|
||||
let _ = ATOMIC_TUPLE.0[0];
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = ATOMIC_TUPLE.1.into_iter();
|
||||
let _ = ATOMIC_TUPLE.2;
|
||||
let _ = &{ ATOMIC_TUPLE };
|
||||
|
||||
CELL.set(2);
|
||||
//~^ borrow_interior_mutable_const
|
||||
assert_eq!(CELL.get(), 6);
|
||||
//~^ borrow_interior_mutable_const
|
||||
|
||||
assert_eq!(INTEGER, 8);
|
||||
assert!(STRING.is_empty());
|
||||
|
||||
let a = ATOMIC;
|
||||
a.store(4, Ordering::SeqCst);
|
||||
assert_eq!(a.load(Ordering::SeqCst), 4);
|
||||
|
||||
STATIC_TUPLE.0.store(3, Ordering::SeqCst);
|
||||
assert_eq!(STATIC_TUPLE.0.load(Ordering::SeqCst), 3);
|
||||
assert!(STATIC_TUPLE.1.is_empty());
|
||||
|
||||
assert_eq!(NO_ANN.to_string(), "70"); // should never lint this.
|
||||
|
||||
let _ = &CELL_REF.0;
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:65:5
|
||||
|
|
||||
LL | ATOMIC.store(1, Ordering::SeqCst);
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:1:9
|
||||
|
|
||||
LL | #![deny(clippy::borrow_interior_mutable_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:67:16
|
||||
|
|
||||
LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5);
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:71:22
|
||||
|
|
||||
LL | let _once_ref = &ONCE_INIT;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:73:25
|
||||
|
|
||||
LL | let _once_ref_2 = &&ONCE_INIT;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:75:27
|
||||
|
|
||||
LL | let _once_ref_4 = &&&&ONCE_INIT;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:77:26
|
||||
|
|
||||
LL | let _once_mut = &mut ONCE_INIT;
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:89:14
|
||||
|
|
||||
LL | let _ = &ATOMIC_TUPLE;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:91:14
|
||||
|
|
||||
LL | let _ = &ATOMIC_TUPLE.0;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:93:19
|
||||
|
|
||||
LL | let _ = &(&&&&ATOMIC_TUPLE).0;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:95:14
|
||||
|
|
||||
LL | let _ = &ATOMIC_TUPLE.0[0];
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:97:13
|
||||
|
|
||||
LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst);
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:103:13
|
||||
|
|
||||
LL | let _ = ATOMIC_TUPLE.0[0];
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:109:5
|
||||
|
|
||||
LL | CELL.set(2);
|
||||
| ^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/others.rs:111:16
|
||||
|
|
||||
LL | assert_eq!(CELL.get(), 6);
|
||||
| ^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: aborting due to 14 previous errors
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#![deny(clippy::borrow_interior_mutable_const)]
|
||||
#![deny(clippy::declare_interior_mutable_const)]
|
||||
|
||||
// Inspired by https://github.com/rust-lang/rust/pull/130543#issuecomment-2364828139
|
||||
|
||||
use std::cell::UnsafeCell;
|
||||
|
||||
trait Trait {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
type Assoc<T> = <T as Trait>::Assoc;
|
||||
|
||||
impl Trait for u8 {
|
||||
type Assoc = UnsafeCell<u8>;
|
||||
}
|
||||
|
||||
impl Trait for () {
|
||||
type Assoc = ();
|
||||
}
|
||||
|
||||
enum MaybeMutable {
|
||||
Mutable(Assoc<u8>),
|
||||
Immutable(Assoc<()>),
|
||||
}
|
||||
|
||||
const CELL: Assoc<u8> = UnsafeCell::new(0); //~ ERROR: interior mutable
|
||||
const UNIT: Assoc<()> = ();
|
||||
const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL); //~ ERROR: interior mutable
|
||||
const IMMUTABLE: MaybeMutable = MaybeMutable::Immutable(UNIT);
|
||||
|
||||
fn print_ref<T>(t: &T) {
|
||||
let p: *const T = t;
|
||||
println!("{p:p}")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print_ref(&CELL); //~ ERROR: interior mutability
|
||||
print_ref(&UNIT);
|
||||
print_ref(&MUTABLE); //~ ERROR: interior mutability
|
||||
print_ref(&IMMUTABLE);
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/borrow_interior_mutable_const/projections.rs:27:1
|
||||
|
|
||||
LL | const CELL: Assoc<u8> = UnsafeCell::new(0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/borrow_interior_mutable_const/projections.rs:2:9
|
||||
|
|
||||
LL | #![deny(clippy::declare_interior_mutable_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/borrow_interior_mutable_const/projections.rs:29:1
|
||||
|
|
||||
LL | const MUTABLE: MaybeMutable = MaybeMutable::Mutable(CELL);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/projections.rs:38:16
|
||||
|
|
||||
LL | print_ref(&CELL);
|
||||
| ^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/borrow_interior_mutable_const/projections.rs:1:9
|
||||
|
|
||||
LL | #![deny(clippy::borrow_interior_mutable_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/projections.rs:40:16
|
||||
|
|
||||
LL | print_ref(&MUTABLE);
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
#![deny(clippy::borrow_interior_mutable_const)]
|
||||
#![allow(clippy::declare_interior_mutable_const)]
|
||||
|
||||
// this file replicates its `declare` counterpart. Please see it for more discussions.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
trait ConcreteTypes {
|
||||
const ATOMIC: AtomicUsize;
|
||||
const STRING: String;
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::ATOMIC;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &Self::STRING;
|
||||
}
|
||||
}
|
||||
|
||||
impl ConcreteTypes for u64 {
|
||||
const ATOMIC: AtomicUsize = AtomicUsize::new(9);
|
||||
const STRING: String = String::new();
|
||||
|
||||
fn function() {
|
||||
// Lint this again since implementers can choose not to borrow it.
|
||||
let _ = &Self::ATOMIC;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &Self::STRING;
|
||||
}
|
||||
}
|
||||
|
||||
// a helper trait used below
|
||||
trait ConstDefault {
|
||||
const DEFAULT: Self;
|
||||
}
|
||||
|
||||
trait GenericTypes<T, U> {
|
||||
const TO_REMAIN_GENERIC: T;
|
||||
const TO_BE_CONCRETE: U;
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::TO_REMAIN_GENERIC;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for Vec<T> {
|
||||
const TO_REMAIN_GENERIC: T = T::DEFAULT;
|
||||
const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::TO_REMAIN_GENERIC;
|
||||
let _ = &Self::TO_BE_CONCRETE;
|
||||
//~^ borrow_interior_mutable_const
|
||||
}
|
||||
}
|
||||
|
||||
// a helper type used below
|
||||
pub struct Wrapper<T>(T);
|
||||
|
||||
trait AssocTypes {
|
||||
type ToBeFrozen;
|
||||
type ToBeUnfrozen;
|
||||
type ToBeGenericParam;
|
||||
|
||||
const TO_BE_FROZEN: Self::ToBeFrozen;
|
||||
const TO_BE_UNFROZEN: Self::ToBeUnfrozen;
|
||||
const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>;
|
||||
const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::TO_BE_FROZEN;
|
||||
let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ConstDefault> AssocTypes for Vec<T> {
|
||||
type ToBeFrozen = u16;
|
||||
type ToBeUnfrozen = AtomicUsize;
|
||||
type ToBeGenericParam = T;
|
||||
|
||||
const TO_BE_FROZEN: Self::ToBeFrozen = 12;
|
||||
const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13);
|
||||
const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
|
||||
const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::TO_BE_FROZEN;
|
||||
let _ = &Self::TO_BE_UNFROZEN;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
// a helper trait used below
|
||||
trait AssocTypesHelper {
|
||||
type NotToBeBounded;
|
||||
type ToBeBounded;
|
||||
|
||||
const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
|
||||
}
|
||||
|
||||
trait AssocTypesFromGenericParam<T>
|
||||
where
|
||||
T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
|
||||
{
|
||||
const NOT_BOUNDED: T::NotToBeBounded;
|
||||
const BOUNDED: T::ToBeBounded;
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::NOT_BOUNDED;
|
||||
let _ = &Self::BOUNDED;
|
||||
//~^ borrow_interior_mutable_const
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AssocTypesFromGenericParam<T> for Vec<T>
|
||||
where
|
||||
T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
|
||||
{
|
||||
const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
|
||||
const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::NOT_BOUNDED;
|
||||
let _ = &Self::BOUNDED;
|
||||
//~^ borrow_interior_mutable_const
|
||||
}
|
||||
}
|
||||
|
||||
trait SelfType: Sized {
|
||||
const SELF: Self;
|
||||
const WRAPPED_SELF: Option<Self>;
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::SELF;
|
||||
let _ = &Self::WRAPPED_SELF;
|
||||
}
|
||||
}
|
||||
|
||||
impl SelfType for u64 {
|
||||
const SELF: Self = 16;
|
||||
const WRAPPED_SELF: Option<Self> = Some(20);
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::SELF;
|
||||
let _ = &Self::WRAPPED_SELF;
|
||||
}
|
||||
}
|
||||
|
||||
impl SelfType for AtomicUsize {
|
||||
const SELF: Self = AtomicUsize::new(17);
|
||||
const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::SELF;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &Self::WRAPPED_SELF;
|
||||
//~^ borrow_interior_mutable_const
|
||||
}
|
||||
}
|
||||
|
||||
trait BothOfCellAndGeneric<T> {
|
||||
const DIRECT: Cell<T>;
|
||||
const INDIRECT: Cell<*const T>;
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::DIRECT;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &Self::INDIRECT;
|
||||
//~^ borrow_interior_mutable_const
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ConstDefault> BothOfCellAndGeneric<T> for Vec<T> {
|
||||
const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
|
||||
const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::DIRECT;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &Self::INDIRECT;
|
||||
//~^ borrow_interior_mutable_const
|
||||
}
|
||||
}
|
||||
|
||||
struct Local<T>(T);
|
||||
|
||||
impl<T> Local<T>
|
||||
where
|
||||
T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
|
||||
{
|
||||
const ATOMIC: AtomicUsize = AtomicUsize::new(18);
|
||||
const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
|
||||
|
||||
const GENERIC_TYPE: T = T::DEFAULT;
|
||||
|
||||
const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
|
||||
const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
|
||||
|
||||
fn function() {
|
||||
let _ = &Self::ATOMIC;
|
||||
//~^ borrow_interior_mutable_const
|
||||
let _ = &Self::COW;
|
||||
let _ = &Self::GENERIC_TYPE;
|
||||
let _ = &Self::ASSOC_TYPE;
|
||||
let _ = &Self::BOUNDED_ASSOC_TYPE;
|
||||
//~^ borrow_interior_mutable_const
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
u64::ATOMIC.store(5, Ordering::SeqCst);
|
||||
//~^ borrow_interior_mutable_const
|
||||
assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9);
|
||||
//~^ borrow_interior_mutable_const
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:15:18
|
||||
|
|
||||
LL | let _ = &Self::ATOMIC;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:1:9
|
||||
|
|
||||
LL | #![deny(clippy::borrow_interior_mutable_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:27:18
|
||||
|
|
||||
LL | let _ = &Self::ATOMIC;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:53:18
|
||||
|
|
||||
LL | let _ = &Self::TO_BE_CONCRETE;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:89:18
|
||||
|
|
||||
LL | let _ = &Self::TO_BE_UNFROZEN;
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:91:18
|
||||
|
|
||||
LL | let _ = &Self::WRAPPED_TO_BE_UNFROZEN;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:114:18
|
||||
|
|
||||
LL | let _ = &Self::BOUNDED;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:128:18
|
||||
|
|
||||
LL | let _ = &Self::BOUNDED;
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:158:18
|
||||
|
|
||||
LL | let _ = &Self::SELF;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:160:18
|
||||
|
|
||||
LL | let _ = &Self::WRAPPED_SELF;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:170:18
|
||||
|
|
||||
LL | let _ = &Self::DIRECT;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:172:18
|
||||
|
|
||||
LL | let _ = &Self::INDIRECT;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:182:18
|
||||
|
|
||||
LL | let _ = &Self::DIRECT;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:184:18
|
||||
|
|
||||
LL | let _ = &Self::INDIRECT;
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:204:18
|
||||
|
|
||||
LL | let _ = &Self::ATOMIC;
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:209:18
|
||||
|
|
||||
LL | let _ = &Self::BOUNDED_ASSOC_TYPE;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:215:5
|
||||
|
|
||||
LL | u64::ATOMIC.store(5, Ordering::SeqCst);
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: a `const` item with interior mutability should not be borrowed
|
||||
--> tests/ui/borrow_interior_mutable_const/traits.rs:217:16
|
||||
|
|
||||
LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9);
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: assign this const to a local or static variable, and use the variable here
|
||||
|
||||
error: aborting due to 17 previous errors
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr
|
||||
const FOO: u8 = 0;
|
||||
@@ -0,0 +1,3 @@
|
||||
#![deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr
|
||||
|
||||
const FOO: u8 = 0;
|
||||
@@ -0,0 +1,3 @@
|
||||
#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr
|
||||
|
||||
const FOO: u8 = 0;
|
||||
@@ -0,0 +1,19 @@
|
||||
error: empty line after outer attribute
|
||||
--> tests/ui/crashes/ice-12979.rs:1:1
|
||||
|
|
||||
LL | / #[deny(clippy::declare_interior_mutable_const)]
|
||||
LL | |
|
||||
| |_^
|
||||
LL | const FOO: u8 = 0;
|
||||
| --------- the attribute applies to this constant item
|
||||
|
|
||||
= note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]`
|
||||
= help: if the empty line is unintentional, remove it
|
||||
help: if the attribute should apply to the crate use an inner attribute
|
||||
|
|
||||
LL | #![deny(clippy::declare_interior_mutable_const)]
|
||||
| +
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit();
|
||||
//~^ declare_interior_mutable_const
|
||||
|
||||
fn main() {}
|
||||
@@ -1,12 +0,0 @@
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/crashes/ice-9445.rs:1:1
|
||||
|
|
||||
LL | const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
|
||||
= note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
#![deny(clippy::declare_interior_mutable_const)]
|
||||
#![allow(clippy::missing_const_for_thread_local)]
|
||||
|
||||
use core::cell::{Cell, RefCell, UnsafeCell};
|
||||
use core::mem::{ManuallyDrop, MaybeUninit};
|
||||
use core::ptr;
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
|
||||
fn main() {}
|
||||
|
||||
const _: Cell<u32> = Cell::new(0);
|
||||
const UNSAFE_CELL: UnsafeCell<u32> = UnsafeCell::new(0); //~ declare_interior_mutable_const
|
||||
const REF_CELL: RefCell<u32> = RefCell::new(0); //~ declare_interior_mutable_const
|
||||
const CELL: Cell<u32> = Cell::new(0); //~ declare_interior_mutable_const
|
||||
|
||||
// Constants can't contain pointers or references to type with interior mutability.
|
||||
const fn make_ptr() -> *const Cell<u32> {
|
||||
ptr::null()
|
||||
}
|
||||
const PTR: *const Cell<u32> = make_ptr();
|
||||
|
||||
const fn casted_to_cell_ptr() -> *const Cell<u32> {
|
||||
const VALUE: u32 = 0;
|
||||
&VALUE as *const _ as *const Cell<u32>
|
||||
}
|
||||
const TRANSMUTED_PTR: *const Cell<u32> = casted_to_cell_ptr();
|
||||
|
||||
const CELL_TUPLE: (bool, Cell<u32>) = (true, Cell::new(0)); //~ declare_interior_mutable_const
|
||||
const CELL_ARRAY: [Cell<u32>; 2] = [Cell::new(0), Cell::new(0)]; //~ declare_interior_mutable_const
|
||||
|
||||
const UNINIT_CELL: MaybeUninit<Cell<&'static ()>> = MaybeUninit::uninit();
|
||||
|
||||
struct CellStruct {
|
||||
x: u32,
|
||||
cell: Cell<u32>,
|
||||
}
|
||||
//~v declare_interior_mutable_const
|
||||
const CELL_STRUCT: CellStruct = CellStruct {
|
||||
x: 0,
|
||||
cell: Cell::new(0),
|
||||
};
|
||||
|
||||
enum CellEnum {
|
||||
Cell(Cell<u32>),
|
||||
}
|
||||
const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0)); //~ declare_interior_mutable_const
|
||||
|
||||
const NONE_CELL: Option<Cell<u32>> = None;
|
||||
const SOME_CELL: Option<Cell<u32>> = Some(Cell::new(0)); //~ declare_interior_mutable_const
|
||||
|
||||
struct NestedCell([(Option<Cell<u32>>,); 1]);
|
||||
const NONE_NESTED_CELL: NestedCell = NestedCell([(None,)]);
|
||||
const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]); //~ declare_interior_mutable_const
|
||||
|
||||
union UnionCell {
|
||||
cell: ManuallyDrop<Cell<u32>>,
|
||||
x: u32,
|
||||
}
|
||||
//~v declare_interior_mutable_const
|
||||
const UNION_CELL: UnionCell = UnionCell {
|
||||
cell: ManuallyDrop::new(Cell::new(0)),
|
||||
};
|
||||
// Access to either union field is valid so we have to be conservative here.
|
||||
const UNION_U32: UnionCell = UnionCell { x: 0 }; //~ declare_interior_mutable_const
|
||||
|
||||
struct Assoc;
|
||||
impl Assoc {
|
||||
const SELF: Self = Self;
|
||||
const CELL: Cell<u32> = Cell::new(0); //~ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
struct AssocCell(Cell<u32>);
|
||||
impl AssocCell {
|
||||
const SELF: Self = Self(Cell::new(0)); //~ declare_interior_mutable_const
|
||||
const NONE_SELF: Option<Self> = None;
|
||||
const SOME_SELF: Option<Self> = Some(Self(Cell::new(0))); //~ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
trait ConstDefault {
|
||||
// May or may not be `Freeze`
|
||||
const DEFAULT: Self;
|
||||
}
|
||||
impl ConstDefault for u32 {
|
||||
const DEFAULT: Self = 0;
|
||||
}
|
||||
impl<T: ConstDefault> ConstDefault for Cell<T> {
|
||||
// Interior mutability is forced by the trait.
|
||||
const DEFAULT: Self = Cell::new(T::DEFAULT);
|
||||
}
|
||||
impl<T: ConstDefault> ConstDefault for Option<Cell<T>> {
|
||||
// Could have been `None`
|
||||
const DEFAULT: Self = Some(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
enum GenericEnumCell<T> {
|
||||
Cell(Cell<T>),
|
||||
Other(T),
|
||||
}
|
||||
impl<T: ConstDefault> ConstDefault for GenericEnumCell<T> {
|
||||
const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT)); //~ declare_interior_mutable_const
|
||||
}
|
||||
impl<T: ConstDefault> GenericEnumCell<T> {
|
||||
const CELL: Self = Self::DEFAULT; //~ declare_interior_mutable_const
|
||||
const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT); //~ declare_interior_mutable_const
|
||||
const OTHER: Self = Self::Other(T::DEFAULT);
|
||||
const FROM_OTHER: Self = Self::OTHER;
|
||||
}
|
||||
|
||||
enum GenericNestedEnumCell<T> {
|
||||
GenericEnumCell(GenericEnumCell<T>),
|
||||
EnumCell(GenericEnumCell<u32>),
|
||||
Other(T),
|
||||
}
|
||||
impl<T: ConstDefault> GenericNestedEnumCell<T> {
|
||||
const GENERIC_OTHER: Self = Self::GenericEnumCell(GenericEnumCell::<T>::FROM_OTHER);
|
||||
const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::<T>::CELL); //~ declare_interior_mutable_const
|
||||
const ENUM_OTHER: Self = Self::EnumCell(GenericEnumCell::<u32>::FROM_OTHER);
|
||||
const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::<u32>::CELL); //~ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
trait CellTrait: ConstDefault + Sized {
|
||||
// Must be non-`Freeze` due to the type
|
||||
const CELL: Cell<Self>; //~ declare_interior_mutable_const
|
||||
// May be non-`Freeze`, but may not be
|
||||
const OPTION_CELL: Option<Cell<Self>>;
|
||||
// May get redefined by the impl, but the default is non-`Freeze`.
|
||||
const SOME_CELL: Option<Cell<Self>> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const
|
||||
// May get redefined by the impl, but the default is `Freeze`.
|
||||
const NONE_CELL: Option<Cell<Self>> = None;
|
||||
}
|
||||
|
||||
trait CellWithAssoc {
|
||||
type T;
|
||||
const DEFAULT: Self::T;
|
||||
// Must be non-`Freeze` due to the type
|
||||
const CELL: Cell<Self::T>; //~ declare_interior_mutable_const
|
||||
// May be non-`Freeze`, but may not be
|
||||
const OPTION_CELL: Option<Cell<Self::T>>;
|
||||
// May get redefined by the impl, but the default is non-`Freeze`.
|
||||
const SOME_CELL: Option<Cell<Self::T>> = Some(Cell::new(Self::DEFAULT)); //~ declare_interior_mutable_const
|
||||
// May get redefined by the impl, but the default is `Freeze`.
|
||||
const NONE_CELL: Option<Cell<Self::T>> = None;
|
||||
}
|
||||
|
||||
impl CellWithAssoc for () {
|
||||
type T = u32;
|
||||
const DEFAULT: Self::T = 0;
|
||||
const CELL: Cell<Self::T> = Cell::new(0);
|
||||
const OPTION_CELL: Option<Cell<Self::T>> = None;
|
||||
}
|
||||
|
||||
trait WithAssoc {
|
||||
type T;
|
||||
const VALUE: Self::T;
|
||||
}
|
||||
|
||||
impl WithAssoc for u32 {
|
||||
type T = Cell<u32>;
|
||||
// The cell comes from the impl block, not the trait.
|
||||
const VALUE: Self::T = Cell::new(0); //~ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
trait WithLayeredAssoc {
|
||||
type T: WithAssoc;
|
||||
const VALUE: <Self::T as WithAssoc>::T;
|
||||
}
|
||||
|
||||
impl WithLayeredAssoc for u32 {
|
||||
type T = u32;
|
||||
// The cell comes from the impl block, not the trait.
|
||||
const VALUE: <Self::T as WithAssoc>::T = Cell::new(0); //~ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
trait WithGenericAssoc {
|
||||
type T<U>;
|
||||
const VALUE: Self::T<u32>;
|
||||
}
|
||||
|
||||
impl WithGenericAssoc for u32 {
|
||||
type T<U> = Cell<U>;
|
||||
const VALUE: Self::T<u32> = Cell::new(0); //~ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
trait WithGenericAssocCell {
|
||||
type T<U>;
|
||||
const VALUE: Self::T<Cell<u32>>;
|
||||
}
|
||||
|
||||
impl WithGenericAssocCell for u32 {
|
||||
type T<U> = Option<U>;
|
||||
const VALUE: Self::T<Cell<u32>> = None;
|
||||
}
|
||||
|
||||
impl WithGenericAssocCell for i32 {
|
||||
type T<U> = Option<U>;
|
||||
const VALUE: Self::T<Cell<u32>> = Some(Cell::new(0)); //~ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
thread_local!(static THREAD_LOCAL_CELL: Cell<u32> = const { Cell::new(0) });
|
||||
thread_local!(static THREAD_LOCAL_CELL2: Cell<u32> = Cell::new(0));
|
||||
@@ -0,0 +1,197 @@
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:12:7
|
||||
|
|
||||
LL | const UNSAFE_CELL: UnsafeCell<u32> = UnsafeCell::new(0);
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
note: the lint level is defined here
|
||||
--> tests/ui/declare_interior_mutable_const.rs:1:9
|
||||
|
|
||||
LL | #![deny(clippy::declare_interior_mutable_const)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:13:7
|
||||
|
|
||||
LL | const REF_CELL: RefCell<u32> = RefCell::new(0);
|
||||
| ^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:14:7
|
||||
|
|
||||
LL | const CELL: Cell<u32> = Cell::new(0);
|
||||
| ^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:28:7
|
||||
|
|
||||
LL | const CELL_TUPLE: (bool, Cell<u32>) = (true, Cell::new(0));
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:29:7
|
||||
|
|
||||
LL | const CELL_ARRAY: [Cell<u32>; 2] = [Cell::new(0), Cell::new(0)];
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:38:7
|
||||
|
|
||||
LL | const CELL_STRUCT: CellStruct = CellStruct {
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:46:7
|
||||
|
|
||||
LL | const CELL_ENUM: CellEnum = CellEnum::Cell(Cell::new(0));
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:49:7
|
||||
|
|
||||
LL | const SOME_CELL: Option<Cell<u32>> = Some(Cell::new(0));
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:53:7
|
||||
|
|
||||
LL | const SOME_NESTED_CELL: NestedCell = NestedCell([(Some(Cell::new(0)),)]);
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:60:7
|
||||
|
|
||||
LL | const UNION_CELL: UnionCell = UnionCell {
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:64:7
|
||||
|
|
||||
LL | const UNION_U32: UnionCell = UnionCell { x: 0 };
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: did you mean to make this a `thread_local!` item
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:69:11
|
||||
|
|
||||
LL | const CELL: Cell<u32> = Cell::new(0);
|
||||
| ^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:74:11
|
||||
|
|
||||
LL | const SELF: Self = Self(Cell::new(0));
|
||||
| ^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:76:11
|
||||
|
|
||||
LL | const SOME_SELF: Option<Self> = Some(Self(Cell::new(0)));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:92:11
|
||||
|
|
||||
LL | const DEFAULT: Self = Some(Cell::new(T::DEFAULT));
|
||||
| ^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:100:11
|
||||
|
|
||||
LL | const DEFAULT: Self = Self::Cell(Cell::new(T::DEFAULT));
|
||||
| ^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:103:11
|
||||
|
|
||||
LL | const CELL: Self = Self::DEFAULT;
|
||||
| ^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:104:11
|
||||
|
|
||||
LL | const CELL_BY_DEFAULT: Self = Self::Cell(Cell::DEFAULT);
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:116:11
|
||||
|
|
||||
LL | const GENERIC_CELL: Self = Self::GenericEnumCell(GenericEnumCell::<T>::CELL);
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:118:11
|
||||
|
|
||||
LL | const ENUM_CELL: Self = Self::EnumCell(GenericEnumCell::<u32>::CELL);
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:123:11
|
||||
|
|
||||
LL | const CELL: Cell<Self>;
|
||||
| ^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:127:11
|
||||
|
|
||||
LL | const SOME_CELL: Option<Cell<Self>> = Some(Cell::new(Self::DEFAULT));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:136:11
|
||||
|
|
||||
LL | const CELL: Cell<Self::T>;
|
||||
| ^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:140:11
|
||||
|
|
||||
LL | const SOME_CELL: Option<Cell<Self::T>> = Some(Cell::new(Self::DEFAULT));
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:160:11
|
||||
|
|
||||
LL | const VALUE: Self::T = Cell::new(0);
|
||||
| ^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:171:11
|
||||
|
|
||||
LL | const VALUE: <Self::T as WithAssoc>::T = Cell::new(0);
|
||||
| ^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:181:11
|
||||
|
|
||||
LL | const VALUE: Self::T<u32> = Cell::new(0);
|
||||
| ^^^^^
|
||||
|
||||
error: named constant with interior mutability
|
||||
--> tests/ui/declare_interior_mutable_const.rs:196:11
|
||||
|
|
||||
LL | const VALUE: Self::T<Cell<u32>> = Some(Cell::new(0));
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 28 previous errors
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
#![warn(clippy::declare_interior_mutable_const)]
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
enum OptionalCell {
|
||||
Unfrozen(Cell<bool>),
|
||||
Frozen,
|
||||
}
|
||||
|
||||
// a constant with enums should be linted only when the used variant is unfrozen (#3962).
|
||||
const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
|
||||
//~^ declare_interior_mutable_const
|
||||
const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
|
||||
|
||||
const fn unfrozen_variant() -> OptionalCell {
|
||||
OptionalCell::Unfrozen(Cell::new(false))
|
||||
}
|
||||
|
||||
const fn frozen_variant() -> OptionalCell {
|
||||
OptionalCell::Frozen
|
||||
}
|
||||
|
||||
const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant();
|
||||
//~^ declare_interior_mutable_const
|
||||
const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant();
|
||||
|
||||
enum NestedInnermost {
|
||||
Unfrozen(AtomicUsize),
|
||||
Frozen,
|
||||
}
|
||||
|
||||
struct NestedInner {
|
||||
inner: NestedInnermost,
|
||||
}
|
||||
|
||||
enum NestedOuter {
|
||||
NestedInner(NestedInner),
|
||||
NotNested(usize),
|
||||
}
|
||||
|
||||
struct NestedOutermost {
|
||||
outer: NestedOuter,
|
||||
}
|
||||
|
||||
// a constant with enums should be linted according to its value, no matter how structs involve.
|
||||
const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
|
||||
//~^ declare_interior_mutable_const
|
||||
outer: NestedOuter::NestedInner(NestedInner {
|
||||
inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
|
||||
}),
|
||||
};
|
||||
const NESTED_FROZEN_VARIANT: NestedOutermost = NestedOutermost {
|
||||
outer: NestedOuter::NestedInner(NestedInner {
|
||||
inner: NestedInnermost::Frozen,
|
||||
}),
|
||||
};
|
||||
|
||||
trait AssocConsts {
|
||||
// When there's no default value, lint it only according to its type.
|
||||
// Further details are on the corresponding code (`NonCopyConst::check_trait_item`).
|
||||
const TO_BE_UNFROZEN_VARIANT: OptionalCell;
|
||||
//~^ declare_interior_mutable_const
|
||||
const TO_BE_FROZEN_VARIANT: OptionalCell;
|
||||
//~^ declare_interior_mutable_const
|
||||
|
||||
// Lint default values accordingly.
|
||||
const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
|
||||
//~^ declare_interior_mutable_const
|
||||
const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
|
||||
}
|
||||
|
||||
// The lint doesn't trigger for an assoc constant in a trait impl with an unfrozen type even if it
|
||||
// has enums. Further details are on the corresponding code in 'NonCopyConst::check_impl_item'.
|
||||
impl AssocConsts for u64 {
|
||||
const TO_BE_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
|
||||
const TO_BE_FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen;
|
||||
|
||||
// even if this sets an unfrozen variant, the lint ignores it.
|
||||
const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
|
||||
}
|
||||
|
||||
// At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters
|
||||
// here are values; and I think substituted generics at definitions won't appear in MIR.
|
||||
trait AssocTypes {
|
||||
type ToBeUnfrozen;
|
||||
|
||||
const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
|
||||
const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen>;
|
||||
}
|
||||
|
||||
impl AssocTypes for u64 {
|
||||
type ToBeUnfrozen = AtomicUsize;
|
||||
|
||||
const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4));
|
||||
//~^ declare_interior_mutable_const
|
||||
const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None;
|
||||
}
|
||||
|
||||
// Use raw pointers since direct generics have a false negative at the type level.
|
||||
enum BothOfCellAndGeneric<T> {
|
||||
Unfrozen(Cell<*const T>),
|
||||
Generic(*const T),
|
||||
Frozen(usize),
|
||||
}
|
||||
|
||||
impl<T> BothOfCellAndGeneric<T> {
|
||||
const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
|
||||
//~^ declare_interior_mutable_const
|
||||
|
||||
// This is a false positive. The argument about this is on `is_value_unfrozen_raw`
|
||||
const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null());
|
||||
//~^ declare_interior_mutable_const
|
||||
|
||||
const FROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Frozen(5);
|
||||
|
||||
// This is what is likely to be a false negative when one tries to fix
|
||||
// the `GENERIC_VARIANT` false positive.
|
||||
const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null());
|
||||
//~^ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
// associated types here is basically the same as the one above.
|
||||
trait BothOfCellAndGenericWithAssocType {
|
||||
type AssocType;
|
||||
|
||||
const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
|
||||
//~^ declare_interior_mutable_const
|
||||
BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
|
||||
const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null());
|
||||
//~^ declare_interior_mutable_const
|
||||
const FROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Frozen(5);
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -1,89 +0,0 @@
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:12:1
|
||||
|
|
||||
LL | const UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
|
||||
= note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:24:1
|
||||
|
|
||||
LL | const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:47:1
|
||||
|
|
||||
LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost {
|
||||
LL | |
|
||||
LL | | outer: NestedOuter::NestedInner(NestedInner {
|
||||
LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)),
|
||||
LL | | }),
|
||||
LL | | };
|
||||
| |__^
|
||||
|
|
||||
= help: consider making this a static item
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:62:5
|
||||
|
|
||||
LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:64:5
|
||||
|
|
||||
LL | const TO_BE_FROZEN_VARIANT: OptionalCell;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:68:5
|
||||
|
|
||||
LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:95:5
|
||||
|
|
||||
LL | const TO_BE_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:108:5
|
||||
|
|
||||
LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:112:5
|
||||
|
|
||||
LL | const GENERIC_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Generic(std::ptr::null());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:119:5
|
||||
|
|
||||
LL | const NO_ENUM: Cell<*const T> = Cell::new(std::ptr::null());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:127:5
|
||||
|
|
||||
LL | / const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> =
|
||||
LL | |
|
||||
LL | | BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null()));
|
||||
| |____________________________________________________________________^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/enums.rs:130:5
|
||||
|
|
||||
LL | const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
#![warn(clippy::declare_interior_mutable_const)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::fmt::Display;
|
||||
use std::ptr;
|
||||
use std::sync::Once;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
const ATOMIC: AtomicUsize = AtomicUsize::new(5);
|
||||
//~^ declare_interior_mutable_const
|
||||
const CELL: Cell<usize> = Cell::new(6);
|
||||
//~^ declare_interior_mutable_const
|
||||
const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
|
||||
//~^ declare_interior_mutable_const
|
||||
|
||||
macro_rules! declare_const {
|
||||
($name:ident: $ty:ty = $e:expr) => {
|
||||
const $name: $ty = $e;
|
||||
//~^ declare_interior_mutable_const
|
||||
};
|
||||
}
|
||||
declare_const!(_ONCE: Once = Once::new());
|
||||
|
||||
// const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492.
|
||||
|
||||
const INTEGER: u8 = 8;
|
||||
const STRING: String = String::new();
|
||||
const STR: &str = "012345";
|
||||
const COW: Cow<str> = Cow::Borrowed("abcdef");
|
||||
// note: a const item of Cow is used in the `postgres` package.
|
||||
|
||||
const NO_ANN: &dyn Display = &70;
|
||||
|
||||
static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
|
||||
// there should be no lints on the line above line
|
||||
|
||||
mod issue_8493 {
|
||||
use std::cell::Cell;
|
||||
|
||||
thread_local! {
|
||||
static _BAR: Cell<i32> = const { Cell::new(0) };
|
||||
}
|
||||
|
||||
macro_rules! issue_8493 {
|
||||
() => {
|
||||
const _BAZ: Cell<usize> = Cell::new(0);
|
||||
//~^ declare_interior_mutable_const
|
||||
static _FOOBAR: () = {
|
||||
thread_local! {
|
||||
static _VAR: Cell<i32> = const { Cell::new(0) };
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
issue_8493!();
|
||||
}
|
||||
|
||||
#[repr(C, align(8))]
|
||||
struct NoAtomic(usize);
|
||||
#[repr(C, align(8))]
|
||||
struct WithAtomic(AtomicUsize);
|
||||
|
||||
const fn with_non_null() -> *const WithAtomic {
|
||||
const NO_ATOMIC: NoAtomic = NoAtomic(0);
|
||||
(&NO_ATOMIC as *const NoAtomic).cast()
|
||||
}
|
||||
const WITH_ATOMIC: *const WithAtomic = with_non_null();
|
||||
|
||||
struct Generic<T>(T);
|
||||
impl<T> Generic<T> {
|
||||
const RAW_POINTER: *const Cell<T> = ptr::null();
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -1,50 +0,0 @@
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/others.rs:10:1
|
||||
|
|
||||
LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider making this a static item
|
||||
= note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/others.rs:12:1
|
||||
|
|
||||
LL | const CELL: Cell<usize> = Cell::new(6);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/others.rs:14:1
|
||||
|
|
||||
LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: consider making this a static item
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/others.rs:19:9
|
||||
|
|
||||
LL | const $name: $ty = $e;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | declare_const!(_ONCE: Once = Once::new());
|
||||
| ----------------------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/others.rs:47:13
|
||||
|
|
||||
LL | const _BAZ: Cell<usize> = Cell::new(0);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | issue_8493!();
|
||||
| ------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `issue_8493` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
#![warn(clippy::declare_interior_mutable_const)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::Cell;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
|
||||
macro_rules! declare_const {
|
||||
($name:ident: $ty:ty = $e:expr) => {
|
||||
const $name: $ty = $e;
|
||||
//~^ declare_interior_mutable_const
|
||||
};
|
||||
}
|
||||
|
||||
// a constant whose type is a concrete type should be linted at the definition site.
|
||||
trait ConcreteTypes {
|
||||
const ATOMIC: AtomicUsize;
|
||||
//~^ declare_interior_mutable_const
|
||||
const INTEGER: u64;
|
||||
const STRING: String;
|
||||
declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC);
|
||||
}
|
||||
|
||||
impl ConcreteTypes for u64 {
|
||||
const ATOMIC: AtomicUsize = AtomicUsize::new(9);
|
||||
const INTEGER: u64 = 10;
|
||||
const STRING: String = String::new();
|
||||
}
|
||||
|
||||
// a helper trait used below
|
||||
trait ConstDefault {
|
||||
const DEFAULT: Self;
|
||||
}
|
||||
|
||||
// a constant whose type is a generic type should be linted at the implementation site.
|
||||
trait GenericTypes<T, U> {
|
||||
const TO_REMAIN_GENERIC: T;
|
||||
const TO_BE_CONCRETE: U;
|
||||
|
||||
const HAVING_DEFAULT: T = Self::TO_REMAIN_GENERIC;
|
||||
declare_const!(IN_MACRO: T = Self::TO_REMAIN_GENERIC);
|
||||
}
|
||||
|
||||
impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for u64 {
|
||||
const TO_REMAIN_GENERIC: T = T::DEFAULT;
|
||||
const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
|
||||
//~^ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
// a helper type used below
|
||||
struct Wrapper<T>(T);
|
||||
|
||||
// a constant whose type is an associated type should be linted at the implementation site, too.
|
||||
trait AssocTypes {
|
||||
type ToBeFrozen;
|
||||
type ToBeUnfrozen;
|
||||
type ToBeGenericParam;
|
||||
|
||||
const TO_BE_FROZEN: Self::ToBeFrozen;
|
||||
const TO_BE_UNFROZEN: Self::ToBeUnfrozen;
|
||||
const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen>;
|
||||
// to ensure it can handle things when a generic type remains after normalization.
|
||||
const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam>;
|
||||
}
|
||||
|
||||
impl<T: ConstDefault> AssocTypes for Vec<T> {
|
||||
type ToBeFrozen = u16;
|
||||
type ToBeUnfrozen = AtomicUsize;
|
||||
type ToBeGenericParam = T;
|
||||
|
||||
const TO_BE_FROZEN: Self::ToBeFrozen = 12;
|
||||
const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13);
|
||||
//~^ declare_interior_mutable_const
|
||||
const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
|
||||
//~^ declare_interior_mutable_const
|
||||
const WRAPPED_TO_BE_GENERIC_PARAM: Wrapper<Self::ToBeGenericParam> = Wrapper(T::DEFAULT);
|
||||
}
|
||||
|
||||
// a helper trait used below
|
||||
trait AssocTypesHelper {
|
||||
type NotToBeBounded;
|
||||
type ToBeBounded;
|
||||
|
||||
const NOT_TO_BE_BOUNDED: Self::NotToBeBounded;
|
||||
}
|
||||
|
||||
// a constant whose type is an assoc type originated from a generic param bounded at the definition
|
||||
// site should be linted at there.
|
||||
trait AssocTypesFromGenericParam<T>
|
||||
where
|
||||
T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
|
||||
{
|
||||
const NOT_BOUNDED: T::NotToBeBounded;
|
||||
const BOUNDED: T::ToBeBounded;
|
||||
//~^ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
impl<T> AssocTypesFromGenericParam<T> for u64
|
||||
where
|
||||
T: AssocTypesHelper<ToBeBounded = AtomicUsize>,
|
||||
{
|
||||
// an associated type could remain unknown in a trait impl.
|
||||
const NOT_BOUNDED: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
|
||||
const BOUNDED: T::ToBeBounded = AtomicUsize::new(15);
|
||||
}
|
||||
|
||||
// a constant whose type is `Self` should be linted at the implementation site as well.
|
||||
// (`Option` requires `Sized` bound.)
|
||||
trait SelfType: Sized {
|
||||
const SELF: Self;
|
||||
// this was the one in the original issue (#5050).
|
||||
const WRAPPED_SELF: Option<Self>;
|
||||
}
|
||||
|
||||
impl SelfType for u64 {
|
||||
const SELF: Self = 16;
|
||||
const WRAPPED_SELF: Option<Self> = Some(20);
|
||||
}
|
||||
|
||||
impl SelfType for AtomicUsize {
|
||||
// this (interior mutable `Self` const) exists in `parking_lot`.
|
||||
// `const_trait_impl` will replace it in the future, hopefully.
|
||||
const SELF: Self = AtomicUsize::new(17);
|
||||
//~^ declare_interior_mutable_const
|
||||
const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
|
||||
//~^ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
// Even though a constant contains a generic type, if it also have an interior mutable type,
|
||||
// it should be linted at the definition site.
|
||||
trait BothOfCellAndGeneric<T> {
|
||||
const DIRECT: Cell<T>;
|
||||
//~^ declare_interior_mutable_const
|
||||
const INDIRECT: Cell<*const T>;
|
||||
//~^ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
impl<T: ConstDefault> BothOfCellAndGeneric<T> for u64 {
|
||||
const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
|
||||
//~^ declare_interior_mutable_const
|
||||
const INDIRECT: Cell<*const T> = Cell::new(std::ptr::null());
|
||||
}
|
||||
|
||||
struct Local<T>(T);
|
||||
|
||||
// a constant in an inherent impl are essentially the same as a normal const item
|
||||
// except there can be a generic or associated type.
|
||||
impl<T> Local<T>
|
||||
where
|
||||
T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>,
|
||||
{
|
||||
const ATOMIC: AtomicUsize = AtomicUsize::new(18);
|
||||
//~^ declare_interior_mutable_const
|
||||
const COW: Cow<'static, str> = Cow::Borrowed("tuvwxy");
|
||||
|
||||
const GENERIC_TYPE: T = T::DEFAULT;
|
||||
|
||||
const ASSOC_TYPE: T::NotToBeBounded = T::NOT_TO_BE_BOUNDED;
|
||||
const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
|
||||
//~^ declare_interior_mutable_const
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -1,88 +0,0 @@
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:16:5
|
||||
|
|
||||
LL | const ATOMIC: AtomicUsize;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: `-D clippy::declare-interior-mutable-const` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:9:9
|
||||
|
|
||||
LL | const $name: $ty = $e;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
...
|
||||
LL | declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC);
|
||||
| ---------------------------------------------------------- in this macro invocation
|
||||
|
|
||||
= note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:45:5
|
||||
|
|
||||
LL | const TO_BE_CONCRETE: AtomicUsize = AtomicUsize::new(11);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:71:5
|
||||
|
|
||||
LL | const TO_BE_UNFROZEN: Self::ToBeUnfrozen = AtomicUsize::new(13);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:73:5
|
||||
|
|
||||
LL | const WRAPPED_TO_BE_UNFROZEN: Wrapper<Self::ToBeUnfrozen> = Wrapper(AtomicUsize::new(14));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:93:5
|
||||
|
|
||||
LL | const BOUNDED: T::ToBeBounded;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:122:5
|
||||
|
|
||||
LL | const SELF: Self = AtomicUsize::new(17);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:124:5
|
||||
|
|
||||
LL | const WRAPPED_SELF: Option<Self> = Some(AtomicUsize::new(21));
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:131:5
|
||||
|
|
||||
LL | const DIRECT: Cell<T>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:133:5
|
||||
|
|
||||
LL | const INDIRECT: Cell<*const T>;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:138:5
|
||||
|
|
||||
LL | const DIRECT: Cell<T> = Cell::new(T::DEFAULT);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:151:5
|
||||
|
|
||||
LL | const ATOMIC: AtomicUsize = AtomicUsize::new(18);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: a `const` item should not be interior mutable
|
||||
--> tests/ui/declare_interior_mutable_const/traits.rs:158:5
|
||||
|
|
||||
LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 13 previous errors
|
||||
|
||||
Reference in New Issue
Block a user