about summary refs log tree commit diff
path: root/clippy_lints/src
diff options
context:
space:
mode:
authorcsmoe <csmoe@msn.com>2023-02-27 11:57:32 +0000
committercsmoe <csmoe@msn.com>2023-03-20 22:51:01 +0800
commit4fdae81c70bfa1481eebc7ebec09f121c6eaf53c (patch)
tree5d752395d7de7997d4e806c3ecaff5643197a99e /clippy_lints/src
parentba7fd68e87cd13ece77baa83684396c8b9cbc633 (diff)
downloadrust-4fdae81c70bfa1481eebc7ebec09f121c6eaf53c.tar.gz
rust-4fdae81c70bfa1481eebc7ebec09f121c6eaf53c.zip
add large future lint
Diffstat (limited to 'clippy_lints/src')
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/large_futures.rs90
-rw-r--r--clippy_lints/src/lib.rs3
-rw-r--r--clippy_lints/src/utils/conf.rs4
4 files changed, 98 insertions, 0 deletions
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index cd5dd7a5706..208112b4025 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -216,6 +216,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
     crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
     crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
+    crate::large_futures::LARGE_FUTURES_INFO,
     crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
     crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
     crate::len_zero::COMPARISON_TO_EMPTY_INFO,
diff --git a/clippy_lints/src/large_futures.rs b/clippy_lints/src/large_futures.rs
new file mode 100644
index 00000000000..494bb2a97d2
--- /dev/null
+++ b/clippy_lints/src/large_futures.rs
@@ -0,0 +1,90 @@
+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 let ExprKind::Match(expr, _, MatchSource::AwaitDesugar) = expr.kind {
+            if let ExprKind::Call(func, [expr, ..]) = expr.kind {
+                if matches!(
+                    func.kind,
+                    ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..))
+                ) {
+                    let ty = cx.typeck_results().expr_ty(expr);
+                    if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
+                        && implements_trait(cx, ty, future_trait_def_id, &[]) {
+                            if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) {
+                                let size = layout.layout.size();
+                                if 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::MachineApplicable,
+                                    );
+                                }
+                            }
+                        }
+                }
+            }
+        }
+    }
+}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 145cf524652..155aa106322 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -161,6 +161,7 @@ mod items_after_statements;
 mod iter_not_returning_iterator;
 mod large_const_arrays;
 mod large_enum_variant;
+mod large_futures;
 mod large_include_file;
 mod large_stack_arrays;
 mod len_zero;
@@ -800,6 +801,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv())));
     store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
+    let future_size_threshold = conf.future_size_threshold;
+    store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold)));
     store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
     store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
     store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs
index 5f74de5a288..639f8d95877 100644
--- a/clippy_lints/src/utils/conf.rs
+++ b/clippy_lints/src/utils/conf.rs
@@ -459,6 +459,10 @@ define_Conf! {
     /// Whether to **only** check for missing documentation in items visible within the current
     /// crate. For example, `pub(crate)` items.
     (missing_docs_in_crate_items: bool = false),
+    /// Lint: LARGE_FUTURES.
+    ///
+    /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
+    (future_size_threshold: u64 = 16 * 1024),
 }
 
 /// Search for the configuration file.