1 use clippy_utils::diagnostics::span_lint_hir_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 /// 28 /// Use instead: 29 /// ```rust 30 /// async fn foo() {} 31 /// 32 /// fn bar() { 33 /// let x = async { 34 /// foo().await 35 /// }; 36 /// } 37 /// ``` 38 #[clippy::version = "1.48.0"] 39 pub ASYNC_YIELDS_ASYNC, 40 correctness, 41 "async blocks that return a type that can be awaited" 42 } 43 44 declare_lint_pass!(AsyncYieldsAsync => [ASYNC_YIELDS_ASYNC]); 45 46 impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync { check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>)47 fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { 48 use AsyncGeneratorKind::{Block, Closure}; 49 // For functions, with explicitly defined types, don't warn. 50 // XXXkhuey maybe we should? 51 if let Some(GeneratorKind::Async(Block | Closure)) = body.generator_kind { 52 if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() { 53 let body_id = BodyId { 54 hir_id: body.value.hir_id, 55 }; 56 let typeck_results = cx.tcx.typeck_body(body_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_hir_and_then( 68 cx, 69 ASYNC_YIELDS_ASYNC, 70 body.value.hir_id, 71 return_expr_span, 72 "an async construct yields a type which is itself awaitable", 73 |db| { 74 db.span_label(body.value.span, "outer async construct"); 75 db.span_label(return_expr_span, "awaitable value not awaited"); 76 db.span_suggestion( 77 return_expr_span, 78 "consider awaiting this value", 79 format!("{}.await", snippet(cx, return_expr_span, "..")), 80 Applicability::MaybeIncorrect, 81 ); 82 }, 83 ); 84 } 85 } 86 } 87 } 88 } 89 } 90