Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions compiler/rustc_attr_parsing/src/attributes/lint_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ impl<S: Stage> NoArgsAttributeParser<S> for PassByValueParser {
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PassByValue;
}

pub(crate) struct RustcMustNotCallOnInteriorMutableConsts;
impl<S: Stage> NoArgsAttributeParser<S> for RustcMustNotCallOnInteriorMutableConsts {
const PATH: &[Symbol] = &[sym::rustc_must_not_call_on_interior_mutable_consts];
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
Allow(Target::Method(MethodKind::Inherent)),
Allow(Target::Method(MethodKind::TraitImpl)),
]);
const CREATE: fn(Span) -> AttributeKind =
AttributeKind::RustcMustNotCallOnInteriorMutableConsts;
}

pub(crate) struct AutomaticallyDerivedParser;
impl<S: Stage> NoArgsAttributeParser<S> for AutomaticallyDerivedParser {
const PATH: &[Symbol] = &[sym::automatically_derived];
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_attr_parsing/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::attributes::link_attrs::{
};
use crate::attributes::lint_helpers::{
AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser,
RustcMustNotCallOnInteriorMutableConsts,
};
use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser};
use crate::attributes::macro_attrs::{
Expand Down Expand Up @@ -241,6 +242,7 @@ attribute_parsers!(
Single<WithoutArgs<PubTransparentParser>>,
Single<WithoutArgs<RustcCoherenceIsCoreParser>>,
Single<WithoutArgs<RustcMainParser>>,
Single<WithoutArgs<RustcMustNotCallOnInteriorMutableConsts>>,
Single<WithoutArgs<SpecializationTraitParser>>,
Single<WithoutArgs<StdInternalSymbolParser>>,
Single<WithoutArgs<TrackCallerParser>>,
Expand Down
5 changes: 5 additions & 0 deletions compiler/rustc_feature/src/builtin_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,11 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
EncodeCrossCrate::Yes,
"`#[rustc_as_ptr]` is used to mark functions returning pointers to their inner allocations."
),
rustc_attr!(
rustc_must_not_call_on_interior_mutable_consts, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::Yes,
"`#[rustc_must_not_call_on_interior_mutable_consts]` is used to mark methods that don't make sense to be called on interior mutable consts."
),
rustc_attr!(
rustc_pass_by_value, Normal, template!(Word), ErrorFollowing,
EncodeCrossCrate::Yes,
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/attrs/data_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_main]`.
RustcMain,

/// Represents `#[rustc_must_not_call_on_interior_mutable_consts]`
RustcMustNotCallOnInteriorMutableConsts(Span),

/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,

Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir/src/attrs/encode_cross_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ impl AttributeKind {
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcMain => No,
RustcMustNotCallOnInteriorMutableConsts(..) => Yes,
RustcObjectLifetimeDefault => No,
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
Sanitize { .. } => No,
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,14 @@ lint_int_to_ptr_transmutes = transmuting an integer to a pointer creates a point
.suggestion_with_exposed_provenance = use `std::ptr::with_exposed_provenance{$suffix}` instead to use a previously exposed provenance
.suggestion_without_provenance_mut = if you truly mean to create a pointer without provenance, use `std::ptr::without_provenance_mut`

lint_interior_mutable_const_item_mutations =
mutation of an interior mutable `const` item with call to `{$method_name}`
.label = `{$const_name}` is a interior mutable `const` item of type `{$const_ty}`
.temporary = each usage of a `const` item creates a new temporary
.never_original = only the temporaries and never the original `const {$const_name}` will be modified
.suggestion_static = for a shared instance of `{$const_name}`, consider making it a `static` item instead
.help = for more details on interior mutability see <https://doc.rust-lang.org/reference/interior-mutability.html>

lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly
.label = use a different label that doesn't start with `0` or `1`
.help = start numbering with `2` instead
Expand Down
124 changes: 124 additions & 0 deletions compiler/rustc_lint/src/interior_mutable_consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
use rustc_hir::attrs::AttributeKind;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind, ItemKind, Node, find_attr};
use rustc_session::{declare_lint, declare_lint_pass};

use crate::lints::{
InteriorMutableConstItemMutationsDiag, InteriorMutableConstItemMutationsSuggestionStatic,
};
use crate::{LateContext, LateLintPass, LintContext};

declare_lint! {
/// The `interior_mutable_const_item_mutations` lint checks for calls which
/// mutates an interior mutable const-item.
///
/// ### Example
///
/// ```rust
/// use std::sync::Once;
///
/// const INIT: Once = Once::new(); // using `INIT` will always create a temporary and
/// // never modify it-self on use, should be a `static`
/// // instead for shared use
///
/// fn init() {
/// INIT.call_once(|| {
/// println!("Once::call_once first call");
/// });
/// INIT.call_once(|| { // this second will also print
/// println!("Once::call_once second call"); // as each call to `INIT` creates
/// }); // new temporary
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Calling a method which mutates an interior mutable type has no effect as const-item
/// are essentially inlined wherever they are used, meaning that they are copied
/// directly into the relevant context when used rendering modification through
/// interior mutability ineffective across usage of that const-item.
///
/// The current implementation of this lint only warns on significant `std` and
/// `core` interior mutable types, like `Once`, `AtomicI32`, ... this is done out
/// of prudence to avoid false-positive and may be extended in the future.
pub INTERIOR_MUTABLE_CONST_ITEM_MUTATIONS,
Warn,
"checks for calls which mutates a interior mutable const-item"
}

declare_lint_pass!(InteriorMutableConsts => [INTERIOR_MUTABLE_CONST_ITEM_MUTATIONS]);

impl<'tcx> LateLintPass<'tcx> for InteriorMutableConsts {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let typeck = cx.typeck_results();

let (method_did, receiver) = match expr.kind {
// matching on `<receiver>.method(..)`
ExprKind::MethodCall(_, receiver, _, _) => {
(typeck.type_dependent_def_id(expr.hir_id), receiver)
}
// matching on `function(&<receiver>, ...)`
ExprKind::Call(path, [receiver, ..]) => match receiver.kind {
ExprKind::AddrOf(_, _, receiver) => match path.kind {
ExprKind::Path(ref qpath) => {
(cx.qpath_res(qpath, path.hir_id).opt_def_id(), receiver)
}
_ => return,
},
_ => return,
},
_ => return,
};

let Some(method_did) = method_did else {
return;
};

if let ExprKind::Path(qpath) = &receiver.kind
&& let Res::Def(DefKind::Const | DefKind::AssocConst, const_did) =
typeck.qpath_res(qpath, receiver.hir_id)
// Let's do the attribute check after the other checks for perf reasons
&& find_attr!(
cx.tcx.get_all_attrs(method_did),
AttributeKind::RustcMustNotCallOnInteriorMutableConsts(_)
)
&& let Some(method_name) = cx.tcx.opt_item_ident(method_did)
&& let Some(const_name) = cx.tcx.opt_item_ident(const_did)
&& let Some(const_ty) = typeck.node_type_opt(receiver.hir_id)
{
// Find the local `const`-item and create the suggestion to use `static` instead
let sugg_static = if let Some(Node::Item(const_item)) =
cx.tcx.hir_get_if_local(const_did)
&& let ItemKind::Const(ident, _generics, _ty, _body_id) = const_item.kind
{
if let Some(vis_span) = const_item.vis_span.find_ancestor_inside(const_item.span)
&& const_item.span.can_be_used_for_suggestions()
&& vis_span.can_be_used_for_suggestions()
{
Some(InteriorMutableConstItemMutationsSuggestionStatic::Spanful {
const_: const_item.vis_span.between(ident.span),
before: if !vis_span.is_empty() { " " } else { "" },
})
} else {
Some(InteriorMutableConstItemMutationsSuggestionStatic::Spanless)
}
} else {
None
};

cx.emit_span_lint(
INTERIOR_MUTABLE_CONST_ITEM_MUTATIONS,
expr.span,
InteriorMutableConstItemMutationsDiag {
method_name,
const_name,
const_ty,
receiver_span: receiver.span,
sugg_static,
},
);
}
}
}
3 changes: 3 additions & 0 deletions compiler/rustc_lint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ mod for_loops_over_fallibles;
mod foreign_modules;
mod if_let_rescope;
mod impl_trait_overcaptures;
mod interior_mutable_consts;
mod internal;
mod invalid_from_utf8;
mod late;
Expand Down Expand Up @@ -94,6 +95,7 @@ use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
use for_loops_over_fallibles::*;
use if_let_rescope::IfLetRescope;
use impl_trait_overcaptures::ImplTraitOvercaptures;
use interior_mutable_consts::*;
use internal::*;
use invalid_from_utf8::*;
use let_underscore::*;
Expand Down Expand Up @@ -240,6 +242,7 @@ late_lint_methods!(
AsyncClosureUsage: AsyncClosureUsage,
AsyncFnInTrait: AsyncFnInTrait,
NonLocalDefinitions: NonLocalDefinitions::default(),
InteriorMutableConsts: InteriorMutableConsts,
ImplTraitOvercaptures: ImplTraitOvercaptures,
IfLetRescope: IfLetRescope::default(),
StaticMutRefs: StaticMutRefs,
Expand Down
33 changes: 33 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,39 @@ pub(crate) enum InvalidFromUtf8Diag {
},
}

