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