@@ -5,7 +5,7 @@ use rustc_middle::lint::in_external_macro;
5
5
use rustc_middle:: ty:: subst:: GenericArgKind ;
6
6
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
7
7
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} ;
9
9
10
10
declare_clippy_lint ! {
11
11
/// **What it does:** Checks for `let _ = <expr>`
@@ -58,7 +58,48 @@ declare_clippy_lint! {
58
58
"non-binding let on a synchronization lock"
59
59
}
60
60
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 ] ) ;
62
103
63
104
const SYNC_GUARD_PATHS : [ & [ & str ] ; 3 ] = [
64
105
& paths:: MUTEX_GUARD ,
@@ -84,6 +125,15 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
84
125
85
126
GenericArgKind :: Lifetime ( _) | GenericArgKind :: Const ( _) => false ,
86
127
} ) ;
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
+ ) ;
87
137
if contains_sync_guard {
88
138
span_lint_and_help(
89
139
cx,
@@ -94,6 +144,16 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
94
144
"consider using an underscore-prefixed named \
95
145
binding or dropping explicitly with `std::mem::drop`"
96
146
)
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
+ )
97
157
} else if is_must_use_ty( cx, cx. typeck_results( ) . expr_ty( init) ) {
98
158
span_lint_and_help(
99
159
cx,
0 commit comments