1
1
use crate :: utils:: sugg:: Sugg ;
2
2
use crate :: utils:: {
3
- differing_macro_contexts, is_type_diagnostic_item, match_type, paths, snippet , span_lint_and_then , walk_ptrs_ty ,
4
- SpanlessEq ,
3
+ differing_macro_contexts, is_type_diagnostic_item, match_type, paths, snippet_with_applicability ,
4
+ span_lint_and_then , walk_ptrs_ty , SpanlessEq ,
5
5
} ;
6
6
use if_chain:: if_chain;
7
7
use matches:: matches;
@@ -97,29 +97,6 @@ fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block) {
97
97
if SpanlessEq :: new( cx) . ignore_fn( ) . eq_expr( tmp_init, lhs1) ;
98
98
if SpanlessEq :: new( cx) . ignore_fn( ) . eq_expr( rhs1, lhs2) ;
99
99
then {
100
- fn check_for_slice<' a>(
101
- cx: & LateContext <' _, ' _>,
102
- lhs1: & ' a Expr ,
103
- lhs2: & ' a Expr ,
104
- ) -> Option <( & ' a Expr , & ' a Expr , & ' a Expr ) > {
105
- if let ExprKind :: Index ( ref lhs1, ref idx1) = lhs1. kind {
106
- if let ExprKind :: Index ( ref lhs2, ref idx2) = lhs2. kind {
107
- if SpanlessEq :: new( cx) . ignore_fn( ) . eq_expr( lhs1, lhs2) {
108
- let ty = walk_ptrs_ty( cx. tables. expr_ty( lhs1) ) ;
109
-
110
- if matches!( ty. kind, ty:: Slice ( _) ) ||
111
- matches!( ty. kind, ty:: Array ( _, _) ) ||
112
- is_type_diagnostic_item( cx, ty, Symbol :: intern( "vec_type" ) ) ||
113
- match_type( cx, ty, & paths:: VEC_DEQUE ) {
114
- return Some ( ( lhs1, idx1, idx2) ) ;
115
- }
116
- }
117
- }
118
- }
119
-
120
- None
121
- }
122
-
123
100
if let ExprKind :: Field ( ref lhs1, _) = lhs1. kind {
124
101
if let ExprKind :: Field ( ref lhs2, _) = lhs2. kind {
125
102
if lhs1. hir_id. owner_def_id( ) == lhs2. hir_id. owner_def_id( ) {
@@ -128,47 +105,113 @@ fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block) {
128
105
}
129
106
}
130
107
131
- let ( replace, what, sugg) = if let Some ( ( slice, idx1, idx2) ) = check_for_slice( cx, lhs1, lhs2) {
108
+ let mut applicability = Applicability :: MachineApplicable ;
109
+
110
+ let slice = check_for_slice( cx, lhs1, lhs2) ;
111
+ let ( replace, what, sugg) = if let Slice :: NotSwappable = slice {
112
+ return ;
113
+ } else if let Slice :: Swappable ( slice, idx1, idx2) = slice {
132
114
if let Some ( slice) = Sugg :: hir_opt( cx, slice) {
133
- ( false ,
134
- format!( " elements of `{}`" , slice) ,
135
- format!( "{}.swap({}, {})" ,
136
- slice. maybe_par( ) ,
137
- snippet( cx, idx1. span, ".." ) ,
138
- snippet( cx, idx2. span, ".." ) ) )
115
+ (
116
+ false ,
117
+ format!( " elements of `{}`" , slice) ,
118
+ format!(
119
+ "{}.swap({}, {})" ,
120
+ slice. maybe_par( ) ,
121
+ snippet_with_applicability( cx, idx1. span, ".." , & mut applicability) ,
122
+ snippet_with_applicability( cx, idx2. span, ".." , & mut applicability) ,
123
+ ) ,
124
+ )
139
125
} else {
140
126
( false , String :: new( ) , String :: new( ) )
141
127
}
142
128
} else if let ( Some ( first) , Some ( second) ) = ( Sugg :: hir_opt( cx, lhs1) , Sugg :: hir_opt( cx, rhs1) ) {
143
- ( true , format!( " `{}` and `{}`" , first, second) ,
144
- format!( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) )
129
+ (
130
+ true ,
131
+ format!( " `{}` and `{}`" , first, second) ,
132
+ format!( "std::mem::swap({}, {})" , first. mut_addr( ) , second. mut_addr( ) ) ,
133
+ )
145
134
} else {
146
135
( true , String :: new( ) , String :: new( ) )
147
136
} ;
148
137
149
138
let span = w[ 0 ] . span. to( second. span) ;
150
139
151
- span_lint_and_then( cx,
152
- MANUAL_SWAP ,
153
- span,
154
- & format!( "this looks like you are swapping{} manually" , what) ,
155
- |db| {
156
- if !sugg. is_empty( ) {
157
- db. span_suggestion(
158
- span,
159
- "try" ,
160
- sugg,
161
- Applicability :: Unspecified ,
162
- ) ;
140
+ span_lint_and_then(
141
+ cx,
142
+ MANUAL_SWAP ,
143
+ span,
144
+ & format!( "this looks like you are swapping{} manually" , what) ,
145
+ |db| {
146
+ if !sugg. is_empty( ) {
147
+ db. span_suggestion(
148
+ span,
149
+ "try" ,
150
+ sugg,
151
+ applicability,
152
+ ) ;
163
153
164
- if replace {
165
- db. note( "or maybe you should use `std::mem::replace`?" ) ;
166
- }
167
- }
168
- } ) ;
154
+ if replace {
155
+ db. note( "or maybe you should use `std::mem::replace`?" ) ;
156
+ }
157
+ }
158
+ }
159
+ ) ;
160
+ }
161
+ }
162
+ }
163
+ }
164
+
165
+ enum Slice < ' a > {
166
+ /// `slice.swap(idx1, idx2)` can be used
167
+ ///
168
+ /// ## Example
169
+ ///
170
+ /// ```rust
171
+ /// # let mut a = vec![0, 1];
172
+ /// let t = a[1];
173
+ /// a[1] = a[0];
174
+ /// a[0] = t;
175
+ /// // can be written as
176
+ /// a.swap(0, 1);
177
+ /// ```
178
+ Swappable ( & ' a Expr , & ' a Expr , & ' a Expr ) ,
179
+ /// The `swap` function cannot be used.
180
+ ///
181
+ /// ## Example
182
+ ///
183
+ /// ```rust
184
+ /// # let mut a = [vec![1, 2], vec![3, 4]];
185
+ /// let t = a[0][1];
186
+ /// a[0][1] = a[1][0];
187
+ /// a[1][0] = t;
188
+ /// ```
189
+ NotSwappable ,
190
+ /// Not a slice
191
+ None ,
192
+ }
193
+
194
+ /// Checks if both expressions are index operations into "slice-like" types.
195
+ fn check_for_slice < ' a > ( cx : & LateContext < ' _ , ' _ > , lhs1 : & ' a Expr , lhs2 : & ' a Expr ) -> Slice < ' a > {
196
+ if let ExprKind :: Index ( ref lhs1, ref idx1) = lhs1. kind {
197
+ if let ExprKind :: Index ( ref lhs2, ref idx2) = lhs2. kind {
198
+ if SpanlessEq :: new ( cx) . ignore_fn ( ) . eq_expr ( lhs1, lhs2) {
199
+ let ty = walk_ptrs_ty ( cx. tables . expr_ty ( lhs1) ) ;
200
+
201
+ if matches ! ( ty. kind, ty:: Slice ( _) )
202
+ || matches ! ( ty. kind, ty:: Array ( _, _) )
203
+ || is_type_diagnostic_item ( cx, ty, Symbol :: intern ( "vec_type" ) )
204
+ || match_type ( cx, ty, & paths:: VEC_DEQUE )
205
+ {
206
+ return Slice :: Swappable ( lhs1, idx1, idx2) ;
207
+ }
208
+ } else {
209
+ return Slice :: NotSwappable ;
169
210
}
170
211
}
171
212
}
213
+
214
+ Slice :: None
172
215
}
173
216
174
217
/// Implementation of the `ALMOST_SWAPPED` lint.
0 commit comments