Auto merge of #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: * #62290
This commit is contained in:
commit
9b0043095a
10 changed files with 242 additions and 20 deletions
129
compiler/rustc_lint/src/async_closures.rs
Normal file
129
compiler/rustc_lint/src/async_closures.rs
Normal file
|
@ -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,
|
||||
}
|
|
@ -11,7 +11,7 @@ use rustc_middle::ty::{
|
|||
self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
|
||||
};
|
||||
use rustc_session::{declare_lint, declare_lint_pass};
|
||||
use rustc_span::{sym, Span};
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::{LateContext, LateLintPass};
|
||||
|
@ -57,7 +57,7 @@ declare_lint! {
|
|||
pub IMPL_TRAIT_OVERCAPTURES,
|
||||
Allow,
|
||||
"`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
|
||||
@feature_gate = sym::precise_capturing;
|
||||
@feature_gate = precise_capturing;
|
||||
//@future_incompatible = FutureIncompatibleInfo {
|
||||
// reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
// reference: "<FIXME>",
|
||||
|
@ -91,7 +91,7 @@ declare_lint! {
|
|||
pub IMPL_TRAIT_REDUNDANT_CAPTURES,
|
||||
Warn,
|
||||
"redundant precise-capturing `use<...>` syntax on an `impl Trait`",
|
||||
@feature_gate = sym::precise_capturing;
|
||||
@feature_gate = precise_capturing;
|
||||
}
|
||||
|
||||
declare_lint_pass!(
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#![feature(trait_upcasting)]
|
||||
// tidy-alphabetical-end
|
||||
|
||||
mod async_closures;
|
||||
mod async_fn_in_trait;
|
||||
pub mod builtin;
|
||||
mod context;
|
||||
|
@ -87,6 +88,7 @@ use rustc_hir::def_id::LocalModDefId;
|
|||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
use async_closures::AsyncClosureUsage;
|
||||
use async_fn_in_trait::AsyncFnInTrait;
|
||||
use builtin::*;
|
||||
use deref_into_dyn_supertrait::*;
|
||||
|
@ -229,6 +231,7 @@ late_lint_methods!(
|
|||
MapUnitFn: MapUnitFn,
|
||||
MissingDebugImplementations: MissingDebugImplementations,
|
||||
MissingDoc: MissingDoc,
|
||||
AsyncClosureUsage: AsyncClosureUsage,
|
||||
AsyncFnInTrait: AsyncFnInTrait,
|
||||
NonLocalDefinitions: NonLocalDefinitions::default(),
|
||||
ImplTraitOvercaptures: ImplTraitOvercaptures,
|
||||
|
|
|
@ -2,7 +2,6 @@ use crate::{LateContext, LateLintPass, LintContext};
|
|||
|
||||
use rustc_hir as hir;
|
||||
use rustc_session::{declare_lint, declare_lint_pass};
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_lint! {
|
||||
/// The `multiple_supertrait_upcastable` lint detects when an object-safe trait has multiple
|
||||
|
@ -30,7 +29,7 @@ declare_lint! {
|
|||
pub MULTIPLE_SUPERTRAIT_UPCASTABLE,
|
||||
Allow,
|
||||
"detect when an object-safe trait has multiple supertraits",
|
||||
@feature_gate = sym::multiple_supertrait_upcastable;
|
||||
@feature_gate = multiple_supertrait_upcastable;
|
||||
}
|
||||
|
||||
declare_lint_pass!(MultipleSupertraitUpcastable => [MULTIPLE_SUPERTRAIT_UPCASTABLE]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue