Skip to content

Commit 05740ad

Browse files
committed
Auto merge of rust-lang#10807 - y21:issue10800, r=Jarcho
[`unused_async`]: do not consider `await` in nested `async` blocks as used Fixes rust-lang#10800. This PR makes sure that `await` expressions inside of inner `async` blocks don't prevent the lint from triggering. For example ```rs async fn foo() { async { std::future::ready(()).await; } } ``` Even though there *is* a `.await` expression in this function, it's contained in an async block, which means that the enclosing function doesn't need to be `async` too. changelog: [`unused_async`]: do not consider `await` in nested `async` blocks as used
2 parents 9374af1 + 8ef6240 commit 05740ad

File tree

3 files changed

+84
-11
lines changed

3 files changed

+84
-11
lines changed

clippy_lints/src/unused_async.rs

+43-7
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_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+
await_in_async_block: Option<Span>,
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 if self.await_in_async_block.is_none() {
59+
self.await_in_async_block = Some(ex.span);
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,16 +92,30 @@ 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+
await_in_async_block: None,
100+
};
74101
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id);
75102
if !visitor.found_await {
76-
span_lint_and_help(
103+
span_lint_and_then(
77104
cx,
78105
UNUSED_ASYNC,
79106
span,
80107
"unused `async` for function with no await statements",
81-
None,
82-
"consider removing the `async` from this function",
108+
|diag| {
109+
diag.help("consider removing the `async` from this function");
110+
111+
if let Some(span) = visitor.await_in_async_block {
112+
diag.span_note(
113+
span,
114+
"`await` used in an async block, which does not require \
115+
the enclosing function to be `async`",
116+
);
117+
}
118+
},
83119
);
84120
}
85121
}

tests/ui/unused_async.rs

+20
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

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,33 @@
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+
--> $DIR/unused_async.rs:13:23
14+
|
15+
LL | ready(()).await;
16+
| ^^^^^
17+
= note: `-D clippy::unused-async` implied by `-D warnings`
18+
19+
error: unused `async` for function with no await statements
20+
--> $DIR/unused_async.rs:26:1
321
|
422
LL | / async fn foo() -> i32 {
523
LL | | 4
624
LL | | }
725
| |_^
826
|
927
= help: consider removing the `async` from this function
10-
= note: `-D clippy::unused-async` implied by `-D warnings`
1128

1229
error: unused `async` for function with no await statements
13-
--> $DIR/unused_async.rs:17:5
30+
--> $DIR/unused_async.rs:37:5
1431
|
1532
LL | / async fn unused(&self) -> i32 {
1633
LL | | 1
@@ -19,5 +36,5 @@ LL | | }
1936
|
2037
= help: consider removing the `async` from this function
2138

22-
error: aborting due to 2 previous errors
39+
error: aborting due to 3 previous errors
2340

0 commit comments

Comments
 (0)