about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSteven Casper <sebastiancasper3@gmail.com>2022-10-31 12:50:59 -0700
committerSteven Casper <sebastiancasper3@gmail.com>2022-10-31 12:50:59 -0700
commit6dcade0692fc516752c811d2b05302f2ed97aaeb (patch)
tree1ef8fcab22ee0b8b5f8c144a2981d8ccd8dbae87
parent37d338c1efd7840fa96c5eaeaa5785094efdab07 (diff)
downloadrust-6dcade0692fc516752c811d2b05302f2ed97aaeb.tar.gz
rust-6dcade0692fc516752c811d2b05302f2ed97aaeb.zip
Implement let_underscore_future
-rw-r--r--clippy_lints/src/let_underscore.rs38
-rw-r--r--tests/ui/let_underscore_future.rs20
-rw-r--r--tests/ui/let_underscore_future.stderr27
3 files changed, 83 insertions, 2 deletions
diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs
index cfc1a21ea87..4855f301d0d 100644
--- a/clippy_lints/src/let_underscore.rs
+++ b/clippy_lints/src/let_underscore.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::{is_must_use_ty, match_type};
+use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
 use clippy_utils::{is_must_use_func_call, paths};
 use rustc_hir::{Local, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -59,7 +59,31 @@ declare_clippy_lint! {
     "non-binding let on a synchronization lock"
 }
 
-declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK]);
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
+    ///
+    /// ### Why is this bad?
+    /// Futures must be polled for work to be done. The original intention was most likely to await the future
+    /// and ignore the resulting value.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// async fn foo() -> Result<(), ()> { }
+    /// let _ = foo();
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// let _ = foo().await;
+    /// ```
+    #[clippy::version = "1.66"]
+    pub LET_UNDERSCORE_FUTURE,
+    suspicious,
+    "non-binding let on a future"
+}
+
+declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]);
 
 const SYNC_GUARD_PATHS: [&[&str]; 3] = [
     &paths::PARKING_LOT_MUTEX_GUARD,
@@ -88,6 +112,16 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
                     "consider using an underscore-prefixed named \
                             binding or dropping explicitly with `std::mem::drop`",
                 );
+            } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
+                && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) {
+                span_lint_and_help(
+                    cx,
+                    LET_UNDERSCORE_FUTURE,
+                    local.span,
+                    "non-binding let on a future",
+                    None,
+                    "consider awaiting the future or dropping explicitly with `std::mem::drop`"
+                );
             } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
                 span_lint_and_help(
                     cx,
diff --git a/tests/ui/let_underscore_future.rs b/tests/ui/let_underscore_future.rs
new file mode 100644
index 00000000000..d8f54cdca91
--- /dev/null
+++ b/tests/ui/let_underscore_future.rs
@@ -0,0 +1,20 @@
+use std::future::Future;
+
+async fn some_async_fn() {}
+
+fn sync_side_effects() {}
+fn custom() -> impl Future<Output = ()> {
+    sync_side_effects();
+    async {}
+}
+
+fn do_something_to_future(future: &mut impl Future<Output = ()>) {}
+
+fn main() {
+    let _ = some_async_fn();
+    let _ = custom();
+
+    let mut future = some_async_fn();
+    do_something_to_future(&mut future);
+    let _ = future;
+}
diff --git a/tests/ui/let_underscore_future.stderr b/tests/ui/let_underscore_future.stderr
new file mode 100644
index 00000000000..f1b9b1d709e
--- /dev/null
+++ b/tests/ui/let_underscore_future.stderr
@@ -0,0 +1,27 @@
+error: non-binding let on a future
+  --> $DIR/let_underscore_future.rs:14:5
+   |
+LL |     let _ = some_async_fn();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider awaiting the future or dropping explicitly with `std::mem::drop`
+   = note: `-D clippy::let-underscore-future` implied by `-D warnings`
+
+error: non-binding let on a future
+  --> $DIR/let_underscore_future.rs:15:5
+   |
+LL |     let _ = custom();
+   |     ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider awaiting the future or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a future
+  --> $DIR/let_underscore_future.rs:19:5
+   |
+LL |     let _ = future;
+   |     ^^^^^^^^^^^^^^^
+   |
+   = help: consider awaiting the future or dropping explicitly with `std::mem::drop`
+
+error: aborting due to 3 previous errors
+