From 04a5ee6f1d6ab1270b95c6c91a718d900a762eb8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 15 Jan 2024 19:38:53 +0000 Subject: [PATCH] Deal with additional wrapping of async closure body in clippy --- .../clippy_lints/src/async_yields_async.rs | 98 ++++++++++++------- .../src/redundant_closure_call.rs | 20 +++- .../clippy/tests/ui/author/blocks.stdout | 6 +- 3 files changed, 82 insertions(+), 42 deletions(-) diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs index bb08ac7508bc2..eeaa3de3725fb 100644 --- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs +++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs @@ -45,50 +45,72 @@ declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]); impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - // For functions, with explicitly defined types, don't warn. - // XXXkhuey maybe we should? - if let ExprKind::Closure(Closure { - kind: - ClosureKind::Coroutine(CoroutineKind::Desugared( - CoroutineDesugaring::Async, - CoroutineSource::Block | CoroutineSource::Closure, - )), + let ExprKind::Closure(Closure { + kind: ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::Async, kind)), body: body_id, .. }) = expr.kind - { - if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() { - let typeck_results = cx.tcx.typeck_body(*body_id); - let body = cx.tcx.hir().body(*body_id); - let expr_ty = typeck_results.expr_ty(body.value); + else { + return; + }; - if implements_trait(cx, expr_ty, future_trait_def_id, &[]) { - let return_expr_span = match &body.value.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, - }; - if let Some(return_expr_span) = return_expr_span { - span_lint_hir_and_then( - cx, - ASYNC_YIELDS_ASYNC, - body.value.hir_id, + let body_expr = match kind { + CoroutineSource::Fn => { + // For functions, with explicitly defined types, don't warn. + // XXXkhuey maybe we should? + return; + }, + CoroutineSource::Block => cx.tcx.hir().body(*body_id).value, + CoroutineSource::Closure => { + // Like `async fn`, async closures are wrapped in an additional block + // to move all of the closure's arguments into the future. + + let async_closure_body = cx.tcx.hir().body(*body_id).value; + let ExprKind::Block(block, _) = async_closure_body.kind else { + return; + }; + let Some(block_expr) = block.expr else { + return; + }; + let ExprKind::DropTemps(body_expr) = block_expr.kind else { + return; + }; + body_expr + }, + }; + + let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() else { + return; + }; + + let typeck_results = cx.tcx.typeck_body(*body_id); + 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, + }; + 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, - "an async construct yields a type which is itself awaitable", - |db| { - db.span_label(body.value.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, - ); - }, + "consider awaiting this value", + format!("{}.await", snippet(cx, return_expr_span, "..")), + Applicability::MaybeIncorrect, ); - } - } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index cde08dfcc748d..334e6770ae407 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -5,7 +5,9 @@ use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; -use rustc_hir::{intravisit as hir_visit, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Node}; +use rustc_hir::{ + intravisit as hir_visit, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; @@ -166,10 +168,22 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { if coroutine_kind.is_async() && let hir::ExprKind::Closure(closure) = body.kind { - let async_closure_body = cx.tcx.hir().body(closure.body); + // Like `async fn`, async closures are wrapped in an additional block + // to move all of the closure's arguments into the future. + + let async_closure_body = cx.tcx.hir().body(closure.body).value; + let ExprKind::Block(block, _) = async_closure_body.kind else { + return; + }; + let Some(block_expr) = block.expr else { + return; + }; + let ExprKind::DropTemps(body_expr) = block_expr.kind else { + return; + }; // `async x` is a syntax error, so it becomes `async { x }` - if !matches!(async_closure_body.value.kind, hir::ExprKind::Block(_, _)) { + if !matches!(body_expr.kind, hir::ExprKind::Block(_, _)) { hint = hint.blockify(); } diff --git a/src/tools/clippy/tests/ui/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout index 62de661f8ff86..8c4d71e68f80f 100644 --- a/src/tools/clippy/tests/ui/author/blocks.stdout +++ b/src/tools/clippy/tests/ui/author/blocks.stdout @@ -48,7 +48,11 @@ if let ExprKind::Closure { capture_clause: CaptureBy::Value { .. }, fn_decl: fn_ && expr2 = &cx.tcx.hir().body(body_id1).value && let ExprKind::Block(block, None) = expr2.kind && block.stmts.is_empty() - && block.expr.is_none() + && let Some(trailing_expr) = block.expr + && let ExprKind::DropTemps(expr3) = trailing_expr.kind + && let ExprKind::Block(block1, None) = expr3.kind + && block1.stmts.is_empty() + && block1.expr.is_none() { // report your lint here }