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;