Add CONST_ITEM_MUTATION lint

Fixes #74053
Fixes #55721

This PR adds a new lint `CONST_ITEM_MUTATION`.
Given an item `const FOO: SomeType = ..`, this lint fires on:

* Attempting to write directly to a field (`FOO.field = some_val`) or
  array entry (`FOO.array_field[0] = val`)
* Taking a mutable reference to the `const` item (`&mut FOO`), including
  through an autoderef `FOO.some_mut_self_method()`

The lint message explains that since each use of a constant creates a
new temporary, the original `const` item will not be modified.
This commit is contained in:
Aaron Hill 2020-08-10 07:16:30 -04:00
parent d2454643e1
commit f422ef141a
No known key found for this signature in database
GPG Key ID: B4087E510E98B164
17 changed files with 403 additions and 68 deletions

View File

@ -899,6 +899,8 @@ pub enum LocalInfo<'tcx> {
User(ClearCrossCrate<BindingForm<'tcx>>),
/// A temporary created that references the static with the given `DefId`.
StaticRef { def_id: DefId, is_thread_local: bool },
/// A temporary created that references the const with the given `DefId`
ConstRef { def_id: DefId },
}
impl<'tcx> LocalDecl<'tcx> {

View File

@ -804,68 +804,51 @@ pub(super) fn move_spans(
debug!("move_spans: target_temp = {:?}", target_temp);
if let Some(Terminator {
kind: TerminatorKind::Call { func, args, fn_span, from_hir_call, .. },
..
kind: TerminatorKind::Call { fn_span, from_hir_call, .. }, ..
}) = &self.body[location.block].terminator
{
let mut method_did = None;
if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
if let ty::FnDef(def_id, _) = *ty.kind() {
debug!("move_spans: fn = {:?}", def_id);
if let Some(ty::AssocItem { fn_has_self_parameter, .. }) =
self.infcx.tcx.opt_associated_item(def_id)
{
if *fn_has_self_parameter {
method_did = Some(def_id);
}
}
}
}
let method_did = if let Some(method_did) =
crate::util::find_self_call(self.infcx.tcx, &self.body, target_temp, location.block)
{
method_did
} else {
return normal_ret;
};
let tcx = self.infcx.tcx;
let method_did = if let Some(did) = method_did { did } else { return normal_ret };
if let [Operand::Move(self_place), ..] = **args {
if self_place.as_local() == Some(target_temp) {
let parent = tcx.parent(method_did);
let is_fn_once = parent == tcx.lang_items().fn_once_trait();
let is_operator = !from_hir_call
&& parent.map_or(false, |p| {
tcx.lang_items().group(LangItemGroup::Op).contains(&p)
});
let fn_call_span = *fn_span;
let parent = tcx.parent(method_did);
let is_fn_once = parent == tcx.lang_items().fn_once_trait();
let is_operator = !from_hir_call
&& parent.map_or(false, |p| tcx.lang_items().group(LangItemGroup::Op).contains(&p));
let fn_call_span = *fn_span;
let self_arg = tcx.fn_arg_names(method_did)[0];
let self_arg = tcx.fn_arg_names(method_did)[0];
let kind = if is_fn_once {
FnSelfUseKind::FnOnceCall
} else if is_operator {
FnSelfUseKind::Operator { self_arg }
} else {
debug!(
"move_spans: method_did={:?}, fn_call_span={:?}",
method_did, fn_call_span
);
let implicit_into_iter = matches!(
fn_call_span.desugaring_kind(),
Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
);
FnSelfUseKind::Normal { self_arg, implicit_into_iter }
};
let kind = if is_fn_once {
FnSelfUseKind::FnOnceCall
} else if is_operator {
FnSelfUseKind::Operator { self_arg }
} else {
debug!("move_spans: method_did={:?}, fn_call_span={:?}", method_did, fn_call_span);
let implicit_into_iter = matches!(
fn_call_span.desugaring_kind(),
Some(DesugaringKind::ForLoop(ForLoopLoc::IntoIter))
);
FnSelfUseKind::Normal { self_arg, implicit_into_iter }
};
return FnSelfUse {
var_span: stmt.source_info.span,
fn_call_span,
fn_span: self
.infcx
.tcx
.sess
.source_map()
.guess_head_span(self.infcx.tcx.def_span(method_did)),
kind,
};
}
}
return FnSelfUse {
var_span: stmt.source_info.span,
fn_call_span,
fn_span: self
.infcx
.tcx
.sess
.source_map()
.guess_head_span(self.infcx.tcx.def_span(method_did)),
kind,
};
}
normal_ret
}

View File

