]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/loops/explicit_counter_loop.rs
Apply uninlined_format-args to clippy_lints
[rust.git] / clippy_lints / src / loops / explicit_counter_loop.rs
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};
11
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>,
17     pat: &'tcx Pat<'_>,
18     arg: &'tcx Expr<'_>,
19     body: &'tcx Expr<'_>,
20     expr: &'tcx Expr<'_>,
21 ) {
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);
25
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);
32
33             if_chain! {
34                 if let Some((name, ty, initializer)) = initialize_visitor.get_result();
35                 if is_integer_const(cx, initializer, 0);
36                 then {
37                     let mut applicability = Applicability::MaybeIncorrect;
38                     let span = expr.span.with_hi(arg.span.hi());
39
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                                     "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                     };
62
63                     span_lint_and_then(
64                         cx,
65                         EXPLICIT_COUNTER_LOOP,
66                         span,
67                         &format!("the variable `{name}` is used as a loop counter"),
68                         |diag| {
69                             diag.span_suggestion(
70                                 span,
71                                 "consider using",
72                                 format!(
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),
76                                 ),
77                                 applicability,
78                             );
79
80                             diag.note(&format!(
81                                 "`{name}` is of type `{int_name}`, making it ineligible for `Iterator::enumerate`"
82                             ));
83                         },
84                     );
85                 }
86             }
87         }
88     }
89 }