]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/mem_replace.rs
Auto merge of #4803 - tomprogrammer:issue-4732, r=phansch
[rust.git] / clippy_lints / src / mem_replace.rs
1 use crate::utils::{
2     match_def_path, match_qpath, paths, snippet_with_applicability, span_help_and_lint, span_lint_and_sugg,
3 };
4 use if_chain::if_chain;
5 use rustc::hir::{Expr, ExprKind, Mutability, QPath};
6 use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
7 use rustc::{declare_lint_pass, declare_tool_lint};
8 use rustc_errors::Applicability;
9
10 declare_clippy_lint! {
11     /// **What it does:** Checks for `mem::replace()` on an `Option` with
12     /// `None`.
13     ///
14     /// **Why is this bad?** `Option` already has the method `take()` for
15     /// taking its current value (Some(..) or None) and replacing it with
16     /// `None`.
17     ///
18     /// **Known problems:** None.
19     ///
20     /// **Example:**
21     /// ```rust
22     /// use std::mem;
23     ///
24     /// let mut an_option = Some(0);
25     /// let replaced = mem::replace(&mut an_option, None);
26     /// ```
27     /// Is better expressed with:
28     /// ```rust
29     /// let mut an_option = Some(0);
30     /// let taken = an_option.take();
31     /// ```
32     pub MEM_REPLACE_OPTION_WITH_NONE,
33     style,
34     "replacing an `Option` with `None` instead of `take()`"
35 }
36
37 declare_clippy_lint! {
38     /// **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())`
39     /// and `mem::replace(&mut _, mem::zeroed())`.
40     ///
41     /// **Why is this bad?** This will lead to undefined behavior even if the
42     /// value is overwritten later, because the uninitialized value may be
43     /// observed in the case of a panic.
44     ///
45     /// **Known problems:** None.
46     ///
47     /// **Example:**
48     ///
49     /// ```
50     /// use std::mem;
51     ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
52     ///
53     /// #[allow(deprecated, invalid_value)]
54     /// fn myfunc (v: &mut Vec<i32>) {
55     ///     let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
56     ///     let new_v = may_panic(taken_v); // undefined behavior on panic
57     ///     mem::forget(mem::replace(v, new_v));
58     /// }
59     /// ```
60     ///
61     /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
62     /// at the cost of either lazily creating a replacement value or aborting
63     /// on panic, to ensure that the uninitialized value cannot be observed.
64     pub MEM_REPLACE_WITH_UNINIT,
65     correctness,
66     "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
67 }
68
69 declare_lint_pass!(MemReplace =>
70     [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT]);
71
72 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
73     fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
74         if_chain! {
75             // Check that `expr` is a call to `mem::replace()`
76             if let ExprKind::Call(ref func, ref func_args) = expr.kind;
77             if func_args.len() == 2;
78             if let ExprKind::Path(ref func_qpath) = func.kind;
79             if let Some(def_id) = cx.tables.qpath_res(func_qpath, func.hir_id).opt_def_id();
80             if match_def_path(cx, def_id, &paths::MEM_REPLACE);
81
82             // Check that second argument is `Option::None`
83             then {
84                 if let ExprKind::Path(ref replacement_qpath) = func_args[1].kind {
85                     if match_qpath(replacement_qpath, &paths::OPTION_NONE) {
86
87                         // Since this is a late pass (already type-checked),
88                         // and we already know that the second argument is an
89                         // `Option`, we do not need to check the first
90                         // argument's type. All that's left is to get
91                         // replacee's path.
92                         let replaced_path = match func_args[0].kind {
93                             ExprKind::AddrOf(Mutability::Mutable, ref replaced) => {
94                                 if let ExprKind::Path(QPath::Resolved(None, ref replaced_path)) = replaced.kind {
95                                     replaced_path
96                                 } else {
97                                     return
98                                 }
99                             },
100                             ExprKind::Path(QPath::Resolved(None, ref replaced_path)) => replaced_path,
101                             _ => return,
102                         };
103
104                         let mut applicability = Applicability::MachineApplicable;
105                         span_lint_and_sugg(
106                             cx,
107                             MEM_REPLACE_OPTION_WITH_NONE,
108                             expr.span,
109                             "replacing an `Option` with `None`",
110                             "consider `Option::take()` instead",
111                             format!("{}.take()", snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)),
112                             applicability,
113                         );
114                     }
115                 }
116                 if let ExprKind::Call(ref repl_func, ref repl_args) = func_args[1].kind {
117                     if_chain! {
118                         if repl_args.is_empty();
119                         if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
120                         if let Some(repl_def_id) = cx.tables.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
121                         then {
122                             if match_def_path(cx, repl_def_id, &paths::MEM_UNINITIALIZED) {
123                                 span_help_and_lint(
124                                     cx,
125                                     MEM_REPLACE_WITH_UNINIT,
126                                     expr.span,
127                                     "replacing with `mem::uninitialized()`",
128                                     "consider using the `take_mut` crate instead",
129                                 );
130                             } else if match_def_path(cx, repl_def_id, &paths::MEM_ZEROED) &&
131                                     !cx.tables.expr_ty(&func_args[1]).is_primitive() {
132                                 span_help_and_lint(
133                                     cx,
134                                     MEM_REPLACE_WITH_UNINIT,
135                                     expr.span,
136                                     "replacing with `mem::zeroed()`",
137                                     "consider using a default value or the `take_mut` crate instead",
138                                 );
139                             }
140                         }
141                     }
142                 }
143             }
144         }
145     }
146 }