]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
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 | } |