]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/loops/never_loop.rs
Auto merge of #82864 - jyn514:short-circuit, r=GuillaumeGomez
[rust.git] / src / tools / clippy / clippy_lints / src / loops / never_loop.rs
1 use super::NEVER_LOOP;
2 use clippy_utils::diagnostics::span_lint;
3 use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Stmt, StmtKind};
4 use rustc_lint::LateContext;
5 use std::iter::{once, Iterator};
6
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 => (),
12         }
13     }
14 }
15
16 enum NeverLoopResult {
17     // A break/return always get triggered but not necessarily for the main loop.
18     AlwaysBreak,
19     // A continue may occur for the main loop.
20     MayContinueMainLoop,
21     Otherwise,
22 }
23
24 #[must_use]
25 fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult {
26     match *arg {
27         NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
28         NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
29     }
30 }
31
32 // Combine two results for parts that are called in order.
33 #[must_use]
34 fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
35     match first {
36         NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
37         NeverLoopResult::Otherwise => second,
38     }
39 }
40
41 // Combine two results where both parts are called but not necessarily in order.
42 #[must_use]
43 fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
44     match (left, right) {
45         (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
46             NeverLoopResult::MayContinueMainLoop
47         },
48         (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
49         (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
50     }
51 }
52
53 // Combine two results where only one of the part may have been executed.
54 #[must_use]
55 fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
56     match (b1, b2) {
57         (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
58         (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
59             NeverLoopResult::MayContinueMainLoop
60         },
61         (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
62     }
63 }
64
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)
70 }
71
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)
75 }
76
77 fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
78     match stmt.kind {
79         StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e),
80         StmtKind::Local(ref local) => local.init.as_deref(),
81         StmtKind::Item(..) => None,
82     }
83 }
84
85 fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
86     match expr.kind {
87         ExprKind::Box(ref e)
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)
98         },
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))
107         },
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);
111             let e3 = e3
112                 .as_ref()
113                 .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id));
114             combine_seq(e1, combine_branches(e2, e3))
115         },
116         ExprKind::Match(ref e, ref arms, _) => {
117             let e = never_loop_expr(e, main_loop_id);
118             if arms.is_empty() {
119                 e
120             } else {
121                 let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id);
122                 combine_seq(e, arms)
123             }
124         },
125         ExprKind::Block(ref b, _) => never_loop_block(b, main_loop_id),
126         ExprKind::Continue(d) => {
127             let id = d
128                 .target_id
129                 .expect("target ID can only be missing in the presence of compilation errors");
130             if id == main_loop_id {
131                 NeverLoopResult::MayContinueMainLoop
132             } else {
133                 NeverLoopResult::AlwaysBreak
134             }
135         },
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)
138         }),
139         ExprKind::InlineAsm(ref asm) => asm
140             .operands
141             .iter()
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)
150                 },
151             })
152             .fold(NeverLoopResult::Otherwise, combine_both),
153         ExprKind::Struct(_, _, None)
154         | ExprKind::Yield(_, _)
155         | ExprKind::Closure(_, _, _, _, _)
156         | ExprKind::LlvmInlineAsm(_)
157         | ExprKind::Path(_)
158         | ExprKind::ConstBlock(_)
159         | ExprKind::Lit(_)
160         | ExprKind::Err => NeverLoopResult::Otherwise,
161     }
162 }
163
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)
167 }
168
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)
172 }