]>
Commit | Line | Data |
---|---|---|
17df50a5 | 1 | use clippy_utils::diagnostics::span_lint_and_help; |
5099ac24 | 2 | use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; |
c295e0f8 | 3 | use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, YieldSource}; |
17df50a5 | 4 | use rustc_lint::{LateContext, LateLintPass}; |
5099ac24 | 5 | use rustc_middle::hir::nested_filter; |
17df50a5 XL |
6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; |
7 | use rustc_span::Span; | |
8 | ||
9 | declare_clippy_lint! { | |
94222f64 XL |
10 | /// ### What it does |
11 | /// Checks for functions that are declared `async` but have no `.await`s inside of them. | |
17df50a5 | 12 | /// |
94222f64 XL |
13 | /// ### Why is this bad? |
14 | /// Async functions with no async code create overhead, both mentally and computationally. | |
17df50a5 XL |
15 | /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which |
16 | /// causes runtime overhead and hassle for the caller. | |
17 | /// | |
94222f64 | 18 | /// ### Example |
17df50a5 | 19 | /// ```rust |
17df50a5 XL |
20 | /// async fn get_random_number() -> i64 { |
21 | /// 4 // Chosen by fair dice roll. Guaranteed to be random. | |
22 | /// } | |
23 | /// let number_future = get_random_number(); | |
923072b8 | 24 | /// ``` |
17df50a5 | 25 | /// |
923072b8 FG |
26 | /// Use instead: |
27 | /// ```rust | |
17df50a5 XL |
28 | /// fn get_random_number_improved() -> i64 { |
29 | /// 4 // Chosen by fair dice roll. Guaranteed to be random. | |
30 | /// } | |
31 | /// let number_future = async { get_random_number_improved() }; | |
32 | /// ``` | |
a2a8927a | 33 | #[clippy::version = "1.54.0"] |
17df50a5 XL |
34 | pub UNUSED_ASYNC, |
35 | pedantic, | |
36 | "finds async functions with no await statements" | |
37 | } | |
38 | ||
39 | declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); | |
40 | ||
41 | struct AsyncFnVisitor<'a, 'tcx> { | |
42 | cx: &'a LateContext<'tcx>, | |
43 | found_await: bool, | |
44 | } | |
45 | ||
46 | impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { | |
5099ac24 | 47 | type NestedFilter = nested_filter::OnlyBodies; |
17df50a5 XL |
48 | |
49 | fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { | |
50 | if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { | |
51 | self.found_await = true; | |
52 | } | |
53 | walk_expr(self, ex); | |
54 | } | |
55 | ||
5099ac24 FG |
56 | fn nested_visit_map(&mut self) -> Self::Map { |
57 | self.cx.tcx.hir() | |
17df50a5 XL |
58 | } |
59 | } | |
60 | ||
61 | impl<'tcx> LateLintPass<'tcx> for UnusedAsync { | |
17df50a5 XL |
62 | fn check_fn( |
63 | &mut self, | |
64 | cx: &LateContext<'tcx>, | |
65 | fn_kind: FnKind<'tcx>, | |
66 | fn_decl: &'tcx FnDecl<'tcx>, | |
67 | body: &Body<'tcx>, | |
68 | span: Span, | |
69 | hir_id: HirId, | |
70 | ) { | |
04454e1e | 71 | if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }) = &fn_kind { |
17df50a5 XL |
72 | if matches!(asyncness, IsAsync::Async) { |
73 | let mut visitor = AsyncFnVisitor { cx, found_await: false }; | |
74 | walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); | |
75 | if !visitor.found_await { | |
76 | span_lint_and_help( | |
77 | cx, | |
78 | UNUSED_ASYNC, | |
79 | span, | |
80 | "unused `async` for function with no await statements", | |
81 | None, | |
82 | "consider removing the `async` from this function", | |
83 | ); | |
84 | } | |
85 | } | |
86 | } | |
87 | } | |
88 | } |