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