Skip to content

Commit 1e73a9e

Browse files
committed
do not consider await in nested async blocks
1 parent 435a8ad commit 1e73a9e

File tree

3 files changed

+81
-8
lines changed

3 files changed

+81
-8
lines changed

clippy_lints/src/unused_async.rs

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use clippy_utils::diagnostics::span_lint_and_help;
2-
use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
1+
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
2+
use rustc_hir::intravisit::{walk_body, walk_expr, walk_fn, FnKind, Visitor};
33
use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource};
44
use rustc_lint::{LateContext, LateLintPass};
55
use rustc_middle::hir::nested_filter;
@@ -42,21 +42,43 @@ declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
4242
struct AsyncFnVisitor<'a, 'tcx> {
4343
cx: &'a LateContext<'tcx>,
4444
found_await: bool,
45+
/// Also keep track of `await`s in nested async blocks so we can mention
46+
/// it in a note
47+
found_await_in_async_block: bool,
48+
async_depth: usize,
4549
}
4650

4751
impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
4852
type NestedFilter = nested_filter::OnlyBodies;
4953

5054
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
5155
if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind {
52-
self.found_await = true;
56+
if self.async_depth == 1 {
57+
self.found_await = true;
58+
} else {
59+
self.found_await_in_async_block = true;
60+
}
5361
}
5462
walk_expr(self, ex);
5563
}
5664

5765
fn nested_visit_map(&mut self) -> Self::Map {
5866
self.cx.tcx.hir()
5967
}
68+
69+
fn visit_body(&mut self, b: &'tcx Body<'tcx>) {
70+
let is_async_block = matches!(b.generator_kind, Some(rustc_hir::GeneratorKind::Async(_)));
71+
72+
if is_async_block {
73+
self.async_depth += 1;
74+
}
75+
76+
walk_body(self, b);
77+
78+
if is_async_block {
79+
self.async_depth -= 1;
80+
}
81+
}
6082
}
6183

6284
impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
@@ -70,9 +92,27 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
7092
def_id: LocalDefId,
7193
) {
7294
if !span.from_expansion() && fn_kind.asyncness().is_async() {
73-
let mut visitor = AsyncFnVisitor { cx, found_await: false };
95+
let mut visitor = AsyncFnVisitor {
96+
cx,
97+
found_await: false,
98+
async_depth: 0,
99+
found_await_in_async_block: false,
100+
};
74101
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id);
75102
if !visitor.found_await {
103+
span_lint_and_then(
104+
cx,
105+
UNUSED_ASYNC,
106+
span,
107+
"unused `async` for function with no await statements",
108+
|diag| {
109+
diag.help("consider removing the `async` from this function");
110+
111+
if visitor.found_await_in_async_block {
112+
diag.note("`await` used in an async block, which does not require the enclosing function to be `async`");
113+
}
114+
},
115+
);
76116
span_lint_and_help(
77117
cx,
78118
UNUSED_ASYNC,

tests/ui/unused_async.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,26 @@
33
use std::future::Future;
44
use std::pin::Pin;
55

6+
mod issue10800 {
7+
#![allow(dead_code, unused_must_use, clippy::no_effect)]
8+
9+
use std::future::ready;
10+
11+
async fn async_block_await() {
12+
async {
13+
ready(()).await;
14+
};
15+
}
16+
17+
async fn normal_block_await() {
18+
{
19+
{
20+
ready(()).await;
21+
}
22+
}
23+
}
24+
}
25+
626
async fn foo() -> i32 {
727
4
828
}

tests/ui/unused_async.stderr

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
11
error: unused `async` for function with no await statements
2-
--> $DIR/unused_async.rs:6:1
2+
--> $DIR/unused_async.rs:11:5
3+
|
4+
LL | / async fn async_block_await() {
5+
LL | | async {
6+
LL | | ready(()).await;
7+
LL | | };
8+
LL | | }
9+
| |_____^
10+
|
11+
= help: consider removing the `async` from this function
12+
= note: `await` used in an async block, which does not require the enclosing function to be `async`
13+
= note: `-D clippy::unused-async` implied by `-D warnings`
14+
15+
error: unused `async` for function with no await statements
16+
--> $DIR/unused_async.rs:26:1
317
|
418
LL | / async fn foo() -> i32 {
519
LL | | 4
620
LL | | }
721
| |_^
822
|
923
= help: consider removing the `async` from this function
10-
= note: `-D clippy::unused-async` implied by `-D warnings`
1124

1225
error: unused `async` for function with no await statements
13-
--> $DIR/unused_async.rs:17:5
26+
--> $DIR/unused_async.rs:37:5
1427
|
1528
LL | / async fn unused(&self) -> i32 {
1629
LL | | 1
@@ -19,5 +32,5 @@ LL | | }
1932
|
2033
= help: consider removing the `async` from this function
2134

22-
error: aborting due to 2 previous errors
35+
error: aborting due to 3 previous errors
2336

0 commit comments

Comments
 (0)