Skip to content

Commit f0514d9

Browse files
Fix async_yields_async wrongly unmangled macros (#15553)
Closes #15552 changelog: [`async_yields_async`] fix wrong unmangle of macros
2 parents b3d0a09 + 137feef commit f0514d9

File tree

5 files changed

+176
-29
lines changed

5 files changed

+176
-29
lines changed

clippy_lints/src/async_yields_async.rs

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use clippy_utils::diagnostics::span_lint_hir_and_then;
2-
use clippy_utils::source::snippet;
2+
use clippy_utils::is_expr_async_block;
3+
use clippy_utils::source::walk_span_to_context;
4+
use clippy_utils::sugg::Sugg;
35
use clippy_utils::ty::implements_trait;
46
use rustc_errors::Applicability;
5-
use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath};
7+
use rustc_hir::{
8+
Block, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath,
9+
};
610
use rustc_lint::{LateContext, LateLintPass};
711
use rustc_session::declare_lint_pass;
812

@@ -87,31 +91,37 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
8791
let expr_ty = typeck_results.expr_ty(body_expr);
8892

8993
if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
90-
let return_expr_span = match &body_expr.kind {
91-
// XXXkhuey there has to be a better way.
92-
ExprKind::Block(block, _) => block.expr.map(|e| e.span),
93-
ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span),
94-
_ => None,
94+
let (return_expr, return_expr_span) = match &body_expr.kind {
95+
ExprKind::Block(Block { expr: Some(e), .. }, _) => (*e, e.span),
96+
ExprKind::Path(QPath::Resolved(_, path)) => (body_expr, path.span),
97+
_ => return,
9598
};
96-
if let Some(return_expr_span) = return_expr_span {
97-
span_lint_hir_and_then(
98-
cx,
99-
ASYNC_YIELDS_ASYNC,
100-
body_expr.hir_id,
101-
return_expr_span,
102-
"an async construct yields a type which is itself awaitable",
103-
|db| {
104-
db.span_label(body_expr.span, "outer async construct");
105-
db.span_label(return_expr_span, "awaitable value not awaited");
106-
db.span_suggestion(
107-
return_expr_span,
108-
"consider awaiting this value",
109-
format!("{}.await", snippet(cx, return_expr_span, "..")),
110-
Applicability::MaybeIncorrect,
111-
);
112-
},
113-
);
99+
100+
let return_expr_span = walk_span_to_context(return_expr_span, expr.span.ctxt()).unwrap_or(return_expr_span);
101+
let mut applicability = Applicability::MaybeIncorrect;
102+
let mut return_expr_snip =
103+
Sugg::hir_with_context(cx, return_expr, expr.span.ctxt(), "..", &mut applicability);
104+
if !is_expr_async_block(return_expr) {
105+
return_expr_snip = return_expr_snip.maybe_paren();
114106
}
107+
108+
span_lint_hir_and_then(
109+
cx,
110+
ASYNC_YIELDS_ASYNC,
111+
body_expr.hir_id,
112+
return_expr_span,
113+
"an async construct yields a type which is itself awaitable",
114+
|db| {
115+
db.span_label(body_expr.span, "outer async construct");
116+
db.span_label(return_expr_span, "awaitable value not awaited");
117+
db.span_suggestion(
118+
return_expr_span,
119+
"consider awaiting this value",
120+
format!("{return_expr_snip}.await"),
121+
applicability,
122+
);
123+
},
124+
);
115125
}
116126
}
117127
}

clippy_utils/src/lib.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
102102
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
103103
use rustc_hir::{
104104
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
105-
CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
106-
ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
107-
Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
105+
CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
106+
HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
107+
OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
108108
TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
109109
};
110110
use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
@@ -3631,3 +3631,17 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>)
36313631
)
36323632
})
36333633
}
3634+
3635+
/// Checks if the expression is an async block (i.e., `async { ... }`).
3636+
pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
3637+
matches!(
3638+
expr.kind,
3639+
ExprKind::Closure(Closure {
3640+
kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
3641+
CoroutineDesugaring::Async,
3642+
CoroutineSource::Block
3643+
)),
3644+
..
3645+
})
3646+
)
3647+
}

