1 use super::utils
::make_iterator_snippet
;
3 use clippy_utils
::diagnostics
::span_lint_and_then
;
4 use clippy_utils
::higher
::ForLoop
;
5 use clippy_utils
::source
::snippet
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}
;
8 use rustc_lint
::LateContext
;
10 use std
::iter
::{once, Iterator}
;
17 for_loop
: Option
<&ForLoop
<'_
>>,
19 match never_loop_block(block
, loop_id
) {
20 NeverLoopResult
::AlwaysBreak
=> {
21 span_lint_and_then(cx
, NEVER_LOOP
, span
, "this loop never actually loops", |diag
| {
29 // Suggests using an `if let` instead. This is `Unspecified` because the
30 // loop may (probably) contain `break` statements which would be invalid
32 diag
.span_suggestion_verbose(
33 for_span
.with_hi(iterator
.span
.hi()),
34 "if you need the first element of the iterator, try writing",
35 for_to_if_let_sugg(cx
, iterator
, pat
),
36 Applicability
::Unspecified
,
41 NeverLoopResult
::MayContinueMainLoop
| NeverLoopResult
::Otherwise
=> (),
45 #[derive(Copy, Clone)]
46 enum NeverLoopResult
{
47 // A break/return always get triggered but not necessarily for the main loop.
49 // A continue may occur for the main loop.
55 fn absorb_break(arg
: NeverLoopResult
) -> NeverLoopResult
{
57 NeverLoopResult
::AlwaysBreak
| NeverLoopResult
::Otherwise
=> NeverLoopResult
::Otherwise
,
58 NeverLoopResult
::MayContinueMainLoop
=> NeverLoopResult
::MayContinueMainLoop
,
62 // Combine two results for parts that are called in order.
64 fn combine_seq(first
: NeverLoopResult
, second
: NeverLoopResult
) -> NeverLoopResult
{
66 NeverLoopResult
::AlwaysBreak
| NeverLoopResult
::MayContinueMainLoop
=> first
,
67 NeverLoopResult
::Otherwise
=> second
,
71 // Combine two results where both parts are called but not necessarily in order.
73 fn combine_both(left
: NeverLoopResult
, right
: NeverLoopResult
) -> NeverLoopResult
{
75 (NeverLoopResult
::MayContinueMainLoop
, _
) | (_
, NeverLoopResult
::MayContinueMainLoop
) => {
76 NeverLoopResult
::MayContinueMainLoop
78 (NeverLoopResult
::AlwaysBreak
, _
) | (_
, NeverLoopResult
::AlwaysBreak
) => NeverLoopResult
::AlwaysBreak
,
79 (NeverLoopResult
::Otherwise
, NeverLoopResult
::Otherwise
) => NeverLoopResult
::Otherwise
,
83 // Combine two results where only one of the part may have been executed.
85 fn combine_branches(b1
: NeverLoopResult
, b2
: NeverLoopResult
) -> NeverLoopResult
{
87 (NeverLoopResult
::AlwaysBreak
, NeverLoopResult
::AlwaysBreak
) => NeverLoopResult
::AlwaysBreak
,
88 (NeverLoopResult
::MayContinueMainLoop
, _
) | (_
, NeverLoopResult
::MayContinueMainLoop
) => {
89 NeverLoopResult
::MayContinueMainLoop
91 (NeverLoopResult
::Otherwise
, _
) | (_
, NeverLoopResult
::Otherwise
) => NeverLoopResult
::Otherwise
,
95 fn never_loop_block(block
: &Block
<'_
>, main_loop_id
: HirId
) -> NeverLoopResult
{
99 .filter_map(stmt_to_expr
)
100 .chain(block
.expr
.map(|expr
| (expr
, None
)));
101 never_loop_expr_seq(&mut iter
, main_loop_id
)
104 fn never_loop_expr_seq
<'a
, T
: Iterator
<Item
= (&'a Expr
<'a
>, Option
<&'a Block
<'a
>>)>>(
107 ) -> NeverLoopResult
{
109 let e
= never_loop_expr(e
, main_loop_id
);
110 els
.map_or(e
, |els
| combine_branches(e
, never_loop_block(els
, main_loop_id
)))
112 .fold(NeverLoopResult
::Otherwise
, combine_seq
)
115 fn stmt_to_expr
<'tcx
>(stmt
: &Stmt
<'tcx
>) -> Option
<(&'tcx Expr
<'tcx
>, Option
<&'tcx Block
<'tcx
>>)> {
117 StmtKind
::Semi(e
, ..) | StmtKind
::Expr(e
, ..) => Some((e
, None
)),
118 StmtKind
::Local(local
) => local
.init
.map(|init
| (init
, local
.els
)),
119 StmtKind
::Item(..) => None
,
123 fn never_loop_expr(expr
: &Expr
<'_
>, main_loop_id
: HirId
) -> NeverLoopResult
{
126 | ExprKind
::Unary(_
, e
)
127 | ExprKind
::Cast(e
, _
)
128 | ExprKind
::Type(e
, _
)
129 | ExprKind
::Field(e
, _
)
130 | ExprKind
::AddrOf(_
, _
, e
)
131 | ExprKind
::Repeat(e
, _
)
132 | ExprKind
::DropTemps(e
) => never_loop_expr(e
, main_loop_id
),
133 ExprKind
::Let(let_expr
) => never_loop_expr(let_expr
.init
, main_loop_id
),
134 ExprKind
::Array(es
) | ExprKind
::Tup(es
) => never_loop_expr_all(&mut es
.iter(), main_loop_id
),
135 ExprKind
::MethodCall(_
, receiver
, es
, _
) => {
136 never_loop_expr_all(&mut std
::iter
::once(receiver
).chain(es
.iter()), main_loop_id
)
138 ExprKind
::Struct(_
, fields
, base
) => {
139 let fields
= never_loop_expr_all(&mut fields
.iter().map(|f
| f
.expr
), main_loop_id
);
140 if let Some(base
) = base
{
141 combine_both(fields
, never_loop_expr(base
, main_loop_id
))
146 ExprKind
::Call(e
, es
) => never_loop_expr_all(&mut once(e
).chain(es
.iter()), main_loop_id
),
147 ExprKind
::Binary(_
, e1
, e2
)
148 | ExprKind
::Assign(e1
, e2
, _
)
149 | ExprKind
::AssignOp(_
, e1
, e2
)
150 | ExprKind
::Index(e1
, e2
) => never_loop_expr_all(&mut [e1
, e2
].iter().copied(), main_loop_id
),
151 ExprKind
::Loop(b
, _
, _
, _
) => {
152 // Break can come from the inner loop so remove them.
153 absorb_break(never_loop_block(b
, main_loop_id
))
155 ExprKind
::If(e
, e2
, e3
) => {
156 let e1
= never_loop_expr(e
, main_loop_id
);
157 let e2
= never_loop_expr(e2
, main_loop_id
);
160 .map_or(NeverLoopResult
::Otherwise
, |e
| never_loop_expr(e
, main_loop_id
));
161 combine_seq(e1
, combine_branches(e2
, e3
))
163 ExprKind
::Match(e
, arms
, _
) => {
164 let e
= never_loop_expr(e
, main_loop_id
);
168 let arms
= never_loop_expr_branch(&mut arms
.iter().map(|a
| a
.body
), main_loop_id
);
172 ExprKind
::Block(b
, _
) => never_loop_block(b
, main_loop_id
),
173 ExprKind
::Continue(d
) => {
176 .expect("target ID can only be missing in the presence of compilation errors");
177 if id
== main_loop_id
{
178 NeverLoopResult
::MayContinueMainLoop
180 NeverLoopResult
::AlwaysBreak
183 ExprKind
::Break(_
, e
) | ExprKind
::Ret(e
) => e
.as_ref().map_or(NeverLoopResult
::AlwaysBreak
, |e
| {
184 combine_seq(never_loop_expr(e
, main_loop_id
), NeverLoopResult
::AlwaysBreak
)
186 ExprKind
::InlineAsm(asm
) => asm
189 .map(|(o
, _
)| match o
{
190 InlineAsmOperand
::In { expr, .. }
| InlineAsmOperand
::InOut { expr, .. }
=> {
191 never_loop_expr(expr
, main_loop_id
)
193 InlineAsmOperand
::Out { expr, .. }
=> never_loop_expr_all(&mut expr
.iter().copied(), main_loop_id
),
194 InlineAsmOperand
::SplitInOut { in_expr, out_expr, .. }
=> {
195 never_loop_expr_all(&mut once(*in_expr
).chain(out_expr
.iter().copied()), main_loop_id
)
197 InlineAsmOperand
::Const { .. }
198 | InlineAsmOperand
::SymFn { .. }
199 | InlineAsmOperand
::SymStatic { .. }
=> NeverLoopResult
::Otherwise
,
201 .fold(NeverLoopResult
::Otherwise
, combine_both
),
202 ExprKind
::Yield(_
, _
)
203 | ExprKind
::Closure { .. }
205 | ExprKind
::ConstBlock(_
)
207 | ExprKind
::Err
=> NeverLoopResult
::Otherwise
,
211 fn never_loop_expr_all
<'a
, T
: Iterator
<Item
= &'a Expr
<'a
>>>(es
: &mut T
, main_loop_id
: HirId
) -> NeverLoopResult
{
212 es
.map(|e
| never_loop_expr(e
, main_loop_id
))
213 .fold(NeverLoopResult
::Otherwise
, combine_both
)
216 fn never_loop_expr_branch
<'a
, T
: Iterator
<Item
= &'a Expr
<'a
>>>(e
: &mut T
, main_loop_id
: HirId
) -> NeverLoopResult
{
217 e
.map(|e
| never_loop_expr(e
, main_loop_id
))
218 .fold(NeverLoopResult
::AlwaysBreak
, combine_branches
)
221 fn for_to_if_let_sugg(cx
: &LateContext
<'_
>, iterator
: &Expr
<'_
>, pat
: &Pat
<'_
>) -> String
{
222 let pat_snippet
= snippet(cx
, pat
.span
, "_");
223 let iter_snippet
= make_iterator_snippet(cx
, iterator
, &mut Applicability
::Unspecified
);
225 format
!("if let Some({pat_snippet}) = {iter_snippet}.next()")