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