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+ } ;
24use if_chain:: if_chain;
35use rustc:: hir:: { Expr , ExprKind , MutMutable , QPath } ;
46use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
@@ -32,7 +34,40 @@ declare_clippy_lint! {
3234 "replacing an `Option` with `None` instead of `take()`"
3335}
3436
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 ] ) ;
3671
3772impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for MemReplace {
3873 fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
@@ -45,37 +80,66 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
4580 if match_def_path( cx, def_id, & paths:: MEM_REPLACE ) ;
4681
4782 // 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-
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 let ExprKind :: Path ( ref replacement_qpath) = func_args[ 1 ] . node {
85+ if match_qpath( replacement_qpath, & paths:: OPTION_NONE ) {
6886
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+ }
79143 }
80144 }
81145 }
0 commit comments