From fbbaa0e0781522ce2c898f9cab86f900414f6950 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Thu, 4 Sep 2025 15:11:25 -0700 Subject: [PATCH] Add discarded_futures lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + clippy_lints/src/discarded_future.rs | 56 +++++++++++++++++++++++++++ clippy_lints/src/discarded_futures.rs | 0 clippy_lints/src/lib.rs | 2 + tests/ui/discarded_future.fixed | 19 +++++++++ tests/ui/discarded_future.rs | 19 +++++++++ tests/ui/discarded_future.stderr | 11 ++++++ 8 files changed, 109 insertions(+) create mode 100644 clippy_lints/src/discarded_future.rs create mode 100644 clippy_lints/src/discarded_futures.rs create mode 100644 tests/ui/discarded_future.fixed create mode 100644 tests/ui/discarded_future.rs create mode 100644 tests/ui/discarded_future.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index eb2a76a81836..5e8c9daef087 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5918,6 +5918,7 @@ Released 2018-09-13 [`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types +[`discarded_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#discarded_future [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression [`doc_broken_link`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_broken_link [`doc_comment_double_space_linebreaks`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_comment_double_space_linebreaks diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index d0c7443a4a4b..7987f36f1d22 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -113,6 +113,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::disallowed_names::DISALLOWED_NAMES_INFO, crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO, crate::disallowed_types::DISALLOWED_TYPES_INFO, + crate::discarded_future::DISCARDED_FUTURE_INFO, crate::doc::DOC_BROKEN_LINK_INFO, crate::doc::DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS_INFO, crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO, diff --git a/clippy_lints/src/discarded_future.rs b/clippy_lints/src/discarded_future.rs new file mode 100644 index 000000000000..bb0ffb91abb4 --- /dev/null +++ b/clippy_lints/src/discarded_future.rs @@ -0,0 +1,56 @@ +use rustc_hir::*; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use clippy_utils::ty::implements_trait; +use rustc_errors::Applicability; +use rustc_middle::ty; +use rustc_span::sym; +use clippy_utils::ty::is_type_diagnostic_item; + +declare_clippy_lint! { + /// ### What it does + /// + /// ### Why restrict this? + /// + /// ### Example + /// ```no_run + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```no_run + /// // example code which does not raise clippy warning + /// ``` + #[clippy::version = "1.91.0"] + pub DISCARDED_FUTURE, + restriction, + "default lint description" +} +declare_lint_pass!(DiscardedFuture => [DISCARDED_FUTURE]); + +impl<'tcx> LateLintPass<'tcx> for DiscardedFuture { + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) { + if let StmtKind::Let(let_stmt) = stmt.kind + && let PatKind::Wild = let_stmt.pat.kind + && let Some(expr) = let_stmt.init + && let ty = cx.typeck_results().expr_ty(expr) + && is_type_diagnostic_item(cx, ty, sym::Result) + && let ty::Adt(_, substs) = ty.kind() + && let Some(inner_ty) = substs[0].as_type() + && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + && implements_trait(cx, inner_ty, future_trait_def_id, &[]) + { + span_lint_and_sugg( + cx, + DISCARDED_FUTURE, + expr.span, + format!("Discarding a Result: did you mean to call .await on this first?"), + "consider `.await` on it", + format!("{}.await", snippet(cx, expr.span, "..")), + Applicability::MaybeIncorrect, + ); + } + } +} \ No newline at end of file diff --git a/clippy_lints/src/discarded_futures.rs b/clippy_lints/src/discarded_futures.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 0d7931aae765..48902c6d996a 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -118,6 +118,7 @@ mod disallowed_methods; mod disallowed_names; mod disallowed_script_idents; mod disallowed_types; +mod discarded_future; mod doc; mod double_parens; mod drop_forget_ref; @@ -831,5 +832,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf))); store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom)); store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny)); + store.register_late_pass(|_| Box::new(discarded_future::DiscardedFuture)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/discarded_future.fixed b/tests/ui/discarded_future.fixed new file mode 100644 index 000000000000..d56a3d759365 --- /dev/null +++ b/tests/ui/discarded_future.fixed @@ -0,0 +1,19 @@ +#![warn(clippy::discarded_future)] +#![allow(clippy::result_unit_err)] + +pub fn result_future() -> Result, ()> { + Ok(async { + dbg!("hello im in a future"); + }) +} + +pub async fn calls_result_future() { + // some async stuff + let _ = result_future().await; + //~^ discarded_future + // more async stuff +} + +fn main() { + +} \ No newline at end of file diff --git a/tests/ui/discarded_future.rs b/tests/ui/discarded_future.rs new file mode 100644 index 000000000000..57810e0fb82d --- /dev/null +++ b/tests/ui/discarded_future.rs @@ -0,0 +1,19 @@ +#![warn(clippy::discarded_future)] +#![allow(clippy::result_unit_err)] + +pub fn result_future() -> Result, ()> { + Ok(async { + dbg!("hello im in a future"); + }) +} + +pub async fn calls_result_future() { + // some async stuff + let _ = result_future(); + //~^ discarded_future + // more async stuff +} + +fn main() { + +} \ No newline at end of file diff --git a/tests/ui/discarded_future.stderr b/tests/ui/discarded_future.stderr new file mode 100644 index 000000000000..180de2e37950 --- /dev/null +++ b/tests/ui/discarded_future.stderr @@ -0,0 +1,11 @@ +error: Discarding a Result: did you mean to call .await on this first? + --> tests/ui/discarded_future.rs:12:13 + | +LL | let _ = result_future(); + | ^^^^^^^^^^^^^^^ help: consider `.await` on it: `result_future().await` + | + = note: `-D clippy::discarded-future` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::discarded_future)]` + +error: aborting due to 1 previous error +