1
- use crate :: utils:: { match_def_path, match_qpath, paths, snippet_with_applicability, span_lint_and_sugg} ;
1
+ use crate :: utils:: { match_def_path, match_qpath, paths, snippet_with_applicability,
2
+ span_help_and_lint, span_lint_and_sugg} ;
2
3
use if_chain:: if_chain;
3
4
use rustc:: hir:: { Expr , ExprKind , MutMutable , QPath } ;
4
5
use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
@@ -32,7 +33,40 @@ declare_clippy_lint! {
32
33
"replacing an `Option` with `None` instead of `take()`"
33
34
}
34
35
35
- declare_lint_pass ! ( MemReplace => [ MEM_REPLACE_OPTION_WITH_NONE ] ) ;
36
+ declare_clippy_lint ! {
37
+ /// **What it does:** Checks for `mem::replace(&mut _, mem::uninitialized())`
38
+ /// and `mem::replace(&mut _, mem::zeroed())`.
39
+ ///
40
+ /// **Why is this bad?** This will lead to undefined behavior even if the
41
+ /// value is overwritten later, because the uninitialized value may be
42
+ /// observed in the case of a panic.
43
+ ///
44
+ /// **Known problems:** None.
45
+ ///
46
+ /// **Example:**
47
+ ///
48
+ /// ```
49
+ /// use std::mem;
50
+ ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
51
+ ///
52
+ /// #[allow(deprecated, invalid_value)]
53
+ /// fn myfunc (v: &mut Vec<i32>) {
54
+ /// let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
55
+ /// let new_v = may_panic(taken_v); // undefined behavior on panic
56
+ /// mem::forget(mem::replace(v, new_v));
57
+ /// }
58
+ /// ```
59
+ ///
60
+ /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
61
+ /// at the cost of aborting on panic, to ensure that the uninitialized
62
+ /// value cannot be observed.
63
+ pub MEM_REPLACE_WITH_UNINIT ,
64
+ correctness,
65
+ "`mem::replace(&mut _, mem::uninitialized())` or `mem::zeroed()`"
66
+ }
67
+
68
+ declare_lint_pass ! ( MemReplace =>
69
+ [ MEM_REPLACE_OPTION_WITH_NONE , MEM_REPLACE_WITH_UNINIT ] ) ;
36
70
37
71
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for MemReplace {
38
72
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
@@ -46,36 +80,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
46
80
47
81
// Check that second argument is `Option::None`
48
82
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 match_qpath( replacement_qpath, & paths:: OPTION_NONE ) {
85
+
86
+ // Since this is a late pass (already type-checked),
87
+ // and we already know that the second argument is an
88
+ // `Option`, we do not need to check the first
89
+ // argument's type. All that's left is to get
90
+ // replacee's path.
91
+ let replaced_path = match func_args[ 0 ] . node {
92
+ ExprKind :: AddrOf ( MutMutable , ref replaced) => {
93
+ if let ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) = replaced. node {
94
+ replaced_path
95
+ } else {
96
+ return
97
+ }
98
+ } ,
99
+ ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) => replaced_path,
100
+ _ => return ,
101
+ } ;
68
102
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
- ) ;
103
+ let mut applicability = Applicability :: MachineApplicable ;
104
+ span_lint_and_sugg(
105
+ cx,
106
+ MEM_REPLACE_OPTION_WITH_NONE ,
107
+ expr. span,
108
+ "replacing an `Option` with `None`" ,
109
+ "consider `Option::take()` instead" ,
110
+ format!( "{}.take()" , snippet_with_applicability( cx, replaced_path. span, "" , & mut applicability) ) ,
111
+ applicability,
112
+ ) ;
113
+ }
114
+ if match_qpath( replacement_qpath, & paths:: MEM_UNINITIALIZED ) ||
115
+ match_qpath( replacement_qpath, & paths:: MEM_ZEROED ) &&
116
+ !cx. tables. expr_ty( & func_args[ 1 ] ) . is_primitive( ) {
117
+ span_help_and_lint(
118
+ cx,
119
+ MEM_REPLACE_WITH_UNINIT ,
120
+ expr. span,
121
+ "replacing with `mem::uninitialized()`" ,
122
+ "consider using the `take_mut` crate instead" ,
123
+ ) ;
124
+ }
79
125
}
80
126
}
81
127
}
0 commit comments