Skip to content

Commit ff65eec

Browse files
committed
Auto merge of #9496 - yotamofek:never_loop_let_else, r=Jarcho
[`never_loop`]: Fix FP with let..else statements. Fixes #9356 This has been bugging me for a while, so I thought I'd take a stab at it! I'm completely uncertain about the quality of my code, but I think it's an alright start, so opening this PR to get some feedback from more experienced clippy people :) changelog: [`never_loop`]: Fix FP with let..else statements
2 parents c8f2f38 + d63aece commit ff65eec

File tree

3 files changed

+71
-20
lines changed

3 files changed

+71
-20
lines changed

clippy_lints/src/loops/never_loop.rs

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub(super) fn check(
4242
}
4343
}
4444

45+
#[derive(Copy, Clone)]
4546
enum NeverLoopResult {
4647
// A break/return always get triggered but not necessarily for the main loop.
4748
AlwaysBreak,
@@ -51,8 +52,8 @@ enum NeverLoopResult {
5152
}
5253

5354
#[must_use]
54-
fn absorb_break(arg: &NeverLoopResult) -> NeverLoopResult {
55-
match *arg {
55+
fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
56+
match arg {
5657
NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
5758
NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
5859
}
@@ -92,19 +93,29 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
9293
}
9394

9495
fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
95-
let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr);
96+
let mut iter = block
97+
.stmts
98+
.iter()
99+
.filter_map(stmt_to_expr)
100+
.chain(block.expr.map(|expr| (expr, None)));
96101
never_loop_expr_seq(&mut iter, main_loop_id)
97102
}
98103

99-
fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
100-
es.map(|e| never_loop_expr(e, main_loop_id))
101-
.fold(NeverLoopResult::Otherwise, combine_seq)
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)))
111+
})
112+
.fold(NeverLoopResult::Otherwise, combine_seq)
102113
}
103114

104-
fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
115+
fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> {
105116
match stmt.kind {
106-
StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e),
107-
StmtKind::Local(local) => local.init,
117+
StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)),
118+
StmtKind::Local(local) => local.init.map(|init| (init, local.els)),
108119
StmtKind::Item(..) => None,
109120
}
110121
}
@@ -139,7 +150,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
139150
| ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
140151
ExprKind::Loop(b, _, _, _) => {
141152
// Break can come from the inner loop so remove them.
142-
absorb_break(&never_loop_block(b, main_loop_id))
153+
absorb_break(never_loop_block(b, main_loop_id))
143154
},
144155
ExprKind::If(e, e2, e3) => {
145156
let e1 = never_loop_expr(e, main_loop_id);

tests/ui/never_loop.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![feature(let_else)]
12
#![allow(
23
clippy::single_match,
34
unused_assignments,
@@ -203,6 +204,32 @@ pub fn test17() {
203204
};
204205
}
205206

207+
// Issue #9356: `continue` in else branch of let..else
208+
pub fn test18() {
209+
let x = Some(0);
210+
let y = 0;
211+
// might loop
212+
let _ = loop {
213+
let Some(x) = x else {
214+
if y > 0 {
215+
continue;
216+
} else {
217+
return;
218+
}
219+
};
220+
221+
break x;
222+
};
223+
// never loops
224+
let _ = loop {
225+
let Some(x) = x else {
226+
return;
227+
};
228+
229+
break x;
230+
};
231+
}
232+
206233
fn main() {
207234
test1();
208235
test2();

tests/ui/never_loop.stderr

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: this loop never actually loops
2-
--> $DIR/never_loop.rs:10:5
2+
--> $DIR/never_loop.rs:11:5
33
|
44
LL | / loop {
55
LL | | // clippy::never_loop
@@ -13,7 +13,7 @@ LL | | }
1313
= note: `#[deny(clippy::never_loop)]` on by default
1414

1515
error: this loop never actually loops
16-
--> $DIR/never_loop.rs:32:5
16+
--> $DIR/never_loop.rs:33:5
1717
|
1818
LL | / loop {
1919
LL | | // never loops
@@ -23,7 +23,7 @@ LL | | }
2323
| |_____^
2424

2525
error: this loop never actually loops
26-
--> $DIR/never_loop.rs:52:5
26+
--> $DIR/never_loop.rs:53:5
2727
|
2828
LL | / loop {
2929
LL | | // never loops
@@ -35,7 +35,7 @@ LL | | }
3535
| |_____^
3636

3737
error: this loop never actually loops
38-
--> $DIR/never_loop.rs:54:9
38+
--> $DIR/never_loop.rs:55:9
3939
|
4040
LL | / while i == 0 {
4141
LL | | // never loops
@@ -44,7 +44,7 @@ LL | | }
4444
| |_________^
4545

4646
error: this loop never actually loops
47-
--> $DIR/never_loop.rs:66:9
47+
--> $DIR/never_loop.rs:67:9
4848
|
4949
LL | / loop {
5050
LL | | // never loops
@@ -56,7 +56,7 @@ LL | | }
5656
| |_________^
5757

5858
error: this loop never actually loops
59-
--> $DIR/never_loop.rs:102:5
59+
--> $DIR/never_loop.rs:103:5
6060
|
6161
LL | / while let Some(y) = x {
6262
LL | | // never loops
@@ -65,7 +65,7 @@ LL | | }
6565
| |_____^
6666

6767
error: this loop never actually loops
68-
--> $DIR/never_loop.rs:109:5
68+
--> $DIR/never_loop.rs:110:5
6969
|
7070
LL | / for x in 0..10 {
7171
LL | | // never loops
@@ -82,7 +82,7 @@ LL | if let Some(x) = (0..10).next() {
8282
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8383

8484
error: this loop never actually loops
85-
--> $DIR/never_loop.rs:157:5
85+
--> $DIR/never_loop.rs:158:5
8686
|
8787
LL | / 'outer: while a {
8888
LL | | // never loops
@@ -94,12 +94,25 @@ LL | | }
9494
| |_____^
9595

9696
error: this loop never actually loops
97-
--> $DIR/never_loop.rs:172:9
97+
--> $DIR/never_loop.rs:173:9
9898
|
9999
LL | / while false {
100100
LL | | break 'label;
101101
LL | | }
102102
| |_________^
103103

104-
error: aborting due to 9 previous errors
104+
error: this loop never actually loops
105+
--> $DIR/never_loop.rs:224:13
106+
|
107+
LL | let _ = loop {
108+
| _____________^
109+
LL | | let Some(x) = x else {
110+
LL | | return;
111+
LL | | };
112+
LL | |
113+
LL | | break x;
114+
LL | | };
115+
| |_____^
116+
117+
error: aborting due to 10 previous errors
105118

0 commit comments

Comments
 (0)