@ -0,0 +1,114 @@
use rustc_errors::DiagnosticBuilder;
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::builtin::CONST_ITEM_MUTATION;
use rustc_span::def_id::DefId;
use crate::transform::{MirPass, MirSource};
pub struct CheckConstItemMutation;
impl<'tcx> MirPass<'tcx> for CheckConstItemMutation {
fn run_pass(&self, tcx: TyCtxt<'tcx>, _src: MirSource<'tcx>, body: &mut Body<'tcx>) {
let mut checker = ConstMutationChecker { body, tcx, target_local: None };
checker.visit_body(&body);
}
}
struct ConstMutationChecker<'a, 'tcx> {
body: &'a Body<'tcx>,
tcx: TyCtxt<'tcx>,
target_local: Option<Local>,
}
impl<'a, 'tcx> ConstMutationChecker<'a, 'tcx> {
fn is_const_item(&self, local: Local) -> Option<DefId> {
if let Some(box LocalInfo::ConstRef { def_id }) = self.body.local_decls[local].local_info {
Some(def_id)
} else {
None
}
}
fn lint_const_item_usage(
&self,
const_item: DefId,
location: Location,
decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>,
) {
let source_info = self.body.source_info(location);
let lint_root = self.body.source_scopes[source_info.scope]
.local_data
.as_ref()
.assert_crate_local()
.lint_root;
self.tcx.struct_span_lint_hir(CONST_ITEM_MUTATION, lint_root, source_info.span, |lint| {
decorate(lint)
.span_note(self.tcx.def_span(const_item), "`const` item defined here")
.emit()
});
}
}
impl<'a, 'tcx> Visitor<'tcx> for ConstMutationChecker<'a, 'tcx> {
fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) {
if let StatementKind::Assign(box (lhs, _)) = &stmt.kind {
// Check for assignment to fields of a constant
// Assigning directly to a constant (e.g. `FOO = true;`) is a hard error,
// so emitting a lint would be redundant.
if !lhs.projection.is_empty() {
if let Some(def_id) = self.is_const_item(lhs.local) {
self.lint_const_item_usage(def_id, loc, |lint| {
let mut lint = lint.build("attempting to modify a `const` item");
lint.note("each usage of a `const` item creates a new temporary - the original `const` item will not be modified");
lint
})
}
}
// We are looking for MIR of the form:
//
// ```
// _1 = const FOO;
// _2 = &mut _1;
// method_call(_2, ..)
// ```
//
// Record our current LHS, so that we can detect this
// pattern in `visit_rvalue`
self.target_local = lhs.as_local();
}
self.super_statement(stmt, loc);
self.target_local = None;
}
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) {
if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue {
let local = place.local;
if let Some(def_id) = self.is_const_item(local) {
// If this Rvalue is being used as the right-hand side of a
// `StatementKind::Assign`, see if it ends up getting used as
// the `self` parameter of a method call (as the terminator of our current
// BasicBlock). If so, we emit a more specific lint.
let method_did = self.target_local.and_then(|target_local| {
crate::util::find_self_call(self.tcx, &self.body, target_local, loc.block)
});
let lint_loc =
if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };
self.lint_const_item_usage(def_id, lint_loc, |lint| {
let mut lint = lint.build("taking a mutable reference to a `const` item");
lint
.note("each usage of a `const` item creates a new temporary")
.note("the mutable reference will refer to this temporary, not the original `const` item");
if let Some(method_did) = method_did {
lint.span_note(self.tcx.def_span(method_did), "mutable reference created due to call to this method");
}
lint
});
}
}
self.super_rvalue(rvalue, loc);
}
}

View File

