]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/async_yields_async.rs
Auto merge of #71780 - jcotton42:string_remove_matches, r=joshtriplett
[rust.git] / src / tools / clippy / 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:** Checks for async blocks that yield values of types
9     /// that can themselves be awaited.
10     ///
11     /// **Why is this bad?** An await is likely missing.
12     ///
13     /// **Known problems:** None.
14     ///
15     /// **Example:**
16     ///
17     /// ```rust
18     /// async fn foo() {}
19     ///
20     /// fn bar() {
21     ///   let x = async {
22     ///     foo()
23     ///   };
24     /// }
25     /// ```
26     /// Use instead:
27     /// ```rust
28     /// async fn foo() {}
29     ///
30     /// fn bar() {
31     ///   let x = async {
32     ///     foo().await
33     ///   };
34     /// }
35     /// ```
36     pub ASYNC_YIELDS_ASYNC,
37     correctness,
38     "async blocks that return a type that can be awaited"
39 }
40
41 declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]);
42
43 impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
44     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
45         use AsyncGeneratorKind::{Block, Closure};
46         // For functions, with explicitly defined types, don't warn.
47         // XXXkhuey maybe we should?
48         if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind {
49             if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() {
50                 let body_id = BodyId {
51                     hir_id: body.value.hir_id,
52                 };
53                 let typeck_results = cx.tcx.typeck_body(body_id);
54                 let expr_ty = typeck_results.expr_ty(&body.value);
55
56                 if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
57                     let return_expr_span = match &body.value.kind {
58                         // XXXkhuey there has to be a better way.
59                         ExprKind::Block(block, _) => block.expr.map(|e| e.span),
60                         ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span),
61                         _ => None,
62                     };
63                     if let Some(return_expr_span) = return_expr_span {
64                         span_lint_and_then(
65                             cx,
66                             ASYNC_YIELDS_ASYNC,
67                             return_expr_span,
68                             "an async construct yields a type which is itself awaitable",
69                             |db| {
70                                 db.span_label(body.value.span, "outer async construct");
71                                 db.span_label(return_expr_span, "awaitable value not awaited");
72                                 db.span_suggestion(
73                                     return_expr_span,
74                                     "consider awaiting this value",
75                                     format!("{}.await", snippet(cx, return_expr_span, "..")),
76                                     Applicability::MaybeIncorrect,
77                                 );
78                             },
79                         );
80                     }
81                 }
82             }
83         }
84     }
85 }