11use crate :: utils:: sugg:: Sugg ;
22use 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 ,
55} ;
66use if_chain:: if_chain;
77use matches:: matches;
@@ -97,29 +97,6 @@ fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block) {
9797 if SpanlessEq :: new( cx) . ignore_fn( ) . eq_expr( tmp_init, lhs1) ;
9898 if SpanlessEq :: new( cx) . ignore_fn( ) . eq_expr( rhs1, lhs2) ;
9999 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-
123100 if let ExprKind :: Field ( ref lhs1, _) = lhs1. kind {
124101 if let ExprKind :: Field ( ref lhs2, _) = lhs2. kind {
125102 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) {
128105 }
129106 }
130107
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 {
132114 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+ )
139125 } else {
140126 ( false , String :: new( ) , String :: new( ) )
141127 }
142128 } 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+ )
145134 } else {
146135 ( true , String :: new( ) , String :: new( ) )
147136 } ;
148137
149138 let span = w[ 0 ] . span. to( second. span) ;
150139
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+ ) ;
163153
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 ;
169210 }
170211 }
171212 }
213+
214+ Slice :: None
172215}
173216
174217/// Implementation of the `ALMOST_SWAPPED` lint.
0 commit comments