]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/drop_forget_ref.rs
Last PR adjustments
[rust.git] / clippy_lints / src / drop_forget_ref.rs
1 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
2 use clippy_utils::get_parent_node;
3 use clippy_utils::is_must_use_func_call;
4 use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
5 use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
6 use rustc_lint::{LateContext, LateLintPass};
7 use rustc_session::{declare_lint_pass, declare_tool_lint};
8 use rustc_span::sym;
9
10 declare_clippy_lint! {
11     /// ### What it does
12     /// Checks for calls to `std::mem::drop` with a reference
13     /// instead of an owned value.
14     ///
15     /// ### Why is this bad?
16     /// Calling `drop` on a reference will only drop the
17     /// reference itself, which is a no-op. It will not call the `drop` method (from
18     /// the `Drop` trait implementation) on the underlying referenced value, which
19     /// is likely what was intended.
20     ///
21     /// ### Example
22     /// ```ignore
23     /// let mut lock_guard = mutex.lock();
24     /// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
25     /// // still locked
26     /// operation_that_requires_mutex_to_be_unlocked();
27     /// ```
28     #[clippy::version = "pre 1.29.0"]
29     pub DROP_REF,
30     correctness,
31     "calls to `std::mem::drop` with a reference instead of an owned value"
32 }
33
34 declare_clippy_lint! {
35     /// ### What it does
36     /// Checks for calls to `std::mem::forget` with a reference
37     /// instead of an owned value.
38     ///
39     /// ### Why is this bad?
40     /// Calling `forget` on a reference will only forget the
41     /// reference itself, which is a no-op. It will not forget the underlying
42     /// referenced
43     /// value, which is likely what was intended.
44     ///
45     /// ### Example
46     /// ```rust
47     /// let x = Box::new(1);
48     /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
49     /// ```
50     #[clippy::version = "pre 1.29.0"]
51     pub FORGET_REF,
52     correctness,
53     "calls to `std::mem::forget` with a reference instead of an owned value"
54 }
55
56 declare_clippy_lint! {
57     /// ### What it does
58     /// Checks for calls to `std::mem::drop` with a value
59     /// that derives the Copy trait
60     ///
61     /// ### Why is this bad?
62     /// Calling `std::mem::drop` [does nothing for types that
63     /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
64     /// value will be copied and moved into the function on invocation.
65     ///
66     /// ### Example
67     /// ```rust
68     /// let x: i32 = 42; // i32 implements Copy
69     /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
70     ///                   // original unaffected
71     /// ```
72     #[clippy::version = "pre 1.29.0"]
73     pub DROP_COPY,
74     correctness,
75     "calls to `std::mem::drop` with a value that implements Copy"
76 }
77
78 declare_clippy_lint! {
79     /// ### What it does
80     /// Checks for calls to `std::mem::forget` with a value that
81     /// derives the Copy trait
82     ///
83     /// ### Why is this bad?
84     /// Calling `std::mem::forget` [does nothing for types that
85     /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
86     /// value will be copied and moved into the function on invocation.
87     ///
88     /// An alternative, but also valid, explanation is that Copy types do not
89     /// implement
90     /// the Drop trait, which means they have no destructors. Without a destructor,
91     /// there
92     /// is nothing for `std::mem::forget` to ignore.
93     ///
94     /// ### Example
95     /// ```rust
96     /// let x: i32 = 42; // i32 implements Copy
97     /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
98     ///                     // original unaffected
99     /// ```
100     #[clippy::version = "pre 1.29.0"]
101     pub FORGET_COPY,
102     correctness,
103     "calls to `std::mem::forget` with a value that implements Copy"
104 }
105
106 declare_clippy_lint! {
107     /// ### What it does
108     /// Checks for calls to `std::mem::drop` with a value that does not implement `Drop`.
109     ///
110     /// ### Why is this bad?
111     /// Calling `std::mem::drop` is no different than dropping such a type. A different value may
112     /// have been intended.
113     ///
114     /// ### Example
115     /// ```rust
116     /// struct Foo;
117     /// let x = Foo;
118     /// std::mem::drop(x);
119     /// ```
120     #[clippy::version = "1.62.0"]
121     pub DROP_NON_DROP,
122     suspicious,
123     "call to `std::mem::drop` with a value which does not implement `Drop`"
124 }
125
126 declare_clippy_lint! {
127     /// ### What it does
128     /// Checks for calls to `std::mem::forget` with a value that does not implement `Drop`.
129     ///
130     /// ### Why is this bad?
131     /// Calling `std::mem::forget` is no different than dropping such a type. A different value may
132     /// have been intended.
133     ///
134     /// ### Example
135     /// ```rust
136     /// struct Foo;
137     /// let x = Foo;
138     /// std::mem::forget(x);
139     /// ```
140     #[clippy::version = "1.62.0"]
141     pub FORGET_NON_DROP,
142     suspicious,
143     "call to `std::mem::forget` with a value which does not implement `Drop`"
144 }
145
146 declare_clippy_lint! {
147     /// ### What it does
148     /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
149     ///
150     /// ### Why is this bad?
151     /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
152     ///
153     /// ### Known problems
154     /// Does not catch cases if the user binds `std::mem::drop`
155     /// to a different name and calls it that way.
156     ///
157     /// ### Example
158     /// ```rust
159     /// struct S;
160     /// drop(std::mem::ManuallyDrop::new(S));
161     /// ```
162     /// Use instead:
163     /// ```rust
164     /// struct S;
165     /// unsafe {
166     ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
167     /// }
168     /// ```
169     #[clippy::version = "1.49.0"]
170     pub UNDROPPED_MANUALLY_DROPS,
171     correctness,
172     "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
173 }
174
175 const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
176                                 Dropping a reference does nothing";
177 const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
178                                   Forgetting a reference does nothing";
179 const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \
180                                  Dropping a copy leaves the original intact";
181 const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
182                                    Forgetting a copy leaves the original intact";
183 const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
184                                  Dropping such a type only extends its contained lifetimes";
185 const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
186                                    Forgetting such a type is the same as dropping it";
187
188 declare_lint_pass!(DropForgetRef => [
189     DROP_REF,
190     FORGET_REF,
191     DROP_COPY,
192     FORGET_COPY,
193     DROP_NON_DROP,
194     FORGET_NON_DROP,
195     UNDROPPED_MANUALLY_DROPS
196 ]);
197
198 impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
199     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
200         if let ExprKind::Call(path, [arg]) = expr.kind
201             && let ExprKind::Path(ref qpath) = path.kind
202             && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
203             && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
204         {
205             let arg_ty = cx.typeck_results().expr_ty(arg);
206             let is_copy = is_copy(cx, arg_ty);
207             let drop_is_single_call_in_arm = is_single_call_in_arm(cx, arg, expr);
208             let (lint, msg) = match fn_name {
209                 sym::mem_drop if arg_ty.is_ref() && !drop_is_single_call_in_arm => (DROP_REF, DROP_REF_SUMMARY),
210                 sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY),
211                 sym::mem_drop if is_copy && !drop_is_single_call_in_arm => (DROP_COPY, DROP_COPY_SUMMARY),
212                 sym::mem_forget if is_copy => (FORGET_COPY, FORGET_COPY_SUMMARY),
213                 sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => {
214                     span_lint_and_help(
215                         cx,
216                         UNDROPPED_MANUALLY_DROPS,
217                         expr.span,
218                         "the inner value of this ManuallyDrop will not be dropped",
219                         None,
220                         "to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
221                     );
222                     return;
223                 }
224                 sym::mem_drop
225                     if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
226                         || is_must_use_func_call(cx, arg)
227                         || is_must_use_ty(cx, arg_ty)
228                         || drop_is_single_call_in_arm
229                         ) =>
230                 {
231                     (DROP_NON_DROP, DROP_NON_DROP_SUMMARY)
232                 },
233                 sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => {
234                     (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY)
235                 },
236                 _ => return,
237             };
238             span_lint_and_note(
239                 cx,
240                 lint,
241                 expr.span,
242                 msg,
243                 Some(arg.span),
244                 &format!("argument has type `{arg_ty}`"),
245             );
246         }
247     }
248 }
249
250 // dropping returned value of a function like in the following snippet is considered idiomatic, see
251 // #9482 for examples match <var> {
252 //     <pat> => drop(fn_with_side_effect_and_returning_some_value()),
253 //     ..
254 // }
255 fn is_single_call_in_arm<'tcx>(cx: &LateContext<'tcx>, arg: &'tcx Expr<'_>, drop_expr: &'tcx Expr<'_>) -> bool {
256     if matches!(arg.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)) {
257         let parent_node = get_parent_node(cx.tcx, drop_expr.hir_id);
258         if let Some(Node::Arm(Arm { body, .. })) = &parent_node {
259             return body.hir_id == drop_expr.hir_id;
260         }
261     }
262     false
263 }