@ -16,6 +16,7 @@
pub mod add_call_guards;
pub mod add_moves_for_packed_drops;
pub mod add_retag;
pub mod check_const_item_mutation;
pub mod check_consts;
pub mod check_packed_ref;
pub mod check_unsafety;
@ -307,6 +308,7 @@ fn mir_const<'tcx>(
&[&[
// MIR-level lints.
&check_packed_ref::CheckPackedRef,
&check_const_item_mutation::CheckConstItemMutation,
// What we need to do constant evaluation.
&simplify::SimplifyCfg::new("initial"),
&rustc_peek::SanityCheck,

View File

@ -0,0 +1,35 @@
use rustc_middle::mir::*;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::def_id::DefId;
/// Checks if the specified `local` is used as the `self` prameter of a method call
/// in the provided `BasicBlock`. If it is, then the `DefId` of the called method is
/// returned.
pub fn find_self_call(
tcx: TyCtxt<'_>,
body: &Body<'_>,
local: Local,
block: BasicBlock,
) -> Option<DefId> {
debug!("find_self_call(local={:?}): terminator={:?}", local, &body[block].terminator);
if let Some(Terminator { kind: TerminatorKind::Call { func, args, .. }, .. }) =
&body[block].terminator
{
debug!("find_self_call: func={:?}", func);
if let Operand::Constant(box Constant { literal: ty::Const { ty, .. }, .. }) = func {
if let ty::FnDef(def_id, _) = *ty.kind() {
if let Some(ty::AssocItem { fn_has_self_parameter: true, .. }) =
tcx.opt_associated_item(def_id)
{
debug!("find_self_call: args={:?}", args);
if let [Operand::Move(self_place) | Operand::Copy(self_place), ..] = **args {
if self_place.as_local() == Some(local) {
return Some(def_id);
}
}
}
}
}
}
None
}

View File

@ -7,12 +7,14 @@
mod alignment;
pub mod collect_writes;
mod find_self_call;
mod graphviz;
pub(crate) mod pretty;
pub(crate) mod spanview;
pub use self::aggregate::expand_aggregate;
pub use self::alignment::is_disaligned;
pub use self::find_self_call::find_self_call;
pub use self::graphviz::write_node_label as write_graphviz_node_label;
pub use self::graphviz::{graphviz_safe_def_name, write_mir_graphviz};
pub use self::pretty::{dump_enabled, dump_mir, write_mir_pretty, PassWhere};

View File

@ -21,7 +21,7 @@ fn expr_as_constant(&mut self, expr: Expr<'tcx>) -> Constant<'tcx> {
let Expr { ty, temp_lifetime: _, span, kind } = expr;
match kind {
ExprKind::Scope { region_scope: _, lint_level: _, value } => this.as_constant(value),
ExprKind::Literal { literal, user_ty } => {
ExprKind::Literal { literal, user_ty, const_id: _ } => {
let user_ty = user_ty.map(|user_ty| {
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
span,

View File

@ -76,6 +76,9 @@ fn expr_as_temp(
local_decl.local_info =
Some(box LocalInfo::StaticRef { def_id, is_thread_local: true });
}
ExprKind::Literal { const_id: Some(def_id), .. } => {
local_decl.local_info = Some(box LocalInfo::ConstRef { def_id });
}
_ => {}
}
this.local_decls.push(local_decl)

View File

@ -247,6 +247,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
hir::ExprKind::Lit(ref lit) => ExprKind::Literal {
literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, false),
user_ty: None,
const_id: None,
},
hir::ExprKind::Binary(op, ref lhs, ref rhs) => {
@ -306,6 +307,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
ExprKind::Literal {
literal: cx.const_eval_literal(&lit.node, expr_ty, lit.span, true),
user_ty: None,
const_id: None,
}
} else {
ExprKind::Unary { op: UnOp::Neg, arg: arg.to_ref() }
@ -447,6 +449,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
kind: ExprKind::Literal {
literal: ty::Const::zero_sized(cx.tcx, ty),
user_ty,
const_id: None,
},
}
.to_ref(),
@ -473,6 +476,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
kind: ExprKind::Literal {
literal: ty::Const::zero_sized(cx.tcx, ty),
user_ty: None,
const_id: None,
},
}
.to_ref(),
@ -585,7 +589,7 @@ fn make_mirror_unadjusted<'a, 'tcx>(
temp_lifetime,
ty: var_ty,
span: expr.span,
kind: ExprKind::Literal { literal, user_ty: None },
kind: ExprKind::Literal { literal, user_ty: None, const_id: None },
}
.to_ref()
};
@ -714,7 +718,11 @@ fn method_callee<'a, 'tcx>(
temp_lifetime,
ty,
span,
kind: ExprKind::Literal { literal: ty::Const::zero_sized(cx.tcx(), ty), user_ty },
kind: ExprKind::Literal {
literal: ty::Const::zero_sized(cx.tcx(), ty),
user_ty,
const_id: None,
},
}
}
@ -777,6 +785,7 @@ fn convert_path_expr<'a, 'tcx>(
ExprKind::Literal {
literal: ty::Const::zero_sized(cx.tcx, cx.typeck_results().node_type(expr.hir_id)),
user_ty,
const_id: None,
}
}
@ -794,6 +803,7 @@ fn convert_path_expr<'a, 'tcx>(
.tcx
.mk_const(ty::Const { val, ty: cx.typeck_results().node_type(expr.hir_id) }),
user_ty: None,
const_id: Some(def_id),
}
}
@ -810,6 +820,7 @@ fn convert_path_expr<'a, 'tcx>(
ty: cx.typeck_results().node_type(expr.hir_id),
}),
user_ty,
const_id: Some(def_id),
}
}

