2 use crate::utils
::span_lint
;
3 use rustc_hir
::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Stmt, StmtKind}
;
4 use rustc_lint
::LateContext
;
5 use std
::iter
::{once, Iterator}
;
7 pub(super) fn check(cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'_
>) {
8 if let ExprKind
::Loop(ref block
, _
, _
, _
) = expr
.kind
{
9 match never_loop_block(block
, expr
.hir_id
) {
10 NeverLoopResult
::AlwaysBreak
=> span_lint(cx
, NEVER_LOOP
, expr
.span
, "this loop never actually loops"),
11 NeverLoopResult
::MayContinueMainLoop
| NeverLoopResult
::Otherwise
=> (),
16 enum NeverLoopResult
{
17 // A break/return always get triggered but not necessarily for the main loop.
19 // A continue may occur for the main loop.
25 fn absorb_break(arg
: &NeverLoopResult
) -> NeverLoopResult
{
27 NeverLoopResult
::AlwaysBreak
| NeverLoopResult
::Otherwise
=> NeverLoopResult
::Otherwise
,
28 NeverLoopResult
::MayContinueMainLoop
=> NeverLoopResult
::MayContinueMainLoop
,
32 // Combine two results for parts that are called in order.
34 fn combine_seq(first
: NeverLoopResult
, second
: NeverLoopResult
) -> NeverLoopResult
{
36 NeverLoopResult
::AlwaysBreak
| NeverLoopResult
::MayContinueMainLoop
=> first
,
37 NeverLoopResult
::Otherwise
=> second
,
41 // Combine two results where both parts are called but not necessarily in order.
43 fn combine_both(left
: NeverLoopResult
, right
: NeverLoopResult
) -> NeverLoopResult
{
45 (NeverLoopResult
::MayContinueMainLoop
, _
) | (_
, NeverLoopResult
::MayContinueMainLoop
) => {
46 NeverLoopResult
::MayContinueMainLoop
48 (NeverLoopResult
::AlwaysBreak
, _
) | (_
, NeverLoopResult
::AlwaysBreak
) => NeverLoopResult
::AlwaysBreak
,
49 (NeverLoopResult
::Otherwise
, NeverLoopResult
::Otherwise
) => NeverLoopResult
::Otherwise
,
53 // Combine two results where only one of the part may have been executed.
55 fn combine_branches(b1
: NeverLoopResult
, b2
: NeverLoopResult
) -> NeverLoopResult
{
57 (NeverLoopResult
::AlwaysBreak
, NeverLoopResult
::AlwaysBreak
) => NeverLoopResult
::AlwaysBreak
,
58 (NeverLoopResult
::MayContinueMainLoop
, _
) | (_
, NeverLoopResult
::MayContinueMainLoop
) => {
59 NeverLoopResult
::MayContinueMainLoop
61 (NeverLoopResult
::Otherwise
, _
) | (_
, NeverLoopResult
::Otherwise
) => NeverLoopResult
::Otherwise
,
65 fn never_loop_block(block
: &Block
<'_
>, main_loop_id
: HirId
) -> NeverLoopResult
{
66 let stmts
= block
.stmts
.iter().map(stmt_to_expr
);
67 let expr
= once(block
.expr
.as_deref());
68 let mut iter
= stmts
.chain(expr
).flatten();
69 never_loop_expr_seq(&mut iter
, main_loop_id
)
72 fn never_loop_expr_seq
<'a
, T
: Iterator
<Item
= &'a Expr
<'a
>>>(es
: &mut T
, main_loop_id
: HirId
) -> NeverLoopResult
{
73 es
.map(|e
| never_loop_expr(e
, main_loop_id
))
74 .fold(NeverLoopResult
::Otherwise
, combine_seq
)
77 fn stmt_to_expr
<'tcx
>(stmt
: &Stmt
<'tcx
>) -> Option
<&'tcx Expr
<'tcx
>> {
79 StmtKind
::Semi(ref e
, ..) | StmtKind
::Expr(ref e
, ..) => Some(e
),
80 StmtKind
::Local(ref local
) => local
.init
.as_deref(),
85 fn never_loop_expr(expr
: &Expr
<'_
>, main_loop_id
: HirId
) -> NeverLoopResult
{
88 | ExprKind
::Unary(_
, ref e
)
89 | ExprKind
::Cast(ref e
, _
)
90 | ExprKind
::Type(ref e
, _
)
91 | ExprKind
::Field(ref e
, _
)
92 | ExprKind
::AddrOf(_
, _
, ref e
)
93 | ExprKind
::Struct(_
, _
, Some(ref e
))
94 | ExprKind
::Repeat(ref e
, _
)
95 | ExprKind
::DropTemps(ref e
) => never_loop_expr(e
, main_loop_id
),
96 ExprKind
::Array(ref es
) | ExprKind
::MethodCall(_
, _
, ref es
, _
) | ExprKind
::Tup(ref es
) => {
97 never_loop_expr_all(&mut es
.iter(), main_loop_id
)
99 ExprKind
::Call(ref e
, ref es
) => never_loop_expr_all(&mut once(&**e
).chain(es
.iter()), main_loop_id
),
100 ExprKind
::Binary(_
, ref e1
, ref e2
)
101 | ExprKind
::Assign(ref e1
, ref e2
, _
)
102 | ExprKind
::AssignOp(_
, ref e1
, ref e2
)
103 | ExprKind
::Index(ref e1
, ref e2
) => never_loop_expr_all(&mut [&**e1
, &**e2
].iter().cloned(), main_loop_id
),
104 ExprKind
::Loop(ref b
, _
, _
, _
) => {
105 // Break can come from the inner loop so remove them.
106 absorb_break(&never_loop_block(b
, main_loop_id
))
108 ExprKind
::If(ref e
, ref e2
, ref e3
) => {
109 let e1
= never_loop_expr(e
, main_loop_id
);
110 let e2
= never_loop_expr(e2
, main_loop_id
);
113 .map_or(NeverLoopResult
::Otherwise
, |e
| never_loop_expr(e
, main_loop_id
));
114 combine_seq(e1
, combine_branches(e2
, e3
))
116 ExprKind
::Match(ref e
, ref arms
, _
) => {
117 let e
= never_loop_expr(e
, main_loop_id
);
121 let arms
= never_loop_expr_branch(&mut arms
.iter().map(|a
| &*a
.body
), main_loop_id
);
125 ExprKind
::Block(ref b
, _
) => never_loop_block(b
, main_loop_id
),
126 ExprKind
::Continue(d
) => {
129 .expect("target ID can only be missing in the presence of compilation errors");
130 if id
== main_loop_id
{
131 NeverLoopResult
::MayContinueMainLoop
133 NeverLoopResult
::AlwaysBreak
136 ExprKind
::Break(_
, ref e
) | ExprKind
::Ret(ref e
) => e
.as_ref().map_or(NeverLoopResult
::AlwaysBreak
, |e
| {
137 combine_seq(never_loop_expr(e
, main_loop_id
), NeverLoopResult
::AlwaysBreak
)
139 ExprKind
::InlineAsm(ref asm
) => asm
142 .map(|(o
, _
)| match o
{
143 InlineAsmOperand
::In { expr, .. }
144 | InlineAsmOperand
::InOut { expr, .. }
145 | InlineAsmOperand
::Const { expr }
146 | InlineAsmOperand
::Sym { expr }
=> never_loop_expr(expr
, main_loop_id
),
147 InlineAsmOperand
::Out { expr, .. }
=> never_loop_expr_all(&mut expr
.iter(), main_loop_id
),
148 InlineAsmOperand
::SplitInOut { in_expr, out_expr, .. }
=> {
149 never_loop_expr_all(&mut once(in_expr
).chain(out_expr
.iter()), main_loop_id
)
152 .fold(NeverLoopResult
::Otherwise
, combine_both
),
153 ExprKind
::Struct(_
, _
, None
)
154 | ExprKind
::Yield(_
, _
)
155 | ExprKind
::Closure(_
, _
, _
, _
, _
)
156 | ExprKind
::LlvmInlineAsm(_
)
158 | ExprKind
::ConstBlock(_
)
160 | ExprKind
::Err
=> NeverLoopResult
::Otherwise
,
164 fn never_loop_expr_all
<'a
, T
: Iterator
<Item
= &'a Expr
<'a
>>>(es
: &mut T
, main_loop_id
: HirId
) -> NeverLoopResult
{
165 es
.map(|e
| never_loop_expr(e
, main_loop_id
))
166 .fold(NeverLoopResult
::Otherwise
, combine_both
)
169 fn never_loop_expr_branch
<'a
, T
: Iterator
<Item
= &'a Expr
<'a
>>>(e
: &mut T
, main_loop_id
: HirId
) -> NeverLoopResult
{
170 e
.map(|e
| never_loop_expr(e
, main_loop_id
))
171 .fold(NeverLoopResult
::AlwaysBreak
, combine_branches
)