@@ -3,7 +3,7 @@ use crate::utils::{
3
3
} ;
4
4
use if_chain:: if_chain;
5
5
use rustc:: declare_lint_pass;
6
- use rustc:: hir:: { BorrowKind , Expr , ExprKind , Mutability , QPath } ;
6
+ use rustc:: hir:: { BorrowKind , Expr , ExprKind , HirVec , Mutability , QPath } ;
7
7
use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
8
8
use rustc_errors:: Applicability ;
9
9
use rustc_session:: declare_tool_lint;
@@ -67,8 +67,127 @@ declare_clippy_lint! {
67
67
"`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
68
68
}
69
69
70
+ declare_clippy_lint ! {
71
+ /// **What it does:** Checks for `std::mem::replace` on a value of type
72
+ /// `T` with `T::default()`.
73
+ ///
74
+ /// **Why is this bad?** `std::mem` module already has the method `take` to
75
+ /// take the current value and replace it with the default value of that type.
76
+ ///
77
+ /// **Known problems:** None.
78
+ ///
79
+ /// **Example:**
80
+ /// ```rust
81
+ /// let mut text = String::from("foo");
82
+ /// let replaced = std::mem::replace(&mut text, String::default());
83
+ /// ```
84
+ /// Is better expressed with:
85
+ /// ```rust
86
+ /// let mut text = String::from("foo");
87
+ /// let taken = std::mem::take(&mut text);
88
+ /// ```
89
+ pub MEM_REPLACE_WITH_DEFAULT ,
90
+ nursery,
91
+ "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
92
+ }
93
+
70
94
declare_lint_pass ! ( MemReplace =>
71
- [ MEM_REPLACE_OPTION_WITH_NONE , MEM_REPLACE_WITH_UNINIT ] ) ;
95
+ [ MEM_REPLACE_OPTION_WITH_NONE , MEM_REPLACE_WITH_UNINIT , MEM_REPLACE_WITH_DEFAULT ] ) ;
96
+
97
+ fn check_replace_option_with_none ( cx : & LateContext < ' _ , ' _ > , expr : & ' _ Expr , args : & HirVec < Expr > ) {
98
+ if let ExprKind :: Path ( ref replacement_qpath) = args[ 1 ] . kind {
99
+ // Check that second argument is `Option::None`
100
+ if match_qpath ( replacement_qpath, & paths:: OPTION_NONE ) {
101
+ // Since this is a late pass (already type-checked),
102
+ // and we already know that the second argument is an
103
+ // `Option`, we do not need to check the first
104
+ // argument's type. All that's left is to get
105
+ // replacee's path.
106
+ let replaced_path = match args[ 0 ] . kind {
107
+ ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mutable , ref replaced) => {
108
+ if let ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) = replaced. kind {
109
+ replaced_path
110
+ } else {
111
+ return ;
112
+ }
113
+ } ,
114
+ ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) => replaced_path,
115
+ _ => return ,
116
+ } ;
117
+
118
+ let mut applicability = Applicability :: MachineApplicable ;
119
+ span_lint_and_sugg (
120
+ cx,
121
+ MEM_REPLACE_OPTION_WITH_NONE ,
122
+ expr. span ,
123
+ "replacing an `Option` with `None`" ,
124
+ "consider `Option::take()` instead" ,
125
+ format ! (
126
+ "{}.take()" ,
127
+ snippet_with_applicability( cx, replaced_path. span, "" , & mut applicability)
128
+ ) ,
129
+ applicability,
130
+ ) ;
131
+ }
132
+ }
133
+ }
134
+
135
+ fn check_replace_with_uninit ( cx : & LateContext < ' _ , ' _ > , expr : & ' _ Expr , args : & HirVec < Expr > ) {
136
+ if let ExprKind :: Call ( ref repl_func, ref repl_args) = args[ 1 ] . kind {
137
+ if_chain ! {
138
+ if repl_args. is_empty( ) ;
139
+ if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind;
140
+ if let Some ( repl_def_id) = cx. tables. qpath_res( repl_func_qpath, repl_func. hir_id) . opt_def_id( ) ;
141
+ then {
142
+ if match_def_path( cx, repl_def_id, & paths:: MEM_UNINITIALIZED ) {
143
+ span_help_and_lint(
144
+ cx,
145
+ MEM_REPLACE_WITH_UNINIT ,
146
+ expr. span,
147
+ "replacing with `mem::uninitialized()`" ,
148
+ "consider using the `take_mut` crate instead" ,
149
+ ) ;
150
+ } else if match_def_path( cx, repl_def_id, & paths:: MEM_ZEROED ) &&
151
+ !cx. tables. expr_ty( & args[ 1 ] ) . is_primitive( ) {
152
+ span_help_and_lint(
153
+ cx,
154
+ MEM_REPLACE_WITH_UNINIT ,
155
+ expr. span,
156
+ "replacing with `mem::zeroed()`" ,
157
+ "consider using a default value or the `take_mut` crate instead" ,
158
+ ) ;
159
+ }
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ fn check_replace_with_default ( cx : & LateContext < ' _ , ' _ > , expr : & ' _ Expr , args : & HirVec < Expr > ) {
166
+ if let ExprKind :: Call ( ref repl_func, ref repl_args) = args[ 1 ] . kind {
167
+ if_chain ! {
168
+ if repl_args. is_empty( ) ;
169
+ if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind;
170
+ if let Some ( repl_def_id) = cx. tables. qpath_res( repl_func_qpath, repl_func. hir_id) . opt_def_id( ) ;
171
+ if match_def_path( cx, repl_def_id, & paths:: DEFAULT_TRAIT_METHOD ) ;
172
+ then {
173
+ let mut applicability = Applicability :: MachineApplicable ;
174
+
175
+ span_lint_and_sugg(
176
+ cx,
177
+ MEM_REPLACE_WITH_DEFAULT ,
178
+ expr. span,
179
+ "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`" ,
180
+ "consider using" ,
181
+ format!(
182
+ "std::mem::take({})" ,
183
+ snippet_with_applicability( cx, args[ 0 ] . span, "" , & mut applicability)
184
+ ) ,
185
+ applicability,
186
+ ) ;
187
+ }
188
+ }
189
+ }
190
+ }
72
191
73
192
impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for MemReplace {
74
193
fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
@@ -80,67 +199,10 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MemReplace {
80
199
if let Some ( def_id) = cx. tables. qpath_res( func_qpath, func. hir_id) . opt_def_id( ) ;
81
200
if match_def_path( cx, def_id, & paths:: MEM_REPLACE ) ;
82
201
83
- // Check that second argument is `Option::None`
84
202
then {
85
- if let ExprKind :: Path ( ref replacement_qpath) = func_args[ 1 ] . kind {
86
- if match_qpath( replacement_qpath, & paths:: OPTION_NONE ) {
87
-
88
- // Since this is a late pass (already type-checked),
89
- // and we already know that the second argument is an
90
- // `Option`, we do not need to check the first
91
- // argument's type. All that's left is to get
92
- // replacee's path.
93
- let replaced_path = match func_args[ 0 ] . kind {
94
- ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mutable , ref replaced) => {
95
- if let ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) = replaced. kind {
96
- replaced_path
97
- } else {
98
- return
99
- }
100
- } ,
101
- ExprKind :: Path ( QPath :: Resolved ( None , ref replaced_path) ) => replaced_path,
102
- _ => return ,
103
- } ;
104
-
105
- let mut applicability = Applicability :: MachineApplicable ;
106
- span_lint_and_sugg(
107
- cx,
108
- MEM_REPLACE_OPTION_WITH_NONE ,
109
- expr. span,
110
- "replacing an `Option` with `None`" ,
111
- "consider `Option::take()` instead" ,
112
- format!( "{}.take()" , snippet_with_applicability( cx, replaced_path. span, "" , & mut applicability) ) ,
113
- applicability,
114
- ) ;
115
- }
116
- }
117
- if let ExprKind :: Call ( ref repl_func, ref repl_args) = func_args[ 1 ] . kind {
118
- if_chain! {
119
- if repl_args. is_empty( ) ;
120
- if let ExprKind :: Path ( ref repl_func_qpath) = repl_func. kind;
121
- if let Some ( repl_def_id) = cx. tables. qpath_res( repl_func_qpath, repl_func. hir_id) . opt_def_id( ) ;
122
- then {
123
- if match_def_path( cx, repl_def_id, & paths:: MEM_UNINITIALIZED ) {
124
- span_help_and_lint(
125
- cx,
126
- MEM_REPLACE_WITH_UNINIT ,
127
- expr. span,
128
- "replacing with `mem::uninitialized()`" ,
129
- "consider using the `take_mut` crate instead" ,
130
- ) ;
131
- } else if match_def_path( cx, repl_def_id, & paths:: MEM_ZEROED ) &&
132
- !cx. tables. expr_ty( & func_args[ 1 ] ) . is_primitive( ) {
133
- span_help_and_lint(
134
- cx,
135
- MEM_REPLACE_WITH_UNINIT ,
136
- expr. span,
137
- "replacing with `mem::zeroed()`" ,
138
- "consider using a default value or the `take_mut` crate instead" ,
139
- ) ;
140
- }
141
- }
142
- }
143
- }
203
+ check_replace_option_with_none( cx, expr, & func_args) ;
204
+ check_replace_with_uninit( cx, expr, & func_args) ;
205
+ check_replace_with_default( cx, expr, & func_args) ;
144
206
}
145
207
}
146
208
}
0 commit comments