View File

@ -273,6 +273,10 @@
Literal {
literal: &'tcx Const<'tcx>,
user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
/// The `DefId` of the `const` item this literal
/// was produced from, if this is not a user-written
/// literal value.
const_id: Option<DefId>,
},
/// A literal containing the address of a `static`.
///

View File

@ -232,6 +232,12 @@
"detects unaligned references to fields of packed structs",
}
declare_lint! {
pub CONST_ITEM_MUTATION,
Warn,
"detects attempts to mutate a `const` item",
}
declare_lint! {
pub SAFE_PACKED_BORROWS,
Warn,
@ -572,6 +578,7 @@
CONST_ERR,
RENAMED_AND_REMOVED_LINTS,
UNALIGNED_REFERENCES,
CONST_ITEM_MUTATION,
SAFE_PACKED_BORROWS,
PATTERNS_IN_FNS_WITHOUT_BODY,
LATE_BOUND_LIFETIME_ARGUMENTS,

View File

@ -3,9 +3,11 @@
static mut M: i32 = 3;
const CR: &'static mut i32 = &mut C; //~ ERROR E0764
//~| WARN taking a mutable
static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0764
//~| ERROR E0019
//~| ERROR cannot borrow
static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0764
//~| WARN taking a mutable
static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M }; //~ ERROR E0764
fn main() {}

View File

@ -1,3 +1,18 @@
warning: taking a mutable reference to a `const` item
--> $DIR/E0017.rs:5:30
|
LL | const CR: &'static mut i32 = &mut C;
| ^^^^^^
|
= note: `#[warn(const_item_mutation)]` on by default
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: `const` item defined here
--> $DIR/E0017.rs:2:1
|
LL | const C: i32 = 2;
| ^^^^^^^^^^^^^^^^^
error[E0764]: mutable references are not allowed in constants
--> $DIR/E0017.rs:5:30
|
@ -5,7 +20,7 @@ LL | const CR: &'static mut i32 = &mut C;
| ^^^^^^ `&mut` is only allowed in `const fn`
error[E0019]: static contains unimplemented expression type
--> $DIR/E0017.rs:6:39
--> $DIR/E0017.rs:7:39
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
| ^^^^^^
@ -13,30 +28,44 @@ LL | static STATIC_REF: &'static mut i32 = &mut X;
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
error[E0764]: mutable references are not allowed in statics
--> $DIR/E0017.rs:6:39
--> $DIR/E0017.rs:7:39
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
| ^^^^^^ `&mut` is only allowed in `const fn`
error[E0596]: cannot borrow immutable static item `X` as mutable
--> $DIR/E0017.rs:6:39
--> $DIR/E0017.rs:7:39
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
| ^^^^^^ cannot borrow as mutable
warning: taking a mutable reference to a `const` item
--> $DIR/E0017.rs:10:38
|
LL | static CONST_REF: &'static mut i32 = &mut C;
| ^^^^^^
|
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: `const` item defined here
--> $DIR/E0017.rs:2:1
|
LL | const C: i32 = 2;
| ^^^^^^^^^^^^^^^^^
error[E0764]: mutable references are not allowed in statics
--> $DIR/E0017.rs:9:38
--> $DIR/E0017.rs:10:38
|
LL | static CONST_REF: &'static mut i32 = &mut C;
| ^^^^^^ `&mut` is only allowed in `const fn`
error[E0764]: mutable references are not allowed in statics
--> $DIR/E0017.rs:10:52
--> $DIR/E0017.rs:12:52
|
LL | static STATIC_MUT_REF: &'static mut i32 = unsafe { &mut M };
| ^^^^^^ `&mut` is only allowed in `const fn`
error: aborting due to 6 previous errors
error: aborting due to 6 previous errors; 2 warnings emitted
Some errors have detailed explanations: E0019, E0596, E0764.
For more information about an error, try `rustc --explain E0019`.

View File

@ -2,9 +2,11 @@
const C: i32 = 2;
const CR: &'static mut i32 = &mut C; //~ ERROR E0764
//~| WARN taking a mutable
static STATIC_REF: &'static mut i32 = &mut X; //~ ERROR E0019
//~| ERROR cannot borrow
//~| ERROR E0764
static CONST_REF: &'static mut i32 = &mut C; //~ ERROR E0764
//~| WARN taking a mutable
fn main() {}

View File

