Skip to content

Commit df995ed

Browse files
authored
Fix explicit_counter_loop FN when loop counter starts at non-zero (rust-lang#16620)
Closes rust-lang#16612 Previous implementation hardcoded `0` as loop counter start, this PR lifts that restriction. changelog: [`explicit_counter_loop`] fix FN when loop counter starts at non-zero
2 parents b2a5ee9 + c234b12 commit df995ed

File tree

3 files changed

+86
-39
lines changed

3 files changed

+86
-39
lines changed

clippy_lints/src/loops/explicit_counter_loop.rs

Lines changed: 48 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1+
use std::borrow::Cow;
2+
13
use super::{EXPLICIT_COUNTER_LOOP, IncrementVisitor, InitializeVisitor, make_iterator_snippet};
2-
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
4+
use clippy_utils::diagnostics::span_lint_and_then;
35
use clippy_utils::source::snippet_with_applicability;
4-
use clippy_utils::{get_enclosing_block, is_integer_const};
5-
use rustc_ast::Label;
6+
use clippy_utils::sugg::{EMPTY, Sugg};
7+
use clippy_utils::{get_enclosing_block, is_integer_const, is_integer_literal_untyped};
8+
use rustc_ast::{Label, RangeLimits};
69
use rustc_errors::Applicability;
710
use rustc_hir::intravisit::{walk_block, walk_expr};
811
use rustc_hir::{Expr, Pat};
912
use rustc_lint::LateContext;
1013
use rustc_middle::ty::{self, Ty, UintTy};
1114

12-
// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
13-
// incremented exactly once in the loop body, and initialized to zero
14-
// at the start of the loop.
15+
// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be incremented exactly once in the
16+
// loop body.
1517
pub(super) fn check<'tcx>(
1618
cx: &LateContext<'tcx>,
1719
pat: &'tcx Pat<'_>,
@@ -31,55 +33,64 @@ pub(super) fn check<'tcx>(
3133
let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
3234
walk_block(&mut initialize_visitor, block);
3335

34-
if let Some((name, ty, initializer)) = initialize_visitor.get_result()
35-
&& is_integer_const(cx, initializer, 0)
36-
{
36+
if let Some((name, ty, initializer)) = initialize_visitor.get_result() {
37+
let is_zero = is_integer_const(cx, initializer, 0);
3738
let mut applicability = Applicability::MaybeIncorrect;
3839
let span = expr.span.with_hi(arg.span.hi());
3940
let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name));
40-
let int_name = match ty.map(Ty::kind) {
41-
// usize or inferred
42-
Some(ty::Uint(UintTy::Usize)) | None => {
43-
span_lint_and_sugg(
44-
cx,
45-
EXPLICIT_COUNTER_LOOP,
46-
span,
47-
format!("the variable `{name}` is used as a loop counter"),
48-
"consider using",
49-
format!(
50-
"{loop_label}for ({name}, {}) in {}.enumerate()",
51-
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
52-
make_iterator_snippet(cx, arg, &mut applicability),
53-
),
54-
applicability,
55-
);
56-
return;
57-
},
58-
Some(ty::Int(int_ty)) => int_ty.name_str(),
59-
Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
60-
_ => return,
61-
};
6241

6342
span_lint_and_then(
6443
cx,
6544
EXPLICIT_COUNTER_LOOP,
6645
span,
6746
format!("the variable `{name}` is used as a loop counter"),
6847
|diag| {
48+
let pat_snippet = snippet_with_applicability(cx, pat.span, "item", &mut applicability);
49+
let iter_snippet = make_iterator_snippet(cx, arg, &mut applicability);
50+
let int_name = match ty.map(Ty::kind) {
51+
Some(ty::Uint(UintTy::Usize)) | None => {
52+
if is_zero {
53+
diag.span_suggestion(
54+
span,
55+
"consider using",
56+
format!(
57+
"{loop_label}for ({name}, {pat_snippet}) in {iter_snippet}.enumerate()",
58+
),
59+
applicability,
60+
);
61+
return;
62+
}
63+
None
64+
},
65+
Some(ty::Int(int_ty)) => Some(int_ty.name_str()),
66+
Some(ty::Uint(uint_ty)) => Some(uint_ty.name_str()),
67+
_ => None,
68+
}
69+
.filter(|_| is_integer_literal_untyped(initializer));
70+
71+
let initializer = Sugg::hir_from_snippet(cx, initializer, |span| {
72+
let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
73+
if let Some(int_name) = int_name {
74+
return Cow::Owned(format!("{snippet}_{int_name}"));
75+
}
76+
snippet
77+
});
78+
6979
diag.span_suggestion(
7080
span,
7181
"consider using",
7282
format!(
73-
"{loop_label}for ({name}, {}) in (0_{int_name}..).zip({})",
74-
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
75-
make_iterator_snippet(cx, arg, &mut applicability),
83+
"{loop_label}for ({name}, {pat_snippet}) in ({}).zip({iter_snippet})",
84+
initializer.range(&EMPTY, RangeLimits::HalfOpen)
7685
),
7786
applicability,
7887
);
7988

80-
diag.note(format!(
81-
"`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
82-
));
89+
if is_zero && let Some(int_name) = int_name {
90+
diag.note(format!(
91+
"`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
92+
));
93+
}
8394
},
8495
);
8596
}

