forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#127097 - compiler-errors:async-closure-lint, …
…r=oli-obk Implement simple, unstable lint to suggest turning closure-of-async-block into async-closure We want to eventually suggest people to turn `|| async {}` to `async || {}`. This begins doing that. It's a pretty rudimentary lint, but I wanted to get something down so I wouldn't lose the code. Tracking: * rust-lang#62290
- Loading branch information
Showing
10 changed files
with
242 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
use rustc_hir as hir; | ||
use rustc_macros::{LintDiagnostic, Subdiagnostic}; | ||
use rustc_session::{declare_lint, declare_lint_pass}; | ||
use rustc_span::Span; | ||
|
||
use crate::{LateContext, LateLintPass}; | ||
|
||
declare_lint! { | ||
/// The `closure_returning_async_block` lint detects cases where users | ||
/// write a closure that returns an async block. | ||
/// | ||
/// ### Example | ||
/// | ||
/// ```rust | ||
/// #![warn(closure_returning_async_block)] | ||
/// let c = |x: &str| async {}; | ||
/// ``` | ||
/// | ||
/// {{produces}} | ||
/// | ||
/// ### Explanation | ||
/// | ||
/// Using an async closure is preferable over a closure that returns an | ||
/// async block, since async closures are less restrictive in how its | ||
/// captures are allowed to be used. | ||
/// | ||
/// For example, this code does not work with a closure returning an async | ||
/// block: | ||
/// | ||
/// ```rust,compile_fail | ||
/// async fn callback(x: &str) {} | ||
/// | ||
/// let captured_str = String::new(); | ||
/// let c = move || async { | ||
/// callback(&captured_str).await; | ||
/// }; | ||
/// ``` | ||
/// | ||
/// But it does work with async closures: | ||
/// | ||
/// ```rust | ||
/// #![feature(async_closure)] | ||
/// | ||
/// async fn callback(x: &str) {} | ||
/// | ||
/// let captured_str = String::new(); | ||
/// let c = async move || { | ||
/// callback(&captured_str).await; | ||
/// }; | ||
/// ``` | ||
pub CLOSURE_RETURNING_ASYNC_BLOCK, | ||
Allow, | ||
"closure that returns `async {}` could be rewritten as an async closure", | ||
@feature_gate = async_closure; | ||
} | ||
|
||
declare_lint_pass!( | ||
/// Lint for potential usages of async closures and async fn trait bounds. | ||
AsyncClosureUsage => [CLOSURE_RETURNING_ASYNC_BLOCK] | ||
); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for AsyncClosureUsage { | ||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { | ||
let hir::ExprKind::Closure(&hir::Closure { | ||
body, | ||
kind: hir::ClosureKind::Closure, | ||
fn_decl_span, | ||
.. | ||
}) = expr.kind | ||
else { | ||
return; | ||
}; | ||
|
||
let mut body = cx.tcx.hir().body(body).value; | ||
|
||
// Only peel blocks that have no expressions. | ||
while let hir::ExprKind::Block(&hir::Block { stmts: [], expr: Some(tail), .. }, None) = | ||
body.kind | ||
{ | ||
body = tail; | ||
} | ||
|
||
let hir::ExprKind::Closure(&hir::Closure { | ||
kind: | ||
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared( | ||
hir::CoroutineDesugaring::Async, | ||
hir::CoroutineSource::Block, | ||
)), | ||
fn_decl_span: async_decl_span, | ||
.. | ||
}) = body.kind | ||
else { | ||
return; | ||
}; | ||
|
||
let deletion_span = cx.tcx.sess.source_map().span_extend_while_whitespace(async_decl_span); | ||
|
||
cx.tcx.emit_node_span_lint( | ||
CLOSURE_RETURNING_ASYNC_BLOCK, | ||
expr.hir_id, | ||
fn_decl_span, | ||
ClosureReturningAsyncBlock { | ||
async_decl_span, | ||
sugg: AsyncClosureSugg { | ||
deletion_span, | ||
insertion_span: fn_decl_span.shrink_to_lo(), | ||
}, | ||
}, | ||
); | ||
} | ||
} | ||
|
||
#[derive(LintDiagnostic)] | ||
#[diag(lint_closure_returning_async_block)] | ||
struct ClosureReturningAsyncBlock { | ||
#[label] | ||
async_decl_span: Span, | ||
#[subdiagnostic] | ||
sugg: AsyncClosureSugg, | ||
} | ||
|
||
#[derive(Subdiagnostic)] | ||
#[multipart_suggestion(lint_suggestion, applicability = "maybe-incorrect")] | ||
struct AsyncClosureSugg { | ||
#[suggestion_part(code = "")] | ||
deletion_span: Span, | ||
#[suggestion_part(code = "async ")] | ||
insertion_span: Span, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
tests/ui/async-await/async-closures/lint-closure-returning-async-block.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
//@ edition: 2021 | ||
|
||
#![feature(async_closure)] | ||
#![deny(closure_returning_async_block)] | ||
|
||
fn main() { | ||
let x = || async {}; | ||
//~^ ERROR closure returning async block can be made into an async closure | ||
|
||
let x = || async move {}; | ||
//~^ ERROR closure returning async block can be made into an async closure | ||
|
||
let x = move || async move {}; | ||
//~^ ERROR closure returning async block can be made into an async closure | ||
|
||
let x = move || async {}; | ||
//~^ ERROR closure returning async block can be made into an async closure | ||
|
||
let x = || {{ async {} }}; | ||
//~^ ERROR closure returning async block can be made into an async closure | ||
} |
Oops, something went wrong.