Skip to content

Commit b464432

Browse files
committed
Merge branch 'master' of github.com:Manishearth/rust-clippy into rust-test
2 parents 127c41f + 09d9885 commit b464432

File tree

3 files changed

+151
-20
lines changed

3 files changed

+151
-20
lines changed

clippy_lints/src/loops.rs

+96-20
Original file line numberDiff line numberDiff line change
@@ -952,16 +952,17 @@ fn check_for_loop_range<'a, 'tcx>(
952952
let mut visitor = VarVisitor {
953953
cx: cx,
954954
var: canonical_id,
955-
indexed: HashMap::new(),
955+
indexed_mut: HashSet::new(),
956+
indexed_indirectly: HashMap::new(),
956957
indexed_directly: HashMap::new(),
957958
referenced: HashSet::new(),
958959
nonindex: false,
960+
prefer_mutable: false,
959961
};
960962
walk_expr(&mut visitor, body);
961963

962964
// linting condition: we only indexed one variable, and indexed it directly
963-
// (`indexed_directly` is subset of `indexed`)
964-
if visitor.indexed.len() == 1 && visitor.indexed_directly.len() == 1 {
965+
if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
965966
let (indexed, indexed_extent) = visitor
966967
.indexed_directly
967968
.into_iter()
@@ -1009,6 +1010,12 @@ fn check_for_loop_range<'a, 'tcx>(
10091010
"".to_owned()
10101011
};
10111012

1013+
let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
1014+
("mut ", "iter_mut")
1015+
} else {
1016+
("", "iter")
1017+
};
1018+
10121019
if visitor.nonindex {
10131020
span_lint_and_then(
10141021
cx,
@@ -1021,16 +1028,16 @@ fn check_for_loop_range<'a, 'tcx>(
10211028
"consider using an iterator".to_string(),
10221029
vec![
10231030
(pat.span, format!("({}, <item>)", ident.node)),
1024-
(arg.span, format!("{}.iter().enumerate(){}{}", indexed, take, skip)),
1031+
(arg.span, format!("{}.{}().enumerate(){}{}", indexed, method, take, skip)),
10251032
],
10261033
);
10271034
},
10281035
);
10291036
} else {
10301037
let repl = if starts_at_zero && take.is_empty() {
1031-
format!("&{}", indexed)
1038+
format!("&{}{}", ref_mut, indexed)
10321039
} else {
1033-
format!("{}.iter(){}{}", indexed, take, skip)
1040+
format!("{}.{}(){}{}", indexed, method, take, skip)
10341041
};
10351042

10361043
span_lint_and_then(
@@ -1537,8 +1544,10 @@ struct VarVisitor<'a, 'tcx: 'a> {
15371544
cx: &'a LateContext<'a, 'tcx>,
15381545
/// var name to look for as index
15391546
var: ast::NodeId,
1540-
/// indexed variables, the extend is `None` for global
1541-
indexed: HashMap<Name, Option<region::Scope>>,
1547+
/// indexed variables that are used mutably
1548+
indexed_mut: HashSet<Name>,
1549+
/// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
1550+
indexed_indirectly: HashMap<Name, Option<region::Scope>>,
15421551
/// subset of `indexed` of vars that are indexed directly: `v[i]`
15431552
/// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
15441553
indexed_directly: HashMap<Name, Option<region::Scope>>,
@@ -1548,20 +1557,21 @@ struct VarVisitor<'a, 'tcx: 'a> {
15481557
/// has the loop variable been used in expressions other than the index of
15491558
/// an index op?
15501559
nonindex: bool,
1560+
/// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
1561+
/// takes `&mut self`
1562+
prefer_mutable: bool,
15511563
}
15521564

1553-
impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
1554-
fn visit_expr(&mut self, expr: &'tcx Expr) {
1565+
impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
1566+
fn check(&mut self, idx: &'tcx Expr, seqexpr: &'tcx Expr, expr: &'tcx Expr) -> bool {
15551567
if_chain! {
1556-
// an index op
1557-
if let ExprIndex(ref seqexpr, ref idx) = expr.node;
15581568
// the indexed container is referenced by a name
15591569
if let ExprPath(ref seqpath) = seqexpr.node;
15601570
if let QPath::Resolved(None, ref seqvar) = *seqpath;
15611571
if seqvar.segments.len() == 1;
15621572
then {
15631573
let index_used_directly = same_var(self.cx, idx, self.var);
1564-
let index_used = index_used_directly || {
1574+
let indexed_indirectly = {
15651575
let mut used_visitor = LocalUsedVisitor {
15661576
cx: self.cx,
15671577
local: self.var,
@@ -1571,7 +1581,10 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
15711581
used_visitor.used
15721582
};
15731583

1574-
if index_used {
1584+
if indexed_indirectly || index_used_directly {
1585+
if self.prefer_mutable {
1586+
self.indexed_mut.insert(seqvar.segments[0].name);
1587+
}
15751588
let def = self.cx.tables.qpath_def(seqpath, seqexpr.hir_id);
15761589
match def {
15771590
Def::Local(node_id) | Def::Upvar(node_id, ..) => {
@@ -1580,24 +1593,48 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
15801593
let parent_id = self.cx.tcx.hir.get_parent(expr.id);
15811594
let parent_def_id = self.cx.tcx.hir.local_def_id(parent_id);
15821595
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
1583-
self.indexed.insert(seqvar.segments[0].name, Some(extent));
1596+
if indexed_indirectly {
1597+
self.indexed_indirectly.insert(seqvar.segments[0].name, Some(extent));
1598+
}
15841599
if index_used_directly {
15851600
self.indexed_directly.insert(seqvar.segments[0].name, Some(extent));
15861601
}
1587-
return; // no need to walk further *on the variable*
1602+
return false; // no need to walk further *on the variable*
15881603
}
15891604
Def::Static(..) | Def::Const(..) => {
1590-
self.indexed.insert(seqvar.segments[0].name, None);
1605+
if indexed_indirectly {
1606+
self.indexed_indirectly.insert(seqvar.segments[0].name, None);
1607+
}
15911608
if index_used_directly {
15921609
self.indexed_directly.insert(seqvar.segments[0].name, None);
15931610
}
1594-
return; // no need to walk further *on the variable*
1611+
return false; // no need to walk further *on the variable*
15951612
}
15961613
_ => (),
15971614
}
15981615
}
15991616
}
16001617
}
1618+
true
1619+
}
1620+
}
1621+
1622+
impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
1623+
fn visit_expr(&mut self, expr: &'tcx Expr) {
1624+
if_chain! {
1625+
// a range index op
1626+
if let ExprMethodCall(ref meth, _, ref args) = expr.node;
1627+
if meth.name == "index" || meth.name == "index_mut";
1628+
if !self.check(&args[1], &args[0], expr);
1629+
then { return }
1630+
}
1631+
1632+
if_chain! {
1633+
// an index op
1634+
if let ExprIndex(ref seqexpr, ref idx) = expr.node;
1635+
if !self.check(idx, seqexpr, expr);
1636+
then { return }
1637+
}
16011638

16021639
if_chain! {
16031640
// directly using a variable
@@ -1615,8 +1652,47 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
16151652
}
16161653
}
16171654
}
1618-
1619-
walk_expr(self, expr);
1655+
let old = self.prefer_mutable;
1656+
match expr.node {
1657+
ExprAssignOp(_, ref lhs, ref rhs) |
1658+
ExprAssign(ref lhs, ref rhs) => {
1659+
self.prefer_mutable = true;
1660+
self.visit_expr(lhs);
1661+
self.prefer_mutable = false;
1662+
self.visit_expr(rhs);
1663+
},
1664+
ExprAddrOf(mutbl, ref expr) => {
1665+
if mutbl == MutMutable {
1666+
self.prefer_mutable = true;
1667+
}
1668+
self.visit_expr(expr);
1669+
},
1670+
ExprCall(ref f, ref args) => {
1671+
for (ty, expr) in self.cx.tables.expr_ty(f).fn_sig(self.cx.tcx).inputs().skip_binder().iter().zip(args) {
1672+
self.prefer_mutable = false;
1673+
if let ty::TyRef(_, mutbl) = ty.sty {
1674+
if mutbl.mutbl == MutMutable {
1675+
self.prefer_mutable = true;
1676+
}
1677+
}
1678+
self.visit_expr(expr);
1679+
}
1680+
},
1681+
ExprMethodCall(_, _, ref args) => {
1682+
let def_id = self.cx.tables.type_dependent_defs()[expr.hir_id].def_id();
1683+
for (ty, expr) in self.cx.tcx.fn_sig(def_id).inputs().skip_binder().iter().zip(args) {
1684+
self.prefer_mutable = false;
1685+
if let ty::TyRef(_, mutbl) = ty.sty {
1686+
if mutbl.mutbl == MutMutable {
1687+
self.prefer_mutable = true;
1688+
}
1689+
}
1690+
self.visit_expr(expr);
1691+
}
1692+
},
1693+
_ => walk_expr(self, expr),
1694+
}
1695+
self.prefer_mutable = old;
16201696
}
16211697
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
16221698
NestedVisitorMap::None

tests/ui/needless_range_loop.rs

+28
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,32 @@ fn main() {
2424
for i in 3..10 {
2525
println!("{}", ns[calc_idx(i) % 4]);
2626
}
27+
28+
let mut ms = vec![1, 2, 3, 4, 5, 6];
29+
for i in 0..ms.len() {
30+
ms[i] *= 2;
31+
}
32+
assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);
33+
34+
let mut ms = vec![1, 2, 3, 4, 5, 6];
35+
for i in 0..ms.len() {
36+
let x = &mut ms[i];
37+
*x *= 2;
38+
}
39+
assert_eq!(ms, vec![2, 4, 6, 8, 10, 12]);
40+
41+
let g = vec![1, 2, 3, 4, 5, 6];
42+
let glen = g.len();
43+
for i in 0..glen {
44+
let x: u32 = g[i+1..].iter().sum();
45+
println!("{}", g[i] + x);
46+
}
47+
assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);
48+
49+
let mut g = vec![1, 2, 3, 4, 5, 6];
50+
let glen = g.len();
51+
for i in 0..glen {
52+
g[i] = g[i+1..].iter().sum();
53+
}
54+
assert_eq!(g, vec![20, 18, 15, 11, 6, 0]);
2755
}

tests/ui/needless_range_loop.stderr

+27
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,30 @@ help: consider using an iterator
1212
8 | for <item> in ns.iter().take(10).skip(3) {
1313
|
1414

15+
error: the loop variable `i` is only used to index `ms`.
16+
--> $DIR/needless_range_loop.rs:29:5
17+
|
18+
29 | / for i in 0..ms.len() {
19+
30 | | ms[i] *= 2;
20+
31 | | }
21+
| |_____^
22+
|
23+
help: consider using an iterator
24+
|
25+
29 | for <item> in &mut ms {
26+
|
27+
28+
error: the loop variable `i` is only used to index `ms`.
29+
--> $DIR/needless_range_loop.rs:35:5
30+
|
31+
35 | / for i in 0..ms.len() {
32+
36 | | let x = &mut ms[i];
33+
37 | | *x *= 2;
34+
38 | | }
35+
| |_____^
36+
|
37+
help: consider using an iterator
38+
|
39+
35 | for <item> in &mut ms {
40+
|
41+

0 commit comments

Comments
 (0)