rustc_lint/
async_closures.rs1use 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
8#[doc =
r" The `closure_returning_async_block` lint detects cases where users"]
#[doc = r" write a closure that returns an async block."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" #![warn(closure_returning_async_block)]"]
#[doc = r" let c = |x: &str| async {};"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Using an async closure is preferable over a closure that returns an"]
#[doc = r" async block, since async closures are less restrictive in how its"]
#[doc = r" captures are allowed to be used."]
#[doc = r""]
#[doc =
r" For example, this code does not work with a closure returning an async"]
#[doc = r" block:"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" async fn callback(x: &str) {}"]
#[doc = r""]
#[doc = r" let captured_str = String::new();"]
#[doc = r" let c = move || async {"]
#[doc = r" callback(&captured_str).await;"]
#[doc = r" };"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" But it does work with async closures:"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" async fn callback(x: &str) {}"]
#[doc = r""]
#[doc = r" let captured_str = String::new();"]
#[doc = r" let c = async move || {"]
#[doc = r" callback(&captured_str).await;"]
#[doc = r" };"]
#[doc = r" ```"]
pub static CLOSURE_RETURNING_ASYNC_BLOCK: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "CLOSURE_RETURNING_ASYNC_BLOCK",
default_level: ::rustc_lint_defs::Allow,
desc: "closure that returns `async {}` could be rewritten as an async closure",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
9 pub CLOSURE_RETURNING_ASYNC_BLOCK,
50 Allow,
51 "closure that returns `async {}` could be rewritten as an async closure",
52}
53
54#[doc =
r" Lint for potential usages of async closures and async fn trait bounds."]
pub struct AsyncClosureUsage;
#[automatically_derived]
impl ::core::marker::Copy for AsyncClosureUsage { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for AsyncClosureUsage { }
#[automatically_derived]
impl ::core::clone::Clone for AsyncClosureUsage {
#[inline]
fn clone(&self) -> AsyncClosureUsage { *self }
}
impl ::rustc_lint_defs::LintPass for AsyncClosureUsage {
fn name(&self) -> &'static str { "AsyncClosureUsage" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([CLOSURE_RETURNING_ASYNC_BLOCK]))
}
}
impl AsyncClosureUsage {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([CLOSURE_RETURNING_ASYNC_BLOCK]))
}
}declare_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(const _: () =
{
impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for
ClosureReturningAsyncBlock {
#[track_caller]
fn decorate_lint<'__b>(self,
diag: &'__b mut rustc_errors::Diag<'__a, ()>) {
match self {
ClosureReturningAsyncBlock {
async_decl_span: __binding_0, sugg: __binding_1 } => {
diag.primary_message(crate::fluent_generated::lint_closure_returning_async_block);
;
diag.span_label(__binding_0,
crate::fluent_generated::_subdiag::label);
diag.subdiagnostic(__binding_1);
diag
}
};
}
}
};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(const _: () =
{
impl rustc_errors::Subdiagnostic for AsyncClosureSugg {
fn add_to_diag<__G>(self, diag: &mut rustc_errors::Diag<'_, __G>)
where __G: rustc_errors::EmissionGuarantee {
match self {
AsyncClosureSugg {
deletion_span: __binding_0, insertion_span: __binding_1 } =>
{
let mut suggestions = Vec::new();
let __code_0 =
::alloc::__export::must_use({
::alloc::fmt::format(format_args!(""))
});
let __code_1 =
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("async "))
});
suggestions.push((__binding_0, __code_0));
suggestions.push((__binding_1, __code_1));
diag.store_args();
let __message =
diag.eagerly_translate(crate::fluent_generated::lint_suggestion);
diag.multipart_suggestion_with_style(__message, suggestions,
rustc_errors::Applicability::MaybeIncorrect,
rustc_errors::SuggestionStyle::ShowCode);
diag.restore_args();
}
}
}
}
};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}