Skip to main content

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
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    /// The `closure_returning_async_block` lint detects cases where users
10    /// write a closure that returns an async block.
11    ///
12    /// ### Example
13    ///
14    /// ```rust
15    /// #![warn(closure_returning_async_block)]
16    /// let c = |x: &str| async {};
17    /// ```
18    ///
19    /// {{produces}}
20    ///
21    /// ### Explanation
22    ///
23    /// Using an async closure is preferable over a closure that returns an
24    /// async block, since async closures are less restrictive in how its
25    /// captures are allowed to be used.
26    ///
27    /// For example, this code does not work with a closure returning an async
28    /// block:
29    ///
30    /// ```rust,compile_fail
31    /// async fn callback(x: &str) {}
32    ///
33    /// let captured_str = String::new();
34    /// let c = move || async {
35    ///     callback(&captured_str).await;
36    /// };
37    /// ```
38    ///
39    /// But it does work with async closures:
40    ///
41    /// ```rust
42    /// async fn callback(x: &str) {}
43    ///
44    /// let captured_str = String::new();
45    /// let c = async move || {
46    ///     callback(&captured_str).await;
47    /// };
48    /// ```
49    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    /// Lint for potential usages of async closures and async fn trait bounds.
56    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        // Only peel blocks that have no expressions.
74        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}