]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/async_yields_async.rs
Add a lint for an async block/closure that yields a type that is itself awaitable.
[rust.git] / clippy_lints / src / async_yields_async.rs
1 use crate::utils::{implements_trait, snippet, span_lint_and_then};
2 use rustc_errors::Applicability;
3 use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath};
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_session::{declare_lint_pass, declare_tool_lint};
6
7 declare_clippy_lint! {
8     /// **What it does:**
9     /// Checks for async blocks that yield values of types that can themselves
10     /// be awaited.
11     ///
12     /// **Why is this bad?**
13     /// An await is likely missing.
14     ///
15     /// **Known problems:** None.
16     ///
17     /// **Example:**
18     ///
19     /// ```rust
20     /// async fn foo() {}
21     ///
22     /// fn bar() {
23     ///   let x = async {
24     ///     foo()
25     ///   };
26     /// }
27     /// ```
28     /// Use instead:
29     /// ```rust
30     /// async fn foo() {}
31     ///
32     /// fn bar() {
33     ///   let x = async {
34     ///     foo().await
35     ///   };
36     /// }
37     /// ```
38     pub ASYNC_YIELDS_ASYNC,
39     correctness,
40     "async blocks that return a type that can be awaited"
41 }
42
43 declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);
44
45 impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
46     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
47         use AsyncGeneratorKind::{Block, Closure};
48         // For functions, with explicitly defined types, don't warn.
49         // XXXkhuey maybe we should?
50         if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind {
51             if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() {
52                 let body_id = BodyId {
53                     hir_id: body.value.hir_id,
54                 };
55                 let def_id = cx.tcx.hir().body_owner_def_id(body_id);
56                 let typeck_results = cx.tcx.typeck(def_id);
57                 let expr_ty = typeck_results.expr_ty(&body.value);
58
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),
64                         _ => None,
65                     };
66                     if let Some(return_expr_span) = return_expr_span {
67                         span_lint_and_then(
68                             cx,
69                             ASYNC_YIELDS_ASYNC,
70                             return_expr_span,
71                             "an async construct yields a type which is itself awaitable",
72                             |db| {
73                                 db.span_label(body.value.span, "outer async construct");
74                                 db.span_label(return_expr_span, "awaitable value not awaited");
75                                 db.span_suggestion(
76                                     return_expr_span,
77                                     "consider awaiting this value",
78                                     format!("{}.await", snippet(cx, return_expr_span, "..")),
79                                     Applicability::MaybeIncorrect,
80                                 );
81                             },
82                         );
83                     }
84                 }
85             }
86         }
87     }
88 }