Skip to content

Commit 7158c94

Browse files
nahuakangY-Nak
authored andcommitted
Refactor while let loop to its own module
1 parent 287a4f8 commit 7158c94

File tree

2 files changed

+92
-84
lines changed

2 files changed

+92
-84
lines changed

clippy_lints/src/loops/mod.rs

Lines changed: 5 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ mod needless_collect;
1212
mod never_loop;
1313
mod same_item_push;
1414
mod utils;
15+
mod while_let_loop;
1516
mod while_let_on_iterator;
1617

1718
use crate::utils::sugg::Sugg;
18-
use crate::utils::{higher, snippet_with_applicability, span_lint_and_sugg, sugg};
19-
use rustc_errors::Applicability;
20-
use rustc_hir::{Block, Expr, ExprKind, LoopSource, MatchSource, Pat, StmtKind};
21-
use rustc_lint::{LateContext, LateLintPass, LintContext};
22-
use rustc_middle::lint::in_external_macro;
19+
use crate::utils::{higher, sugg};
20+
use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
21+
use rustc_lint::{LateContext, LateLintPass};
2322
use rustc_session::{declare_lint_pass, declare_tool_lint};
2423
use rustc_span::source_map::Span;
2524
use utils::{get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor};
@@ -564,49 +563,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
564563
if let ExprKind::Loop(ref block, _, LoopSource::Loop, _) = expr.kind {
565564
// also check for empty `loop {}` statements, skipping those in #[panic_handler]
566565
empty_loop::check_empty_loop(cx, expr, block);
567-
568-
// extract the expression from the first statement (if any) in a block
569-
let inner_stmt_expr = extract_expr_from_first_stmt(block);
570-
// or extract the first expression (if any) from the block
571-
if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(block)) {
572-
if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind {
573-
// ensure "if let" compatible match structure
574-
match *source {
575-
MatchSource::Normal | MatchSource::IfLetDesugar { .. } => {
576-
if arms.len() == 2
577-
&& arms[0].guard.is_none()
578-
&& arms[1].guard.is_none()
579-
&& is_simple_break_expr(&arms[1].body)
580-
{
581-
if in_external_macro(cx.sess(), expr.span) {
582-
return;
583-
}
584-
585-
// NOTE: we used to build a body here instead of using
586-
// ellipsis, this was removed because:
587-
// 1) it was ugly with big bodies;
588-
// 2) it was not indented properly;
589-
// 3) it wasn’t very smart (see #675).
590-
let mut applicability = Applicability::HasPlaceholders;
591-
span_lint_and_sugg(
592-
cx,
593-
WHILE_LET_LOOP,
594-
expr.span,
595-
"this loop could be written as a `while let` loop",
596-
"try",
597-
format!(
598-
"while let {} = {} {{ .. }}",
599-
snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability),
600-
snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability),
601-
),
602-
applicability,
603-
);
604-
}
605-
},
606-
_ => (),
607-
}
608-
}
609-
}
566+
while_let_loop::check_while_let_loop(cx, expr, block);
610567
}
611568

612569
while_let_on_iterator::check_while_let_on_iterator(cx, expr);
@@ -709,39 +666,3 @@ impl std::ops::Sub<&MinifyingSugg<'static>> for MinifyingSugg<'static> {
709666
}
710667
}
711668
}
712-
713-
/// If a block begins with a statement (possibly a `let` binding) and has an
714-
/// expression, return it.
715-
fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
716-
if block.stmts.is_empty() {
717-
return None;
718-
}
719-
if let StmtKind::Local(ref local) = block.stmts[0].kind {
720-
local.init //.map(|expr| expr)
721-
} else {
722-
None
723-
}
724-
}
725-
726-
/// If a block begins with an expression (with or without semicolon), return it.
727-
fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
728-
match block.expr {
729-
Some(ref expr) if block.stmts.is_empty() => Some(expr),
730-
None if !block.stmts.is_empty() => match block.stmts[0].kind {
731-
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr),
732-
StmtKind::Local(..) | StmtKind::Item(..) => None,
733-
},
734-
_ => None,
735-
}
736-
}
737-
738-
/// Returns `true` if expr contains a single break expr without destination label
739-
/// and
740-
/// passed expression. The expression may be within a block.
741-
fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
742-
match expr.kind {
743-
ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
744-
ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
745-
_ => false,
746-
}
747-
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use super::WHILE_LET_LOOP;
2+
use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Block, Expr, ExprKind, MatchSource, StmtKind};
5+
use rustc_lint::{LateContext, LintContext};
6+
use rustc_middle::lint::in_external_macro;
7+
8+
pub(super) fn check_while_let_loop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
9+
// extract the expression from the first statement (if any) in a block
10+
let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
11+
// or extract the first expression (if any) from the block
12+
if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) {
13+
if let ExprKind::Match(ref matchexpr, ref arms, ref source) = inner.kind {
14+
// ensure "if let" compatible match structure
15+
match *source {
16+
MatchSource::Normal | MatchSource::IfLetDesugar { .. } => {
17+
if arms.len() == 2
18+
&& arms[0].guard.is_none()
19+
&& arms[1].guard.is_none()
20+
&& is_simple_break_expr(&arms[1].body)
21+
{
22+
if in_external_macro(cx.sess(), expr.span) {
23+
return;
24+
}
25+
26+
// NOTE: we used to build a body here instead of using
27+
// ellipsis, this was removed because:
28+
// 1) it was ugly with big bodies;
29+
// 2) it was not indented properly;
30+
// 3) it wasn’t very smart (see #675).
31+
let mut applicability = Applicability::HasPlaceholders;
32+
span_lint_and_sugg(
33+
cx,
34+
WHILE_LET_LOOP,
35+
expr.span,
36+
"this loop could be written as a `while let` loop",
37+
"try",
38+
format!(
39+
"while let {} = {} {{ .. }}",
40+
snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability),
41+
snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability),
42+
),
43+
applicability,
44+
);
45+
}
46+
},
47+
_ => (),
48+
}
49+
}
50+
}
51+
}
52+
53+
/// If a block begins with a statement (possibly a `let` binding) and has an
54+
/// expression, return it.
55+
fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
56+
if block.stmts.is_empty() {
57+
return None;
58+
}
59+
if let StmtKind::Local(ref local) = block.stmts[0].kind {
60+
local.init //.map(|expr| expr)
61+
} else {
62+
None
63+
}
64+
}
65+
66+
/// If a block begins with an expression (with or without semicolon), return it.
67+
fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
68+
match block.expr {
69+
Some(ref expr) if block.stmts.is_empty() => Some(expr),
70+
None if !block.stmts.is_empty() => match block.stmts[0].kind {
71+
StmtKind::Expr(ref expr) | StmtKind::Semi(ref expr) => Some(expr),
72+
StmtKind::Local(..) | StmtKind::Item(..) => None,
73+
},
74+
_ => None,
75+
}
76+
}
77+
78+
/// Returns `true` if expr contains a single break expr without destination label
79+
/// and
80+
/// passed expression. The expression may be within a block.
81+
fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
82+
match expr.kind {
83+
ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
84+
ExprKind::Block(ref b, _) => extract_first_expr(b).map_or(false, |subexpr| is_simple_break_expr(subexpr)),
85+
_ => false,
86+
}
87+
}

0 commit comments

Comments
 (0)