]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
Rollup merge of #87528 - :stack_overflow_obsd, r=joshtriplett
[rust.git] / src / tools / clippy / clippy_lints / src / loops / explicit_counter_loop.rs
1 use super::{
2     get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP,
3 };
4 use clippy_utils::diagnostics::span_lint_and_sugg;
5 use clippy_utils::source::snippet_with_applicability;
6 use clippy_utils::{get_enclosing_block, is_integer_const};
7 use if_chain::if_chain;
8 use rustc_errors::Applicability;
9 use rustc_hir::intravisit::{walk_block, walk_expr};
10 use rustc_hir::{Expr, Pat};
11 use rustc_lint::LateContext;
12
13 // To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
14 // incremented exactly once in the loop body, and initialized to zero
15 // at the start of the loop.
16 pub(super) fn check<'tcx>(
17     cx: &LateContext<'tcx>,
18     pat: &'tcx Pat<'_>,
19     arg: &'tcx Expr<'_>,
20     body: &'tcx Expr<'_>,
21     expr: &'tcx Expr<'_>,
22 ) {
23     // Look for variables that are incremented once per loop iteration.
24     let mut increment_visitor = IncrementVisitor::new(cx);
25     walk_expr(&mut increment_visitor, body);
26
27     // For each candidate, check the parent block to see if
28     // it's initialized to zero at the start of the loop.
29     if let Some(block) = get_enclosing_block(cx, expr.hir_id) {
30         for id in increment_visitor.into_results() {
31             let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
32             walk_block(&mut initialize_visitor, block);
33
34             if_chain! {
35                 if let Some((name, initializer)) = initialize_visitor.get_result();
36                 if is_integer_const(cx, initializer, 0);
37                 then {
38                     let mut applicability = Applicability::MachineApplicable;
39
40                     let for_span = get_span_of_entire_for_loop(expr);
41
42                     span_lint_and_sugg(
43                         cx,
44                         EXPLICIT_COUNTER_LOOP,
45                         for_span.with_hi(arg.span.hi()),
46                         &format!("the variable `{}` is used as a loop counter", name),
47                         "consider using",
48                         format!(
49                             "for ({}, {}) in {}.enumerate()",
50                             name,
51                             snippet_with_applicability(cx, pat.span, "item", &mut applicability),
52                             make_iterator_snippet(cx, arg, &mut applicability),
53                         ),
54                         applicability,
55                     );
56                 }
57             }
58         }
59     }
60 }