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::{LateContext, LateLintPass, LintContext};
13
14#[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.
38    pub 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}
42
43#[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.
61    pub FORGETTING_REFERENCES,
62    Warn,
63    "calls to `std::mem::forget` with a reference instead of an owned value"
64}
65
66#[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.
85    pub DROPPING_COPY_TYPES,
86    Warn,
87    "calls to `drop` and `drop_in_place` where the dropped value implements Copy"
88}
89
90#[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.
113    pub FORGETTING_COPY_TYPES,
114    Warn,
115    "calls to `std::mem::forget` with a value that implements Copy"
116}
117
118#[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.
136    pub UNDROPPED_MANUALLY_DROPS,
137    Deny,
138    "calls to `drop` and `drop_in_place` where the dropped value is `std::mem::ManuallyDrop`"
139}
140
141pub 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]);
142
143impl<'tcx> LateLintPass<'tcx> for DropForgetUseless {
144    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
145        let (fn_did, arg) = match expr.kind {
146            // matching on `function(<receiver>, ...)`
147            ExprKind::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(..)`
151            ExprKind::MethodCall(_, arg, _, _) => {
152                (cx.typeck_results().type_dependent_def_id(expr.hir_id), arg)
153            }
154            _ => return,
155        };
156
157        if let Some(fn_did) = fn_did
158            && let Some(fn_name) = cx.tcx.get_diagnostic_name(fn_did)
159        {
160            let arg_ty = cx.typeck_results().expr_ty(arg);
161            let is_copy = cx.type_is_copy_modulo_regions(arg_ty);
162            let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
163
164            let let_underscore_ignore_sugg = || {
165                if let Some((_, node)) = cx.tcx.hir_parent_iter(expr.hir_id).nth(0)
166                    && let Node::Stmt(stmt) = node
167                    && 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::Note
177                }
178            };
179
180            match fn_name {
181                sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => {
182                    cx.emit_span_lint(
183                        DROPPING_REFERENCES,
184                        expr.span,
185                        DropRefDiag { arg_ty, label: arg.span, sugg: let_underscore_ignore_sugg() },
186                    );
187                }
188                sym::ptr_drop_in_place | sym::ptr_drop_in_place_self
189                    if let &ty::RawPtr(inner_ty, _mutbl) = arg_ty.kind()
190                        && inner_ty.is_ref()
191                        && !drop_is_single_call_in_arm =>
192                {
193                    cx.emit_span_lint(
194                        DROPPING_REFERENCES,
195                        expr.span,
196                        DropInPlaceRefDiag {
197                            arg_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_forget if arg_ty.is_ref() => {
205                    cx.emit_span_lint(
206                        FORGETTING_REFERENCES,
207                        expr.span,
208                        ForgetRefDiag {
209                            arg_ty,
210                            label: arg.span,
211                            sugg: let_underscore_ignore_sugg(),
212                        },
213                    );
214                }
215                sym::mem_drop if is_copy && !drop_is_single_call_in_arm => {
216                    cx.emit_span_lint(
217                        DROPPING_COPY_TYPES,
218                        expr.span,
219                        DropCopyDiag {
220                            arg_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_self
227                    if 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                {
231                    cx.emit_span_lint(
232                        DROPPING_COPY_TYPES,
233                        expr.span,
234                        DropInPlaceCopyDiag {
235                            arg_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_forget if is_copy => {
243                    cx.emit_span_lint(
244                        FORGETTING_COPY_TYPES,
245                        expr.span,
246                        ForgetCopyDiag {
247                            arg_ty,
248                            label: arg.span,
249                            sugg: let_underscore_ignore_sugg(),
250                        },
251                    );
252                }
253                sym::mem_drop
254                    if let ty::Adt(adt, _) = arg_ty.kind()
255                        && adt.is_manually_drop() =>
256                {
257                    cx.emit_span_lint(
258                        UNDROPPED_MANUALLY_DROPS,
259                        expr.span,
260                        UndroppedManuallyDropsDiag {
261                            arg_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_self
271                    if let &ty::RawPtr(inner_ty, _mutbl) = arg_ty.kind()
272                        && let ty::Adt(adt, _) = inner_ty.kind()
273                        && adt.is_manually_drop() =>
274                {
275                    cx.emit_span_lint(
276                        UNDROPPED_MANUALLY_DROPS,
277                        expr.span,
278                        UndroppedManuallyDropsInPlaceDiag {
279                            arg_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}
293
294// 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 {
308    if arg.can_have_side_effects() {
309        if let Node::Arm(Arm { body, .. }) = cx.tcx.parent_hir_node(drop_expr.hir_id) {
310            return body.hir_id == drop_expr.hir_id;
311        }
312    }
313    false
314}