Skip to content

Commit a182a67

Browse files
committed
Auto merge of #10311 - samueltardieu:issue-10304, r=Jarcho
[never_loop] Fix #10304 It is not sufficient to ignore break from a block inside the loop. Instructions after the break must be ignored, as they are unreachable. This is also true for all instructions in outer blocks and loops until the right block is reached. Fixes #10304 --- changelog: FP: [`never_loop`]: No longer lints, for statements following break statements for outer blocks. [#10311](#10311) <!-- changelog_checked-->
2 parents 63562a6 + 657ee48 commit a182a67

File tree

3 files changed

+93
-26
lines changed

3 files changed

+93
-26
lines changed

clippy_lints/src/loops/never_loop.rs

+34-25
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub(super) fn check(
3939
});
4040
},
4141
NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
42+
NeverLoopResult::IgnoreUntilEnd(_) => unreachable!(),
4243
}
4344
}
4445

@@ -48,6 +49,8 @@ enum NeverLoopResult {
4849
AlwaysBreak,
4950
// A continue may occur for the main loop.
5051
MayContinueMainLoop,
52+
// Ignore everything until the end of the block with this id
53+
IgnoreUntilEnd(HirId),
5154
Otherwise,
5255
}
5356

@@ -56,34 +59,34 @@ fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
5659
match arg {
5760
NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
5861
NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
62+
NeverLoopResult::IgnoreUntilEnd(id) => NeverLoopResult::IgnoreUntilEnd(id),
5963
}
6064
}
6165

6266
// Combine two results for parts that are called in order.
6367
#[must_use]
6468
fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
6569
match first {
66-
NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
67-
NeverLoopResult::Otherwise => second,
68-
}
69-
}
70-
71-
// Combine two results where both parts are called but not necessarily in order.
72-
#[must_use]
73-
fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
74-
match (left, right) {
75-
(NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
76-
NeverLoopResult::MayContinueMainLoop
70+
NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop | NeverLoopResult::IgnoreUntilEnd(_) => {
71+
first
7772
},
78-
(NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
79-
(NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
73+
NeverLoopResult::Otherwise => second,
8074
}
8175
}
8276

8377
// Combine two results where only one of the part may have been executed.
8478
#[must_use]
85-
fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
79+
fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult, ignore_ids: &[HirId]) -> NeverLoopResult {
8680
match (b1, b2) {
81+
(NeverLoopResult::IgnoreUntilEnd(a), NeverLoopResult::IgnoreUntilEnd(b)) => {
82+
if ignore_ids.iter().find(|&e| e == &a || e == &b).unwrap() == &a {
83+
NeverLoopResult::IgnoreUntilEnd(b)
84+
} else {
85+
NeverLoopResult::IgnoreUntilEnd(a)
86+
}
87+
},
88+
(i @ NeverLoopResult::IgnoreUntilEnd(_), NeverLoopResult::AlwaysBreak)
89+
| (NeverLoopResult::AlwaysBreak, i @ NeverLoopResult::IgnoreUntilEnd(_)) => i,
8790
(NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
8891
(NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
8992
NeverLoopResult::MayContinueMainLoop
@@ -103,7 +106,7 @@ fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id
103106
let e = never_loop_expr(e, ignore_ids, main_loop_id);
104107
// els is an else block in a let...else binding
105108
els.map_or(e, |els| {
106-
combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id))
109+
combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id), ignore_ids)
107110
})
108111
})
109112
.fold(NeverLoopResult::Otherwise, combine_seq)
@@ -139,7 +142,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
139142
ExprKind::Struct(_, fields, base) => {
140143
let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
141144
if let Some(base) = base {
142-
combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id))
145+
combine_seq(fields, never_loop_expr(base, ignore_ids, main_loop_id))
143146
} else {
144147
fields
145148
}
@@ -159,7 +162,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
159162
let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
160163
never_loop_expr(e, ignore_ids, main_loop_id)
161164
});
162-
combine_seq(e1, combine_branches(e2, e3))
165+
combine_seq(e1, combine_branches(e2, e3, ignore_ids))
163166
},
164167
ExprKind::Match(e, arms, _) => {
165168
let e = never_loop_expr(e, ignore_ids, main_loop_id);
@@ -175,8 +178,13 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
175178
ignore_ids.push(b.hir_id);
176179
}
177180
let ret = never_loop_block(b, ignore_ids, main_loop_id);
178-
ignore_ids.pop();
179-
ret
181+
if l.is_some() {
182+
ignore_ids.pop();
183+
}
184+
match ret {
185+
NeverLoopResult::IgnoreUntilEnd(a) if a == b.hir_id => NeverLoopResult::Otherwise,
186+
_ => ret,
187+
}
180188
},
181189
ExprKind::Continue(d) => {
182190
let id = d
@@ -190,8 +198,8 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
190198
},
191199
// checks if break targets a block instead of a loop
192200
ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
193-
.map_or(NeverLoopResult::Otherwise, |e| {
194-
combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise)
201+
.map_or(NeverLoopResult::IgnoreUntilEnd(t), |e| {
202+
never_loop_expr(e, ignore_ids, main_loop_id)
195203
}),
196204
ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
197205
combine_seq(
@@ -218,7 +226,7 @@ fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: H
218226
| InlineAsmOperand::SymFn { .. }
219227
| InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
220228
})
221-
.fold(NeverLoopResult::Otherwise, combine_both),
229+
.fold(NeverLoopResult::Otherwise, combine_seq),
222230
ExprKind::Yield(_, _)
223231
| ExprKind::Closure { .. }
224232
| ExprKind::Path(_)
@@ -234,16 +242,17 @@ fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(
234242
main_loop_id: HirId,
235243
) -> NeverLoopResult {
236244
es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
237-
.fold(NeverLoopResult::Otherwise, combine_both)
245+
.fold(NeverLoopResult::Otherwise, combine_seq)
238246
}
239247

