]> git.lizzy.rs Git - rust.git/blobdiff - src/tools/clippy/clippy_lints/src/let_underscore.rs
Rollup merge of #106287 - Nilstrieb:its-bugging-me-how-we-dont-have-docs, r=jyn514
[rust.git] / src / tools / clippy / clippy_lints / src / let_underscore.rs
index b7798b1c1d749ae488a6ed08fee2824f7ca9999f..61f87b91400d76405f8fcb914066040f49a9b4ff 100644 (file)
@@ -1,13 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type};
+use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
 use clippy_utils::{is_must_use_func_call, paths};
-use if_chain::if_chain;
 use rustc_hir::{Local, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{sym, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
     #[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! {
     /// ### What it does
-    /// Checks for `let _ = sync_lock`.
-    /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`.
+    /// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in
+    /// `parking_lot`. For `std` locks see the `rustc` lint
+    /// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock)
     ///
     /// ### Why is this bad?
     /// This statement immediately drops the lock instead of
     #[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_clippy_lint! {
     /// ### What it does
-    /// Checks for `let _ = <expr>`
-    /// where expr has a type that implements `Drop`
+    /// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
     ///
     /// ### Why is this bad?
-    /// This statement immediately drops the initializer
-    /// expression instead of extending its lifetime to the end of the scope, which
-    /// is often not intended. To extend the expression's lifetime to the end of the
-    /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
-    /// explicitly drop the expression, `std::mem::drop` conveys your intention
-    /// better and is less error-prone.
+    /// 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
-    /// # struct DroppableItem;
-    /// {
-    ///     let _ = DroppableItem;
-    ///     //                   ^ dropped here
-    ///     /* more code */
+    /// async fn foo() -> Result<(), ()> {
+    ///     Ok(())
     /// }
+    /// let _ = foo();
     /// ```
     ///
     /// Use instead:
     /// ```rust
-    /// # struct DroppableItem;
-    /// {
-    ///     let _droppable = DroppableItem;
-    ///     /* more code */
-    ///     // dropped at end of scope
+    /// # async fn context() {
+    /// async fn foo() -> Result<(), ()> {
+    ///     Ok(())
     /// }
+    /// let _ = foo().await;
+    /// # }
     /// ```
-    #[clippy::version = "1.50.0"]
-    pub LET_UNDERSCORE_DROP,
-    pedantic,
-    "non-binding let on a type that implements `Drop`"
+    #[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_DROP]);
-
-const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard];
+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,
 
 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
     fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
-        if in_external_macro(cx.tcx.sess, local.span) {
-            return;
-        }
-
-        if_chain! {
-            if let PatKind::Wild = local.pat.kind;
-            if let Some(init) = local.init;
-            then {
-                let init_ty = cx.typeck_results().expr_ty(init);
-                let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
-                    GenericArgKind::Type(inner_ty) => {
-                        SYNC_GUARD_SYMS
-                            .iter()
-                            .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym))
-                            || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
-                    },
-
-                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-                });
-                if contains_sync_guard {
-                    span_lint_and_help(
-                        cx,
-                        LET_UNDERSCORE_LOCK,
-                        local.span,
-                        "non-binding let on a synchronization lock",
-                        None,
-                        "consider using an underscore-prefixed named \
-                            binding or dropping explicitly with `std::mem::drop`",
-                    );
-                } else if init_ty.needs_drop(cx.tcx, cx.param_env) {
-                    span_lint_and_help(
-                        cx,
-                        LET_UNDERSCORE_DROP,
-                        local.span,
-                        "non-binding `let` on a type that implements `Drop`",
-                        None,
-                        "consider using an underscore-prefixed named \
+        if !in_external_macro(cx.tcx.sess, local.span)
+            && let PatKind::Wild = local.pat.kind
+            && let Some(init) = local.init
+        {
+            let init_ty = cx.typeck_results().expr_ty(init);
+            let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
+                GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
+                GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+            });
+            if contains_sync_guard {
+                span_lint_and_help(
+                    cx,
+                    LET_UNDERSCORE_LOCK,
+                    local.span,
+                    "non-binding `let` on a synchronization lock",
+                    None,
+                    "consider using an underscore-prefixed named \
                             binding 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",
-                        None,
-                        "consider explicitly using expression value",
-                    );
-                } else if is_must_use_func_call(cx, init) {
-                    span_lint_and_help(
-                        cx,
-                        LET_UNDERSCORE_MUST_USE,
-                        local.span,
-                        "non-binding let on a result of a `#[must_use]` function",
-                        None,
-                        "consider explicitly using function result",
-                    );
-                }
+                );
+            } 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",
+                    None,
+                    "consider explicitly using expression value",
+                );
+            } else if is_must_use_func_call(cx, init) {
+                span_lint_and_help(
+                    cx,
+                    LET_UNDERSCORE_MUST_USE,
+                    local.span,
+                    "non-binding `let` on a result of a `#[must_use]` function",
+                    None,
+                    "consider explicitly using function result",
+                );
             }
         }
     }