diff options
| author | Jason Newcomb <jsnewcomb@pm.me> | 2024-08-03 00:29:23 -0400 |
|---|---|---|
| committer | Jason Newcomb <jsnewcomb@pm.me> | 2025-05-16 06:08:35 -0400 |
| commit | 57782e0ff1e9833e121187f1c33b72bec6a8a61a (patch) | |
| tree | 8d05a5d3f4678f6635191403c2dcf414ceadf14e | |
| parent | 95d7eda0b44cc883516b9198dce3b05db3a581f9 (diff) | |
| download | rust-57782e0ff1e9833e121187f1c33b72bec6a8a61a.tar.gz rust-57782e0ff1e9833e121187f1c33b72bec6a8a61a.zip | |
Rewrite `non_copy_const`
21 files changed, 981 insertions, 677 deletions
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs index 6d3e77b6b6e..f57ab0e6781 100644 --- a/clippy_lints/src/non_copy_const.rs +++ b/clippy_lints/src/non_copy_const.rs @@ -1,25 +1,46 @@ -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 @@ -74,8 +95,6 @@ declare_clippy_lint! { "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., @@ -113,60 +132,96 @@ declare_clippy_lint! { "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) + } -impl Source<'_> { - #[must_use] - fn lint(&self) -> (&'static Lint, &'static str, Span) { + /// Checks if this is definitely not `Freeze`. + fn is_not_freeze(self) -> bool { + matches!(self, Self::No) + } +} + +/// What operation caused a borrow to occur. +#[derive(Clone, Copy)] +enum BorrowCause { + Borrow, + Deref, + Index, + AutoDeref, + AutoBorrow, +} +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"), } } } -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), + _ => (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 +229,631 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE 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)) - }, - 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, + .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 }, - _ => false, } } - fn is_value_unfrozen_raw( - cx: &LateContext<'tcx>, - result: Result<Result<ty::ValTree<'tcx>, Ty<'tcx>>, ErrorHandled>, + /// 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: TypingEnv<'tcx>, 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: 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) }, - |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) + IsFreeze::Maybe | IsFreeze::No => Ok(false), + } } - pub fn const_eval_resolve( + /// 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: 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>, + 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, }, - Ok(None) => Err(ErrorHandled::TooGeneric(span)), - Err(err) => Err(ErrorHandled::Reported( - ReportedErrorInfo::non_const_eval_error(err), - span, - )), } } -} -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 }); + /// 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; } - } - } - - 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 }); + 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; } } - 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 }); + /// 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); } }, - 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 }); + 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); } } } - 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; + /// 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)); } - // 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; + // Then read through constants and blocks on the init expression before + // applying the next use expression. loop { - let parent_id = cx.tcx.parent_hir_id(cur_expr.hir_id); - if parent_id == cur_expr.hir_id { - break; + 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, } - 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; - } + // 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; + } - 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; + // 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>, 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, + ), + }, + } + && !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, + "a `const` item should not be interior mutable", + |diag| { + 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`", + ); + } + }, + ); + } + } + + 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 }, - _ => break, + _ => !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, + ), } - cur_expr = parent_expr; - } else { - break; - } + }, + IsFreeze::Yes | IsFreeze::Maybe => false, } + && !item.span.in_external_macro(cx.sess().source_map()) + { + span_lint( + cx, + DECLARE_INTERIOR_MUTABLE_CONST, + item.ident.span, + "a `const` item should not be interior mutable", + ); + } + } - 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) + 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 { - adjustments[i - 1].target + 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, + "a `const` item should not be interior mutable", + ); + } + } + + 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 { - // No borrow adjustments means the entire const is moved. - return; + self.is_non_freeze_expr_borrowed(cx.tcx, cx.typing_env(), typeck, e) } - } 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 }); } + && !borrow_src.expr.span.in_external_macro(cx.sess().source_map()) + { + span_lint_and_then( + cx, + BORROW_INTERIOR_MUTABLE_CONST, + borrow_src.expr.span, + "a `const` item with interior mutability should not be borrowed", + |diag| { + if let Some(msg) = borrow_src.cause.note() { + diag.note(msg); + } + diag.help("assign this const to a local or static variable, and use the variable here"); + }, + ); } } } -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 +861,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)) +} diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 26d41cfb497..c50ad17bfad 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -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, + } +} diff --git a/tests/ui/borrow_interior_mutable_const/enums.rs b/tests/ui/borrow_interior_mutable_const/enums.rs index da940a4cfb5..ea47e588858 100644 --- a/tests/ui/borrow_interior_mutable_const/enums.rs +++ b/tests/ui/borrow_interior_mutable_const/enums.rs @@ -19,7 +19,7 @@ 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 _ = &UNFROZEN_VARIANT; //~ borrow_interior_mutable_const let _ = &FROZEN_VARIANT; } @@ -34,11 +34,11 @@ trait AssocConsts { // 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 + let _ = &Self::TO_BE_FROZEN_VARIANT; // 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 + let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; } } @@ -47,9 +47,9 @@ impl AssocConsts for u64 { 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_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const let _ = &<Self as AssocConsts>::TO_BE_FROZEN_VARIANT; - let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ ERROR: interior mutability + let _ = &Self::DEFAULTED_ON_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const let _ = &Self::DEFAULTED_ON_FROZEN_VARIANT; } } @@ -71,7 +71,7 @@ impl AssocTypes for u64 { 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_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const let _ = &<Self as AssocTypes>::TO_BE_FROZEN_VARIANT; } } @@ -88,14 +88,14 @@ impl<T> BothOfCellAndGeneric<T> { 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::UNFROZEN_VARIANT; //~ borrow_interior_mutable_const + let _ = &Self::GENERIC_VARIANT; let _ = &Self::FROZEN_VARIANT; } } fn main() { // constants defined in foreign crates - let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ ERROR: interior mutability + let _ = &helper::WRAPPED_PRIVATE_UNFROZEN_VARIANT; //~ borrow_interior_mutable_const let _ = &helper::WRAPPED_PRIVATE_FROZEN_VARIANT; } diff --git a/tests/ui/borrow_interior_mutable_const/enums.stderr b/tests/ui/borrow_interior_mutable_const/enums.stderr index 43850384b90..7a3cb7d6aa9 100644 --- a/tests/ui/borrow_interior_mutable_const/enums.stderr +++ b/tests/ui/borrow_interior_mutable_const/enums.stderr @@ -1,8 +1,8 @@ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/enums.rs:22:14 + --> tests/ui/borrow_interior_mutable_const/enums.rs:22:13 | 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 @@ -12,68 +12,44 @@ 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 + --> tests/ui/borrow_interior_mutable_const/enums.rs:50:17 | 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 + --> tests/ui/borrow_interior_mutable_const/enums.rs:52:17 | 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 + --> tests/ui/borrow_interior_mutable_const/enums.rs:74:17 | 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 + --> tests/ui/borrow_interior_mutable_const/enums.rs:91:17 | 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 + --> tests/ui/borrow_interior_mutable_const/enums.rs:99:13 | 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 +error: aborting due to 6 previous errors diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs index fa729b62d7f..720f88326d7 100644 --- a/tests/ui/borrow_interior_mutable_const/others.rs +++ b/tests/ui/borrow_interior_mutable_const/others.rs @@ -62,20 +62,14 @@ mod issue12979 { 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 + 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 _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); @@ -86,30 +80,22 @@ fn main() { 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; //~ 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; //~ borrow_interior_mutable_const + let _ = (&&&&ATOMIC_TUPLE).2; //~ borrow_interior_mutable_const 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 + CELL.set(2); //~ borrow_interior_mutable_const + assert_eq!(CELL.get(), 6); //~ borrow_interior_mutable_const assert_eq!(INTEGER, 8); assert!(STRING.is_empty()); diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr index decea153f71..6e887406dcd 100644 --- a/tests/ui/borrow_interior_mutable_const/others.stderr +++ b/tests/ui/borrow_interior_mutable_const/others.stderr @@ -4,6 +4,7 @@ error: a `const` item with interior mutability should not be borrowed LL | ATOMIC.store(1, Ordering::SeqCst); | ^^^^^^ | + = note: there is a compiler inserted borrow here = 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 @@ -12,108 +13,120 @@ 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:66:16 | LL | assert_eq!(ATOMIC.load(Ordering::SeqCst), 5); | ^^^^^^ | + = note: there is a compiler inserted borrow here = 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:69:21 | 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:70:24 | 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:71:26 | 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:72:21 | 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:83:13 | 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:84:13 | 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:85:18 | 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:86: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:97:13 + --> tests/ui/borrow_interior_mutable_const/others.rs:87:13 | LL | let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^ | + = note: there is a compiler inserted borrow here = 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:89:17 | -LL | let _ = ATOMIC_TUPLE.0[0]; - | ^^^^^^^^^^^^ +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:109:5 + --> tests/ui/borrow_interior_mutable_const/others.rs:90:17 + | +LL | let _ = (&&&&ATOMIC_TUPLE).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:97:5 | LL | CELL.set(2); | ^^^^ | + = note: there is a compiler inserted borrow here = 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 + --> tests/ui/borrow_interior_mutable_const/others.rs:98:16 | LL | assert_eq!(CELL.get(), 6); | ^^^^ | + = note: there is a compiler inserted borrow here = help: assign this const to a local or static variable, and use the variable here -error: aborting due to 14 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/borrow_interior_mutable_const/projections.stderr b/tests/ui/borrow_interior_mutable_const/projections.stderr index eabaf66560a..b0e1883f8bf 100644 --- a/tests/ui/borrow_interior_mutable_const/projections.stderr +++ b/tests/ui/borrow_interior_mutable_const/projections.stderr @@ -1,8 +1,8 @@ error: a `const` item should not be interior mutable - --> tests/ui/borrow_interior_mutable_const/projections.rs:27:1 + --> tests/ui/borrow_interior_mutable_const/projections.rs:27:7 | 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 @@ -12,18 +12,18 @@ 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 + --> tests/ui/borrow_interior_mutable_const/projections.rs:29:7 | 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 + --> tests/ui/borrow_interior_mutable_const/projections.rs:38:15 | 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 @@ -33,10 +33,10 @@ 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 + --> tests/ui/borrow_interior_mutable_const/projections.rs:40:15 | LL | print_ref(&MUTABLE); - | ^^^^^^^ + | ^^^^^^^^ | = help: assign this const to a local or static variable, and use the variable here diff --git a/tests/ui/borrow_interior_mutable_const/traits.rs b/tests/ui/borrow_interior_mutable_const/traits.rs index c4878dbe57b..34a758efa2c 100644 --- a/tests/ui/borrow_interior_mutable_const/traits.rs +++ b/tests/ui/borrow_interior_mutable_const/traits.rs @@ -12,8 +12,7 @@ trait ConcreteTypes { const STRING: String; fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const + let _ = &Self::ATOMIC; //~ borrow_interior_mutable_const let _ = &Self::STRING; } } @@ -24,8 +23,7 @@ impl ConcreteTypes for u64 { fn function() { // Lint this again since implementers can choose not to borrow it. - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const + let _ = &Self::ATOMIC; //~ borrow_interior_mutable_const let _ = &Self::STRING; } } @@ -50,8 +48,7 @@ impl<T: ConstDefault> GenericTypes<T, AtomicUsize> for Vec<T> { fn function() { let _ = &Self::TO_REMAIN_GENERIC; - let _ = &Self::TO_BE_CONCRETE; - //~^ borrow_interior_mutable_const + let _ = &Self::TO_BE_CONCRETE; //~ borrow_interior_mutable_const } } @@ -86,10 +83,8 @@ impl<T: ConstDefault> AssocTypes for Vec<T> { 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::TO_BE_UNFROZEN; //~ borrow_interior_mutable_const + let _ = &Self::WRAPPED_TO_BE_UNFROZEN; //~ borrow_interior_mutable_const let _ = &Self::WRAPPED_TO_BE_GENERIC_PARAM; } } @@ -111,8 +106,7 @@ where fn function() { let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const + let _ = &Self::BOUNDED; //~ borrow_interior_mutable_const } } @@ -125,8 +119,7 @@ where fn function() { let _ = &Self::NOT_BOUNDED; - let _ = &Self::BOUNDED; - //~^ borrow_interior_mutable_const + let _ = &Self::BOUNDED; //~ borrow_interior_mutable_const } } @@ -155,10 +148,8 @@ impl SelfType for AtomicUsize { 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 + let _ = &Self::SELF; //~ borrow_interior_mutable_const + let _ = &Self::WRAPPED_SELF; //~ borrow_interior_mutable_const } } @@ -167,10 +158,8 @@ trait BothOfCellAndGeneric<T> { const INDIRECT: Cell<*const T>; fn function() { - let _ = &Self::DIRECT; - //~^ borrow_interior_mutable_const - let _ = &Self::INDIRECT; - //~^ borrow_interior_mutable_const + let _ = &Self::DIRECT; //~ borrow_interior_mutable_const + let _ = &Self::INDIRECT; //~ borrow_interior_mutable_const } } @@ -179,10 +168,8 @@ impl<T: ConstDefault> BothOfCellAndGeneric<T> for Vec<T> { 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 + let _ = &Self::DIRECT; //~ borrow_interior_mutable_const + let _ = &Self::INDIRECT; //~ borrow_interior_mutable_const } } @@ -201,19 +188,15 @@ where const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); fn function() { - let _ = &Self::ATOMIC; - //~^ borrow_interior_mutable_const + 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 + 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 + u64::ATOMIC.store(5, Ordering::SeqCst); //~ borrow_interior_mutable_const + assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); //~ borrow_interior_mutable_const } diff --git a/tests/ui/borrow_interior_mutable_const/traits.stderr b/tests/ui/borrow_interior_mutable_const/traits.stderr index cad68ca9260..233ec6dad67 100644 --- a/tests/ui/borrow_interior_mutable_const/traits.stderr +++ b/tests/ui/borrow_interior_mutable_const/traits.stderr @@ -1,8 +1,8 @@ error: a `const` item with interior mutability should not be borrowed - --> tests/ui/borrow_interior_mutable_const/traits.rs:15:18 + --> tests/ui/borrow_interior_mutable_const/traits.rs:15:17 | 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 @@ -12,131 +12,133 @@ 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:26:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:51:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:86:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:87:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:109:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:122:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:151:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:152:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:161:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:162:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:171:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:172:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:191:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:195:17 | 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:200:5 | LL | u64::ATOMIC.store(5, Ordering::SeqCst); | ^^^^^^^^^^^ | + = note: there is a compiler inserted borrow here = 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 + --> tests/ui/borrow_interior_mutable_const/traits.rs:201:16 | LL | assert_eq!(u64::ATOMIC.load(Ordering::SeqCst), 9); | ^^^^^^^^^^^ | + = note: there is a compiler inserted borrow here = help: assign this const to a local or static variable, and use the variable here error: aborting due to 17 previous errors diff --git a/tests/ui/crashes/ice-12979.1.fixed b/tests/ui/crashes/ice-12979.1.fixed new file mode 100644 index 00000000000..e68f1c20a8e --- /dev/null +++ b/tests/ui/crashes/ice-12979.1.fixed @@ -0,0 +1,2 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr +const FOO: u8 = 0; diff --git a/tests/ui/crashes/ice-12979.2.fixed b/tests/ui/crashes/ice-12979.2.fixed new file mode 100644 index 00000000000..e89fa636d4b --- /dev/null +++ b/tests/ui/crashes/ice-12979.2.fixed @@ -0,0 +1,3 @@ +#![deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/tests/ui/crashes/ice-12979.rs b/tests/ui/crashes/ice-12979.rs new file mode 100644 index 00000000000..a2787291d9e --- /dev/null +++ b/tests/ui/crashes/ice-12979.rs @@ -0,0 +1,3 @@ +#[deny(clippy::declare_interior_mutable_const)] //~ empty_line_after_outer_attr + +const FOO: u8 = 0; diff --git a/tests/ui/crashes/ice-12979.stderr b/tests/ui/crashes/ice-12979.stderr new file mode 100644 index 00000000000..5e760816164 --- /dev/null +++ b/tests/ui/crashes/ice-12979.stderr @@ -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 + diff --git a/tests/ui/crashes/ice-9445.rs b/tests/ui/crashes/ice-9445.rs deleted file mode 100644 index 232b8e4a795..00000000000 --- a/tests/ui/crashes/ice-9445.rs +++ /dev/null @@ -1,4 +0,0 @@ -const UNINIT: core::mem::MaybeUninit<core::cell::Cell<&'static ()>> = core::mem::MaybeUninit::uninit(); -//~^ declare_interior_mutable_const - -fn main() {} diff --git a/tests/ui/crashes/ice-9445.stderr b/tests/ui/crashes/ice-9445.stderr deleted file mode 100644 index 76689cd6f5c..00000000000 --- a/tests/ui/crashes/ice-9445.stderr +++ /dev/null @@ -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 - diff --git a/tests/ui/declare_interior_mutable_const/enums.rs b/tests/ui/declare_interior_mutable_const/enums.rs index c87468277fb..2ca6b7bc303 100644 --- a/tests/ui/declare_interior_mutable_const/enums.rs +++ b/tests/ui/declare_interior_mutable_const/enums.rs @@ -9,8 +9,7 @@ enum OptionalCell { } // 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 UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(true)); //~ declare_interior_mutable_const const FROZEN_VARIANT: OptionalCell = OptionalCell::Frozen; const fn unfrozen_variant() -> OptionalCell { @@ -21,8 +20,7 @@ const fn frozen_variant() -> OptionalCell { OptionalCell::Frozen } -const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); -//~^ declare_interior_mutable_const +const UNFROZEN_VARIANT_FROM_FN: OptionalCell = unfrozen_variant(); //~ declare_interior_mutable_const const FROZEN_VARIANT_FROM_FN: OptionalCell = frozen_variant(); enum NestedInnermost { @@ -60,24 +58,21 @@ 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_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_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ declare_interior_mutable_const 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)); + const DEFAULTED_ON_FROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); //~ declare_interior_mutable_const } // At first, I thought I'd need to check every patterns in `trait.rs`; but, what matters @@ -92,8 +87,7 @@ trait AssocTypes { 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_UNFROZEN_VARIANT: Option<Self::ToBeUnfrozen> = Some(Self::ToBeUnfrozen::new(4)); //~ declare_interior_mutable_const const TO_BE_FROZEN_VARIANT: Option<Self::ToBeUnfrozen> = None; } @@ -105,30 +99,25 @@ enum BothOfCellAndGeneric<T> { } impl<T> BothOfCellAndGeneric<T> { - const UNFROZEN_VARIANT: BothOfCellAndGeneric<T> = BothOfCellAndGeneric::Unfrozen(Cell::new(std::ptr::null())); - //~^ declare_interior_mutable_const + 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 + 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 + 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); } diff --git a/tests/ui/declare_interior_mutable_const/enums.stderr b/tests/ui/declare_interior_mutable_const/enums.stderr index 32839d14f0e..25f39f243e0 100644 --- a/tests/ui/declare_interior_mutable_const/enums.stderr +++ b/tests/ui/declare_interior_mutable_const/enums.stderr @@ -1,89 +1,70 @@ error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/enums.rs:12:1 + --> tests/ui/declare_interior_mutable_const/enums.rs:12:7 | 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 + --> tests/ui/declare_interior_mutable_const/enums.rs:23:7 | 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 + --> tests/ui/declare_interior_mutable_const/enums.rs:45:7 | -LL | / const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { -LL | | -LL | | outer: NestedOuter::NestedInner(NestedInner { -LL | | inner: NestedInnermost::Unfrozen(AtomicUsize::new(2)), -LL | | }), -LL | | }; - | |__^ +LL | const NESTED_UNFROZEN_VARIANT: NestedOutermost = NestedOutermost { + | ^^^^^^^^^^^^^^^^^^^^^^^ | = 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 + --> tests/ui/declare_interior_mutable_const/enums.rs:64:11 | -LL | const TO_BE_UNFROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +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:64:5 + --> tests/ui/declare_interior_mutable_const/enums.rs:71:11 | -LL | const TO_BE_FROZEN_VARIANT: OptionalCell; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const TO_BE_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:68:5 + --> tests/ui/declare_interior_mutable_const/enums.rs:75:11 | -LL | const DEFAULTED_ON_UNFROZEN_VARIANT: OptionalCell = OptionalCell::Unfrozen(Cell::new(false)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const DEFAULTED_ON_FROZEN_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 + --> tests/ui/declare_interior_mutable_const/enums.rs:90:11 | 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 + --> tests/ui/declare_interior_mutable_const/enums.rs:102:11 | 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 + --> tests/ui/declare_interior_mutable_const/enums.rs:111:11 | 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 + --> tests/ui/declare_interior_mutable_const/enums.rs:118:11 | -LL | const GENERIC_VARIANT: BothOfCellAndGeneric<Self::AssocType> = BothOfCellAndGeneric::Generic(std::ptr::null()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | const UNFROZEN_VARIANT: BothOfCellAndGeneric<Self::AssocType> = + | ^^^^^^^^^^^^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs index 7ce04a3f2c3..362f28b8c53 100644 --- a/tests/ui/declare_interior_mutable_const/others.rs +++ b/tests/ui/declare_interior_mutable_const/others.rs @@ -7,20 +7,17 @@ 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: 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()); +declare_const!(_ONCE: Once = Once::new()); //~ declare_interior_mutable_const // const ATOMIC_REF: &AtomicUsize = &AtomicUsize::new(7); // This will simply trigger E0492. diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr index 09299b29041..243c6b0cc5f 100644 --- a/tests/ui/declare_interior_mutable_const/others.stderr +++ b/tests/ui/declare_interior_mutable_const/others.stderr @@ -1,49 +1,47 @@ error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:10:1 + --> tests/ui/declare_interior_mutable_const/others.rs:10:7 | 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 + --> tests/ui/declare_interior_mutable_const/others.rs:11:7 | 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 + --> tests/ui/declare_interior_mutable_const/others.rs:12:7 | 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 + --> tests/ui/declare_interior_mutable_const/others.rs:20:16 | -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) + = help: consider making this a static item error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/others.rs:47:13 + --> tests/ui/declare_interior_mutable_const/others.rs:44:19 | LL | const _BAZ: Cell<usize> = Cell::new(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^ ... LL | issue_8493!(); | ------------- in this macro invocation | + = help: consider making this `Sync` so that it can go in a static item or using a `thread_local` = 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 diff --git a/tests/ui/declare_interior_mutable_const/traits.rs b/tests/ui/declare_interior_mutable_const/traits.rs index d3139be6859..f4916e1b4cf 100644 --- a/tests/ui/declare_interior_mutable_const/traits.rs +++ b/tests/ui/declare_interior_mutable_const/traits.rs @@ -7,17 +7,15 @@ 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 ATOMIC: AtomicUsize; //~ declare_interior_mutable_const const INTEGER: u64; const STRING: String; - declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); + declare_const!(ANOTHER_ATOMIC: AtomicUsize = Self::ATOMIC); //~ declare_interior_mutable_const } impl ConcreteTypes for u64 { @@ -43,7 +41,6 @@ trait GenericTypes<T, U> { 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 @@ -68,10 +65,8 @@ impl<T: ConstDefault> AssocTypes for Vec<T> { 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 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); } @@ -90,8 +85,7 @@ where T: AssocTypesHelper<ToBeBounded = AtomicUsize>, { const NOT_BOUNDED: T::NotToBeBounded; - const BOUNDED: T::ToBeBounded; - //~^ declare_interior_mutable_const + const BOUNDED: T::ToBeBounded; //~ declare_interior_mutable_const } impl<T> AssocTypesFromGenericParam<T> for u64 @@ -120,23 +114,18 @@ 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 + 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 + 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()); } @@ -148,15 +137,13 @@ impl<T> Local<T> where T: ConstDefault + AssocTypesHelper<ToBeBounded = AtomicUsize>, { - const ATOMIC: AtomicUsize = AtomicUsize::new(18); - //~^ declare_interior_mutable_const + 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 + const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); //~ declare_interior_mutable_const } fn main() {} diff --git a/tests/ui/declare_interior_mutable_const/traits.stderr b/tests/ui/declare_interior_mutable_const/traits.stderr index b03dd7a0840..9b9d9ddeef9 100644 --- a/tests/ui/declare_interior_mutable_const/traits.stderr +++ b/tests/ui/declare_interior_mutable_const/traits.stderr @@ -1,88 +1,65 @@ error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:16:5 + --> tests/ui/declare_interior_mutable_const/traits.rs:15:11 | 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 + --> tests/ui/declare_interior_mutable_const/traits.rs:18:20 | -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 + --> tests/ui/declare_interior_mutable_const/traits.rs:68:11 | 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 + --> tests/ui/declare_interior_mutable_const/traits.rs:69:11 | 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 + --> tests/ui/declare_interior_mutable_const/traits.rs:88:11 | 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 + --> tests/ui/declare_interior_mutable_const/traits.rs:117:11 | 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 + --> tests/ui/declare_interior_mutable_const/traits.rs:123:11 | LL | const DIRECT: Cell<T>; - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^ error: a `const` item should not be interior mutable - --> tests/ui/declare_interior_mutable_const/traits.rs:133:5 + --> tests/ui/declare_interior_mutable_const/traits.rs:124:11 | 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 + --> tests/ui/declare_interior_mutable_const/traits.rs:140:11 | 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 + --> tests/ui/declare_interior_mutable_const/traits.rs:146:11 | LL | const BOUNDED_ASSOC_TYPE: T::ToBeBounded = AtomicUsize::new(19); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^ -error: aborting due to 13 previous errors +error: aborting due to 10 previous errors |
