summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_lints/src/async_yields_async.rs
blob: 9464694a3b55ad16e0531810740e49fefd80f255 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet;
use clippy_utils::ty::implements_trait;
use rustc_errors::Applicability;
use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// ### What it does
    /// Checks for async blocks that yield values of types
    /// that can themselves be awaited.
    ///
    /// ### Why is this bad?
    /// An await is likely missing.
    ///
    /// ### Example
    /// ```rust
    /// async fn foo() {}
    ///
    /// fn bar() {
    ///   let x = async {
    ///     foo()
    ///   };
    /// }
    /// ```
    ///
    /// Use instead:
    /// ```rust
    /// async fn foo() {}
    ///
    /// fn bar() {
    ///   let x = async {
    ///     foo().await
    ///   };
    /// }
    /// ```
    #[clippy::version = "1.48.0"]
    pub ASYNC_YIELDS_ASYNC,
    correctness,
    "async blocks that return a type that can be awaited"
}

declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);

impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
        use AsyncGeneratorKind::{Block, Closure};
        // For functions, with explicitly defined types, don't warn.
        // XXXkhuey maybe we should?
        if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind {
            if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() {
                let body_id = BodyId {
                    hir_id: body.value.hir_id,
                };
                let typeck_results = cx.tcx.typeck_body(body_id);
                let expr_ty = typeck_results.expr_ty(body.value);

                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,
                            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,
                                );
                            },
                        );
                    }
                }
            }
        }
    }
}