1 use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP};
2 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
3 use clippy_utils::source::snippet_with_applicability;
4 use clippy_utils::{get_enclosing_block, is_integer_const};
5 use if_chain::if_chain;
6 use rustc_errors::Applicability;
7 use rustc_hir::intravisit::{walk_block, walk_expr};
8 use rustc_hir::{Expr, Pat};
9 use rustc_lint::LateContext;
10 use rustc_middle::ty::{self, Ty, UintTy};
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 pub(super) fn check<'tcx>(
16 cx: &LateContext<'tcx>,
22 // Look for variables that are incremented once per loop iteration.
23 let mut increment_visitor = IncrementVisitor::new(cx);
24 walk_expr(&mut increment_visitor, body);
26 // For each candidate, check the parent block to see if
27 // it's initialized to zero at the start of the loop.
28 if let Some(block) = get_enclosing_block(cx, expr.hir_id) {
29 for id in increment_visitor.into_results() {
30 let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
31 walk_block(&mut initialize_visitor, block);
34 if let Some((name, ty, initializer)) = initialize_visitor.get_result();
35 if is_integer_const(cx, initializer, 0);
37 let mut applicability = Applicability::MaybeIncorrect;
38 let span = expr.span.with_hi(arg.span.hi());
40 let int_name = match ty.map(Ty::kind) {
42 Some(ty::Uint(UintTy::Usize)) | None => {
45 EXPLICIT_COUNTER_LOOP,
47 &format!("the variable `{name}` is used as a loop counter"),
50 "for ({name}, {}) in {}.enumerate()",
51 snippet_with_applicability(cx, pat.span, "item", &mut applicability),
52 make_iterator_snippet(cx, arg, &mut applicability),
58 Some(ty::Int(int_ty)) => int_ty.name_str(),
59 Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
65 EXPLICIT_COUNTER_LOOP,
67 &format!("the variable `{name}` is used as a loop counter"),
73 "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),
81 "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"