Skip to content

Commit 2338adf

Browse files
committed
Allow MIR borrowck to catch unused mutable locals
1 parent 1eb0cef commit 2338adf

File tree

2 files changed

+44
-7
lines changed

2 files changed

+44
-7
lines changed

src/librustc/mir/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,20 @@ impl<'tcx> Mir<'tcx> {
247247
})
248248
}
249249

250+
/// Returns an iterator over all user-declared mutable locals.
251+
#[inline]
252+
pub fn mut_vars_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
253+
(self.arg_count+1..self.local_decls.len()).filter_map(move |index| {
254+
let local = Local::new(index);
255+
let decl = &self.local_decls[local];
256+
if decl.is_user_variable && decl.mutability == Mutability::Mut {
257+
Some(local)
258+
} else {
259+
None
260+
}
261+
})
262+
}
263+
250264
/// Returns an iterator over all function arguments.
251265
#[inline]
252266
pub fn args_iter(&self) -> impl Iterator<Item=Local> {

src/librustc_mir/borrow_check/mod.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@ use rustc::hir::map::definitions::DefPathData;
1717
use rustc::infer::InferCtxt;
1818
use rustc::ty::{self, ParamEnv, TyCtxt};
1919
use rustc::ty::maps::Providers;
20-
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Place};
21-
use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
22-
use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
23-
use rustc::mir::{ClosureRegionRequirements, Local};
20+
use rustc::lint::builtin::UNUSED_MUT;
21+
use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, ClearCrossCrate, Local};
22+
use rustc::mir::{Location, Place, Mir, Mutability, Operand, Projection, ProjectionElem};
23+
use rustc::mir::{Rvalue, Field, Statement, StatementKind, Terminator, TerminatorKind};
24+
use rustc::mir::ClosureRegionRequirements;
2425

2526
use rustc_data_structures::control_flow_graph::dominators::Dominators;
2627
use rustc_data_structures::fx::FxHashSet;
@@ -236,7 +237,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
236237
access_place_error_reported: FxHashSet(),
237238
reservation_error_reported: FxHashSet(),
238239
moved_error_reported: FxHashSet(),
239-
nonlexical_regioncx: regioncx,
240+
nonlexical_regioncx: opt_regioncx,
241+
used_mut: FxHashSet(),
240242
nonlexical_cause_info: None,
241243
borrow_set,
242244
dominators,
@@ -287,6 +289,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
287289
/// This field keeps track of errors reported in the checking of moved variables,
288290
/// so that we don't report report seemingly duplicate errors.
289291
moved_error_reported: FxHashSet<Place<'tcx>>,
292+
/// This field keeps track of all the local variables that are declared mut and are mutated.
293+
/// Used for the warning issued by an unused mutable local variable.
294+
used_mut: FxHashSet<Local>,
290295
/// Non-lexical region inference context, if NLL is enabled. This
291296
/// contains the results from region inference and lets us e.g.
292297
/// find out which CFG points are contained in each borrow region.
@@ -434,6 +439,22 @@ impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx
434439

435440
self.check_activations(location, span, flow_state);
436441

442+
for local in self.mir.mut_vars_iter().filter(|local| !self.used_mut.contains(local)) {
443+
if let ClearCrossCrate::Set(ref vsi) = self.mir.visibility_scope_info {
444+
let source_info = self.mir.local_decls[local].source_info;
445+
let mut_span = self.tcx.sess.codemap().span_until_non_whitespace(source_info.span);
446+
447+
self.tcx.struct_span_lint_node(
448+
UNUSED_MUT,
449+
vsi[source_info.scope].lint_root,
450+
source_info.span,
451+
"variable does not need to be mutable"
452+
)
453+
.span_suggestion_short(mut_span, "remove this `mut`", "".to_owned())
454+
.emit();
455+
}
456+
}
457+
437458
match term.kind {
438459
TerminatorKind::SwitchInt {
439460
ref discr,
@@ -1594,7 +1615,7 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
15941615
///
15951616
/// Returns true if an error is reported, false otherwise.
15961617
fn check_access_permissions(
1597-
&self,
1618+
&mut self,
15981619
(place, span): (&Place<'tcx>, Span),
15991620
kind: ReadOrWrite,
16001621
is_local_mutation_allowed: LocalMutationIsAllowed,
@@ -1631,7 +1652,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
16311652
err.emit();
16321653
},
16331654
Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => {
1634-
1655+
if let Place::Local(local) = *place {
1656+
self.used_mut.insert(local);
1657+
}
16351658
if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) {
16361659
error_reported = true;
16371660
let mut err_info = None;

0 commit comments

Comments
 (0)