forked from rust-lang/rust
-
-
Notifications
You must be signed in to change notification settings - Fork 2
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#10448 - samueltardieu:issue-10444, r=llogiq
Add new `redundant_async_block` lint Fixes rust-lang#10444 changelog: [`redundant_async_block`]: new lint to detect `async { future.await }`
- Loading branch information
Showing
13 changed files
with
260 additions
and
12 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
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,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), | ||
_ => (), | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |
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,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); | ||
} |
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,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 | ||
|
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
Oops, something went wrong.