]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use super::NEVER_LOOP; |
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}; | |
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 | _ => 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 | } |