240248
fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
241249
e: &mut T,
242250
ignore_ids: &mut Vec<HirId>,
243251
main_loop_id: HirId,
244252
) -> NeverLoopResult {
245-
e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
246-
.fold(NeverLoopResult::AlwaysBreak, combine_branches)
253+
e.fold(NeverLoopResult::AlwaysBreak, |a, b| {
254+
combine_branches(a, never_loop_expr(b, ignore_ids, main_loop_id), ignore_ids)
255+
})
247256
}
248257

249258
fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {

tests/ui/never_loop.rs

+45
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,51 @@ pub fn test20() {
250250
}
251251
}
252252

253+
pub fn test21() {
254+
loop {
255+
'a: {
256+
{}
257+
break 'a;
258+
}
259+
}
260+
}
261+
262+
// Issue 10304: code after break from block was not considered
263+
// unreachable code and was considered for further analysis of
264+
// whether the loop would ever be executed or not.
265+
pub fn test22() {
266+
for _ in 0..10 {
267+
'block: {
268+
break 'block;
269+
return;
270+
}
271+
println!("looped");
272+
}
273+
}
274+
275+
pub fn test23() {
276+
for _ in 0..10 {
277+
'block: {
278+
for _ in 0..20 {
279+
break 'block;
280+
}
281+
}
282+
println!("looped");
283+
}
284+
}
285+
286+
pub fn test24() {
287+
'a: for _ in 0..10 {
288+
'b: {
289+
let x = Some(1);
290+
match x {
291+
None => break 'a,
292+
Some(_) => break 'b,
293+
}
294+
}
295+
}
296+
}
297+
253298
fn main() {
254299
test1();
255300
test2();

tests/ui/never_loop.stderr

+14-1
Original file line numberDiff line numberDiff line change
@@ -126,5 +126,18 @@ LL | | }
126126
LL | | }
127127
| |_____^
128128

129-
error: aborting due to 11 previous errors
129+
error: this loop never actually loops
130+
--> $DIR/never_loop.rs:278:13
131+
|
132+
LL | / for _ in 0..20 {
133+
LL | | break 'block;
134+
LL | | }
135+
| |_____________^
136+
|
137+
help: if you need the first element of the iterator, try writing
138+
|
139+
LL | if let Some(_) = (0..20).next() {
140+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
141+
142+
error: aborting due to 12 previous errors
130143

0 commit comments

Comments
 (0)