]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/unused_async.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / unused_async.rs
1 use clippy_utils::diagnostics::span_lint_and_help;
2 use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
3 use rustc_hir::{Body, Expr, ExprKind, FnDecl, YieldSource};
4 use rustc_lint::{LateContext, LateLintPass};
5 use rustc_middle::hir::nested_filter;
6 use rustc_session::{declare_lint_pass, declare_tool_lint};
7 use rustc_span::def_id::LocalDefId;
8 use rustc_span::Span;
9
10 declare_clippy_lint! {
11 /// ### What it does
12 /// Checks for functions that are declared `async` but have no `.await`s inside of them.
13 ///
14 /// ### Why is this bad?
15 /// Async functions with no async code create overhead, both mentally and computationally.
16 /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which
17 /// causes runtime overhead and hassle for the caller.
18 ///
19 /// ### Example
20 /// ```rust
21 /// async fn get_random_number() -> i64 {
22 /// 4 // Chosen by fair dice roll. Guaranteed to be random.
23 /// }
24 /// let number_future = get_random_number();
25 /// ```
26 ///
27 /// Use instead:
28 /// ```rust
29 /// fn get_random_number_improved() -> i64 {
30 /// 4 // Chosen by fair dice roll. Guaranteed to be random.
31 /// }
32 /// let number_future = async { get_random_number_improved() };
33 /// ```
34 #[clippy::version = "1.54.0"]
35 pub UNUSED_ASYNC,
36 pedantic,
37 "finds async functions with no await statements"
38 }
39
40 declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]);
41
42 struct AsyncFnVisitor<'a, 'tcx> {
43 cx: &'a LateContext<'tcx>,
44 found_await: bool,
45 }
46
47 impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> {
48 type NestedFilter = nested_filter::OnlyBodies;
49
50 fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
51 if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind {
52 self.found_await = true;
53 }
54 walk_expr(self, ex);
55 }
56
57 fn nested_visit_map(&mut self) -> Self::Map {
58 self.cx.tcx.hir()
59 }
60 }
61
62 impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
63 fn check_fn(
64 &mut self,
65 cx: &LateContext<'tcx>,
66 fn_kind: FnKind<'tcx>,
67 fn_decl: &'tcx FnDecl<'tcx>,
68 body: &Body<'tcx>,
69 span: Span,
70 def_id: LocalDefId,
71 ) {
72 if !span.from_expansion() && fn_kind.asyncness().is_async() {
73 let mut visitor = AsyncFnVisitor { cx, found_await: false };
74 walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_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 }