// interior_mutable_consts.rs
#[derive(LintDiagnostic)]
#[diag(lint_interior_mutable_const_item_mutations)]
#[note(lint_temporary)]
#[note(lint_never_original)]
#[help]
pub(crate) struct InteriorMutableConstItemMutationsDiag<'tcx> {
pub method_name: Ident,
pub const_name: Ident,
pub const_ty: Ty<'tcx>,
#[label]
pub receiver_span: Span,
#[subdiagnostic]
pub sugg_static: Option<InteriorMutableConstItemMutationsSuggestionStatic>,
}

#[derive(Subdiagnostic)]
pub(crate) enum InteriorMutableConstItemMutationsSuggestionStatic {
#[suggestion(
lint_suggestion_static,
code = "{before}static ",
style = "verbose",
applicability = "maybe-incorrect"
)]
Spanful {
#[primary_span]
const_: Span,
before: &'static str,
},
#[help(lint_suggestion_static)]
Spanless,
}

// reference_casting.rs
#[derive(LintDiagnostic)]
pub(crate) enum InvalidReferenceCastingDiag<'tcx> {
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_passes/src/check_attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::RustcLayoutScalarValidRangeStart(..)
| AttributeKind::RustcLayoutScalarValidRangeEnd(..)
| AttributeKind::RustcSimdMonomorphizeLaneLimit(..)
| AttributeKind::RustcMustNotCallOnInteriorMutableConsts(..)
| AttributeKind::ExportStable
| AttributeKind::FfiConst(..)
| AttributeKind::UnstableFeatureBound(..)
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1928,6 +1928,7 @@ symbols! {
rustc_main,
rustc_mir,
rustc_must_implement_one_of,
rustc_must_not_call_on_interior_mutable_consts,
rustc_never_returns_null_ptr,
rustc_never_type_options,
rustc_no_implicit_autorefs,
Expand Down
Loading
Loading