diff --git a/CHANGELOG.md b/CHANGELOG.md
index adde7af9171..8330ee899c8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4123,6 +4123,7 @@ Released 2018-09-13
 [`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 [`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 [`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
+[`let_underscore_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future
 [`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 [`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 [`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
diff --git a/clippy_lints/foo.txt b/clippy_lints/foo.txt
new file mode 100644
index 00000000000..5ab2f8a4323
--- /dev/null
+++ b/clippy_lints/foo.txt
@@ -0,0 +1 @@
+Hello
\ No newline at end of file
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index f4fc2bd3330..747636b7ec3 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -215,6 +215,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO,
     crate::len_zero::LEN_ZERO_INFO,
     crate::let_if_seq::USELESS_LET_IF_SEQ_INFO,
+    crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO,
     crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
     crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
     crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs
index cfc1a21ea87..61f87b91400 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};
@@ -28,7 +28,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.42.0"]
     pub LET_UNDERSCORE_MUST_USE,
     restriction,
-    "non-binding let on a `#[must_use]` expression"
+    "non-binding `let` on a `#[must_use]` expression"
 }
 
 declare_clippy_lint! {
@@ -56,10 +56,41 @@ declare_clippy_lint! {
     #[clippy::version = "1.43.0"]
     pub LET_UNDERSCORE_LOCK,
     correctness,
-    "non-binding let on a synchronization lock"
+    "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
+    /// async fn foo() -> Result<(), ()> {
+    ///     Ok(())
+    /// }
+    /// let _ = foo();
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # async fn context() {
+    /// async fn foo() -> Result<(), ()> {
+    ///     Ok(())
+    /// }
+    /// 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,
@@ -83,17 +114,27 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
                     cx,
                     LET_UNDERSCORE_LOCK,
                     local.span,
-                    "non-binding let on a synchronization lock",
+                    "non-binding `let` on a synchronization lock",
                     None,
                     "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,
                     LET_UNDERSCORE_MUST_USE,
                     local.span,
-                    "non-binding let on an expression with `#[must_use]` type",
+                    "non-binding `let` on an expression with `#[must_use]` type",
                     None,
                     "consider explicitly using expression value",
                 );
@@ -102,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
                     cx,
                     LET_UNDERSCORE_MUST_USE,
                     local.span,
-                    "non-binding let on a result of a `#[must_use]` function",
+                    "non-binding `let` on a result of a `#[must_use]` function",
                     None,
                     "consider explicitly using function result",
                 );
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..33a748736a8
--- /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
+
diff --git a/tests/ui/let_underscore_lock.stderr b/tests/ui/let_underscore_lock.stderr
index 89350b92e82..f137d411209 100644
--- a/tests/ui/let_underscore_lock.stderr
+++ b/tests/ui/let_underscore_lock.stderr
@@ -1,4 +1,4 @@
-error: non-binding let on a synchronization lock
+error: non-binding `let` on a synchronization lock
   --> $DIR/let_underscore_lock.rs:9:5
    |
 LL |     let _ = p_m.lock();
@@ -7,7 +7,7 @@ LL |     let _ = p_m.lock();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
    = note: `-D clippy::let-underscore-lock` implied by `-D warnings`
 
-error: non-binding let on a synchronization lock
+error: non-binding `let` on a synchronization lock
   --> $DIR/let_underscore_lock.rs:12:5
    |
 LL |     let _ = p_m1.lock();
@@ -15,7 +15,7 @@ LL |     let _ = p_m1.lock();
    |
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
-error: non-binding let on a synchronization lock
+error: non-binding `let` on a synchronization lock
   --> $DIR/let_underscore_lock.rs:15:5
    |
 LL |     let _ = p_rw.read();
@@ -23,7 +23,7 @@ LL |     let _ = p_rw.read();
    |
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
-error: non-binding let on a synchronization lock
+error: non-binding `let` on a synchronization lock
   --> $DIR/let_underscore_lock.rs:16:5
    |
 LL |     let _ = p_rw.write();
diff --git a/tests/ui/let_underscore_must_use.stderr b/tests/ui/let_underscore_must_use.stderr
index bae60f2ff9b..28d760eb46e 100644
--- a/tests/ui/let_underscore_must_use.stderr
+++ b/tests/ui/let_underscore_must_use.stderr
@@ -1,4 +1,4 @@
-error: non-binding let on a result of a `#[must_use]` function
+error: non-binding `let` on a result of a `#[must_use]` function
   --> $DIR/let_underscore_must_use.rs:67:5
    |
 LL |     let _ = f();
@@ -7,7 +7,7 @@ LL |     let _ = f();
    = help: consider explicitly using function result
    = note: `-D clippy::let-underscore-must-use` implied by `-D warnings`
 
-error: non-binding let on an expression with `#[must_use]` type
+error: non-binding `let` on an expression with `#[must_use]` type
   --> $DIR/let_underscore_must_use.rs:68:5
    |
 LL |     let _ = g();
@@ -15,7 +15,7 @@ LL |     let _ = g();
    |
    = help: consider explicitly using expression value
 
-error: non-binding let on a result of a `#[must_use]` function
+error: non-binding `let` on a result of a `#[must_use]` function
   --> $DIR/let_underscore_must_use.rs:70:5
    |
 LL |     let _ = l(0_u32);
@@ -23,7 +23,7 @@ LL |     let _ = l(0_u32);
    |
    = help: consider explicitly using function result
 
-error: non-binding let on a result of a `#[must_use]` function
+error: non-binding `let` on a result of a `#[must_use]` function
   --> $DIR/let_underscore_must_use.rs:74:5
    |
 LL |     let _ = s.f();
@@ -31,7 +31,7 @@ LL |     let _ = s.f();
    |
    = help: consider explicitly using function result
 
-error: non-binding let on an expression with `#[must_use]` type
+error: non-binding `let` on an expression with `#[must_use]` type
   --> $DIR/let_underscore_must_use.rs:75:5
    |
 LL |     let _ = s.g();
@@ -39,7 +39,7 @@ LL |     let _ = s.g();
    |
    = help: consider explicitly using expression value
 
-error: non-binding let on a result of a `#[must_use]` function
+error: non-binding `let` on a result of a `#[must_use]` function
   --> $DIR/let_underscore_must_use.rs:78:5
    |
 LL |     let _ = S::h();
@@ -47,7 +47,7 @@ LL |     let _ = S::h();
    |
    = help: consider explicitly using function result
 
-error: non-binding let on an expression with `#[must_use]` type
+error: non-binding `let` on an expression with `#[must_use]` type
   --> $DIR/let_underscore_must_use.rs:79:5
    |
 LL |     let _ = S::p();
@@ -55,7 +55,7 @@ LL |     let _ = S::p();
    |
    = help: consider explicitly using expression value
 
-error: non-binding let on a result of a `#[must_use]` function
+error: non-binding `let` on a result of a `#[must_use]` function
   --> $DIR/let_underscore_must_use.rs:81:5
    |
 LL |     let _ = S::a();
@@ -63,7 +63,7 @@ LL |     let _ = S::a();
    |
    = help: consider explicitly using function result
 
-error: non-binding let on an expression with `#[must_use]` type
+error: non-binding `let` on an expression with `#[must_use]` type
   --> $DIR/let_underscore_must_use.rs:83:5
    |
 LL |     let _ = if true { Ok(()) } else { Err(()) };
@@ -71,7 +71,7 @@ LL |     let _ = if true { Ok(()) } else { Err(()) };
    |
    = help: consider explicitly using expression value
 
-error: non-binding let on a result of a `#[must_use]` function
+error: non-binding `let` on a result of a `#[must_use]` function
   --> $DIR/let_underscore_must_use.rs:87:5
    |
 LL |     let _ = a.is_ok();
@@ -79,7 +79,7 @@ LL |     let _ = a.is_ok();
    |
    = help: consider explicitly using function result
 
-error: non-binding let on an expression with `#[must_use]` type
+error: non-binding `let` on an expression with `#[must_use]` type
   --> $DIR/let_underscore_must_use.rs:89:5
    |
 LL |     let _ = a.map(|_| ());
@@ -87,7 +87,7 @@ LL |     let _ = a.map(|_| ());
    |
    = help: consider explicitly using expression value
 
-error: non-binding let on an expression with `#[must_use]` type
+error: non-binding `let` on an expression with `#[must_use]` type
   --> $DIR/let_underscore_must_use.rs:91:5
    |
 LL |     let _ = a;