]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/drop_forget_ref.rs
Auto merge of #3593 - mikerite:readme-syspath-2, r=phansch
[rust.git] / clippy_lints / src / drop_forget_ref.rs
1 // Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution.
3 //
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
9
10 use crate::utils::{is_copy, match_def_path, opt_def_id, paths, span_note_and_lint};
11 use if_chain::if_chain;
12 use rustc::hir::*;
13 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
14 use rustc::ty;
15 use rustc::{declare_tool_lint, lint_array};
16
17 /// **What it does:** Checks for calls to `std::mem::drop` with a reference
18 /// instead of an owned value.
19 ///
20 /// **Why is this bad?** Calling `drop` on a reference will only drop the
21 /// reference itself, which is a no-op. It will not call the `drop` method (from
22 /// the `Drop` trait implementation) on the underlying referenced value, which
23 /// is likely what was intended.
24 ///
25 /// **Known problems:** None.
26 ///
27 /// **Example:**
28 /// ```rust
29 /// let mut lock_guard = mutex.lock();
30 /// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
31 /// // still locked
32 /// operation_that_requires_mutex_to_be_unlocked();
33 /// ```
34 declare_clippy_lint! {
35     pub DROP_REF,
36     correctness,
37     "calls to `std::mem::drop` with a reference instead of an owned value"
38 }
39
40 /// **What it does:** Checks for calls to `std::mem::forget` with a reference
41 /// instead of an owned value.
42 ///
43 /// **Why is this bad?** Calling `forget` on a reference will only forget the
44 /// reference itself, which is a no-op. It will not forget the underlying
45 /// referenced
46 /// value, which is likely what was intended.
47 ///
48 /// **Known problems:** None.
49 ///
50 /// **Example:**
51 /// ```rust
52 /// let x = Box::new(1);
53 /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
54 /// ```
55 declare_clippy_lint! {
56     pub FORGET_REF,
57     correctness,
58     "calls to `std::mem::forget` with a reference instead of an owned value"
59 }
60
61 /// **What it does:** Checks for calls to `std::mem::drop` with a value
62 /// that derives the Copy trait
63 ///
64 /// **Why is this bad?** Calling `std::mem::drop` [does nothing for types that
65 /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
66 /// value will be copied and moved into the function on invocation.
67 ///
68 /// **Known problems:** None.
69 ///
70 /// **Example:**
71 /// ```rust
72 /// let x: i32 = 42; // i32 implements Copy
73 /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
74 ///                   // original unaffected
75 /// ```
76 declare_clippy_lint! {
77     pub DROP_COPY,
78     correctness,
79     "calls to `std::mem::drop` with a value that implements Copy"
80 }
81
82 /// **What it does:** Checks for calls to `std::mem::forget` with a value that
83 /// derives the Copy trait
84 ///
85 /// **Why is this bad?** Calling `std::mem::forget` [does nothing for types that
86 /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
87 /// value will be copied and moved into the function on invocation.
88 ///
89 /// An alternative, but also valid, explanation is that Copy types do not
90 /// implement
91 /// the Drop trait, which means they have no destructors. Without a destructor,
92 /// there
93 /// is nothing for `std::mem::forget` to ignore.
94 ///
95 /// **Known problems:** None.
96 ///
97 /// **Example:**
98 /// ```rust
99 /// let x: i32 = 42; // i32 implements Copy
100 /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
101 ///                     // original unaffected
102 /// ```
103 declare_clippy_lint! {
104     pub FORGET_COPY,
105     correctness,
106     "calls to `std::mem::forget` with a value that implements Copy"
107 }
108
109 const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
110                                 Dropping a reference does nothing.";
111 const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
112                                   Forgetting a reference does nothing.";
113 const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements Copy. \
114                                  Dropping a copy leaves the original intact.";
115 const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements Copy. \
116                                    Forgetting a copy leaves the original intact.";
117
118 pub struct Pass;
119
120 impl LintPass for Pass {
121     fn get_lints(&self) -> LintArray {
122         lint_array!(DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY)
123     }
124 }
125
126 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
127     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
128         if_chain! {
129             if let ExprKind::Call(ref path, ref args) = expr.node;
130             if let ExprKind::Path(ref qpath) = path.node;
131             if args.len() == 1;
132             if let Some(def_id) = opt_def_id(cx.tables.qpath_def(qpath, path.hir_id));
133             then {
134                 let lint;
135                 let msg;
136                 let arg = &args[0];
137                 let arg_ty = cx.tables.expr_ty(arg);
138
139                 if let ty::Ref(..) = arg_ty.sty {
140                     if match_def_path(cx.tcx, def_id, &paths::DROP) {
141                         lint = DROP_REF;
142                         msg = DROP_REF_SUMMARY.to_string();
143                     } else if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
144                         lint = FORGET_REF;
145                         msg = FORGET_REF_SUMMARY.to_string();
146                     } else {
147                         return;
148                     }
149                     span_note_and_lint(cx,
150                                        lint,
151                                        expr.span,
152                                        &msg,
153                                        arg.span,
154                                        &format!("argument has type {}", arg_ty));
155                 } else if is_copy(cx, arg_ty) {
156                     if match_def_path(cx.tcx, def_id, &paths::DROP) {
157                         lint = DROP_COPY;
158                         msg = DROP_COPY_SUMMARY.to_string();
159                     } else if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
160                         lint = FORGET_COPY;
161                         msg = FORGET_COPY_SUMMARY.to_string();
162                     } else {
163                         return;
164                     }
165                     span_note_and_lint(cx,
166                                        lint,
167                                        expr.span,
168                                        &msg,
169                                        arg.span,
170                                        &format!("argument has type {}", arg_ty));
171                 }
172             }
173         }
174     }
175 }