Skip to content

Commit e4dc892

Browse files
committed
Auto merge of rust-lang#13608 - J-ZhengLi:issue12338-new, r=Alexendoo
[`infinite_loops`]: fix incorrect suggestions on async functions/closures closes: rust-lang#12338 I intend to fix this in rust-lang#12421 but got distracted by some other problems in the same lint, delaying the process of closing the actual issue. So here's a separated PR that only focus on the issue and nothing else. --- changelog: [`infinite_loops`]: fix suggestion error on async functions/closures
2 parents c782988 + c4815ae commit e4dc892

File tree

3 files changed

+95
-29
lines changed

3 files changed

+95
-29
lines changed

clippy_lints/src/loops/infinite_loop.rs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::{fn_def_id, is_from_proc_macro, is_lint_allowed};
33
use hir::intravisit::{Visitor, walk_expr};
4-
use hir::{Expr, ExprKind, FnRetTy, FnSig, Node};
4+
use hir::{Expr, ExprKind, FnRetTy, FnSig, Node, TyKind};
55
use rustc_ast::Label;
66
use rustc_errors::Applicability;
77
use rustc_hir as hir;
88
use rustc_lint::{LateContext, LintContext};
99
use rustc_middle::lint::in_external_macro;
10+
use rustc_span::sym;
1011

1112
use super::INFINITE_LOOP;
1213