tests/ui/explicit_counter_loop.rs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ fn main() {
3333
}
3434

3535
let vec = [1, 2, 3, 4];
36-
// Potential false positives
3736
let mut _index = 0;
3837
_index = 1;
3938
for _v in &vec {
39+
//~^ explicit_counter_loop
4040
_index += 1
4141
}
4242

@@ -299,3 +299,21 @@ mod issue_13123 {
299299
}
300300
}
301301
}
302+
303+
fn issue16612(v: Vec<u8>, s: i64) {
304+
use std::hint::black_box;
305+
306+
let mut i = 1;
307+
for item in &v {
308+
//~^ explicit_counter_loop
309+
black_box((i, *item));
310+
i += 1;
311+
}
312+
313+
let mut j = s + 1;
314+
for item in &v {
315+
//~^ explicit_counter_loop
316+
black_box((j, *item));
317+
j += 1;
318+
}
319+
}

tests/ui/explicit_counter_loop.stderr

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ error: the variable `_index` is used as a loop counter
2525
LL | for _v in vec {
2626
| ^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in vec.into_iter().enumerate()`
2727

28+
error: the variable `_index` is used as a loop counter
29+
--> tests/ui/explicit_counter_loop.rs:38:5
30+
|
31+
LL | for _v in &vec {
32+
| ^^^^^^^^^^^^^^ help: consider using: `for (_index, _v) in (1..).zip(vec.iter())`
33+
2834
error: the variable `count` is used as a loop counter
2935
--> tests/ui/explicit_counter_loop.rs:118:9
3036
|
@@ -63,5 +69,17 @@ error: the variable `_index` is used as a loop counter
6369
LL | 'label: for v in vec {
6470
| ^^^^^^^^^^^^^^^^^^^^ help: consider using: `'label: for (_index, v) in vec.into_iter().enumerate()`
6571

66-
error: aborting due to 10 previous errors
72+
error: the variable `i` is used as a loop counter
73+
--> tests/ui/explicit_counter_loop.rs:307:5
74+
|
75+
LL | for item in &v {
76+
| ^^^^^^^^^^^^^^ help: consider using: `for (i, item) in (1..).zip(v.iter())`
77+
78+
error: the variable `j` is used as a loop counter
79+
--> tests/ui/explicit_counter_loop.rs:314:5
80+
|
81+
LL | for item in &v {
82+
| ^^^^^^^^^^^^^^ help: consider using: `for (j, item) in (s + 1..).zip(v.iter())`
83+
84+
error: aborting due to 13 previous errors
6785

0 commit comments

Comments
 (0)