1
- use crate :: utils:: { match_def_path, match_qpath, paths, snippet_with_applicability, span_lint_and_sugg} ;
1
+ use crate :: utils:: {
2
+ match_def_path, match_qpath, paths, snippet_with_applicability, span_help_and_lint, span_lint_and_sugg,
3
+ } ;
2
4
use if_chain:: if_chain;
3
5
use rustc:: hir:: { Expr , ExprKind , MutMutable , QPath } ;
4
6
use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
@@ -32,7 +34,40 @@ declare_clippy_lint! {
32
34
"replacing an `Option` with `None` instead of `take()`"
33
35
}
34
36
35
- declare_lint_pass ! ( MemReplace => [ MEM_REPLACE_OPTION_WITH_NONE ] ) ;
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 ] ) ;
36
71
37
72
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for MemReplace {
38
73
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
@@ -45,37 +80,66 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
45
80
if match_def_path( cx, def_id, & paths:: MEM_REPLACE ) ;
46
81
47
82
// Check that second argument is `Option::None`
48
- if let ExprKind :: Path ( ref replacement_qpath) = func_args[ 1 ] . node;
49
- if match_qpath( replacement_qpath, & paths:: OPTION_NONE ) ;
50
-
51
83
then {
52
- // Since this is a late pass (already type-checked),
53
- // and we already know that the second argument is an
54
- // `Option`, we do not need to check the first
55
- // argument's type. All that's left is to get
56
- // replacee's path.
57
- let replaced_path = match func_args[ 0 ] . node {
58
- ExprKind :: AddrOf ( MutMutable , ref replaced) => {
59
- if let ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) = replaced. node {
60
- replaced_path
61
- } else {
62
- return
63
- }
64
- } ,
65
- ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) => replaced_path,
66
- _ => return ,
67
- } ;
84
+ if let ExprKind :: Path ( ref replacement_qpath) = func_args[ 1 ] . node {
85
+ if match_qpath( replacement_qpath, & paths:: OPTION_NONE ) {
68
86
69
- let mut applicability = Applicability :: MachineApplicable ;
70
- span_lint_and_sugg(
71
- cx,
72
- MEM_REPLACE_OPTION_WITH_NONE ,
73
- expr. span,
74
- "replacing an `Option` with `None`" ,
75
- "consider `Option::take()` instead" ,
76
- format!( "{}.take()" , snippet_with_applicability( cx, replaced_path. span, "" , & mut applicability) ) ,
77
- applicability,
78
- ) ;
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 ] . node {
93
+ ExprKind :: AddrOf ( MutMutable , ref replaced) => {
94
+ if let ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) = replaced. node {
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 ] . node {
117
+ if_chain! {
118
+ if repl_args. is_empty( ) ;
119
+ if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. node;
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
+ }
79
143
}
80
144
}
81
145
}
0 commit comments