Skip to content

Commit 28c401f

Browse files
authored
Merge pull request #2118 from chyvonomys/relax-needless-loop
relax `needless_range_loop` so that it reports only direct indexing
2 parents 73a1dd8 + 1dc0b5c commit 28c401f

File tree

3 files changed

+57
-4
lines changed

3 files changed

+57
-4
lines changed

clippy_lints/src/loops.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -925,14 +925,16 @@ fn check_for_loop_range<'a, 'tcx>(
925925
cx: cx,
926926
var: canonical_id,
927927
indexed: HashMap::new(),
928+
indexed_directly: HashMap::new(),
928929
referenced: HashSet::new(),
929930
nonindex: false,
930931
};
931932
walk_expr(&mut visitor, body);
932933

933-
// linting condition: we only indexed one variable
934-
if visitor.indexed.len() == 1 {
935-
let (indexed, indexed_extent) = visitor.indexed.into_iter().next().expect(
934+
// linting condition: we only indexed one variable, and indexed it directly
935+
// (`indexed_directly` is subset of `indexed`)
936+
if visitor.indexed.len() == 1 && visitor.indexed_directly.len() == 1 {
937+
let (indexed, indexed_extent) = visitor.indexed_directly.into_iter().next().expect(
936938
"already checked that we have exactly 1 element",
937939
);
938940

@@ -1481,6 +1483,9 @@ struct VarVisitor<'a, 'tcx: 'a> {
14811483
var: ast::NodeId,
14821484
/// indexed variables, the extend is `None` for global
14831485
indexed: HashMap<Name, Option<region::Scope>>,
1486+
/// subset of `indexed` of vars that are indexed directly: `v[i]`
1487+
/// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
1488+
indexed_directly: HashMap<Name, Option<region::Scope>>,
14841489
/// Any names that are used outside an index operation.
14851490
/// Used to detect things like `&mut vec` used together with `vec[i]`
14861491
referenced: HashSet<Name>,
@@ -1499,7 +1504,8 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
14991504
let QPath::Resolved(None, ref seqvar) = *seqpath,
15001505
seqvar.segments.len() == 1,
15011506
], {
1502-
let index_used = same_var(self.cx, idx, self.var) || {
1507+
let index_used_directly = same_var(self.cx, idx, self.var);
1508+
let index_used = index_used_directly || {
15031509
let mut used_visitor = LocalUsedVisitor {
15041510
cx: self.cx,
15051511
local: self.var,
@@ -1519,10 +1525,16 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
15191525
let parent_def_id = self.cx.tcx.hir.local_def_id(parent_id);
15201526
let extent = self.cx.tcx.region_scope_tree(parent_def_id).var_scope(hir_id.local_id);
15211527
self.indexed.insert(seqvar.segments[0].name, Some(extent));
1528+
if index_used_directly {
1529+
self.indexed_directly.insert(seqvar.segments[0].name, Some(extent));
1530+
}
15221531
return; // no need to walk further *on the variable*
15231532
}
15241533
Def::Static(..) | Def::Const(..) => {
15251534
self.indexed.insert(seqvar.segments[0].name, None);
1535+
if index_used_directly {
1536+
self.indexed_directly.insert(seqvar.segments[0].name, None);
1537+
}
15261538
return; // no need to walk further *on the variable*
15271539
}
15281540
_ => (),

tests/ui/needless_range_loop.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
fn calc_idx(i: usize) -> usize {
2+
(i + i + 20) % 4
3+
}
4+
5+
fn main() {
6+
let ns = [2, 3, 5, 7];
7+
8+
for i in 3..10 {
9+
println!("{}", ns[i]);
10+
}
11+
12+
for i in 3..10 {
13+
println!("{}", ns[i % 4]);
14+
}
15+
16+
for i in 3..10 {
17+
println!("{}", ns[i % ns.len()]);
18+
}
19+
20+
for i in 3..10 {
21+
println!("{}", ns[calc_idx(i)]);
22+
}
23+
24+
for i in 3..10 {
25+
println!("{}", ns[calc_idx(i) % 4]);
26+
}
27+
}

tests/ui/needless_range_loop.stderr

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: the loop variable `i` is only used to index `ns`.
2+
--> $DIR/needless_range_loop.rs:8:5
3+
|
4+
8 | / for i in 3..10 {
5+
9 | | println!("{}", ns[i]);
6+
10 | | }
7+
| |_____^
8+
|
9+
= note: `-D needless-range-loop` implied by `-D warnings`
10+
help: consider using an iterator
11+
|
12+
8 | for <item> in ns.iter().take(10).skip(3) {
13+
| ^^^^^^
14+

0 commit comments

Comments
 (0)