Skip to content

Commit 122da1d

Browse files
bors[bot]HMPerson1
andcommitted
Merge #3339
3339: Check for known array length in `needless_range_loop` r=phansch a=HMPerson1 In `VarVisitor`, we now keep track of the type of the thing that was directly indexed and, if it's an array, check if the range's end is (or is past) the array's length. Fixes #3033 Co-authored-by: HMPerson1 <hmperson1@gmail.com>
2 parents 3ff2c1f + 553d01d commit 122da1d

File tree

4 files changed

+83
-11
lines changed

4 files changed

+83
-11
lines changed

clippy_lints/src/loops.rs

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1068,7 +1068,7 @@ fn check_for_loop_range<'a, 'tcx>(
10681068

10691069
// linting condition: we only indexed one variable, and indexed it directly
10701070
if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
1071-
let (indexed, indexed_extent) = visitor
1071+
let (indexed, (indexed_extent, indexed_ty)) = visitor
10721072
.indexed_directly
10731073
.into_iter()
10741074
.next()
@@ -1119,7 +1119,7 @@ fn check_for_loop_range<'a, 'tcx>(
11191119
}
11201120
}
11211121

1122-
if is_len_call(end, indexed) {
1122+
if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
11231123
String::new()
11241124
} else {
11251125
match limits {
@@ -1207,6 +1207,28 @@ fn is_len_call(expr: &Expr, var: Name) -> bool {
12071207
false
12081208
}
12091209

1210+
fn is_end_eq_array_len(
1211+
cx: &LateContext<'_, '_>,
1212+
end: &Expr,
1213+
limits: ast::RangeLimits,
1214+
indexed_ty: Ty<'_>,
1215+
) -> bool {
1216+
if_chain! {
1217+
if let ExprKind::Lit(ref lit) = end.node;
1218+
if let ast::LitKind::Int(end_int, _) = lit.node;
1219+
if let ty::TyKind::Array(_, arr_len_const) = indexed_ty.sty;
1220+
if let Some(arr_len) = arr_len_const.assert_usize(cx.tcx);
1221+
then {
1222+
return match limits {
1223+
ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
1224+
ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
1225+
};
1226+
}
1227+
}
1228+
1229+
false
1230+
}
1231+
12101232
fn check_for_loop_reverse_range<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, arg: &'tcx Expr, expr: &'tcx Expr) {
12111233
// if this for loop is iterating over a two-sided range...
12121234
if let Some(higher::Range {
@@ -1678,7 +1700,7 @@ struct VarVisitor<'a, 'tcx: 'a> {
16781700
indexed_indirectly: FxHashMap<Name, Option<region::Scope>>,
16791701
/// subset of `indexed` of vars that are indexed directly: `v[i]`
16801702
/// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
1681-
indexed_directly: FxHashMap<Name, Option<region::Scope>>,
1703+
indexed_directly: FxHashMap<Name, (Option<region::Scope>, Ty<'tcx>)>,
16821704
/// Any names that are used outside an index operation.
16831705
/// Used to detect things like `&mut vec` used together with `vec[i]`
16841706
referenced: FxHashSet<Name>,
@@ -1725,7 +1747,10 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
17251747
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
17261748
}
17271749
if index_used_directly {
1728-
self.indexed_directly.insert(seqvar.segments[0].ident.name, Some(extent));
1750+
self.indexed_directly.insert(
1751+
seqvar.segments[0].ident.name,
1752+
(Some(extent), self.cx.tables.node_id_to_type(seqexpr.hir_id)),
1753+
);
17291754
}
17301755
return false; // no need to walk further *on the variable*
17311756
}
@@ -1734,7 +1759,10 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
17341759
self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
17351760
}
17361761
if index_used_directly {
1737-
self.indexed_directly.insert(seqvar.segments[0].ident.name, None);
1762+
self.indexed_directly.insert(
1763+
seqvar.segments[0].ident.name,
1764+
(None, self.cx.tables.node_id_to_type(seqexpr.hir_id)),
1765+
);
17381766
}
17391767
return false; // no need to walk further *on the variable*
17401768
}

tests/ui/for_loop.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ error: the loop variable `j` is only used to index `STATIC`.
9797
| ^^^^
9898
help: consider using an iterator
9999
|
100-
110 | for <item> in STATIC.iter().take(4) {
101-
| ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^
100+
110 | for <item> in &STATIC {
101+
| ^^^^^^ ^^^^^^^
102102

103103
error: the loop variable `j` is only used to index `CONST`.
104104
--> $DIR/for_loop.rs:114:14
@@ -107,8 +107,8 @@ error: the loop variable `j` is only used to index `CONST`.
107107
| ^^^^
108108
help: consider using an iterator
109109
|
110-
114 | for <item> in CONST.iter().take(4) {
111-
| ^^^^^^ ^^^^^^^^^^^^^^^^^^^^
110+
114 | for <item> in &CONST {
111+
| ^^^^^^ ^^^^^^
112112

113113
error: the loop variable `i` is used to index `vec`
114114
--> $DIR/for_loop.rs:118:14

tests/ui/needless_range_loop.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn calc_idx(i: usize) -> usize {
1313
}
1414

1515
fn main() {
16-
let ns = [2, 3, 5, 7];
16+
let ns = vec![2, 3, 5, 7];
1717

1818
for i in 3..10 {
1919
println!("{}", ns[i]);
@@ -76,4 +76,18 @@ fn main() {
7676
for i in x..=x + 4 {
7777
vec[i] += 1;
7878
}
79+
80+
let arr = [1,2,3];
81+
82+
for i in 0..3 {
83+
println!("{}", arr[i]);
84+
}
85+
86+
for i in 0..2 {
87+
println!("{}", arr[i]);
88+
}
89+
90+
for i in 1..3 {
91+
println!("{}", arr[i]);
92+
}
7993
}

tests/ui/needless_range_loop.stderr

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,35 @@ help: consider using an iterator
5050
76 | for <item> in vec.iter_mut().skip(x).take(4 + 1) {
5151
| ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5252

53-
error: aborting due to 5 previous errors
53+
error: the loop variable `i` is only used to index `arr`.
54+
--> $DIR/needless_range_loop.rs:82:14
55+
|
56+
82 | for i in 0..3 {
57+
| ^^^^
58+
help: consider using an iterator
59+
|
60+
82 | for <item> in &arr {
61+
| ^^^^^^ ^^^^
62+
63+
error: the loop variable `i` is only used to index `arr`.
64+
--> $DIR/needless_range_loop.rs:86:14
65+
|
66+
86 | for i in 0..2 {
67+
| ^^^^
68+
help: consider using an iterator
69+
|
70+
86 | for <item> in arr.iter().take(2) {
71+
| ^^^^^^ ^^^^^^^^^^^^^^^^^^
72+
73+
error: the loop variable `i` is only used to index `arr`.
74+
--> $DIR/needless_range_loop.rs:90:14
75+
|
76+
90 | for i in 1..3 {
77+
| ^^^^
78+
help: consider using an iterator
79+
|
80+
90 | for <item> in arr.iter().skip(1) {
81+
| ^^^^^^ ^^^^^^^^^^^^^^^^^^
82+
83+
error: aborting due to 8 previous errors
5484

0 commit comments

Comments
 (0)