Skip to content

Commit 9f3b6e9

Browse files
committed
don't emit AlwaysBreaks if it targets a block
Introduced an ignored_ids parameter. Takes O(n^2) time in the worst case. Can be changed to collect block ids in first phase, and then filter with binary search in second.
1 parent 9899861 commit 9f3b6e9

File tree

2 files changed

+79
-45
lines changed

2 files changed

+79
-45
lines changed

clippy_lints/src/loops/never_loop.rs

Lines changed: 69 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
44
use clippy_utils::higher::ForLoop;
55
use clippy_utils::source::snippet;
66
use rustc_errors::Applicability;
7-
use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
7+
use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
88
use rustc_lint::LateContext;
99
use rustc_span::Span;
1010
use std::iter::{once, Iterator};
@@ -16,7 +16,7 @@ pub(super) fn check(
1616
span: Span,
1717
for_loop: Option<&ForLoop<'_>>,
1818
) {
19-
match never_loop_block(block, loop_id) {
19+
match never_loop_block(block, &mut Vec::new(), loop_id) {
2020
NeverLoopResult::AlwaysBreak => {
2121
span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
2222
if let Some(ForLoop {
@@ -92,35 +92,33 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
9292
}
9393
}
9494

95-
fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
96-
let mut iter = block
95+
fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
96+
let iter = block
9797
.stmts
9898
.iter()
9999
.filter_map(stmt_to_expr)
100100
.chain(block.expr.map(|expr| (expr, None)));
101-
never_loop_expr_seq(&mut iter, main_loop_id)
102-
}
103101

104-
fn never_loop_expr_seq<'a, T: Iterator<Item = (&'a Expr<'a>, Option<&'a Block<'a>>)>>(
105-
es: &mut T,
106-
main_loop_id: HirId,
107-
) -> NeverLoopResult {
108-
es.map(|(e, els)| {
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)))
102+
iter.map(|(e, els)| {
103+
let e = never_loop_expr(e, ignore_ids, main_loop_id);
104+
// els is an else block in a let...else binding
105+
els.map_or(e, |els| {
106+
combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id))
107+
})
111108
})
112109
.fold(NeverLoopResult::Otherwise, combine_seq)
113110
}
114111

115112
fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> {
116113
match stmt.kind {
117-
StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)),
114+
StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)),
115+
// add the let...else expression (if present)
118116
StmtKind::Local(local) => local.init.map(|init| (init, local.els)),
119117
StmtKind::Item(..) => None,
120118
}
121119
}
122120

