1 use clippy_utils::diagnostics::span_lint_hir_and_then;
2 use clippy_utils::source::snippet;
3 use clippy_utils::ty::implements_trait;
4 use rustc_errors::Applicability;
5 use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
11 /// Checks for async blocks that yield values of types
12 /// that can themselves be awaited.
14 /// ### Why is this bad?
15 /// An await is likely missing.
38 #[clippy::version = "1.48.0"]
39 pub ASYNC_YIELDS_ASYNC,
41 "async blocks that return a type that can be awaited"
44 declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);
46 impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
47 fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
48 use AsyncGeneratorKind::{Block, Closure};
49 // For functions, with explicitly defined types, don't warn.
50 // XXXkhuey maybe we should?
51 if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind {
52 if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() {
53 let body_id = BodyId {
54 hir_id: body.value.hir_id,
56 let typeck_results = cx.tcx.typeck_body(body_id);
57 let expr_ty = typeck_results.expr_ty(&body.value);
59 if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
60 let return_expr_span = match &body.value.kind {
61 // XXXkhuey there has to be a better way.
62 ExprKind::Block(block, _) => block.expr.map(|e| e.span),
63 ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span),
66 if let Some(return_expr_span) = return_expr_span {
67 span_lint_hir_and_then(
72 "an async construct yields a type which is itself awaitable",
74 db.span_label(body.value.span, "outer async construct");
75 db.span_label(return_expr_span, "awaitable value not awaited");
78 "consider awaiting this value",
79 format!("{}.await", snippet(cx, return_expr_span, "..")),
80 Applicability::MaybeIncorrect,