@@ -6,6 +6,7 @@ use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
6
6
use rustc_lint:: { LateContext , LateLintPass } ;
7
7
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
8
8
use rustc_span:: sym;
9
+ use std:: borrow:: Cow ;
9
10
10
11
declare_clippy_lint ! {
11
12
/// ### What it does
@@ -76,6 +77,27 @@ declare_clippy_lint! {
76
77
"use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
77
78
}
78
79
80
+ declare_clippy_lint ! {
81
+ /// ### What it does
82
+ /// Checks for usage of `std::mem::forget(t)` where `t` is
83
+ /// `Drop` or has a field that implements `Drop`.
84
+ ///
85
+ /// ### Why is this bad?
86
+ /// `std::mem::forget(t)` prevents `t` from running its
87
+ /// destructor, possibly causing leaks.
88
+ ///
89
+ /// ### Example
90
+ /// ```rust
91
+ /// # use std::mem;
92
+ /// # use std::rc::Rc;
93
+ /// mem::forget(Rc::new(55))
94
+ /// ```
95
+ #[ clippy:: version = "pre 1.29.0" ]
96
+ pub MEM_FORGET ,
97
+ restriction,
98
+ "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
99
+ }
100
+
79
101
const DROP_NON_DROP_SUMMARY : & str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
80
102
Dropping such a type only extends its contained lifetimes";
81
103
const FORGET_NON_DROP_SUMMARY : & str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
@@ -84,7 +106,8 @@ const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value t
84
106
declare_lint_pass ! ( DropForgetRef => [
85
107
DROP_NON_DROP ,
86
108
FORGET_NON_DROP ,
87
- UNDROPPED_MANUALLY_DROPS
109
+ UNDROPPED_MANUALLY_DROPS ,
110
+ MEM_FORGET ,
88
111
] ) ;
89
112
90
113
impl < ' tcx > LateLintPass < ' tcx > for DropForgetRef {
@@ -97,7 +120,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
97
120
let arg_ty = cx. typeck_results ( ) . expr_ty ( arg) ;
98
121
let is_copy = is_copy ( cx, arg_ty) ;
99
122
let drop_is_single_call_in_arm = is_single_call_in_arm ( cx, arg, expr) ;
100
- let ( lint, msg) = match fn_name {
123
+ let ( lint, msg, note_span ) = match fn_name {
101
124
// early return for uplifted lints: dropping_references, dropping_copy_types, forgetting_references, forgetting_copy_types
102
125
sym:: mem_drop if arg_ty. is_ref ( ) && !drop_is_single_call_in_arm => return ,
103
126
sym:: mem_forget if arg_ty. is_ref ( ) => return ,
@@ -121,19 +144,34 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
121
144
|| drop_is_single_call_in_arm
122
145
) =>
123
146
{
124
- ( DROP_NON_DROP , DROP_NON_DROP_SUMMARY )
125
- } ,
126
- sym:: mem_forget if !arg_ty. needs_drop ( cx. tcx , cx. param_env ) => {
127
- ( FORGET_NON_DROP , FORGET_NON_DROP_SUMMARY )
147
+ ( DROP_NON_DROP , DROP_NON_DROP_SUMMARY . into ( ) , Some ( arg. span ) )
128
148
} ,
149
+ sym:: mem_forget => {
150
+ if arg_ty. needs_drop ( cx. tcx , cx. param_env ) {
151
+ (
152
+ MEM_FORGET ,
153
+ Cow :: Owned ( format ! (
154
+ "usage of `mem::forget` on {}" ,
155
+ if arg_ty. ty_adt_def( ) . map_or( false , |def| def. has_dtor( cx. tcx) ) {
156
+ "`Drop` type"
157
+ } else {
158
+ "type with `Drop` fields"
159
+ }
160
+ ) ) ,
161
+ None ,
162
+ )
163
+ } else {
164
+ ( FORGET_NON_DROP , FORGET_NON_DROP_SUMMARY . into ( ) , Some ( arg. span ) )
165
+ }
166
+ }
129
167
_ => return ,
130
168
} ;
131
169
span_lint_and_note (
132
170
cx,
133
171
lint,
134
172
expr. span ,
135
- msg,
136
- Some ( arg . span ) ,
173
+ & msg,
174
+ note_span ,
137
175
& format ! ( "argument has type `{arg_ty}`" ) ,
138
176
) ;
139
177
}
0 commit comments