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#10414 - csmoe:large-future, r=xFrednet
add large future lint Closes rust-lang#5263 --- changelog: new lint: [`large_futures`] [rust-lang#10414](rust-lang/rust-clippy#10414) <!-- changelog_checked -->
- Loading branch information
Showing
12 changed files
with
287 additions
and
0 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,87 @@ | ||
use clippy_utils::source::snippet; | ||
use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait}; | ||
use rustc_errors::Applicability; | ||
use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath}; | ||
use rustc_lint::{LateContext, LateLintPass}; | ||
use rustc_session::{declare_tool_lint, impl_lint_pass}; | ||
use rustc_target::abi::Size; | ||
|
||
declare_clippy_lint! { | ||
/// ### What it does | ||
/// It checks for the size of a `Future` created by `async fn` or `async {}`. | ||
/// | ||
/// ### Why is this bad? | ||
/// Due to the current [unideal implemention](https://github.com/rust-lang/rust/issues/69826) of `Generator`, | ||
/// large size of a `Future` may cause stack overflows. | ||
/// | ||
/// ### Example | ||
/// ```rust | ||
/// async fn wait(f: impl std::future::Future<Output = ()>) {} | ||
/// | ||
/// async fn big_fut(arg: [u8; 1024]) {} | ||
/// | ||
/// pub async fn test() { | ||
/// let fut = big_fut([0u8; 1024]); | ||
/// wait(fut).await; | ||
/// } | ||
/// ``` | ||
/// | ||
/// `Box::pin` the big future instead. | ||
/// | ||
/// ```rust | ||
/// async fn wait(f: impl std::future::Future<Output = ()>) {} | ||
/// | ||
/// async fn big_fut(arg: [u8; 1024]) {} | ||
/// | ||
/// pub async fn test() { | ||
/// let fut = Box::pin(big_fut([0u8; 1024])); | ||
/// wait(fut).await; | ||
/// } | ||
/// ``` | ||
#[clippy::version = "1.68.0"] | ||
pub LARGE_FUTURES, | ||
pedantic, | ||
"large future may lead to unexpected stack overflows" | ||
} | ||
|
||
#[derive(Copy, Clone)] | ||
pub struct LargeFuture { | ||
future_size_threshold: u64, | ||
} | ||
|
||
impl LargeFuture { | ||
pub fn new(future_size_threshold: u64) -> Self { | ||
Self { future_size_threshold } | ||
} | ||
} | ||
|
||
impl_lint_pass!(LargeFuture => [LARGE_FUTURES]); | ||
|
||
impl<'tcx> LateLintPass<'tcx> for LargeFuture { | ||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { | ||
if matches!(expr.span.ctxt().outer_expn_data().kind, rustc_span::ExpnKind::Macro(..)) { | ||
return; | ||
} | ||
if let ExprKind::Match(expr, _, MatchSource::AwaitDesugar) = expr.kind { | ||
if let ExprKind::Call(func, [expr, ..]) = expr.kind | ||
&& let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind | ||
&& let ty = cx.typeck_results().expr_ty(expr) | ||
&& let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() | ||
&& implements_trait(cx, ty, future_trait_def_id, &[]) | ||
&& let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) | ||
&& let size = layout.layout.size() | ||
&& size >= Size::from_bytes(self.future_size_threshold) | ||
{ | ||
span_lint_and_sugg( | ||
cx, | ||
LARGE_FUTURES, | ||
expr.span, | ||
&format!("large future with a size of {} bytes", size.bytes()), | ||
"consider `Box::pin` on it", | ||
format!("Box::pin({})", snippet(cx, expr.span, "..")), | ||
Applicability::Unspecified, | ||
); | ||
} | ||
} | ||
} | ||
} |
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 @@ | ||
future-size-threshold = 1024 |
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,27 @@ | ||
#![warn(clippy::large_futures)] | ||
|
||
fn main() {} | ||
|
||
pub async fn should_warn() { | ||
let x = [0u8; 1024]; | ||
async {}.await; | ||
dbg!(x); | ||
} | ||
|
||
pub async fn should_not_warn() { | ||
let x = [0u8; 1020]; | ||
async {}.await; | ||
dbg!(x); | ||
} | ||
|
||
pub async fn bar() { | ||
should_warn().await; | ||
|
||
async { | ||
let x = [0u8; 1024]; | ||
dbg!(x); | ||
} | ||
.await; | ||
|
||
should_not_warn().await; | ||
} |
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,10 @@ | ||
error: large future with a size of 1026 bytes | ||
--> $DIR/large_futures.rs:18:5 | ||
| | ||
LL | should_warn().await; | ||
| ^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(should_warn())` | ||
| | ||
= note: `-D clippy::large-futures` implied by `-D warnings` | ||
|
||
error: aborting due to previous error | ||
|
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,61 @@ | ||
#![feature(generators)] | ||
#![warn(clippy::large_futures)] | ||
#![allow(clippy::future_not_send)] | ||
#![allow(clippy::manual_async_fn)] | ||
|
||
async fn big_fut(_arg: [u8; 1024 * 16]) {} | ||
|
||
async fn wait() { | ||
let f = async { | ||
big_fut([0u8; 1024 * 16]).await; | ||
}; | ||
f.await | ||
} | ||
async fn calls_fut(fut: impl std::future::Future<Output = ()>) { | ||
loop { | ||
wait().await; | ||
if true { | ||
return fut.await; | ||
} else { | ||
wait().await; | ||
} | ||
} | ||
} | ||
|
||
pub async fn test() { | ||
let fut = big_fut([0u8; 1024 * 16]); | ||
foo().await; | ||
calls_fut(fut).await; | ||
} | ||
|
||
pub fn foo() -> impl std::future::Future<Output = ()> { | ||
async { | ||
let x = [0i32; 1024 * 16]; | ||
async {}.await; | ||
dbg!(x); | ||
} | ||
} | ||
|
||
pub async fn lines() { | ||
async { | ||
let x = [0i32; 1024 * 16]; | ||
async {}.await; | ||
println!("{:?}", x); | ||
} | ||
.await; | ||
} | ||
|
||
pub async fn macro_expn() { | ||
macro_rules! macro_ { | ||
() => { | ||
async { | ||
let x = [0i32; 1024 * 16]; | ||
async {}.await; | ||
println!("macro: {:?}", x); | ||
} | ||
}; | ||
} | ||
macro_!().await | ||
} | ||
|
||
fn main() {} |
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,82 @@ | ||
error: large future with a size of 16385 bytes | ||
--> $DIR/large_futures.rs:10:9 | ||
| | ||
LL | big_fut([0u8; 1024 * 16]).await; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(big_fut([0u8; 1024 * 16]))` | ||
| | ||
= note: `-D clippy::large-futures` implied by `-D warnings` | ||
|
||
error: large future with a size of 16386 bytes | ||
--> $DIR/large_futures.rs:12:5 | ||
| | ||
LL | f.await | ||
| ^ help: consider `Box::pin` on it: `Box::pin(f)` | ||
|
||
error: large future with a size of 16387 bytes | ||
--> $DIR/large_futures.rs:16:9 | ||
| | ||
LL | wait().await; | ||
| ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())` | ||
|
||
error: large future with a size of 16387 bytes | ||
--> $DIR/large_futures.rs:20:13 | ||
| | ||
LL | wait().await; | ||
| ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())` | ||
|
||
error: large future with a size of 65540 bytes | ||
--> $DIR/large_futures.rs:27:5 | ||
| | ||
LL | foo().await; | ||
| ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())` | ||
|
||
error: large future with a size of 49159 bytes | ||
--> $DIR/large_futures.rs:28:5 | ||
| | ||
LL | calls_fut(fut).await; | ||
| ^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(calls_fut(fut))` | ||
|
||
error: large future with a size of 65540 bytes | ||
--> $DIR/large_futures.rs:40:5 | ||
| | ||
LL | / async { | ||
LL | | let x = [0i32; 1024 * 16]; | ||
LL | | async {}.await; | ||
LL | | println!("{:?}", x); | ||
LL | | } | ||
| |_____^ | ||
| | ||
help: consider `Box::pin` on it | ||
| | ||
LL ~ Box::pin(async { | ||
LL + let x = [0i32; 1024 * 16]; | ||
LL + async {}.await; | ||
LL + println!("{:?}", x); | ||
LL + }) | ||
| | ||
|
||
error: large future with a size of 65540 bytes | ||
--> $DIR/large_futures.rs:51:13 | ||
| | ||
LL | / async { | ||
LL | | let x = [0i32; 1024 * 16]; | ||
LL | | async {}.await; | ||
LL | | println!("macro: {:?}", x); | ||
LL | | } | ||
| |_____________^ | ||
... | ||
LL | macro_!().await | ||
| --------- in this macro invocation | ||
| | ||
= note: this error originates in the macro `macro_` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
help: consider `Box::pin` on it | ||
| | ||
LL ~ Box::pin(async { | ||
LL + let x = [0i32; 1024 * 16]; | ||
LL + async {}.await; | ||
LL + println!("macro: {:?}", x); | ||
LL + }) | ||
| | ||
|
||
error: aborting due to 8 previous errors | ||
|