tests/ui/async_yields_async.fixed

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,42 @@ fn check_expect_suppression() {
8080
}
8181
};
8282
}
83+
84+
#[allow(clippy::let_underscore_future)]
85+
fn issue15552() {
86+
async fn bar(i: i32) {}
87+
88+
macro_rules! call_bar {
89+
() => {
90+
async { bar(5).await }
91+
};
92+
($e:expr) => {
93+
bar($e)
94+
};
95+
}
96+
let x = async { call_bar!(5).await };
97+
//~^ async_yields_async
98+
let y = async { call_bar!().await };
99+
//~^ async_yields_async
100+
//~| async_yields_async
101+
102+
use std::future::{Future, Ready};
103+
use std::ops::Add;
104+
use std::pin::Pin;
105+
use std::task::{Context, Poll};
106+
struct CustomFutureType;
107+
impl Add for CustomFutureType {
108+
type Output = Self;
109+
fn add(self, other: Self) -> Self {
110+
self
111+
}
112+
}
113+
impl Future for CustomFutureType {
114+
type Output = ();
115+
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
116+
Poll::Ready(())
117+
}
118+
}
119+
let _ = async { (CustomFutureType + CustomFutureType).await };
120+
//~^ async_yields_async
121+
}

tests/ui/async_yields_async.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,42 @@ fn check_expect_suppression() {
8080
}
8181
};
8282
}
83+
84+
#[allow(clippy::let_underscore_future)]
85+
fn issue15552() {
86+
async fn bar(i: i32) {}
87+
88+
macro_rules! call_bar {
89+
() => {
90+
async { bar(5) }
91+
};
92+
($e:expr) => {
93+
bar($e)
94+
};
95+
}
96+
let x = async { call_bar!(5) };
97+
//~^ async_yields_async
98+
let y = async { call_bar!() };
99+
//~^ async_yields_async
100+
//~| async_yields_async
101+
102+
use std::future::{Future, Ready};
103+
use std::ops::Add;
104+
use std::pin::Pin;
105+
use std::task::{Context, Poll};
106+
struct CustomFutureType;
107+
impl Add for CustomFutureType {
108+
type Output = Self;
109+
fn add(self, other: Self) -> Self {
110+
self
111+
}
112+
}
113+
impl Future for CustomFutureType {
114+
type Output = ();
115+
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
116+
Poll::Ready(())
117+
}
118+
}
119+
let _ = async { CustomFutureType + CustomFutureType };
120+
//~^ async_yields_async
121+
}

tests/ui/async_yields_async.stderr

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,50 @@ LL | | CustomFutureType
8989
LL | | };
9090
| |_____- outer async construct
9191

92-
error: aborting due to 6 previous errors
92+
error: an async construct yields a type which is itself awaitable
93+
--> tests/ui/async_yields_async.rs:96:21
94+
|
95+
LL | let x = async { call_bar!(5) };
96+
| --^^^^^^^^^^^^--
97+
| | |
98+
| | awaitable value not awaited
99+
| | help: consider awaiting this value: `call_bar!(5).await`
100+
| outer async construct
101+
102+
error: an async construct yields a type which is itself awaitable
103+
--> tests/ui/async_yields_async.rs:98:21
104+
|
105+
LL | let y = async { call_bar!() };
106+
| --^^^^^^^^^^^--
107+
| | |
108+
| | awaitable value not awaited
109+
| | help: consider awaiting this value: `call_bar!().await`
110+
| outer async construct
111+
112+
error: an async construct yields a type which is itself awaitable
113+
--> tests/ui/async_yields_async.rs:90:21
114+
|
115+
LL | async { bar(5) }
116+
| --^^^^^^--
117+
| | |
118+
| | awaitable value not awaited
119+
| | help: consider awaiting this value: `bar(5).await`
120+
| outer async construct
121+
...
122+
LL | let y = async { call_bar!() };
123+
| ----------- in this macro invocation
124+
|
125+
= note: this error originates in the macro `call_bar` (in Nightly builds, run with -Z macro-backtrace for more info)
126+
127+
error: an async construct yields a type which is itself awaitable
128+
--> tests/ui/async_yields_async.rs:119:21
129+
|
130+
LL | let _ = async { CustomFutureType + CustomFutureType };
131+
| --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--
132+
| | |
133+
| | awaitable value not awaited
134+
| | help: consider awaiting this value: `(CustomFutureType + CustomFutureType).await`
135+
| outer async construct
136+
137+
error: aborting due to 10 previous errors
93138

0 commit comments

Comments
 (0)