123-
fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
121+
fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
124122
match expr.kind {
125123
ExprKind::Box(e)
126124
| ExprKind::Unary(_, e)
@@ -129,48 +127,56 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
129127
| ExprKind::Field(e, _)
130128
| ExprKind::AddrOf(_, _, e)
131129
| 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)
137-
},
130+
| ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id),
131+
ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id),
132+
ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id),
133+
ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all(
134+
&mut std::iter::once(receiver).chain(es.iter()),
135+
ignore_ids,
136+
main_loop_id,
137+
),
138138
ExprKind::Struct(_, fields, base) => {
139-
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id);
139+
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
140140
if let Some(base) = base {
141-
combine_both(fields, never_loop_expr(base, main_loop_id))
141+
combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id))
142142
} else {
143143
fields
144144
}
145145
},
146-
ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
146+
ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
147147
ExprKind::Binary(_, e1, e2)
148148
| ExprKind::Assign(e1, e2, _)
149149
| ExprKind::AssignOp(_, e1, e2)
150-
| ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
150+
| ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
151151
ExprKind::Loop(b, _, _, _) => {
152152
// Break can come from the inner loop so remove them.
153-
absorb_break(never_loop_block(b, main_loop_id))
153+
absorb_break(never_loop_block(b, ignore_ids, main_loop_id))
154154
},
155155
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);
158-
let e3 = e3
159-
.as_ref()
160-
.map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id));
156+
let e1 = never_loop_expr(e, ignore_ids, main_loop_id);
157+
let e2 = never_loop_expr(e2, ignore_ids, main_loop_id);
158+
let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
159+
never_loop_expr(e, ignore_ids, main_loop_id)
160+
});
161161
combine_seq(e1, combine_branches(e2, e3))
162162
},
163163
ExprKind::Match(e, arms, _) => {
164-
let e = never_loop_expr(e, main_loop_id);
164+
let e = never_loop_expr(e, ignore_ids, main_loop_id);
165165
if arms.is_empty() {
166166
e
167167
} else {
168-
let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id);
168+
let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
169169
combine_seq(e, arms)
170170
}
171171
},
172-
ExprKind::Block(b, None) => never_loop_block(b, main_loop_id),
173-
ExprKind::Block(b, Some(_label)) => absorb_break(never_loop_block(b, main_loop_id)),
172+
ExprKind::Block(b, l) => {
173+
if let Some(_) = l {
174+
ignore_ids.push(b.hir_id);
175+
}
176+
let ret = never_loop_block(b, ignore_ids, main_loop_id);
177+
ignore_ids.pop();
178+
ret
179+
},
174180
ExprKind::Continue(d) => {
175181
let id = d
176182
.target_id
@@ -181,20 +187,31 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
181187
NeverLoopResult::AlwaysBreak
182188
}
183189
},
190+
ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
191+
.map_or(NeverLoopResult::Otherwise, |e| {
192+
combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise)
193+
}),
184194
ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
185-
combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
195+
combine_seq(
196+
never_loop_expr(e, ignore_ids, main_loop_id),
197+
NeverLoopResult::AlwaysBreak,
198+
)
186199
}),
187200
ExprKind::InlineAsm(asm) => asm
188201
.operands
189202
.iter()
190203
.map(|(o, _)| match o {
191204
InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
192-
never_loop_expr(expr, main_loop_id)
205+
never_loop_expr(expr, ignore_ids, main_loop_id)
193206
},
194-
InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id),
195-
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
196-
never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id)
207+
InlineAsmOperand::Out { expr, .. } => {
208+
never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id)
197209
},
210+
InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
211+
&mut once(*in_expr).chain(out_expr.iter().copied()),
212+
ignore_ids,
213+
main_loop_id,
214+
),
198215
InlineAsmOperand::Const { .. }
199216
| InlineAsmOperand::SymFn { .. }
200217
| InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
@@ -209,13 +226,21 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
209226
}
210227
}
211228

212-
fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
213-
es.map(|e| never_loop_expr(e, main_loop_id))
229+
fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(
230+
es: &mut T,
231+
ignore_ids: &mut Vec<HirId>,
232+
main_loop_id: HirId,
233+
) -> NeverLoopResult {
234+
es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
214235
.fold(NeverLoopResult::Otherwise, combine_both)
215236
}
216237

217-
fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult {
218-
e.map(|e| never_loop_expr(e, main_loop_id))
238+
fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
239+
e: &mut T,
240+
ignore_ids: &mut Vec<HirId>,
241+
main_loop_id: HirId,
242+
) -> NeverLoopResult {
243+
e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
219244
.fold(NeverLoopResult::AlwaysBreak, combine_branches)
220245
}
221246

tests/ui/never_loop.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,22 @@ pub fn test19() {
234234
fn thing(iter: impl Iterator) {
235235
for _ in iter {
236236
'b: {
237-
// error goes away if we just have the block's value be ().
238237
break 'b;
239238
}
240239
}
241240
}
242241
}
243242

243+
pub fn test20() {
244+
'a: loop {
245+
'b: {
246+
break 'b 'c: {
247+
break 'a;
248+
};
249+
}
250+
}
251+
}
252+
244253
fn main() {
245254
test1();
246255
test2();

0 commit comments

Comments
 (0)