rustc_lint/
async_closures.rs
1use rustc_hir as hir;
2use rustc_macros::{LintDiagnostic, Subdiagnostic};
3use rustc_session::{declare_lint, declare_lint_pass};
4use rustc_span::Span;
5
6use crate::{LateContext, LateLintPass};
7
8declare_lint! {
9 pub CLOSURE_RETURNING_ASYNC_BLOCK,
50 Allow,
51 "closure that returns `async {}` could be rewritten as an async closure",
52}
53
54declare_lint_pass!(
55 AsyncClosureUsage => [CLOSURE_RETURNING_ASYNC_BLOCK]
57);
58
59impl<'tcx> LateLintPass<'tcx> for AsyncClosureUsage {
60 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
61 let hir::ExprKind::Closure(&hir::Closure {
62 body,
63 kind: hir::ClosureKind::Closure,
64 fn_decl_span,
65 ..
66 }) = expr.kind
67 else {
68 return;
69 };
70
71 let mut body = cx.tcx.hir_body(body).value;
72
73 while let hir::ExprKind::Block(&hir::Block { stmts: [], expr: Some(tail), .. }, None) =
75 body.kind
76 {
77 body = tail;
78 }
79
80 let hir::ExprKind::Closure(&hir::Closure {
81 kind:
82 hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
83 hir::CoroutineDesugaring::Async,
84 hir::CoroutineSource::Block,
85 )),
86 fn_decl_span: async_decl_span,
87 ..
88 }) = body.kind
89 else {
90 return;
91 };
92
93 let deletion_span = cx.tcx.sess.source_map().span_extend_while_whitespace(async_decl_span);
94
95 cx.tcx.emit_node_span_lint(
96 CLOSURE_RETURNING_ASYNC_BLOCK,
97 expr.hir_id,
98 fn_decl_span,
99 ClosureReturningAsyncBlock {
100 async_decl_span,
101 sugg: AsyncClosureSugg {
102 deletion_span,
103 insertion_span: fn_decl_span.shrink_to_lo(),
104 },
105 },
106 );
107 }
108}
109
110#[derive(LintDiagnostic)]
111#[diag(lint_closure_returning_async_block)]
112struct ClosureReturningAsyncBlock {
113 #[label]
114 async_decl_span: Span,
115 #[subdiagnostic]
116 sugg: AsyncClosureSugg,
117}
118
119#[derive(Subdiagnostic)]
120#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")]
121struct AsyncClosureSugg {
122 #[suggestion_part(code = "")]
123 deletion_span: Span,
124 #[suggestion_part(code = "async ")]
125 insertion_span: Span,
126}