1
- use clippy_utils:: diagnostics:: span_lint_and_then;
1
+ 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
- use rustc_hir:: { Block , Expr , ExprKind , PatKind , QPath , StmtKind } ;
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
- use rustc_span:: sym;
12
+ use rustc_span:: source_map:: Spanned ;
13
+ use rustc_span:: { sym, Span } ;
13
14
14
15
declare_clippy_lint ! {
15
16
/// ### What it does
@@ -70,9 +71,67 @@ impl<'tcx> LateLintPass<'tcx> for Swap {
70
71
fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & ' tcx Block < ' _ > ) {
71
72
check_manual_swap ( cx, block) ;
72
73
check_suspicious_swap ( cx, block) ;
74
+ check_xor_swap ( cx, block) ;
73
75
}
74
76
}
75
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
+
76
135
/// Implementation of the `MANUAL_SWAP` lint.
77
136
fn check_manual_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
78
137
for w in block. stmts . windows ( 3 ) {
@@ -96,121 +155,11 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
96
155
if eq_expr_value( cx, tmp_init, lhs1) ;
97
156
if eq_expr_value( cx, rhs1, lhs2) ;
98
157
then {
99
- if let ExprKind :: Field ( lhs1, _) = lhs1. kind {
100
- if let ExprKind :: Field ( lhs2, _) = lhs2. kind {
101
- if lhs1. hir_id. owner == lhs2. hir_id. owner {
102
- return ;
103
- }
104
- }
105
- }
106
-
107
- let mut applicability = Applicability :: MachineApplicable ;
108
-
109
- let slice = check_for_slice( cx, lhs1, lhs2) ;
110
- let ( replace, what, sugg) = if let Slice :: NotSwappable = slice {
111
- return ;
112
- } else if let Slice :: Swappable ( slice, idx1, idx2) = slice {
113
- if let Some ( slice) = Sugg :: hir_opt( cx, slice) {
114
- (
115
- false ,
116
- format!( " elements of `{}`" , slice) ,
117
- format!(
118
- "{}.swap({}, {})" ,
119
- slice. maybe_par( ) ,
120
- snippet_with_applicability( cx, idx1. span, ".." , & mut applicability) ,
121
- snippet_with_applicability( cx, idx2. span, ".." , & mut applicability) ,
122
- ) ,
123
- )
124
- } else {
125
- ( false , String :: new( ) , String :: new( ) )
126
- }
127
- } else if let ( Some ( first) , Some ( second) ) = ( Sugg :: hir_opt( cx, lhs1) , Sugg :: hir_opt( cx, rhs1) ) {
128
- (
129
- true ,
130
- format!( " `{}` and `{}`" , first, second) ,
131
- format!( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
132
- )
133
- } else {
134
- ( true , String :: new( ) , String :: new( ) )
135
- } ;
136
-
137
158
let span = w[ 0 ] . span. to( second. span) ;
138
-
139
- span_lint_and_then(
140
- cx,
141
- MANUAL_SWAP ,
142
- span,
143
- & format!( "this looks like you are swapping{} manually" , what) ,
144
- |diag| {
145
- if !sugg. is_empty( ) {
146
- diag. span_suggestion(
147
- span,
148
- "try" ,
149
- sugg,
150
- applicability,
151
- ) ;
152
-
153
- if replace {
154
- diag. note( "or maybe you should use `std::mem::replace`?" ) ;
155
- }
156
- }
157
- }
158
- ) ;
159
- }
160
- }
161
- }
162
- }
163
-
164
- enum Slice < ' a > {
165
- /// `slice.swap(idx1, idx2)` can be used
166
- ///
167
- /// ## Example
168
- ///
169
- /// ```rust
170
- /// # let mut a = vec![0, 1];
171
- /// let t = a[1];
172
- /// a[1] = a[0];
173
- /// a[0] = t;
174
- /// // can be written as
175
- /// a.swap(0, 1);
176
- /// ```
177
- Swappable ( & ' a Expr < ' a > , & ' a Expr < ' a > , & ' a Expr < ' a > ) ,
178
- /// The `swap` function cannot be used.
179
- ///
180
- /// ## Example
181
- ///
182
- /// ```rust
183
- /// # let mut a = [vec![1, 2], vec![3, 4]];
184
- /// let t = a[0][1];
185
- /// a[0][1] = a[1][0];
186
- /// a[1][0] = t;
187
- /// ```
188
- NotSwappable ,
189
- /// Not a slice
190
- None ,
191
- }
192
-
193
- /// Checks if both expressions are index operations into "slice-like" types.
194
- fn check_for_slice < ' a > ( cx : & LateContext < ' _ > , lhs1 : & ' a Expr < ' _ > , lhs2 : & ' a Expr < ' _ > ) -> Slice < ' a > {
195
- if let ExprKind :: Index ( lhs1, idx1) = lhs1. kind {
196
- if let ExprKind :: Index ( lhs2, idx2) = lhs2. kind {
197
- if eq_expr_value ( cx, lhs1, lhs2) {
198
- let ty = cx. typeck_results ( ) . expr_ty ( lhs1) . peel_refs ( ) ;
199
-
200
- if matches ! ( ty. kind( ) , ty:: Slice ( _) )
201
- || matches ! ( ty. kind( ) , ty:: Array ( _, _) )
202
- || is_type_diagnostic_item ( cx, ty, sym:: vec_type)
203
- || is_type_diagnostic_item ( cx, ty, sym:: vecdeque_type)
204
- {
205
- return Slice :: Swappable ( lhs1, idx1, idx2) ;
206
- }
207
- } else {
208
- return Slice :: NotSwappable ;
159
+ generate_swap_warning( cx, lhs1, lhs2, span, false ) ;
209
160
}
210
161
}
211
162
}
212
-
213
- Slice :: None
214
163
}
215
164
216
165
/// Implementation of the `ALMOST_SWAPPED` lint.
@@ -262,3 +211,40 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
262
211
}
263
212
}
264
213
}
214
+
215
+ /// Implementation of the xor case for `MANUAL_SWAP` lint.
216
+ fn check_xor_swap ( cx : & LateContext < ' _ > , block : & Block < ' _ > ) {
217
+ for window in block. stmts . windows ( 3 ) {
218
+ if_chain ! {
219
+ if let Some ( ( lhs0, rhs0) ) = extract_sides_of_xor_assign( & window[ 0 ] ) ;
220
+ if let Some ( ( lhs1, rhs1) ) = extract_sides_of_xor_assign( & window[ 1 ] ) ;
221
+ if let Some ( ( lhs2, rhs2) ) = extract_sides_of_xor_assign( & window[ 2 ] ) ;
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) ;
226
+ then {
227
+ let span = window[ 0 ] . span. to( window[ 2 ] . span) ;
228
+ generate_swap_warning( cx, lhs0, rhs0, span, true ) ;
229
+ }
230
+ } ;
231
+ }
232
+ }
233
+
234
+ /// Returns the lhs and rhs of an xor assignment statement.
235
+ fn extract_sides_of_xor_assign < ' a , ' hir > ( stmt : & ' a Stmt < ' hir > ) -> Option < ( & ' a Expr < ' hir > , & ' a Expr < ' hir > ) > {
236
+ if let StmtKind :: Semi ( expr) = stmt. kind {
237
+ if let ExprKind :: AssignOp (
238
+ Spanned {
239
+ node : BinOpKind :: BitXor ,
240
+ ..
241
+ } ,
242
+ lhs,
243
+ rhs,
244
+ ) = expr. kind
245
+ {
246
+ return Some ( ( lhs, rhs) ) ;
247
+ }
248
+ }
249
+ None
250
+ }
0 commit comments