@@ -5,7 +5,7 @@ use rustc_middle::lint::in_external_macro;
55use rustc_middle:: ty:: subst:: GenericArgKind ;
66use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
77
8- use crate :: utils:: { is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help} ;
8+ use crate :: utils:: { implements_trait , is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help} ;
99
1010declare_clippy_lint ! {
1111 /// **What it does:** Checks for `let _ = <expr>`
@@ -58,7 +58,48 @@ declare_clippy_lint! {
5858 "non-binding let on a synchronization lock"
5959}
6060
61- declare_lint_pass ! ( LetUnderscore => [ LET_UNDERSCORE_MUST_USE , LET_UNDERSCORE_LOCK ] ) ;
61+ declare_clippy_lint ! {
62+ /// **What it does:** Checks for `let _ = <expr>`
63+ /// where expr has a type that implements `Drop`
64+ ///
65+ /// **Why is this bad?** This statement immediately drops the initializer
66+ /// expression instead of extending its lifetime to the end of the scope, which
67+ /// is often not intended. To extend the expression's lifetime to the end of the
68+ /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
69+ /// explicitly drop the expression, `std::mem::drop` conveys your intention
70+ /// better and is less error-prone.
71+ ///
72+ /// **Known problems:** None.
73+ ///
74+ /// **Example:**
75+ ///
76+ /// Bad:
77+ /// ```rust,ignore
78+ /// struct Droppable;
79+ /// impl Drop for Droppable {
80+ /// fn drop(&mut self) {}
81+ /// }
82+ /// {
83+ /// let _ = Droppable;
84+ /// // ^ dropped here
85+ /// /* more code */
86+ /// }
87+ /// ```
88+ ///
89+ /// Good:
90+ /// ```rust,ignore
91+ /// {
92+ /// let _droppable = Droppable;
93+ /// /* more code */
94+ /// // dropped at end of scope
95+ /// }
96+ /// ```
97+ pub LET_UNDERSCORE_DROP ,
98+ pedantic,
99+ "non-binding let on a type that implements `Drop`"
100+ }
101+
102+ declare_lint_pass ! ( LetUnderscore => [ LET_UNDERSCORE_MUST_USE , LET_UNDERSCORE_LOCK , LET_UNDERSCORE_DROP ] ) ;
62103
63104const SYNC_GUARD_PATHS : [ & [ & str ] ; 3 ] = [
64105 & paths:: MUTEX_GUARD ,
@@ -84,6 +125,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
84125
85126 GenericArgKind :: Lifetime ( _) | GenericArgKind :: Const ( _) => false ,
86127 } ) ;
128+ let implements_drop = cx. tcx. lang_items( ) . drop_trait( ) . map_or( false , |drop_trait|
129+ init_ty. walk( ) . any( |inner| match inner. unpack( ) {
130+ GenericArgKind :: Type ( inner_ty) => {
131+ implements_trait( cx, inner_ty, drop_trait, & [ ] )
132+ } ,
133+
134+ GenericArgKind :: Lifetime ( _) | GenericArgKind :: Const ( _) => false ,
135+ } )
136+ ) ;
87137 if contains_sync_guard {
88138 span_lint_and_help(
89139 cx,
@@ -94,6 +144,16 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
94144 "consider using an underscore-prefixed named \
95145 binding or dropping explicitly with `std::mem::drop`"
96146 )
147+ } else if implements_drop {
148+ span_lint_and_help(
149+ cx,
150+ LET_UNDERSCORE_DROP ,
151+ local. span,
152+ "non-binding `let` on a type that implements `Drop`" ,
153+ None ,
154+ "consider using an underscore-prefixed named \
155+ binding or dropping explicitly with `std::mem::drop`"
156+ )
97157 } else if is_must_use_ty( cx, cx. typeck_results( ) . expr_ty( init) ) {
98158 span_lint_and_help(
99159 cx,
0 commit comments