]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/loops/explicit_counter_loop.rs
Auto merge of #8464 - Jarcho:ptr_arg_8463, r=camsteffen
[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::MachineApplicable;
38
39                     let int_name = match ty.map(Ty::kind) {
40                         // usize or inferred
41                         Some(ty::Uint(UintTy::Usize)) | None => {
42                             span_lint_and_sugg(
43                                 cx,
44                                 EXPLICIT_COUNTER_LOOP,
45                                 expr.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                             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                         expr.span.with_hi(arg.span.hi()),
67                         &format!("the variable `{}` is used as a loop counter", name),
68                         |diag| {
69                             diag.span_suggestion(
70                                 expr.span.with_hi(arg.span.hi()),
71                                 "consider using",
72                                 format!(
73                                     "for ({}, {}) in (0_{}..).zip({})",
74                                     name,
75                                     snippet_with_applicability(cx, pat.span, "item", &mut applicability),
76                                     int_name,
77                                     make_iterator_snippet(cx, arg, &mut applicability),
78                                 ),
79                                 applicability,
80                             );
81
82                             diag.note(&format!(
83                                 "`{}` is of type `{}`, making it ineligible for `Iterator::enumerate`",
84                                 name, int_name
85                             ));
86                         },
87                     );
88                 }
89             }
90         }
91     }
92 }