]> git.lizzy.rs Git - rust.git/commitdiff
Allow MIR borrowck to catch unused mutable locals
authorKeith Yeung <kungfukeith11@gmail.com>
Wed, 28 Feb 2018 09:09:08 +0000 (01:09 -0800)
committerKeith Yeung <kungfukeith11@gmail.com>
Sat, 28 Apr 2018 08:55:23 +0000 (01:55 -0700)
src/librustc/mir/mod.rs
src/librustc_mir/borrow_check/mod.rs

index c525c4ed651f244faf93df66289f4884629441c3..501f77547e282c1320b7796c14a0e83f39174cd1 100644 (file)
@@ -247,6 +247,20 @@ pub fn vars_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
         })
     }
 
+    /// Returns an iterator over all user-declared mutable locals.
+    #[inline]
+    pub fn mut_vars_iter<'a>(&'a self) -> impl Iterator<Item=Local> + 'a {
+        (self.arg_count+1..self.local_decls.len()).filter_map(move |index| {
+            let local = Local::new(index);
+            let decl = &self.local_decls[local];
+            if decl.is_user_variable && decl.mutability == Mutability::Mut {
+                Some(local)
+            } else {
+                None
+            }
+        })
+    }
+
     /// Returns an iterator over all function arguments.
     #[inline]
     pub fn args_iter(&self) -> impl Iterator<Item=Local> {
index 5c7061abbb6608b9104401710d0dd04c113d286c..6cebd290b9a25908f6fade7234981b0513c3618b 100644 (file)
 use rustc::infer::InferCtxt;
 use rustc::ty::{self, ParamEnv, TyCtxt};
 use rustc::ty::maps::Providers;
-use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Place};
-use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
-use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
-use rustc::mir::{ClosureRegionRequirements, Local};
+use rustc::lint::builtin::UNUSED_MUT;
+use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, ClearCrossCrate, Local};
+use rustc::mir::{Location, Place, Mir, Mutability, Operand, Projection, ProjectionElem};
+use rustc::mir::{Rvalue, Field, Statement, StatementKind, Terminator, TerminatorKind};
+use rustc::mir::ClosureRegionRequirements;
 
 use rustc_data_structures::control_flow_graph::dominators::Dominators;
 use rustc_data_structures::fx::FxHashSet;
@@ -236,7 +237,8 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>(
         access_place_error_reported: FxHashSet(),
         reservation_error_reported: FxHashSet(),
         moved_error_reported: FxHashSet(),
-        nonlexical_regioncx: regioncx,
+        nonlexical_regioncx: opt_regioncx,
+        used_mut: FxHashSet(),
         nonlexical_cause_info: None,
         borrow_set,
         dominators,
@@ -287,6 +289,9 @@ pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
     /// This field keeps track of errors reported in the checking of moved variables,
     /// so that we don't report report seemingly duplicate errors.
     moved_error_reported: FxHashSet<Place<'tcx>>,
+    /// This field keeps track of all the local variables that are declared mut and are mutated.
+    /// Used for the warning issued by an unused mutable local variable.
+    used_mut: FxHashSet<Local>,
     /// Non-lexical region inference context, if NLL is enabled.  This
     /// contains the results from region inference and lets us e.g.
     /// find out which CFG points are contained in each borrow region.
@@ -434,6 +439,22 @@ fn visit_terminator_entry(
 
         self.check_activations(location, span, flow_state);
 
+        for local in self.mir.mut_vars_iter().filter(|local| !self.used_mut.contains(local)) {
+            if let ClearCrossCrate::Set(ref vsi) = self.mir.visibility_scope_info {
+                let source_info = self.mir.local_decls[local].source_info;
+                let mut_span = self.tcx.sess.codemap().span_until_non_whitespace(source_info.span);
+
+                self.tcx.struct_span_lint_node(
+                    UNUSED_MUT,
+                    vsi[source_info.scope].lint_root,
+                    source_info.span,
+                    "variable does not need to be mutable"
+                )
+                .span_suggestion_short(mut_span, "remove this `mut`", "".to_owned())
+                .emit();
+            }
+        }
+
         match term.kind {
             TerminatorKind::SwitchInt {
                 ref discr,
@@ -1594,7 +1615,7 @@ fn get_primary_err_msg(&self, place:&Place<'tcx>) -> String{
     ///
     /// Returns true if an error is reported, false otherwise.
     fn check_access_permissions(
-        &self,
+        &mut self,
         (place, span): (&Place<'tcx>, Span),
         kind: ReadOrWrite,
         is_local_mutation_allowed: LocalMutationIsAllowed,
@@ -1631,7 +1652,9 @@ fn check_access_permissions(
                 err.emit();
             },
             Reservation(WriteKind::Mutate) | Write(WriteKind::Mutate) => {
-
+                if let Place::Local(local) = *place {
+                    self.used_mut.insert(local);
+                }
                 if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) {
                     error_reported = true;
                     let mut err_info = None;