@@ -2,15 +2,15 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
2
2
use clippy_utils:: source:: snippet_with_applicability;
3
3
use clippy_utils:: sugg:: Sugg ;
4
4
use clippy_utils:: ty:: is_type_diagnostic_item;
5
- use clippy_utils:: { differing_macro_contexts, eq_expr_value} ;
5
+ use clippy_utils:: { can_mut_borrow_both , differing_macro_contexts, eq_expr_value} ;
6
6
use if_chain:: if_chain;
7
7
use rustc_errors:: Applicability ;
8
8
use rustc_hir:: { BinOpKind , Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
9
9
use rustc_lint:: { LateContext , LateLintPass } ;
10
10
use rustc_middle:: ty;
11
11
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
12
12
use rustc_span:: source_map:: Spanned ;
13
- use rustc_span:: sym;
13
+ use rustc_span:: { sym, Span } ;
14
14
15
15
declare_clippy_lint ! {
16
16
/// ### What it does
@@ -65,37 +65,7 @@ declare_clippy_lint! {
65
65
"`foo = bar; bar = foo` sequence"
66
66
}
67
67
68
- declare_clippy_lint ! {
69
- /// **What it does:** Checks for uses of xor-based swaps.
70
- ///
71
- /// **Why is this bad?** The `std::mem::swap` function exposes the intent better
72
- /// without deinitializing or copying either variable.
73
- ///
74
- /// **Known problems:** None.
75
- ///
76
- /// **Example:**
77
- ///
78
- /// ```rust
79
- /// let mut a = 1;
80
- /// let mut b = 2;
81
- ///
82
- /// a ^= b;
83
- /// b ^= a;
84
- /// a ^= b;
85
- /// ```
86
- ///
87
- /// Use std::mem::swap() instead:
88
- /// ```rust
89
- /// let mut a = 1;
90
- /// let mut b = 2;
91
- /// std::mem::swap(&mut a, &mut b);
92
- /// ```
93
- pub XOR_SWAP ,
94
- complexity,
95
- "xor-based swap of two variables"
96
- }
97
-
98
- declare_lint_pass ! ( Swap => [ MANUAL_SWAP , ALMOST_SWAPPED , XOR_SWAP ] ) ;
68
+ declare_lint_pass ! ( Swap => [ MANUAL_SWAP , ALMOST_SWAPPED ] ) ;
99
69
100
70
impl < ' tcx > LateLintPass < ' tcx > for Swap {
101
71
fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx Block < ' _ > ) {
@@ -105,6 +75,63 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
105
75
}
106
76
}
107
77
78
+ fn generate_swap_warning ( cx : & LateContext < ' _ > , e1 : & Expr < ' _ > , e2 : & Expr < ' _ > , span : Span , is_xor_based : bool ) {
79
+ let mut applicability = Applicability :: MachineApplicable ;
80
+
81
+ if !can_mut_borrow_both ( cx, e1, e2) {
82
+ if let ExprKind :: Index ( lhs1, idx1) = e1. kind {
83
+ if let ExprKind :: Index ( lhs2, idx2) = e2. kind {
84
+ if eq_expr_value ( cx, lhs1, lhs2) {
85
+ let ty = cx. typeck_results ( ) . expr_ty ( lhs1) . peel_refs ( ) ;
86
+
87
+ if matches ! ( ty. kind( ) , ty:: Slice ( _) )
88
+ || matches ! ( ty. kind( ) , ty:: Array ( _, _) )
89
+ || is_type_diagnostic_item ( cx, ty, sym:: vec_type)
90
+ || is_type_diagnostic_item ( cx, ty, sym:: vecdeque_type)
91
+ {
92
+ let slice = Sugg :: hir_with_applicability ( cx, lhs1, "<slice>" , & mut applicability) ;
93
+ span_lint_and_sugg (
94
+ cx,
95
+ MANUAL_SWAP ,
96
+ span,
97
+ & format ! ( "this looks like you are swapping elements of `{}` manually" , slice) ,
98
+ "try" ,
99
+ format ! (
100
+ "{}.swap({}, {})" ,
101
+ slice. maybe_par( ) ,
102
+ snippet_with_applicability( cx, idx1. span, ".." , & mut applicability) ,
103
+ snippet_with_applicability( cx, idx2. span, ".." , & mut applicability) ,
104
+ ) ,
105
+ applicability,
106
+ ) ;
107
+ }
108
+ }
109
+ }
110
+ }
111
+ return ;
112
+ }
113
+
114
+ let first = Sugg :: hir_with_applicability ( cx, e1, ".." , & mut applicability) ;
115
+ let second = Sugg :: hir_with_applicability ( cx, e2, ".." , & mut applicability) ;
116
+ span_lint_and_then (
117
+ cx,
118
+ MANUAL_SWAP ,
119
+ span,
120
+ & format ! ( "this looks like you are swapping `{}` and `{}` manually" , first, second) ,
121
+ |diag| {
122
+ diag. span_suggestion (
123
+ span,
124
+ "try" ,
125
+ format ! ( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
126
+ applicability,
127
+ ) ;
128
+ if !is_xor_based {
129
+ diag. note ( "or maybe you should use `std::mem::replace`?" ) ;
130
+ }
131
+ } ,
132
+ ) ;
133
+ }
134
+
108
135
/// Implementation of the `MANUAL_SWAP` lint.
109
136
fn check_manual_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
110
137
for w in block. stmts . windows ( 3 ) {
@@ -128,123 +155,13 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
128
155
if eq_expr_value( cx, tmp_init, lhs1) ;
129
156
if eq_expr_value( cx, rhs1, lhs2) ;
130
157
then {
131
- if let ExprKind :: Field ( lhs1, _) = lhs1. kind {
132
- if let ExprKind :: Field ( lhs2, _) = lhs2. kind {
133
- if lhs1. hir_id. owner == lhs2. hir_id. owner {
134
- return ;
135
- }
136
- }
137
- }
138
-
139
- let mut applicability = Applicability :: MachineApplicable ;
140
-
141
- let slice = check_for_slice( cx, lhs1, lhs2) ;
142
- let ( replace, what, sugg) = if let Slice :: NotSwappable = slice {
143
- return ;
144
- } else if let Slice :: Swappable ( slice, idx1, idx2) = slice {
145
- if let Some ( slice) = Sugg :: hir_opt( cx, slice) {
146
- (
147
- false ,
148
- format!( " elements of `{}`" , slice) ,
149
- format!(
150
- "{}.swap({}, {})" ,
151
- slice. maybe_par( ) ,
152
- snippet_with_applicability( cx, idx1. span, ".." , & mut applicability) ,
153
- snippet_with_applicability( cx, idx2. span, ".." , & mut applicability) ,
154
- ) ,
155
- )
156
- } else {
157
- ( false , String :: new( ) , String :: new( ) )
158
- }
159
- } else if let ( Some ( first) , Some ( second) ) = ( Sugg :: hir_opt( cx, lhs1) , Sugg :: hir_opt( cx, rhs1) ) {
160
- (
161
- true ,
162
- format!( " `{}` and `{}`" , first, second) ,
163
- format!( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
164
- )
165
- } else {
166
- ( true , String :: new( ) , String :: new( ) )
167
- } ;
168
-
169
158
let span = w[ 0 ] . span. to( second. span) ;
170
-
171
- span_lint_and_then(
172
- cx,
173
- MANUAL_SWAP ,
174
- span,
175
- & format!( "this looks like you are swapping{} manually" , what) ,
176
- |diag| {
177
- if !sugg. is_empty( ) {
178
- diag. span_suggestion(
179
- span,
180
- "try" ,
181
- sugg,
182
- applicability,
183
- ) ;
184
-
185
- if replace {
186
- diag. note( "or maybe you should use `std::mem::replace`?" ) ;
187
- }
188
- }
189
- }
190
- ) ;
159
+ generate_swap_warning( cx, lhs1, lhs2, span, false ) ;
191
160
}
192
161
}
193
162
}
194
163
}
195
164
196
- enum Slice < ' a > {
197
- /// `slice.swap(idx1, idx2)` can be used
198
- ///
199
- /// ## Example
200
- ///
201
- /// ```rust
202
- /// # let mut a = vec![0, 1];
203
- /// let t = a[1];
204
- /// a[1] = a[0];
205
- /// a[0] = t;
206
- /// // can be written as
207
- /// a.swap(0, 1);
208
- /// ```
209
- Swappable ( & ' a Expr < ' a > , & ' a Expr < ' a > , & ' a Expr < ' a > ) ,
210
- /// The `swap` function cannot be used.
211
- ///
212
- /// ## Example
213
- ///
214
- /// ```rust
215
- /// # let mut a = [vec![1, 2], vec![3, 4]];
216
- /// let t = a[0][1];
217
- /// a[0][1] = a[1][0];
218
- /// a[1][0] = t;
219
- /// ```
220
- NotSwappable ,
221
- /// Not a slice
222
- None ,
223
- }
224
-
225
- /// Checks if both expressions are index operations into "slice-like" types.
226
- fn check_for_slice < ' a > ( cx : & LateContext < ' _ > , lhs1 : & ' a Expr < ' _ > , lhs2 : & ' a Expr < ' _ > ) -> Slice < ' a > {
227
- if let ExprKind :: Index ( lhs1, idx1) = lhs1. kind {
228
- if let ExprKind :: Index ( lhs2, idx2) = lhs2. kind {
229
- if eq_expr_value ( cx, lhs1, lhs2) {
230
- let ty = cx. typeck_results ( ) . expr_ty ( lhs1) . peel_refs ( ) ;
231
-
232
- if matches ! ( ty. kind( ) , ty:: Slice ( _) )
233
- || matches ! ( ty. kind( ) , ty:: Array ( _, _) )
234
- || is_type_diagnostic_item ( cx, ty, sym:: vec_type)
235
- || is_type_diagnostic_item ( cx, ty, sym:: vecdeque_type)
236
- {
237
- return Slice :: Swappable ( lhs1, idx1, idx2) ;
238
- }
239
- } else {
240
- return Slice :: NotSwappable ;
241
- }
242
- }
243
- }
244
-
245
- Slice :: None
246
- }
247
-
248
165
/// Implementation of the `ALMOST_SWAPPED` lint.
249
166
fn check_suspicious_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
250
167
for w in block. stmts . windows ( 2 ) {
@@ -295,62 +212,20 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
295
212
}
296
213
}
297
214
298
- /// Implementation of the `XOR_SWAP ` lint.
215
+ /// Implementation of the xor case for `MANUAL_SWAP ` lint.
299
216
fn check_xor_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
300
217
for window in block. stmts . windows ( 3 ) {
301
218
if_chain ! {
302
219
if let Some ( ( lhs0, rhs0) ) = extract_sides_of_xor_assign( & window[ 0 ] ) ;
303
220
if let Some ( ( lhs1, rhs1) ) = extract_sides_of_xor_assign( & window[ 1 ] ) ;
304
221
if let Some ( ( lhs2, rhs2) ) = extract_sides_of_xor_assign( & window[ 2 ] ) ;
305
- if eq_expr_value( cx, lhs0, rhs1)
306
- && eq_expr_value( cx, rhs1 , lhs2 )
307
- && eq_expr_value( cx, rhs0 , lhs1 )
308
- && eq_expr_value( cx, lhs1, rhs2) ;
222
+ if eq_expr_value( cx, lhs0, rhs1) ;
223
+ if eq_expr_value( cx, lhs2 , rhs1 ) ;
224
+ if eq_expr_value( cx, lhs1 , rhs0 ) ;
225
+ if eq_expr_value( cx, lhs1, rhs2) ;
309
226
then {
310
227
let span = window[ 0 ] . span. to( window[ 2 ] . span) ;
311
- let mut applicability = Applicability :: MachineApplicable ;
312
- match check_for_slice( cx, lhs0, rhs0) {
313
- Slice :: Swappable ( slice, idx0, idx1) => {
314
- if let Some ( slice) = Sugg :: hir_opt( cx, slice) {
315
- span_lint_and_sugg(
316
- cx,
317
- XOR_SWAP ,
318
- span,
319
- & format!(
320
- "this xor-based swap of the elements of `{}` can be \
321
- more clearly expressed using `.swap`",
322
- slice
323
- ) ,
324
- "try" ,
325
- format!(
326
- "{}.swap({}, {})" ,
327
- slice. maybe_par( ) ,
328
- snippet_with_applicability( cx, idx0. span, ".." , & mut applicability) ,
329
- snippet_with_applicability( cx, idx1. span, ".." , & mut applicability)
330
- ) ,
331
- applicability
332
- )
333
- }
334
- }
335
- Slice :: None => {
336
- if let ( Some ( first) , Some ( second) ) = ( Sugg :: hir_opt( cx, lhs0) , Sugg :: hir_opt( cx, rhs0) ) {
337
- span_lint_and_sugg(
338
- cx,
339
- XOR_SWAP ,
340
- span,
341
- & format!(
342
- "this xor-based swap of `{}` and `{}` can be \
343
- more clearly expressed using `std::mem::swap`",
344
- first, second
345
- ) ,
346
- "try" ,
347
- format!( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
348
- applicability,
349
- ) ;
350
- }
351
- }
352
- Slice :: NotSwappable => { }
353
- }
228
+ generate_swap_warning( cx, lhs0, rhs0, span, true ) ;
354
229
}
355
230
} ;
356
231
}
0 commit comments