1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use rustc::hir::intravisit::{Visitor, NestedVisitorMap};
use rustc::hir::{self, HirId};
use rustc::lint::builtin::UNUSED_MUT;
use rustc::ty;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use std::slice;
use syntax::ptr::P;

use borrowck::BorrowckCtxt;

pub fn check<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, body: &'tcx hir::Body) {
    let mut used_mut = bccx.used_mut_nodes.borrow().clone();
    UsedMutFinder {
        bccx,
        set: &mut used_mut,
    }.visit_expr(&body.value);
    let mut cx = UnusedMutCx { bccx, used_mut };
    for arg in body.arguments.iter() {
        cx.check_unused_mut_pat(slice::from_ref(&arg.pat));
    }
    cx.visit_expr(&body.value);
}

struct UsedMutFinder<'a, 'tcx: 'a> {
    bccx: &'a BorrowckCtxt<'a, 'tcx>,
    set: &'a mut FxHashSet<HirId>,
}

struct UnusedMutCx<'a, 'tcx: 'a> {
    bccx: &'a BorrowckCtxt<'a, 'tcx>,
    used_mut: FxHashSet<HirId>,
}

impl<'a, 'tcx> UnusedMutCx<'a, 'tcx> {
    fn check_unused_mut_pat(&self, pats: &[P<hir::Pat>]) {
        let tcx = self.bccx.tcx;
        let mut mutables = FxHashMap();
        for p in pats {
            p.each_binding(|_, hir_id, span, path1| {
                let name = path1.node;

                // Skip anything that looks like `_foo`
                if name.as_str().starts_with("_") {
                    return;
                }

                // Skip anything that looks like `&foo` or `&mut foo`, only look
                // for by-value bindings
                let bm = match self.bccx.tables.pat_binding_modes().get(hir_id) {
                    Some(&bm) => bm,
                    None => span_bug!(span, "missing binding mode"),
                };
                match bm {
                    ty::BindByValue(hir::MutMutable) => {}
                    _ => return,
                }

                mutables.entry(name).or_insert(Vec::new()).push((hir_id, span));
            });
        }

        for (_name, ids) in mutables {
            // If any id for this name was used mutably then consider them all
            // ok, so move on to the next
            if ids.iter().any(|&(ref hir_id, _)| self.used_mut.contains(hir_id)) {
                continue;
            }

            let (hir_id, span) = ids[0];
            let mut_span = tcx.sess.codemap().span_until_non_whitespace(span);

            // Ok, every name wasn't used mutably, so issue a warning that this
            // didn't need to be mutable.
            tcx.struct_span_lint_hir(UNUSED_MUT,
                                     hir_id,
                                     span,
                                     "variable does not need to be mutable")
                .span_suggestion_short(mut_span, "remove this `mut`", "".to_owned())
                .emit();
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for UnusedMutCx<'a, 'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.bccx.tcx.hir)
    }

    fn visit_arm(&mut self, arm: &hir::Arm) {
        self.check_unused_mut_pat(&arm.pats)
    }

    fn visit_local(&mut self, local: &hir::Local) {
        self.check_unused_mut_pat(slice::from_ref(&local.pat));
    }
}

impl<'a, 'tcx> Visitor<'tcx> for UsedMutFinder<'a, 'tcx> {
    fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
        NestedVisitorMap::OnlyBodies(&self.bccx.tcx.hir)
    }

    fn visit_nested_body(&mut self, id: hir::BodyId) {
        let def_id = self.bccx.tcx.hir.body_owner_def_id(id);
        self.set.extend(self.bccx.tcx.borrowck(def_id).used_mut_nodes.iter().cloned());
        self.visit_body(self.bccx.tcx.hir.body(id));
    }
}