@@ -25,13 +26,7 @@ pub(super) fn check<'tcx>(
2526
return;
2627
};
2728
// Or, its parent function is already returning `Never`
28-
if matches!(
29-
parent_fn_ret,
30-
FnRetTy::Return(hir::Ty {
31-
kind: hir::TyKind::Never,
32-
..
33-
})
34-
) {
29+
if is_never_return(parent_fn_ret) {
3530
return;
3631
}
3732

@@ -69,6 +64,16 @@ pub(super) fn check<'tcx>(
6964
fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<FnRetTy<'tcx>> {
7065
for (_, parent_node) in cx.tcx.hir().parent_iter(expr.hir_id) {
7166
match parent_node {
67+
// Skip `Coroutine` closures, these are the body of `async fn`, not async closures.
68+
// This is because we still need to backtrack one parent node to get the `OpaqueDef` ty.
69+
Node::Expr(Expr {
70+
kind:
71+
ExprKind::Closure(hir::Closure {
72+
kind: hir::ClosureKind::Coroutine(_),
73+
..
74+
}),
75+
..
76+
}) => (),
7277
Node::Item(hir::Item {
7378
kind: hir::ItemKind::Fn(FnSig { decl, .. }, _, _),
7479
..
@@ -143,3 +148,44 @@ impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> {
143148
}
144149
}
145150
}
151+
152+
/// Return `true` if the given [`FnRetTy`] is never (!).
153+
///
154+
/// Note: This function also take care of return type of async fn,
155+
/// as the actual type is behind an [`OpaqueDef`](TyKind::OpaqueDef).
156+
fn is_never_return(ret_ty: FnRetTy<'_>) -> bool {
157+
let FnRetTy::Return(hir_ty) = ret_ty else { return false };
158+
159+
match hir_ty.kind {
160+
TyKind::Never => true,
161+
TyKind::OpaqueDef(
162+
hir::OpaqueTy {
163+
origin: hir::OpaqueTyOrigin::AsyncFn { .. },
164+
bounds,
165+
..
166+
},
167+
_,
168+
) => {
169+
if let Some(trait_ref) = bounds.iter().find_map(|b| b.trait_ref())
170+
&& let Some(segment) = trait_ref
171+
.path
172+
.segments
173+
.iter()
174+
.find(|seg| seg.ident.name == sym::future_trait)
175+
&& let Some(args) = segment.args
176+
&& let Some(cst_kind) = args
177+
.constraints
178+
.iter()
179+
.find_map(|cst| (cst.ident.name == sym::Output).then_some(cst.kind))
180+
&& let hir::AssocItemConstraintKind::Equality {
181+
term: hir::Term::Ty(ty),
182+
} = cst_kind
183+
{
184+
matches!(ty.kind, TyKind::Never)
185+
} else {
186+
false
187+
}
188+
},
189+
_ => false,
190+
}
191+
}

tests/ui/infinite_loops.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#![allow(clippy::never_loop)]
55
#![warn(clippy::infinite_loop)]
6+
#![feature(async_closure)]
67

78
extern crate proc_macros;
89
use proc_macros::{external, with_span};
@@ -428,4 +429,23 @@ fn continue_outer() {
428429
}
429430
}
430431

432+
// don't suggest adding `-> !` to async fn/closure that already returning `-> !`
433+
mod issue_12338 {
434+
use super::do_something;
435+
436+
async fn foo() -> ! {
437+
loop {
438+
do_something();
439+
}
440+
}
441+
442+
fn bar() {
443+
let _ = async || -> ! {
444+
loop {
445+
do_something();
446+
}
447+
};
448+
}
449+
}
450+
431451
fn main() {}

tests/ui/infinite_loops.stderr

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: infinite loop detected
2-
--> tests/ui/infinite_loops.rs:13:5
2+
--> tests/ui/infinite_loops.rs:14:5
33
|
44
LL | / loop {
55
LL | |
@@ -15,7 +15,7 @@ LL | fn no_break() -> ! {
1515
| ++++
1616

1717
error: infinite loop detected
18-
--> tests/ui/infinite_loops.rs:20:5
18+
--> tests/ui/infinite_loops.rs:21:5
1919
|
2020
LL | / loop {
2121
LL | |
@@ -32,7 +32,7 @@ LL | fn all_inf() -> ! {
3232
| ++++
3333

3434
error: infinite loop detected
35-
--> tests/ui/infinite_loops.rs:22:9
35+
--> tests/ui/infinite_loops.rs:23:9
3636
|
3737
LL | / loop {
3838
LL | |
@@ -49,7 +49,7 @@ LL | fn all_inf() -> ! {
4949
| ++++
5050

5151
error: infinite loop detected
52-
--> tests/ui/infinite_loops.rs:24:13
52+
--> tests/ui/infinite_loops.rs:25:13
5353
|
5454
LL | / loop {
5555
LL | |
@@ -63,7 +63,7 @@ LL | fn all_inf() -> ! {
6363
| ++++
6464

6565
error: infinite loop detected
66-
--> tests/ui/infinite_loops.rs:38:5
66+
--> tests/ui/infinite_loops.rs:39:5
6767
|
6868
LL | / loop {
6969
LL | |
@@ -74,7 +74,7 @@ LL | | }
7474
= help: if this is not intended, try adding a `break` or `return` condition in the loop
7575

7676
error: infinite loop detected
77-
--> tests/ui/infinite_loops.rs:51:5
77+
--> tests/ui/infinite_loops.rs:52:5
7878
|
7979
LL | / loop {
8080
LL | | fn inner_fn() -> ! {
@@ -90,7 +90,7 @@ LL | fn no_break_never_ret_noise() -> ! {
9090
| ++++
9191

9292
error: infinite loop detected
93-
--> tests/ui/infinite_loops.rs:94:5
93+
--> tests/ui/infinite_loops.rs:95:5
9494
|
9595
LL | / loop {
9696
LL | |
@@ -107,7 +107,7 @@ LL | fn break_inner_but_not_outer_1(cond: bool) -> ! {
107107
| ++++
108108

109109
error: infinite loop detected
110-
--> tests/ui/infinite_loops.rs:105:5
110+
--> tests/ui/infinite_loops.rs:106:5
111111
|
112112
LL | / loop {
113113
LL | |
@@ -124,7 +124,7 @@ LL | fn break_inner_but_not_outer_2(cond: bool) -> ! {
124124
| ++++
125125

126126
error: infinite loop detected
127-
--> tests/ui/infinite_loops.rs:119:9
127+
--> tests/ui/infinite_loops.rs:120:9
128128
|
129129
LL | / loop {
130130
LL | |
@@ -138,7 +138,7 @@ LL | fn break_outer_but_not_inner() -> ! {
138138
| ++++
139139

140140
error: infinite loop detected
141-
--> tests/ui/infinite_loops.rs:142:9
141+
--> tests/ui/infinite_loops.rs:143:9
142142
|
143143
LL | / loop {
144144
LL | |
@@ -155,7 +155,7 @@ LL | fn break_wrong_loop(cond: bool) -> ! {
155155
| ++++
156156

157157
error: infinite loop detected
158-
--> tests/ui/infinite_loops.rs:182:5
158+
--> tests/ui/infinite_loops.rs:183:5
159159
|
160160
LL | / loop {
161161
LL | |
@@ -172,7 +172,7 @@ LL | fn match_like() -> ! {
172172
| ++++
173173

174174
error: infinite loop detected
175-
--> tests/ui/infinite_loops.rs:223:5
175+
--> tests/ui/infinite_loops.rs:224:5
176176
|
177177
LL | / loop {
178178
LL | |
@@ -186,7 +186,7 @@ LL | fn match_like() -> ! {
186186
| ++++
187187

188188
error: infinite loop detected
189-
--> tests/ui/infinite_loops.rs:228:5
189+
--> tests/ui/infinite_loops.rs:229:5
190190
|
191191
LL | / loop {
192192
LL | |
@@ -203,7 +203,7 @@ LL | fn match_like() -> ! {
203203
| ++++
204204

205205
error: infinite loop detected
206-
--> tests/ui/infinite_loops.rs:333:9
206+
--> tests/ui/infinite_loops.rs:334:9
207207
|
208208
LL | / loop {
209209
LL | |
@@ -217,7 +217,7 @@ LL | fn problematic_trait_method() -> ! {
217217
| ++++
218218

219219
error: infinite loop detected
220-
--> tests/ui/infinite_loops.rs:343:9
220+
--> tests/ui/infinite_loops.rs:344:9
221221
|
222222
LL | / loop {
223223
LL | |
@@ -231,7 +231,7 @@ LL | fn could_be_problematic() -> ! {
231231
| ++++
232232

233233
error: infinite loop detected
234-
--> tests/ui/infinite_loops.rs:352:9
234+
--> tests/ui/infinite_loops.rs:353:9
235235
|
236236
LL | / loop {
237237
LL | |
@@ -245,7 +245,7 @@ LL | let _loop_forever = || -> ! {
245245
| ++++
246246

247247
error: infinite loop detected
248-
--> tests/ui/infinite_loops.rs:366:8
248+
--> tests/ui/infinite_loops.rs:367:8
249249
|
250250
LL | Ok(loop {
251251
| ________^
@@ -256,7 +256,7 @@ LL | | })
256256
= help: if this is not intended, try adding a `break` or `return` condition in the loop
257257

258258
error: infinite loop detected
259-
--> tests/ui/infinite_loops.rs:408:5
259+
--> tests/ui/infinite_loops.rs:409:5
260260
|
261261
LL | / 'infinite: loop {
262262
LL | |
@@ -272,7 +272,7 @@ LL | fn continue_outer() -> ! {
272272
| ++++
273273

274274
error: infinite loop detected
275-
--> tests/ui/infinite_loops.rs:415:5
275+
--> tests/ui/infinite_loops.rs:416:5
276276
|
277277
LL | / loop {
278278
LL | |
@@ -289,7 +289,7 @@ LL | fn continue_outer() -> ! {
289289
| ++++
290290

291291
error: infinite loop detected
292-
--> tests/ui/infinite_loops.rs:417:9
292+
--> tests/ui/infinite_loops.rs:418:9
293293
|
294294
LL | / 'inner: loop {
295295
LL | | loop {
@@ -304,7 +304,7 @@ LL | fn continue_outer() -> ! {
304304
| ++++
305305

306306
error: infinite loop detected
307-
--> tests/ui/infinite_loops.rs:425:5
307+
--> tests/ui/infinite_loops.rs:426:5
308308
|
309309
LL | / loop {
310310
LL | |

0 commit comments

Comments
 (0)