Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 35 additions & 25 deletions clippy_lints/src/async_yields_async.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -87,31 +91,37 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
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,
);
},
);
}
}
}
20 changes: 17 additions & 3 deletions clippy_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
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};
Expand Down Expand Up @@ -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 changes: 39 additions & 0 deletions tests/ui/async_yields_async.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 39 additions & 0 deletions tests/ui/async_yields_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
47 changes: 46 additions & 1 deletion tests/ui/async_yields_async.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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