@@ -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,40 @@ 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+ /// let _ = Droppable;
83+ /// ```
84+ ///
85+ /// Good:
86+ /// ```rust,ignore
87+ /// let _droppable = Droppable;
88+ /// ```
89+ pub LET_UNDERSCORE_DROP ,
90+ correctness,
91+ "non-binding let on a type that implements `Drop`"
92+ }
93+
94+ declare_lint_pass ! ( LetUnderscore => [ LET_UNDERSCORE_MUST_USE , LET_UNDERSCORE_LOCK , LET_UNDERSCORE_DROP ] ) ;
6295
6396const SYNC_GUARD_PATHS : [ & [ & str ] ; 3 ] = [
6497 & paths:: MUTEX_GUARD ,
@@ -84,6 +117,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
84117
85118 GenericArgKind :: Lifetime ( _) | GenericArgKind :: Const ( _) => false ,
86119 } ) ;
120+ let implements_drop = cx. tcx. lang_items( ) . drop_trait( ) . map_or( false , |drop_trait|
121+ init_ty. walk( ) . any( |inner| match inner. unpack( ) {
122+ GenericArgKind :: Type ( inner_ty) => {
123+ implements_trait( cx, inner_ty, drop_trait, & [ ] )
124+ } ,
125+
126+ GenericArgKind :: Lifetime ( _) | GenericArgKind :: Const ( _) => false ,
127+ } )
128+ ) ;
87129 if contains_sync_guard {
88130 span_lint_and_help(
89131 cx,
@@ -94,6 +136,16 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
94136 "consider using an underscore-prefixed named \
95137 binding or dropping explicitly with `std::mem::drop`"
96138 )
139+ } else if implements_drop {
140+ span_lint_and_help(
141+ cx,
142+ LET_UNDERSCORE_DROP ,
143+ local. span,
144+ "non-binding let on a type that implements `Drop`" ,
145+ None ,
146+ "consider using an underscore-prefixed named \
147+ binding or dropping explicitly with `std::mem::drop`"
148+ )
97149 } else if is_must_use_ty( cx, cx. typeck_results( ) . expr_ty( init) ) {
98150 span_lint_and_help(
99151 cx,
0 commit comments