@ -1,3 +1,18 @@
warning: taking a mutable reference to a `const` item
--> $DIR/E0388.rs:4:30
|
LL | const CR: &'static mut i32 = &mut C;
| ^^^^^^
|
= note: `#[warn(const_item_mutation)]` on by default
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: `const` item defined here
--> $DIR/E0388.rs:2:1
|
LL | const C: i32 = 2;
| ^^^^^^^^^^^^^^^^^
error[E0764]: mutable references are not allowed in constants
--> $DIR/E0388.rs:4:30
|
@ -5,7 +20,7 @@ LL | const CR: &'static mut i32 = &mut C;
| ^^^^^^ `&mut` is only allowed in `const fn`
error[E0019]: static contains unimplemented expression type
--> $DIR/E0388.rs:5:39
--> $DIR/E0388.rs:6:39
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
| ^^^^^^
@ -13,24 +28,38 @@ LL | static STATIC_REF: &'static mut i32 = &mut X;
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
error[E0764]: mutable references are not allowed in statics
--> $DIR/E0388.rs:5:39
--> $DIR/E0388.rs:6:39
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
| ^^^^^^ `&mut` is only allowed in `const fn`
error[E0596]: cannot borrow immutable static item `X` as mutable
--> $DIR/E0388.rs:5:39
--> $DIR/E0388.rs:6:39
|
LL | static STATIC_REF: &'static mut i32 = &mut X;
| ^^^^^^ cannot borrow as mutable
warning: taking a mutable reference to a `const` item
--> $DIR/E0388.rs:9:38
|
LL | static CONST_REF: &'static mut i32 = &mut C;
| ^^^^^^
|
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: `const` item defined here
--> $DIR/E0388.rs:2:1
|
LL | const C: i32 = 2;
| ^^^^^^^^^^^^^^^^^
error[E0764]: mutable references are not allowed in statics
--> $DIR/E0388.rs:8:38
--> $DIR/E0388.rs:9:38
|
LL | static CONST_REF: &'static mut i32 = &mut C;
| ^^^^^^ `&mut` is only allowed in `const fn`
error: aborting due to 5 previous errors
error: aborting due to 5 previous errors; 2 warnings emitted
Some errors have detailed explanations: E0019, E0596, E0764.
For more information about an error, try `rustc --explain E0019`.

View File

@ -0,0 +1,21 @@
// check-pass
struct MyStruct {
field: bool,
inner_array: [char; 1],
}
impl MyStruct {
fn use_mut(&mut self) {}
}
const ARRAY: [u8; 1] = [25];
const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
fn main() {
ARRAY[0] = 5; //~ WARN attempting to modify
MY_STRUCT.field = false; //~ WARN attempting to modify
MY_STRUCT.inner_array[0] = 'b'; //~ WARN attempting to modify
MY_STRUCT.use_mut(); //~ WARN taking
&mut MY_STRUCT; //~ WARN taking
(&mut MY_STRUCT).use_mut(); //~ WARN taking
}

View File

@ -0,0 +1,89 @@
warning: attempting to modify a `const` item
--> $DIR/lint-const-item-mutation.rs:15:5
|
LL | ARRAY[0] = 5;
| ^^^^^^^^^^^^
|
= note: `#[warn(const_item_mutation)]` on by default
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:11:1
|
LL | const ARRAY: [u8; 1] = [25];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: attempting to modify a `const` item
--> $DIR/lint-const-item-mutation.rs:16:5
|
LL | MY_STRUCT.field = false;
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: attempting to modify a `const` item
--> $DIR/lint-const-item-mutation.rs:17:5
|
LL | MY_STRUCT.inner_array[0] = 'b';
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary - the original `const` item will not be modified
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: taking a mutable reference to a `const` item
--> $DIR/lint-const-item-mutation.rs:18:5
|
LL | MY_STRUCT.use_mut();
| ^^^^^^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: mutable reference created due to call to this method
--> $DIR/lint-const-item-mutation.rs:8:5
|
LL | fn use_mut(&mut self) {}
| ^^^^^^^^^^^^^^^^^^^^^
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: taking a mutable reference to a `const` item
--> $DIR/lint-const-item-mutation.rs:19:5
|
LL | &mut MY_STRUCT;
| ^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: taking a mutable reference to a `const` item
--> $DIR/lint-const-item-mutation.rs:20:5
|
LL | (&mut MY_STRUCT).use_mut();
| ^^^^^^^^^^^^^^^^
|
= note: each usage of a `const` item creates a new temporary
= note: the mutable reference will refer to this temporary, not the original `const` item
note: `const` item defined here
--> $DIR/lint-const-item-mutation.rs:12:1
|
LL | const MY_STRUCT: MyStruct = MyStruct { field: true, inner_array: ['a'] };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: 6 warnings emitted