about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSamuel E. Moelius III <sam@moeli.us>2020-11-07 18:26:14 -0500
committerSamuel E. Moelius III <sam@moeli.us>2020-11-08 07:00:35 -0500
commitf1f780c9422f038ae78e72a99b1ca2a0d7b392bc (patch)
tree8de0cea1a46e40758688b34fc2d6537e95d36d63
parent96d5f45adee883c565062fbb44de7ea29918539d (diff)
downloadrust-f1f780c9422f038ae78e72a99b1ca2a0d7b392bc.tar.gz
rust-f1f780c9422f038ae78e72a99b1ca2a0d7b392bc.zip
Add let_underscore_drop
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/let_underscore.rs56
-rw-r--r--clippy_lints/src/lib.rs3
-rw-r--r--src/lintlist/mod.rs7
-rw-r--r--tests/ui/let_underscore_drop.rs19
-rw-r--r--tests/ui/let_underscore_drop.stderr27
6 files changed, 111 insertions, 2 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e0770a45c53..816d25bcd93 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1787,6 +1787,7 @@ Released 2018-09-13
 [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 [`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_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/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs
index ae2f6131b5b..9c7fd634547 100644
--- a/clippy_lints/src/let_underscore.rs
+++ b/clippy_lints/src/let_underscore.rs
@@ -5,7 +5,7 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};
+use crate::utils::{implements_trait, is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for `let _ = <expr>`
@@ -58,7 +58,40 @@ 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 expr has a type that implements `Drop`
+    ///
+    /// **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.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// Bad:
+    /// ```rust,ignore
+    /// struct Droppable;
+    /// impl Drop for Droppable {
+    ///     fn drop(&mut self) {}
+    /// }
+    /// let _ = Droppable;
+    /// ```
+    ///
+    /// Good:
+    /// ```rust,ignore
+    /// let _droppable = Droppable;
+    /// ```
+    pub LET_UNDERSCORE_DROP,
+    correctness,
+    "non-binding let on a type that implements `Drop`"
+}
+
+declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
 
 const SYNC_GUARD_PATHS: [&[&str]; 3] = [
     &paths::MUTEX_GUARD,
@@ -84,6 +117,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
 
                     GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
                 });
+                let implements_drop = cx.tcx.lang_items().drop_trait().map_or(false, |drop_trait|
+                    init_ty.walk().any(|inner| match inner.unpack() {
+                        GenericArgKind::Type(inner_ty) => {
+                            implements_trait(cx, inner_ty, drop_trait, &[])
+                        },
+
+                        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+                    })
+                );
                 if contains_sync_guard {
                     span_lint_and_help(
                         cx,
@@ -94,6 +136,16 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
                         "consider using an underscore-prefixed named \
                             binding or dropping explicitly with `std::mem::drop`"
                     )
+                } else if implements_drop {
+                    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 \
+                            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,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 1f4bed92e69..b44e28b4596 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -622,6 +622,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &len_zero::LEN_WITHOUT_IS_EMPTY,
         &len_zero::LEN_ZERO,
         &let_if_seq::USELESS_LET_IF_SEQ,
+        &let_underscore::LET_UNDERSCORE_DROP,
         &let_underscore::LET_UNDERSCORE_LOCK,
         &let_underscore::LET_UNDERSCORE_MUST_USE,
         &lifetimes::EXTRA_UNUSED_LIFETIMES,
@@ -1383,6 +1384,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&len_zero::COMPARISON_TO_EMPTY),
         LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
         LintId::of(&len_zero::LEN_ZERO),
+        LintId::of(&let_underscore::LET_UNDERSCORE_DROP),
         LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
         LintId::of(&lifetimes::EXTRA_UNUSED_LIFETIMES),
         LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
@@ -1809,6 +1811,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&infinite_iter::INFINITE_ITER),
         LintId::of(&inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
         LintId::of(&inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
+        LintId::of(&let_underscore::LET_UNDERSCORE_DROP),
         LintId::of(&let_underscore::LET_UNDERSCORE_LOCK),
         LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
         LintId::of(&loops::FOR_LOOPS_OVER_FALLIBLES),
diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs
index bc0a0ad2b17..8b5e6cc916f 100644
--- a/src/lintlist/mod.rs
+++ b/src/lintlist/mod.rs
@@ -1118,6 +1118,13 @@ vec![
         module: "returns",
     },
     Lint {
+        name: "let_underscore_drop",
+        group: "correctness",
+        desc: "non-binding let on a type that implements `Drop`",
+        deprecation: None,
+        module: "let_underscore",
+    },
+    Lint {
         name: "let_underscore_lock",
         group: "correctness",
         desc: "non-binding let on a synchronization lock",
diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs
new file mode 100644
index 00000000000..98593edb9c5
--- /dev/null
+++ b/tests/ui/let_underscore_drop.rs
@@ -0,0 +1,19 @@
+#![warn(clippy::let_underscore_drop)]
+
+struct Droppable;
+
+impl Drop for Droppable {
+    fn drop(&mut self) {}
+}
+
+fn main() {
+    let unit = ();
+    let boxed = Box::new(());
+    let droppable = Droppable;
+    let optional = Some(Droppable);
+
+    let _ = ();
+    let _ = Box::new(());
+    let _ = Droppable;
+    let _ = Some(Droppable);
+}
diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr
new file mode 100644
index 00000000000..6dc8904c4fe
--- /dev/null
+++ b/tests/ui/let_underscore_drop.stderr
@@ -0,0 +1,27 @@
+error: non-binding let on a type that implements `Drop`
+  --> $DIR/let_underscore_drop.rs:16:5
+   |
+LL |     let _ = Box::new(());
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::let-underscore-drop` implied by `-D warnings`
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a type that implements `Drop`
+  --> $DIR/let_underscore_drop.rs:17:5
+   |
+LL |     let _ = Droppable;
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: non-binding let on a type that implements `Drop`
+  --> $DIR/let_underscore_drop.rs:18:5
+   |
+LL |     let _ = Some(Droppable);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
+
+error: aborting due to 3 previous errors
+