2021-08-12 04:16:25 -05:00
|
|
|
use super::utils::make_iterator_snippet;
|
2021-03-12 08:30:50 -06:00
|
|
|
use super::NEVER_LOOP;
|
2021-08-12 04:16:25 -05:00
|
|
|
use clippy_utils::diagnostics::span_lint_and_then;
|
2021-08-08 09:49:13 -05:00
|
|
|
use clippy_utils::higher::ForLoop;
|
2021-08-12 04:16:25 -05:00
|
|
|
use clippy_utils::source::snippet;
|
|
|
|
use rustc_errors::Applicability;
|
2021-10-27 09:48:06 -05:00
|
|
|
use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
|
2021-03-12 08:30:50 -06:00
|
|
|
use rustc_lint::LateContext;
|
2021-10-27 09:48:06 -05:00
|
|
|
use rustc_span::Span;
|
2021-03-12 08:30:50 -06:00
|
|
|
use std::iter::{once, Iterator};
|
|
|
|
|
2021-10-27 09:48:06 -05:00
|
|
|
pub(super) fn check(
|
2022-01-13 06:18:19 -06:00
|
|
|
cx: &LateContext<'_>,
|
|
|
|
block: &Block<'_>,
|
2021-10-27 09:48:06 -05:00
|
|
|
loop_id: HirId,
|
|
|
|
span: Span,
|
|
|
|
for_loop: Option<&ForLoop<'_>>,
|
|
|
|
) {
|
|
|
|
match never_loop_block(block, loop_id) {
|
|
|
|
NeverLoopResult::AlwaysBreak => {
|
|
|
|
span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
|
|
|
|
if let Some(ForLoop {
|
|
|
|
arg: iterator,
|
|
|
|
pat,
|
|
|
|
span: for_span,
|
|
|
|
..
|
|
|
|
}) = for_loop
|
|
|
|
{
|
|
|
|
// Suggests using an `if let` instead. This is `Unspecified` because the
|
|
|
|
// loop may (probably) contain `break` statements which would be invalid
|
|
|
|
// in an `if let`.
|
|
|
|
diag.span_suggestion_verbose(
|
|
|
|
for_span.with_hi(iterator.span.hi()),
|
|
|
|
"if you need the first element of the iterator, try writing",
|
|
|
|
for_to_if_let_sugg(cx, iterator, pat),
|
|
|
|
Applicability::Unspecified,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
|
2021-03-12 08:30:50 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-06 02:44:38 -05:00
|
|
|
#[derive(Copy, Clone)]
|
2021-03-12 08:30:50 -06:00
|
|
|
enum NeverLoopResult {
|
|
|
|
// A break/return always get triggered but not necessarily for the main loop.
|
|
|
|
AlwaysBreak,
|
|
|
|
// A continue may occur for the main loop.
|
|
|
|
MayContinueMainLoop,
|
|
|
|
Otherwise,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[must_use]
|
2022-10-06 02:44:38 -05:00
|
|
|
fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
|
|
|
|
match arg {
|
2021-03-12 08:30:50 -06:00
|
|
|
NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
|
|
|
|
NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Combine two results for parts that are called in order.
|
|
|
|
#[must_use]
|
|
|
|
fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
|
|
|
|
match first {
|
|
|
|
NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
|
|
|
|
NeverLoopResult::Otherwise => second,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Combine two results where both parts are called but not necessarily in order.
|
|
|
|
#[must_use]
|
|
|
|
fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
|
|
|
|
match (left, right) {
|
|
|
|
(NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
|
|
|
|
NeverLoopResult::MayContinueMainLoop
|
|
|
|
},
|
|
|
|
(NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
|
|
|
|
(NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Combine two results where only one of the part may have been executed.
|
|
|
|
#[must_use]
|
|
|
|
fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
|
|
|
|
match (b1, b2) {
|
|
|
|
(NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
|
|
|
|
(NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
|
|
|
|
NeverLoopResult::MayContinueMainLoop
|
|
|
|
},
|
|
|
|
(NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
2022-10-06 02:44:38 -05:00
|
|
|
let mut iter = block
|
|
|
|
.stmts
|
|
|
|
.iter()
|
|
|
|
.filter_map(stmt_to_expr)
|
|
|
|
.chain(block.expr.map(|expr| (expr, None)));
|
2021-03-12 08:30:50 -06:00
|
|
|
never_loop_expr_seq(&mut iter, main_loop_id)
|
|
|
|
}
|
|
|
|
|
2022-10-06 02:44:38 -05:00
|
|
|
fn never_loop_expr_seq<'a, T: Iterator<Item = (&'a Expr<'a>, Option<&'a Block<'a>>)>>(
|
|
|
|
es: &mut T,
|
|
|
|
main_loop_id: HirId,
|
|
|
|
) -> NeverLoopResult {
|
|
|
|
es.map(|(e, els)| {
|
|
|
|
let e = never_loop_expr(e, main_loop_id);
|
|
|
|
els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id)))
|
|
|
|
})
|
|
|
|
.fold(NeverLoopResult::Otherwise, combine_seq)
|
2021-03-12 08:30:50 -06:00
|
|
|
}
|
|
|
|
|
2022-10-06 02:44:38 -05:00
|
|
|
fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> {
|
2021-03-12 08:30:50 -06:00
|
|
|
match stmt.kind {
|
2022-10-06 02:44:38 -05:00
|
|
|
StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)),
|
|
|
|
StmtKind::Local(local) => local.init.map(|init| (init, local.els)),
|
2021-03-25 13:29:11 -05:00
|
|
|
StmtKind::Item(..) => None,
|
2021-03-12 08:30:50 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
|
|
|
|
match expr.kind {
|
2021-04-08 10:50:13 -05:00
|
|
|
ExprKind::Box(e)
|
|
|
|
| ExprKind::Unary(_, e)
|
|
|
|
| ExprKind::Cast(e, _)
|
|
|
|
| ExprKind::Type(e, _)
|
|
|
|
| ExprKind::Field(e, _)
|
|
|
|
| ExprKind::AddrOf(_, _, e)
|
|
|
|
| ExprKind::Repeat(e, _)
|
|
|
|
| ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id),
|
2021-10-13 02:44:47 -05:00
|
|
|
ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id),
|
2022-09-01 04:43:35 -05:00
|
|
|
ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id),
|
|
|
|
ExprKind::MethodCall(_, receiver, es, _) => {
|
|
|
|
never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id)
|
2021-03-12 08:30:50 -06:00
|
|
|
},
|
2022-06-16 10:39:06 -05:00
|
|
|
ExprKind::Struct(_, fields, base) => {
|
|
|
|
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id);
|
|
|
|
if let Some(base) = base {
|
|
|
|
combine_both(fields, never_loop_expr(base, main_loop_id))
|
|
|
|
} else {
|
|
|
|
fields
|
|
|
|
}
|
|
|
|
},
|
2021-04-08 10:50:13 -05:00
|
|
|
ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
|
|
|
|
ExprKind::Binary(_, e1, e2)
|
|
|
|
| ExprKind::Assign(e1, e2, _)
|
|
|
|
| ExprKind::AssignOp(_, e1, e2)
|
2021-04-22 04:31:13 -05:00
|
|
|
| ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
|
2021-04-08 10:50:13 -05:00
|
|
|
ExprKind::Loop(b, _, _, _) => {
|
2021-03-12 08:30:50 -06:00
|
|
|
// Break can come from the inner loop so remove them.
|
2022-10-06 02:44:38 -05:00
|
|
|
absorb_break(never_loop_block(b, main_loop_id))
|
2021-03-12 08:30:50 -06:00
|
|
|
},
|
2021-08-08 09:49:13 -05:00
|
|
|
ExprKind::If(e, e2, e3) => {
|
2021-03-12 08:30:50 -06:00
|
|
|
let e1 = never_loop_expr(e, main_loop_id);
|
|
|
|
let e2 = never_loop_expr(e2, main_loop_id);
|
|
|
|
let e3 = e3
|
|
|
|
.as_ref()
|
|
|
|
.map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id));
|
|
|
|
combine_seq(e1, combine_branches(e2, e3))
|
|
|
|
},
|
2021-04-08 10:50:13 -05:00
|
|
|
ExprKind::Match(e, arms, _) => {
|
2021-03-12 08:30:50 -06:00
|
|
|
let e = never_loop_expr(e, main_loop_id);
|
|
|
|
if arms.is_empty() {
|
|
|
|
e
|
|
|
|
} else {
|
2022-06-04 06:34:07 -05:00
|
|
|
let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id);
|
2021-03-12 08:30:50 -06:00
|
|
|
combine_seq(e, arms)
|
|
|
|
}
|
|
|
|
},
|
2021-04-08 10:50:13 -05:00
|
|
|
ExprKind::Block(b, _) => never_loop_block(b, main_loop_id),
|
2021-03-12 08:30:50 -06:00
|
|
|
ExprKind::Continue(d) => {
|
|
|
|
let id = d
|
|
|
|
.target_id
|
|
|
|
.expect("target ID can only be missing in the presence of compilation errors");
|
|
|
|
if id == main_loop_id {
|
|
|
|
NeverLoopResult::MayContinueMainLoop
|
|
|
|
} else {
|
|
|
|
NeverLoopResult::AlwaysBreak
|
|
|
|
}
|
|
|
|
},
|
2021-08-08 09:49:13 -05:00
|
|
|
ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
|
2021-03-12 08:30:50 -06:00
|
|
|
combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
|
|
|
|
}),
|
2021-04-08 10:50:13 -05:00
|
|
|
ExprKind::InlineAsm(asm) => asm
|
2021-03-12 08:30:50 -06:00
|
|
|
.operands
|
|
|
|
.iter()
|
|
|
|
.map(|(o, _)| match o {
|
2022-05-05 09:12:52 -05:00
|
|
|
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
|
|
|
|
never_loop_expr(expr, main_loop_id)
|
|
|
|
},
|
2022-08-26 00:43:00 -05:00
|
|
|
InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id),
|
2021-03-12 08:30:50 -06:00
|
|
|
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
2022-08-26 00:43:00 -05:00
|
|
|
never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id)
|
2021-03-12 08:30:50 -06:00
|
|
|
},
|
2022-02-28 18:50:56 -06:00
|
|
|
InlineAsmOperand::Const { .. }
|
|
|
|
| InlineAsmOperand::SymFn { .. }
|
|
|
|
| InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
|
2021-03-12 08:30:50 -06:00
|
|
|
})
|
|
|
|
.fold(NeverLoopResult::Otherwise, combine_both),
|
2022-06-16 10:39:06 -05:00
|
|
|
ExprKind::Yield(_, _)
|
2022-06-11 14:25:25 -05:00
|
|
|
| ExprKind::Closure { .. }
|
2021-03-12 08:30:50 -06:00
|
|
|
| ExprKind::Path(_)
|
|
|
|
| ExprKind::ConstBlock(_)
|
|
|
|
| ExprKind::Lit(_)
|
|
|
|
| ExprKind::Err => NeverLoopResult::Otherwise,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
|
|
|
|
es.map(|e| never_loop_expr(e, main_loop_id))
|
|
|
|
.fold(NeverLoopResult::Otherwise, combine_both)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult {
|
|
|
|
e.map(|e| never_loop_expr(e, main_loop_id))
|
|
|
|
.fold(NeverLoopResult::AlwaysBreak, combine_branches)
|
|
|
|
}
|
2021-08-12 04:16:25 -05:00
|
|
|
|
|
|
|
fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {
|
|
|
|
let pat_snippet = snippet(cx, pat.span, "_");
|
|
|
|
let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified);
|
|
|
|
|
2022-10-06 02:44:38 -05:00
|
|
|
format!("if let Some({pat_snippet}) = {iter_snippet}.next()")
|
2021-08-12 04:16:25 -05:00
|
|
|
}
|