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
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;
if name.as_str().starts_with("_") {
return;
}
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 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);
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));
}
}