Skip to main content

rustc_lint/
drop_forget_useless.rs

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;
5
6use crate::diagnostics::{
7    DropCopyDiag, DropInPlaceCopyDiag, DropInPlaceRefDiag, DropRefDiag, ForgetCopyDiag,
8    ForgetRefDiag, UndroppedManuallyDropsDiag, UndroppedManuallyDropsInPlaceDiag,
9    UndroppedManuallyDropsInPlaceSuggestion, UndroppedManuallyDropsSuggestion,
10    UseLetUnderscoreIgnoreSuggestion,
11};
12use crate::utils::std_or_core;
13use crate::{LateContext, LateLintPass, LintContext};
14
15#[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.
39    pub 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}
43
44#[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.
62    pub FORGETTING_REFERENCES,
63    Warn,
64    "calls to `std::mem::forget` with a reference instead of an owned value"
65}
66
67#[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.
86    pub DROPPING_COPY_TYPES,
87    Warn,
88    "calls to `drop` and `drop_in_place` where the dropped value implements Copy"
89}
90
91#[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.
114    pub FORGETTING_COPY_TYPES,
115    Warn,
116    "calls to `std::mem::forget` with a value that implements Copy"
117}
118
119#[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.
137    pub UNDROPPED_MANUALLY_DROPS,
138    Deny,
139    "calls to `drop` and `drop_in_place` where the dropped value is `std::mem::ManuallyDrop`"
140}
141
142pub 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]);
143
144impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
145    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
146        let (fn_did, arg) = match expr.kind {
147            // matching on `function(<receiver>, ...)`
148            ExprKind::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(..)`
152            ExprKind::MethodCall(_, arg, _, _) => {
153                (cx.typeck_results().type_dependent_def_id(expr.hir_id), arg)
154            }
155            _ => return,
156        };
157
158        if let Some(fn_did) = fn_did
159            && let Some(fn_name) = cx.tcx.get_diagnostic_name(fn_did)
160        {
161            let arg_ty = cx.typeck_results().expr_ty(arg);
162            let is_copy = cx.type_is_copy_modulo_regions(arg_ty);
163            let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
164
165            let let_underscore_ignore_sugg = || {
166                if let Some((_, node)) = cx.tcx.hir_parent_iter(expr.hir_id).nth(0)
167                    && let Node::Stmt(stmt) = node
168                    && 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::Note
178                }
179            };
180
181            match fn_name {
182                sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => {
183                    cx.emit_span_lint(
184                        DROPPING_REFERENCES,
185                        expr.span,
186                        DropRefDiag { arg_ty, label: arg.span, sugg: let_underscore_ignore_sugg() },
187                    );
188                }
189                sym::ptr_drop_in_place | sym::ptr_drop_in_place_self
190                    if let &ty::RawPtr(inner_ty, _mutbl) = arg_ty.kind()
191                        && inner_ty.is_ref()
192                        && !drop_is_single_call_in_arm =>
193                {
194                    cx.emit_span_lint(
195                        DROPPING_REFERENCES,
196                        expr.span,
197                        DropInPlaceRefDiag {
198                            arg_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() => {
206                    cx.emit_span_lint(
207                        FORGETTING_REFERENCES,
208                        expr.span,
209                        ForgetRefDiag {
210                            arg_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 => {
217                    cx.emit_span_lint(
218                        DROPPING_COPY_TYPES,
219                        expr.span,
220                        DropCopyDiag {
221                            arg_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
228                    if 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                {
232                    cx.emit_span_lint(
233                        DROPPING_COPY_TYPES,
234                        expr.span,
235                        DropInPlaceCopyDiag {
236                            arg_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 => {
244                    cx.emit_span_lint(
245                        FORGETTING_COPY_TYPES,
246                        expr.span,
247                        ForgetCopyDiag {
248                            arg_ty,
249                            label: arg.span,
250                            sugg: let_underscore_ignore_sugg(),
251                        },
252                    );
253                }
254                sym::mem_drop
255                    if let ty::Adt(adt, _) = arg_ty.kind()
256                        && adt.is_manually_drop()
257                        && let Some(krate) = std_or_core(cx) =>
258                {
259                    cx.emit_span_lint(
260                        UNDROPPED_MANUALLY_DROPS,
261                        expr.span,
262                        UndroppedManuallyDropsDiag {
263                            krate,
264                            arg_ty,
265                            label: arg.span,
266                            suggestion: UndroppedManuallyDropsSuggestion {
267                                krate,
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
275                    if 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                {
280                    cx.emit_span_lint(
281                        UNDROPPED_MANUALLY_DROPS,
282                        expr.span,
283                        UndroppedManuallyDropsInPlaceDiag {
284                            krate,
285                            arg_ty,
286                            label: arg.span,
287                            suggestion: UndroppedManuallyDropsInPlaceSuggestion {
288                                krate,
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}
300
301// 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 {
315    if arg.can_have_side_effects() {
316        if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) {
317            return body.hir_id == drop_expr.hir_id;
318        }
319    }
320    false
321}