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} ;
23use if_chain:: if_chain;
34use rustc:: hir:: { Expr , ExprKind , MutMutable , QPath } ;
45use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
@@ -32,7 +33,40 @@ declare_clippy_lint! {
3233 "replacing an `Option` with `None` instead of `take()`"
3334}
3435
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 ] ) ;
3670
3771impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for MemReplace {
3872 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
@@ -46,36 +80,48 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
4680
4781 // Check that second argument is `Option::None`
4882 if let ExprKind :: Path ( ref replacement_qpath) = func_args[ 1 ] . node;
49- if match_qpath( replacement_qpath, & paths:: OPTION_NONE ) ;
50-
5183 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+ } ;
68102
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+ }
79125 }
80126 }
81127 }
0 commit comments