mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-23 21:23:56 +00:00
Add new redundant_async_block
lint
This commit is contained in:
parent
5eefbb39a9
commit
d5429eab8a
13 changed files with 260 additions and 12 deletions
|
@ -4836,6 +4836,7 @@ Released 2018-09-13
|
|||
[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
|
||||
[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
|
||||
[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
|
||||
[`redundant_async_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_async_block
|
||||
[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
|
||||
[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
|
||||
[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
|
||||
|
|
|
@ -519,6 +519,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::ranges::REVERSED_EMPTY_RANGES_INFO,
|
||||
crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
|
||||
crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
|
||||
crate::redundant_async_block::REDUNDANT_ASYNC_BLOCK_INFO,
|
||||
crate::redundant_clone::REDUNDANT_CLONE_INFO,
|
||||
crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
|
||||
crate::redundant_else::REDUNDANT_ELSE_INFO,
|
||||
|
|
|
@ -251,6 +251,7 @@ mod question_mark_used;
|
|||
mod ranges;
|
||||
mod rc_clone_in_vec_init;
|
||||
mod read_zero_byte_vec;
|
||||
mod redundant_async_block;
|
||||
mod redundant_clone;
|
||||
mod redundant_closure_call;
|
||||
mod redundant_else;
|
||||
|
@ -928,6 +929,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
|||
store.register_late_pass(|_| Box::new(no_mangle_with_rust_abi::NoMangleWithRustAbi));
|
||||
store.register_late_pass(|_| Box::new(collection_is_never_read::CollectionIsNeverRead));
|
||||
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
|
||||
store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
|
84
clippy_lints/src/redundant_async_block.rs
Normal file
84
clippy_lints/src/redundant_async_block.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet};
|
||||
use rustc_ast::ast::*;
|
||||
use rustc_ast::visit::Visitor as AstVisitor;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `async` block that only returns `await` on a future.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is simpler and more efficient to use the future directly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// async fn f() -> i32 {
|
||||
/// 1 + 2
|
||||
/// }
|
||||
///
|
||||
/// let fut = async {
|
||||
/// f().await
|
||||
/// };
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// async fn f() -> i32 {
|
||||
/// 1 + 2
|
||||
/// }
|
||||
///
|
||||
/// let fut = f();
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub REDUNDANT_ASYNC_BLOCK,
|
||||
complexity,
|
||||
"`async { future.await }` can be replaced by `future`"
|
||||
}
|
||||
declare_lint_pass!(RedundantAsyncBlock => [REDUNDANT_ASYNC_BLOCK]);
|
||||
|
||||
impl EarlyLintPass for RedundantAsyncBlock {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if expr.span.from_expansion() {
|
||||
return;
|
||||
}
|
||||
if let ExprKind::Async(_, _, block) = &expr.kind && block.stmts.len() == 1 &&
|
||||
let Some(Stmt { kind: StmtKind::Expr(last), .. }) = block.stmts.last() &&
|
||||
let ExprKind::Await(future) = &last.kind &&
|
||||
!future.span.from_expansion() &&
|
||||
!await_in_expr(future)
|
||||
{
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
REDUNDANT_ASYNC_BLOCK,
|
||||
expr.span,
|
||||
"this async expression only awaits a single future",
|
||||
"you can reduce it to",
|
||||
snippet(cx, future.span, "..").into_owned(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether an expression contains `.await`
|
||||
fn await_in_expr(expr: &Expr) -> bool {
|
||||
let mut detector = AwaitDetector::default();
|
||||
detector.visit_expr(expr);
|
||||
detector.await_found
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct AwaitDetector {
|
||||
await_found: bool,
|
||||
}
|
||||
|
||||
impl<'ast> AstVisitor<'ast> for AwaitDetector {
|
||||
fn visit_expr(&mut self, ex: &'ast Expr) {
|
||||
match (&ex.kind, self.await_found) {
|
||||
(ExprKind::Await(_), _) => self.await_found = true,
|
||||
(_, false) => rustc_ast::visit::walk_expr(self, ex),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
#![feature(lint_reasons)]
|
||||
#![feature(async_closure)]
|
||||
#![warn(clippy::async_yields_async)]
|
||||
#![allow(clippy::redundant_async_block)]
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#![feature(lint_reasons)]
|
||||
#![feature(async_closure)]
|
||||
#![warn(clippy::async_yields_async)]
|
||||
#![allow(clippy::redundant_async_block)]
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: an async construct yields a type which is itself awaitable
|
||||
--> $DIR/async_yields_async.rs:39:9
|
||||
--> $DIR/async_yields_async.rs:40:9
|
||||
|
|
||||
LL | let _h = async {
|
||||
| _____________________-
|
||||
|
@ -19,7 +19,7 @@ LL + }.await
|
|||
|
|
||||
|
||||
error: an async construct yields a type which is itself awaitable
|
||||
--> $DIR/async_yields_async.rs:44:9
|
||||
--> $DIR/async_yields_async.rs:45:9
|
||||
|
|
||||
LL | let _i = async {
|
||||
| ____________________-
|
||||
|
@ -32,7 +32,7 @@ LL | | };
|
|||
| |_____- outer async construct
|
||||
|
||||
error: an async construct yields a type which is itself awaitable
|
||||
--> $DIR/async_yields_async.rs:50:9
|
||||
--> $DIR/async_yields_async.rs:51:9
|
||||
|
|
||||
LL | let _j = async || {
|
||||
| ________________________-
|
||||
|
@ -51,7 +51,7 @@ LL + }.await
|
|||
|
|
||||
|
||||
error: an async construct yields a type which is itself awaitable
|
||||
--> $DIR/async_yields_async.rs:55:9
|
||||
--> $DIR/async_yields_async.rs:56:9
|
||||
|
|
||||
LL | let _k = async || {
|
||||
| _______________________-
|
||||
|
@ -64,7 +64,7 @@ LL | | };
|
|||
| |_____- outer async construct
|
||||
|
||||
error: an async construct yields a type which is itself awaitable
|
||||
--> $DIR/async_yields_async.rs:57:23
|
||||
--> $DIR/async_yields_async.rs:58:23
|
||||
|
|
||||
LL | let _l = async || CustomFutureType;
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
@ -74,7 +74,7 @@ LL | let _l = async || CustomFutureType;
|
|||
| help: consider awaiting this value: `CustomFutureType.await`
|
||||
|
||||
error: an async construct yields a type which is itself awaitable
|
||||
--> $DIR/async_yields_async.rs:63:9
|
||||
--> $DIR/async_yields_async.rs:64:9
|
||||
|
|
||||
LL | let _m = async || {
|
||||
| _______________________-
|
||||
|
|
64
tests/ui/redundant_async_block.fixed
Normal file
64
tests/ui/redundant_async_block.fixed
Normal file
|
@ -0,0 +1,64 @@
|
|||
// run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::redundant_async_block)]
|
||||
|
||||
async fn func1(n: usize) -> usize {
|
||||
n + 1
|
||||
}
|
||||
|
||||
async fn func2() -> String {
|
||||
let s = String::from("some string");
|
||||
let f = async { (*s).to_owned() };
|
||||
let x = f;
|
||||
x.await
|
||||
}
|
||||
|
||||
macro_rules! await_in_macro {
|
||||
($e:expr) => {
|
||||
std::convert::identity($e).await
|
||||
};
|
||||
}
|
||||
|
||||
async fn func3(n: usize) -> usize {
|
||||
// Do not lint (suggestion would be `std::convert::identity(func1(n))`
|
||||
// which copies code from inside the macro)
|
||||
async move { await_in_macro!(func1(n)) }.await
|
||||
}
|
||||
|
||||
// This macro should never be linted as `$e` might contain `.await`
|
||||
macro_rules! async_await_parameter_in_macro {
|
||||
($e:expr) => {
|
||||
async { $e.await }
|
||||
};
|
||||
}
|
||||
|
||||
// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
|
||||
// contain code coming from the parameters
|
||||
macro_rules! async_await_in_macro {
|
||||
($f:expr) => {
|
||||
($f)(async { func2().await })
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let fut1 = async { 17 };
|
||||
let fut2 = fut1;
|
||||
|
||||
let fut1 = async { 25 };
|
||||
let fut2 = fut1;
|
||||
|
||||
let fut = async { 42 };
|
||||
|
||||
// Do not lint: not a single expression
|
||||
let fut = async {
|
||||
func1(10).await;
|
||||
func2().await
|
||||
};
|
||||
|
||||
// Do not lint: expression contains `.await`
|
||||
let fut = async { func1(func2().await.len()).await };
|
||||
|
||||
let fut = async_await_parameter_in_macro!(func2());
|
||||
let fut = async_await_in_macro!(std::convert::identity);
|
||||
}
|
64
tests/ui/redundant_async_block.rs
Normal file
64
tests/ui/redundant_async_block.rs
Normal file
|
@ -0,0 +1,64 @@
|
|||
// run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::redundant_async_block)]
|
||||
|
||||
async fn func1(n: usize) -> usize {
|
||||
n + 1
|
||||
}
|
||||
|
||||
async fn func2() -> String {
|
||||
let s = String::from("some string");
|
||||
let f = async { (*s).to_owned() };
|
||||
let x = async { f.await };
|
||||
x.await
|
||||
}
|
||||
|
||||
macro_rules! await_in_macro {
|
||||
($e:expr) => {
|
||||
std::convert::identity($e).await
|
||||
};
|
||||
}
|
||||
|
||||
async fn func3(n: usize) -> usize {
|
||||
// Do not lint (suggestion would be `std::convert::identity(func1(n))`
|
||||
// which copies code from inside the macro)
|
||||
async move { await_in_macro!(func1(n)) }.await
|
||||
}
|
||||
|
||||
// This macro should never be linted as `$e` might contain `.await`
|
||||
macro_rules! async_await_parameter_in_macro {
|
||||
($e:expr) => {
|
||||
async { $e.await }
|
||||
};
|
||||
}
|
||||
|
||||
// MISSED OPPORTUNITY: this macro could be linted as the `async` block does not
|
||||
// contain code coming from the parameters
|
||||
macro_rules! async_await_in_macro {
|
||||
($f:expr) => {
|
||||
($f)(async { func2().await })
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let fut1 = async { 17 };
|
||||
let fut2 = async { fut1.await };
|
||||
|
||||
let fut1 = async { 25 };
|
||||
let fut2 = async move { fut1.await };
|
||||
|
||||
let fut = async { async { 42 }.await };
|
||||
|
||||
// Do not lint: not a single expression
|
||||
let fut = async {
|
||||
func1(10).await;
|
||||
func2().await
|
||||
};
|
||||
|
||||
// Do not lint: expression contains `.await`
|
||||
let fut = async { func1(func2().await.len()).await };
|
||||
|
||||
let fut = async_await_parameter_in_macro!(func2());
|
||||
let fut = async_await_in_macro!(std::convert::identity);
|
||||
}
|
28
tests/ui/redundant_async_block.stderr
Normal file
28
tests/ui/redundant_async_block.stderr
Normal file
|
@ -0,0 +1,28 @@
|
|||
error: this async expression only awaits a single future
|
||||
--> $DIR/redundant_async_block.rs:13:13
|
||||
|
|
||||
LL | let x = async { f.await };
|
||||
| ^^^^^^^^^^^^^^^^^ help: you can reduce it to: `f`
|
||||
|
|
||||
= note: `-D clippy::redundant-async-block` implied by `-D warnings`
|
||||
|
||||
error: this async expression only awaits a single future
|
||||
--> $DIR/redundant_async_block.rs:46:16
|
||||
|
|
||||
LL | let fut2 = async { fut1.await };
|
||||
| ^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
|
||||
|
||||
error: this async expression only awaits a single future
|
||||
--> $DIR/redundant_async_block.rs:49:16
|
||||
|
|
||||
LL | let fut2 = async move { fut1.await };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `fut1`
|
||||
|
||||
error: this async expression only awaits a single future
|
||||
--> $DIR/redundant_async_block.rs:51:15
|
||||
|
|
||||
LL | let fut = async { async { 42 }.await };
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `async { 42 }`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#![feature(async_closure)]
|
||||
#![warn(clippy::redundant_closure_call)]
|
||||
#![allow(clippy::redundant_async_block)]
|
||||
#![allow(unused)]
|
||||
|
||||
async fn something() -> u32 {
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#![feature(async_closure)]
|
||||
#![warn(clippy::redundant_closure_call)]
|
||||
#![allow(clippy::redundant_async_block)]
|
||||
#![allow(unused)]
|
||||
|
||||
async fn something() -> u32 {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: try not to call a closure in the expression where it is declared
|
||||
--> $DIR/redundant_closure_call_fixable.rs:16:13
|
||||
--> $DIR/redundant_closure_call_fixable.rs:17:13
|
||||
|
|
||||
LL | let a = (|| 42)();
|
||||
| ^^^^^^^^^ help: try doing something like: `42`
|
||||
|
@ -7,7 +7,7 @@ LL | let a = (|| 42)();
|
|||
= note: `-D clippy::redundant-closure-call` implied by `-D warnings`
|
||||
|
||||
error: try not to call a closure in the expression where it is declared
|
||||
--> $DIR/redundant_closure_call_fixable.rs:17:13
|
||||
--> $DIR/redundant_closure_call_fixable.rs:18:13
|
||||
|
|
||||
LL | let b = (async || {
|
||||
| _____________^
|
||||
|
@ -27,7 +27,7 @@ LL ~ };
|
|||
|
|
||||
|
||||
error: try not to call a closure in the expression where it is declared
|
||||
--> $DIR/redundant_closure_call_fixable.rs:22:13
|
||||
--> $DIR/redundant_closure_call_fixable.rs:23:13
|
||||
|
|
||||
LL | let c = (|| {
|
||||
| _____________^
|
||||
|
@ -47,13 +47,13 @@ LL ~ };
|
|||
|
|
||||
|
||||
error: try not to call a closure in the expression where it is declared
|
||||
--> $DIR/redundant_closure_call_fixable.rs:27:13
|
||||
--> $DIR/redundant_closure_call_fixable.rs:28:13
|
||||
|
|
||||
LL | let d = (async || something().await)();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
|
||||
|
||||
error: try not to call a closure in the expression where it is declared
|
||||
--> $DIR/redundant_closure_call_fixable.rs:36:13
|
||||
--> $DIR/redundant_closure_call_fixable.rs:37:13
|
||||
|
|
||||
LL | (|| m!())()
|
||||
| ^^^^^^^^^^^ help: try doing something like: `m!()`
|
||||
|
@ -64,7 +64,7 @@ LL | m2!();
|
|||
= note: this error originates in the macro `m2` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: try not to call a closure in the expression where it is declared
|
||||
--> $DIR/redundant_closure_call_fixable.rs:31:13
|
||||
--> $DIR/redundant_closure_call_fixable.rs:32:13
|
||||
|
|
||||
LL | (|| 0)()
|
||||
| ^^^^^^^^ help: try doing something like: `0`
|
||||
|
|
Loading…
Reference in a new issue