1use rustc_hir::{Arm, Expr, ExprKind, Node, StmtKind};
2use rustc_lint_defs::{declare_lint, declare_lint_pass};
3use rustc_middle::ty;
4use rustc_span::sym;
56use crate::diagnostics::{
7DropCopyDiag, DropInPlaceCopyDiag, DropInPlaceRefDiag, DropRefDiag, ForgetCopyDiag,
8ForgetRefDiag, UndroppedManuallyDropsDiag, UndroppedManuallyDropsInPlaceDiag,
9UndroppedManuallyDropsInPlaceSuggestion, UndroppedManuallyDropsSuggestion,
10UseLetUnderscoreIgnoreSuggestion,
11};
12use crate::utils::std_or_core;
13use crate::{LateContext, LateLintPass, LintContext};
1415#[doc =
r" The `dropping_references` lint checks for calls to `std::mem::drop`"]
#[doc =
r" and `std::ptr::drop_in_place` where the dropped type is a reference instead of"]
#[doc = r" an owned value."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc =
r" # fn operation_that_requires_mutex_to_be_unlocked() {} // just to make it compile"]
#[doc =
r" # let mutex = std::sync::Mutex::new(1); // just to make it compile"]
#[doc = r" let mut lock_guard = mutex.lock();"]
#[doc =
r" std::mem::drop(&lock_guard); // Should have been drop(lock_guard), mutex"]
#[doc = r" // still locked"]
#[doc = r" operation_that_requires_mutex_to_be_unlocked();"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" Calling `drop` on a reference will only drop the"]
#[doc =
r" reference itself, which is a no-op. It will not call the `drop` method (from"]
#[doc =
r" the `Drop` trait implementation) on the underlying referenced value, which"]
#[doc = r" is likely what was intended."]
pub static DROPPING_REFERENCES: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "DROPPING_REFERENCES",
default_level: ::rustc_lint_defs::Warn,
desc: "calls to `drop` and `drop_in_place` where the dropped type is a reference instead of an owned value",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
16/// The `dropping_references` lint checks for calls to `std::mem::drop`
17 /// and `std::ptr::drop_in_place` where the dropped type is a reference instead of
18 /// an owned value.
19 ///
20 /// ### Example
21 ///
22 /// ```rust
23 /// # fn operation_that_requires_mutex_to_be_unlocked() {} // just to make it compile
24 /// # let mutex = std::sync::Mutex::new(1); // just to make it compile
25 /// let mut lock_guard = mutex.lock();
26 /// std::mem::drop(&lock_guard); // Should have been drop(lock_guard), mutex
27 /// // still locked
28 /// operation_that_requires_mutex_to_be_unlocked();
29 /// ```
30 ///
31 /// {{produces}}
32 ///
33 /// ### Explanation
34 ///
35 /// Calling `drop` on a reference will only drop the
36 /// reference itself, which is a no-op. It will not call the `drop` method (from
37 /// the `Drop` trait implementation) on the underlying referenced value, which
38 /// is likely what was intended.
39pub DROPPING_REFERENCES,
40 Warn,
41"calls to `drop` and `drop_in_place` where the dropped type is a reference instead of an owned value"
42}4344#[doc =
r" The `forgetting_references` lint checks for calls to `std::mem::forget` with a reference"]
#[doc = r" instead of an owned value."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" let x = Box::new(1);"]
#[doc =
r" std::mem::forget(&x); // Should have been forget(x), x will still be dropped"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" Calling `forget` on a reference will only forget the"]
#[doc =
r" reference itself, which is a no-op. It will not forget the underlying"]
#[doc = r" referenced value, which is likely what was intended."]
pub static FORGETTING_REFERENCES: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "FORGETTING_REFERENCES",
default_level: ::rustc_lint_defs::Warn,
desc: "calls to `std::mem::forget` with a reference instead of an owned value",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
45/// The `forgetting_references` lint checks for calls to `std::mem::forget` with a reference
46 /// instead of an owned value.
47 ///
48 /// ### Example
49 ///
50 /// ```rust
51 /// let x = Box::new(1);
52 /// std::mem::forget(&x); // Should have been forget(x), x will still be dropped
53 /// ```
54 ///
55 /// {{produces}}
56 ///
57 /// ### Explanation
58 ///
59 /// Calling `forget` on a reference will only forget the
60 /// reference itself, which is a no-op. It will not forget the underlying
61 /// referenced value, which is likely what was intended.
62pub FORGETTING_REFERENCES,
63 Warn,
64"calls to `std::mem::forget` with a reference instead of an owned value"
65}6667#[doc =
r" The `dropping_copy_types` lint checks for calls to `std::mem::drop`"]
#[doc =
r" and `std::ptr::drop_in_place` where the dropped value implements the `Copy` trait."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" let x: i32 = 42; // i32 implements Copy"]
#[doc =
r" std::mem::drop(x); // A copy of x is passed to the function, leaving the"]
#[doc = r" // original unaffected"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" Calling `std::mem::drop` [does nothing for types that"]
#[doc =
r" implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the"]
#[doc = r" value will be copied and moved into the function on invocation."]
pub static DROPPING_COPY_TYPES: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "DROPPING_COPY_TYPES",
default_level: ::rustc_lint_defs::Warn,
desc: "calls to `drop` and `drop_in_place` where the dropped value implements Copy",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
68/// The `dropping_copy_types` lint checks for calls to `std::mem::drop`
69 /// and `std::ptr::drop_in_place` where the dropped value implements the `Copy` trait.
70 ///
71 /// ### Example
72 ///
73 /// ```rust
74 /// let x: i32 = 42; // i32 implements Copy
75 /// std::mem::drop(x); // A copy of x is passed to the function, leaving the
76 /// // original unaffected
77 /// ```
78 ///
79 /// {{produces}}
80 ///
81 /// ### Explanation
82 ///
83 /// Calling `std::mem::drop` [does nothing for types that
84 /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
85 /// value will be copied and moved into the function on invocation.
86pub DROPPING_COPY_TYPES,
87 Warn,
88"calls to `drop` and `drop_in_place` where the dropped value implements Copy"
89}9091#[doc =
r" The `forgetting_copy_types` lint checks for calls to `std::mem::forget` with a value"]
#[doc = r" that derives the Copy trait."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" let x: i32 = 42; // i32 implements Copy"]
#[doc =
r" std::mem::forget(x); // A copy of x is passed to the function, leaving the"]
#[doc = r" // original unaffected"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc = r" Calling `std::mem::forget` [does nothing for types that"]
#[doc =
r" implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the"]
#[doc = r" value will be copied and moved into the function on invocation."]
#[doc = r""]
#[doc =
r" An alternative, but also valid, explanation is that Copy types do not"]
#[doc =
r" implement the Drop trait, which means they have no destructors. Without a"]
#[doc = r" destructor, there is nothing for `std::mem::forget` to ignore."]
pub static FORGETTING_COPY_TYPES: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "FORGETTING_COPY_TYPES",
default_level: ::rustc_lint_defs::Warn,
desc: "calls to `std::mem::forget` with a value that implements Copy",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
92/// The `forgetting_copy_types` lint checks for calls to `std::mem::forget` with a value
93 /// that derives the Copy trait.
94 ///
95 /// ### Example
96 ///
97 /// ```rust
98 /// let x: i32 = 42; // i32 implements Copy
99 /// std::mem::forget(x); // A copy of x is passed to the function, leaving the
100 /// // original unaffected
101 /// ```
102 ///
103 /// {{produces}}
104 ///
105 /// ### Explanation
106 ///
107 /// Calling `std::mem::forget` [does nothing for types that
108 /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
109 /// value will be copied and moved into the function on invocation.
110 ///
111 /// An alternative, but also valid, explanation is that Copy types do not
112 /// implement the Drop trait, which means they have no destructors. Without a
113 /// destructor, there is nothing for `std::mem::forget` to ignore.
114pub FORGETTING_COPY_TYPES,
115 Warn,
116"calls to `std::mem::forget` with a value that implements Copy"
117}118119#[doc =
r" The `undropped_manually_drops` lint check for calls to `std::mem::drop`"]
#[doc =
r" and `std::ptr::drop_in_place` where the dropped value is `std::mem::ManuallyDrop`"]
#[doc = r" which doesn't drop."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,compile_fail"]
#[doc = r" struct S;"]
#[doc = r" drop(std::mem::ManuallyDrop::new(S));"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" `ManuallyDrop` does not drop it's inner value so calling `std::mem::drop` will"]
#[doc = r" not drop the inner value of the `ManuallyDrop` either."]
pub static UNDROPPED_MANUALLY_DROPS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "UNDROPPED_MANUALLY_DROPS",
default_level: ::rustc_lint_defs::Deny,
desc: "calls to `drop` and `drop_in_place` where the dropped value is `std::mem::ManuallyDrop`",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
120/// The `undropped_manually_drops` lint check for calls to `std::mem::drop`
121 /// and `std::ptr::drop_in_place` where the dropped value is `std::mem::ManuallyDrop`
122 /// which doesn't drop.
123 ///
124 /// ### Example
125 ///
126 /// ```rust,compile_fail
127 /// struct S;
128 /// drop(std::mem::ManuallyDrop::new(S));
129 /// ```
130 ///
131 /// {{produces}}
132 ///
133 /// ### Explanation
134 ///
135 /// `ManuallyDrop` does not drop it's inner value so calling `std::mem::drop` will
136 /// not drop the inner value of the `ManuallyDrop` either.
137pub UNDROPPED_MANUALLY_DROPS,
138 Deny,
139"calls to `drop` and `drop_in_place` where the dropped value is `std::mem::ManuallyDrop`"
140}141142pub struct DropForgetUseless;
#[automatically_derived]
impl ::core::marker::Copy for DropForgetUseless { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for DropForgetUseless { }
#[automatically_derived]
impl ::core::clone::Clone for DropForgetUseless {
#[inline]
fn clone(&self) -> Self { *self }
}
impl ::rustc_lint_defs::LintPass for DropForgetUseless {
fn name(&self) -> &'static str { "DropForgetUseless" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[DROPPING_REFERENCES, FORGETTING_REFERENCES,
DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES,
UNDROPPED_MANUALLY_DROPS]))
}
}
impl DropForgetUseless {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[DROPPING_REFERENCES, FORGETTING_REFERENCES,
DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES,
UNDROPPED_MANUALLY_DROPS]))
}
}declare_lint_pass!(DropForgetUseless => [DROPPING_REFERENCES, FORGETTING_REFERENCES, DROPPING_COPY_TYPES, FORGETTING_COPY_TYPES, UNDROPPED_MANUALLY_DROPS]);
143144impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
145fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
146let (fn_did, arg) = match expr.kind {
147// matching on `function(<receiver>, ...)`
148ExprKind::Call(path, [arg]) if let ExprKind::Path(ref qpath) = path.kind => {
149 (cx.qpath_res(qpath, path.hir_id).opt_def_id(), arg)
150 }
151// matching on `<receiver>.method(..)`
152ExprKind::MethodCall(_, arg, _, _) => {
153 (cx.typeck_results().type_dependent_def_id(expr.hir_id), arg)
154 }
155_ => return,
156 };
157158if let Some(fn_did) = fn_did159 && let Some(fn_name) = cx.tcx.get_diagnostic_name(fn_did)
160 {
161let arg_ty = cx.typeck_results().expr_ty(arg);
162let is_copy = cx.type_is_copy_modulo_regions(arg_ty);
163let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
164165let let_underscore_ignore_sugg = || {
166if let Some((_, node)) = cx.tcx.hir_parent_iter(expr.hir_id).nth(0)
167 && let Node::Stmt(stmt) = node168 && let StmtKind::Semi(e) = stmt.kind
169 && e.hir_id == expr.hir_id
170 && let Some(arg_span) = arg.span.find_ancestor_inside_same_ctxt(expr.span)
171 {
172 UseLetUnderscoreIgnoreSuggestion::Suggestion {
173 start_span: expr.span.shrink_to_lo().until(arg_span),
174 end_span: arg_span.shrink_to_hi().until(expr.span.shrink_to_hi()),
175 }
176 } else {
177 UseLetUnderscoreIgnoreSuggestion::Note178 }
179 };
180181match fn_name {
182 sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => {
183cx.emit_span_lint(
184DROPPING_REFERENCES,
185expr.span,
186DropRefDiag { arg_ty, label: arg.span, sugg: let_underscore_ignore_sugg() },
187 );
188 }
189 sym::ptr_drop_in_place | sym::ptr_drop_in_place_self
190if let &ty::RawPtr(inner_ty, _mutbl) = arg_ty.kind()
191 && inner_ty.is_ref()
192 && !drop_is_single_call_in_arm =>
193 {
194cx.emit_span_lint(
195DROPPING_REFERENCES,
196expr.span,
197DropInPlaceRefDiag {
198arg_ty,
199 label: arg.span,
200 sugg: let_underscore_ignore_sugg(),
201 from_fn: fn_name == sym::ptr_drop_in_place,
202 },
203 );
204 }
205 sym::mem_forget if arg_ty.is_ref() => {
206cx.emit_span_lint(
207FORGETTING_REFERENCES,
208expr.span,
209ForgetRefDiag {
210arg_ty,
211 label: arg.span,
212 sugg: let_underscore_ignore_sugg(),
213 },
214 );
215 }
216 sym::mem_drop if is_copy && !drop_is_single_call_in_arm => {
217cx.emit_span_lint(
218DROPPING_COPY_TYPES,
219expr.span,
220DropCopyDiag {
221arg_ty,
222 label: arg.span,
223 sugg: let_underscore_ignore_sugg(),
224 },
225 );
226 }
227 sym::ptr_drop_in_place | sym::ptr_drop_in_place_self
228if let &ty::RawPtr(inner_ty, _mutbl) = arg_ty.kind()
229 && cx.type_is_copy_modulo_regions(inner_ty)
230 && !drop_is_single_call_in_arm =>
231 {
232cx.emit_span_lint(
233DROPPING_COPY_TYPES,
234expr.span,
235DropInPlaceCopyDiag {
236arg_ty,
237 label: arg.span,
238 sugg: let_underscore_ignore_sugg(),
239 from_fn: fn_name == sym::ptr_drop_in_place,
240 },
241 );
242 }
243 sym::mem_forget if is_copy => {
244cx.emit_span_lint(
245FORGETTING_COPY_TYPES,
246expr.span,
247ForgetCopyDiag {
248arg_ty,
249 label: arg.span,
250 sugg: let_underscore_ignore_sugg(),
251 },
252 );
253 }
254 sym::mem_drop
255if let ty::Adt(adt, _) = arg_ty.kind()
256 && adt.is_manually_drop()
257 && let Some(krate) = std_or_core(cx) =>
258 {
259cx.emit_span_lint(
260UNDROPPED_MANUALLY_DROPS,
261expr.span,
262UndroppedManuallyDropsDiag {
263krate,
264arg_ty,
265 label: arg.span,
266 suggestion: UndroppedManuallyDropsSuggestion {
267krate,
268 start_span: arg.span.shrink_to_lo(),
269 end_span: arg.span.shrink_to_hi(),
270 },
271 },
272 );
273 }
274 sym::ptr_drop_in_place | sym::ptr_drop_in_place_self
275if let &ty::RawPtr(inner_ty, _mutbl) = arg_ty.kind()
276 && let ty::Adt(adt, _) = inner_ty.kind()
277 && adt.is_manually_drop()
278 && let Some(krate) = std_or_core(cx) =>
279 {
280cx.emit_span_lint(
281UNDROPPED_MANUALLY_DROPS,
282expr.span,
283UndroppedManuallyDropsInPlaceDiag {
284krate,
285arg_ty,
286 label: arg.span,
287 suggestion: UndroppedManuallyDropsInPlaceSuggestion {
288krate,
289 start_span: expr.span.shrink_to_lo().until(arg.span.shrink_to_lo()),
290 end_span: arg.span.shrink_to_hi().until(expr.span.shrink_to_hi()),
291 },
292 },
293 );
294 }
295_ => return,
296 };
297 }
298 }
299}
300301// Dropping returned value of a function, as in the following snippet is considered idiomatic, see
302// rust-lang/rust-clippy#9482 for examples.
303//
304// ```
305// match <var> {
306// <pat> => drop(fn_with_side_effect_and_returning_some_value()),
307// ..
308// }
309// ```
310fn is_single_call_in_arm<'tcx>(
311 cx: &LateContext<'tcx>,
312 arg: &'tcx Expr<'_>,
313 drop_expr: &'tcx Expr<'_>,
314) -> bool {
315if arg.can_have_side_effects() {
316if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) {
317return body.hir_id == drop_expr.hir_id;
318 }
319 }
320false
321}