fix: async_yields_async wrongly unmangled macros

This commit is contained in:
yanglsh
2025-08-25 08:01:37 +08:00
parent 55286e796f
commit 137feef308
5 changed files with 176 additions and 29 deletions
+35 -25
View File
@@ -1,8 +1,12 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet;
use clippy_utils::is_expr_async_block;
use clippy_utils::source::walk_span_to_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath};
use rustc_hir::{
Block, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@@ -87,31 +91,37 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let expr_ty = typeck_results.expr_ty(body_expr);
if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
let return_expr_span = match &body_expr.kind {
// XXXkhuey there has to be a better way.
ExprKind::Block(block, _) => block.expr.map(|e| e.span),
ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span),
_ => None,
let (return_expr, return_expr_span) = match &body_expr.kind {
ExprKind::Block(Block { expr: Some(e), .. }, _) => (*e, e.span),
ExprKind::Path(QPath::Resolved(_, path)) => (body_expr, path.span),
_ => return,
};
if let Some(return_expr_span) = return_expr_span {
span_lint_hir_and_then(
cx,
ASYNC_YIELDS_ASYNC,
body_expr.hir_id,
return_expr_span,
"an async construct yields a type which is itself awaitable",
|db| {
db.span_label(body_expr.span, "outer async construct");
db.span_label(return_expr_span, "awaitable value not awaited");
db.span_suggestion(
return_expr_span,
"consider awaiting this value",
format!("{}.await", snippet(cx, return_expr_span, "..")),
Applicability::MaybeIncorrect,
);
},
);
let return_expr_span = walk_span_to_context(return_expr_span, expr.span.ctxt()).unwrap_or(return_expr_span);
let mut applicability = Applicability::MaybeIncorrect;
let mut return_expr_snip =
Sugg::hir_with_context(cx, return_expr, expr.span.ctxt(), "..", &mut applicability);
if !is_expr_async_block(return_expr) {
return_expr_snip = return_expr_snip.maybe_paren();
}
span_lint_hir_and_then(
cx,
ASYNC_YIELDS_ASYNC,
body_expr.hir_id,
return_expr_span,
"an async construct yields a type which is itself awaitable",
|db| {
db.span_label(body_expr.span, "outer async construct");
db.span_label(return_expr_span, "awaitable value not awaited");
db.span_suggestion(
return_expr_span,
"consider awaiting this value",
format!("{return_expr_snip}.await"),
applicability,
);
},
);
}
}
}
+17 -3
View File
@@ -102,9 +102,9 @@
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
use rustc_hir::{
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
};
use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
@@ -3632,3 +3632,17 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>)
)
})
}
/// Checks if the expression is an async block (i.e., `async { ... }`).
pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
matches!(
expr.kind,
ExprKind::Closure(Closure {
kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
CoroutineDesugaring::Async,
CoroutineSource::Block
)),
..
})
)
}
+39
View File
@@ -80,3 +80,42 @@ fn check_expect_suppression() {
}
};
}
#[allow(clippy::let_underscore_future)]
fn issue15552() {
async fn bar(i: i32) {}
macro_rules! call_bar {
() => {
async { bar(5).await }
};
($e:expr) => {
bar($e)
};
}
let x = async { call_bar!(5).await };
//~^ async_yields_async
let y = async { call_bar!().await };
//~^ async_yields_async
//~| async_yields_async
use std::future::{Future, Ready};
use std::ops::Add;
use std::pin::Pin;
use std::task::{Context, Poll};
struct CustomFutureType;
impl Add for CustomFutureType {
type Output = Self;
fn add(self, other: Self) -> Self {
self
}
}
impl Future for CustomFutureType {
type Output = ();
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(())
}
}
let _ = async { (CustomFutureType + CustomFutureType).await };
//~^ async_yields_async
}
+39
View File
@@ -80,3 +80,42 @@ fn check_expect_suppression() {
}
};
}
#[allow(clippy::let_underscore_future)]
fn issue15552() {
async fn bar(i: i32) {}
macro_rules! call_bar {
() => {
async { bar(5) }
};
($e:expr) => {
bar($e)
};
}
let x = async { call_bar!(5) };
//~^ async_yields_async
let y = async { call_bar!() };
//~^ async_yields_async
//~| async_yields_async
use std::future::{Future, Ready};
use std::ops::Add;
use std::pin::Pin;
use std::task::{Context, Poll};
struct CustomFutureType;
impl Add for CustomFutureType {
type Output = Self;
fn add(self, other: Self) -> Self {
self
}
}
impl Future for CustomFutureType {
type Output = ();
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(())
}
}
let _ = async { CustomFutureType + CustomFutureType };
//~^ async_yields_async
}
+46 -1
View File
@@ -89,5 +89,50 @@ LL | | CustomFutureType
LL | | };
| |_____- outer async construct
error: aborting due to 6 previous errors
error: an async construct yields a type which is itself awaitable
--> tests/ui/async_yields_async.rs:96:21
|
LL | let x = async { call_bar!(5) };
| --^^^^^^^^^^^^--
| | |
| | awaitable value not awaited
| | help: consider awaiting this value: `call_bar!(5).await`
| outer async construct
error: an async construct yields a type which is itself awaitable
--> tests/ui/async_yields_async.rs:98:21
|
LL | let y = async { call_bar!() };
| --^^^^^^^^^^^--
| | |
| | awaitable value not awaited
| | help: consider awaiting this value: `call_bar!().await`
| outer async construct
error: an async construct yields a type which is itself awaitable
--> tests/ui/async_yields_async.rs:90:21
|
LL | async { bar(5) }
| --^^^^^^--
| | |
| | awaitable value not awaited
| | help: consider awaiting this value: `bar(5).await`
| outer async construct
...
LL | let y = async { call_bar!() };
| ----------- in this macro invocation
|
= note: this error originates in the macro `call_bar` (in Nightly builds, run with -Z macro-backtrace for more info)
error: an async construct yields a type which is itself awaitable
--> tests/ui/async_yields_async.rs:119:21
|
LL | let _ = async { CustomFutureType + CustomFutureType };
| --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--
| | |
| | awaitable value not awaited
| | help: consider awaiting this value: `(CustomFutureType + CustomFutureType).await`
| outer async construct
error: aborting due to 10 previous errors