Skip to content

Commit 71026ca

Browse files
nahuakangY-Nak
authored andcommitted
Refactor check_for_loop_explicit_counter to its own module
1 parent 71c9fdf commit 71026ca

File tree

3 files changed

+71
-62
lines changed

3 files changed

+71
-62
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
use super::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor};
2+
use crate::utils::{get_enclosing_block, is_integer_const, snippet_with_applicability, span_lint_and_sugg};
3+
use if_chain::if_chain;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::intravisit::{walk_block, walk_expr};
6+
use rustc_hir::{Expr, Pat};
7+
use rustc_lint::LateContext;
8+
9+
// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
10+
// incremented exactly once in the loop body, and initialized to zero
11+
// at the start of the loop.
12+
pub(super) fn check_for_loop_explicit_counter<'tcx>(
13+
cx: &LateContext<'tcx>,
14+
pat: &'tcx Pat<'_>,
15+
arg: &'tcx Expr<'_>,
16+
body: &'tcx Expr<'_>,
17+
expr: &'tcx Expr<'_>,
18+
) {
19+
// Look for variables that are incremented once per loop iteration.
20+
let mut increment_visitor = IncrementVisitor::new(cx);
21+
walk_expr(&mut increment_visitor, body);
22+
23+
// For each candidate, check the parent block to see if
24+
// it's initialized to zero at the start of the loop.
25+
if let Some(block) = get_enclosing_block(&cx, expr.hir_id) {
26+
for id in increment_visitor.into_results() {
27+
let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
28+
walk_block(&mut initialize_visitor, block);
29+
30+
if_chain! {
31+
if let Some((name, initializer)) = initialize_visitor.get_result();
32+
if is_integer_const(cx, initializer, 0);
33+
then {
34+
let mut applicability = Applicability::MachineApplicable;
35+
36+
let for_span = get_span_of_entire_for_loop(expr);
37+
38+
span_lint_and_sugg(
39+
cx,
40+
super::EXPLICIT_COUNTER_LOOP,
41+
for_span.with_hi(arg.span.hi()),
42+
&format!("the variable `{}` is used as a loop counter", name),
43+
"consider using",
44+
format!(
45+
"for ({}, {}) in {}.enumerate()",
46+
name,
47+
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
48+
make_iterator_snippet(cx, arg, &mut applicability),
49+
),
50+
applicability,
51+
);
52+
}
53+
}
54+
}
55+
}
56+
}

clippy_lints/src/loops/mod.rs

Lines changed: 3 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
mod for_loop_arg;
2+
mod for_loop_explicit_counter;
23
mod for_loop_over_map_kv;
34
mod for_loop_range;
45
mod for_mut_range_bound;
@@ -32,7 +33,7 @@ use rustc_session::{declare_lint_pass, declare_tool_lint};
3233
use rustc_span::source_map::Span;
3334
use rustc_span::symbol::{sym, Ident, Symbol};
3435
use std::iter::{once, Iterator};
35-
use utils::make_iterator_snippet;
36+
use utils::{get_span_of_entire_for_loop, make_iterator_snippet};
3637

3738
declare_clippy_lint! {
3839
/// **What it does:** Checks for for-loops that manually copy items between
@@ -857,7 +858,7 @@ fn check_for_loop<'tcx>(
857858
let is_manual_memcpy_triggered = detect_manual_memcpy(cx, pat, arg, body, expr);
858859
if !is_manual_memcpy_triggered {
859860
for_loop_range::check_for_loop_range(cx, pat, arg, body, expr);
860-
check_for_loop_explicit_counter(cx, pat, arg, body, expr);
861+
for_loop_explicit_counter::check_for_loop_explicit_counter(cx, pat, arg, body, expr);
861862
}
862863
for_loop_arg::check_for_loop_arg(cx, pat, arg, expr);
863864
for_loop_over_map_kv::check_for_loop_over_map_kv(cx, pat, arg, body, expr);
@@ -867,17 +868,6 @@ fn check_for_loop<'tcx>(
867868
manual_flatten::check_manual_flatten(cx, pat, arg, body, span);
868869
}
869870

870-
// this function assumes the given expression is a `for` loop.
871-
fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span {
872-
// for some reason this is the only way to get the `Span`
873-
// of the entire `for` loop
874-
if let ExprKind::Match(_, arms, _) = &expr.kind {
875-
arms[0].body.span
876-
} else {
877-
unreachable!()
878-
}
879-
}
880-
881871
/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`;
882872
/// and also, it avoids subtracting a variable from the same one by replacing it with `0`.
883873
/// it exists for the convenience of the overloaded operators while normal functions can do the
@@ -1474,55 +1464,6 @@ fn detect_same_item_push<'tcx>(
14741464
}
14751465
}
14761466

1477-
// To trigger the EXPLICIT_COUNTER_LOOP lint, a variable must be
1478-
// incremented exactly once in the loop body, and initialized to zero
1479-
// at the start of the loop.
1480-
fn check_for_loop_explicit_counter<'tcx>(
1481-
cx: &LateContext<'tcx>,
1482-
pat: &'tcx Pat<'_>,
1483-
arg: &'tcx Expr<'_>,
1484-
body: &'tcx Expr<'_>,
1485-
expr: &'tcx Expr<'_>,
1486-
) {
1487-
// Look for variables that are incremented once per loop iteration.
1488-
let mut increment_visitor = IncrementVisitor::new(cx);
1489-
walk_expr(&mut increment_visitor, body);
1490-
1491-
// For each candidate, check the parent block to see if
1492-
// it's initialized to zero at the start of the loop.
1493-
if let Some(block) = get_enclosing_block(&cx, expr.hir_id) {
1494-
for id in increment_visitor.into_results() {
1495-
let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
1496-
walk_block(&mut initialize_visitor, block);
1497-
1498-
if_chain! {
1499-
if let Some((name, initializer)) = initialize_visitor.get_result();
1500-
if is_integer_const(cx, initializer, 0);
1501-
then {
1502-
let mut applicability = Applicability::MachineApplicable;
1503-
1504-
let for_span = get_span_of_entire_for_loop(expr);
1505-
1506-
span_lint_and_sugg(
1507-
cx,
1508-
EXPLICIT_COUNTER_LOOP,
1509-
for_span.with_hi(arg.span.hi()),
1510-
&format!("the variable `{}` is used as a loop counter", name),
1511-
"consider using",
1512-
format!(
1513-
"for ({}, {}) in {}.enumerate()",
1514-
name,
1515-
snippet_with_applicability(cx, pat.span, "item", &mut applicability),
1516-
make_iterator_snippet(cx, arg, &mut applicability),
1517-
),
1518-
applicability,
1519-
);
1520-
}
1521-
}
1522-
}
1523-
}
1524-
}
1525-
15261467
fn check_for_single_element_loop<'tcx>(
15271468
cx: &LateContext<'tcx>,
15281469
pat: &'tcx Pat<'_>,

clippy_lints/src/loops/utils.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@ use crate::utils::{get_trait_def_id, has_iter_method, implements_trait, paths, s
22
use rustc_errors::Applicability;
33
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
44
use rustc_lint::LateContext;
5+
use rustc_span::source_map::Span;
6+
7+
// this function assumes the given expression is a `for` loop.
8+
pub(super) fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span {
9+
// for some reason this is the only way to get the `Span`
10+
// of the entire `for` loop
11+
if let ExprKind::Match(_, arms, _) = &expr.kind {
12+
arms[0].body.span
13+
} else {
14+
unreachable!()
15+
}
16+
}
517

618
/// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
719
/// actual `Iterator` that the loop uses.

0 commit comments

Comments
 (0)