1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
+ use clippy_utils:: is_trait_method;
2
3
use clippy_utils:: sugg:: Sugg ;
3
- use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item } ;
4
+ use clippy_utils:: ty:: implements_trait;
4
5
use if_chain:: if_chain;
5
6
use rustc_errors:: Applicability ;
6
7
use rustc_hir:: { Closure , Expr , ExprKind , Mutability , Param , Pat , PatKind , Path , PathSegment , QPath } ;
7
- use rustc_lint:: { LateContext , LateLintPass } ;
8
+ use rustc_lint:: LateContext ;
8
9
use rustc_middle:: ty:: { self , subst:: GenericArgKind } ;
9
- use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
10
10
use rustc_span:: sym;
11
11
use rustc_span:: symbol:: Ident ;
12
12
use std:: iter;
13
13
14
- declare_clippy_lint ! {
15
- /// ### What it does
16
- /// Detects uses of `Vec::sort_by` passing in a closure
17
- /// which compares the two arguments, either directly or indirectly.
18
- ///
19
- /// ### Why is this bad?
20
- /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
21
- /// possible) than to use `Vec::sort_by` and a more complicated
22
- /// closure.
23
- ///
24
- /// ### Known problems
25
- /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
26
- /// imported by a use statement, then it will need to be added manually.
27
- ///
28
- /// ### Example
29
- /// ```rust
30
- /// # struct A;
31
- /// # impl A { fn foo(&self) {} }
32
- /// # let mut vec: Vec<A> = Vec::new();
33
- /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
34
- /// ```
35
- /// Use instead:
36
- /// ```rust
37
- /// # struct A;
38
- /// # impl A { fn foo(&self) {} }
39
- /// # let mut vec: Vec<A> = Vec::new();
40
- /// vec.sort_by_key(|a| a.foo());
41
- /// ```
42
- #[ clippy:: version = "1.46.0" ]
43
- pub UNNECESSARY_SORT_BY ,
44
- complexity,
45
- "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
46
- }
47
-
48
- declare_lint_pass ! ( UnnecessarySortBy => [ UNNECESSARY_SORT_BY ] ) ;
14
+ use super :: UNNECESSARY_SORT_BY ;
49
15
50
16
enum LintTrigger {
51
17
Sort ( SortDetection ) ,
@@ -54,15 +20,13 @@ enum LintTrigger {
54
20
55
21
struct SortDetection {
56
22
vec_name : String ,
57
- unstable : bool ,
58
23
}
59
24
60
25
struct SortByKeyDetection {
61
26
vec_name : String ,
62
27
closure_arg : String ,
63
28
closure_body : String ,
64
29
reverse : bool ,
65
- unstable : bool ,
66
30
}
67
31
68
32
/// Detect if the two expressions are mirrored (identical, except one
@@ -150,20 +114,20 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident
150
114
}
151
115
}
152
116
153
- fn detect_lint ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Option < LintTrigger > {
117
+ fn detect_lint ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , recv : & Expr < ' _ > , arg : & Expr < ' _ > ) -> Option < LintTrigger > {
154
118
if_chain ! {
155
- if let ExprKind :: MethodCall ( name_ident, args, _) = & expr. kind;
156
- if let name = name_ident. ident. name. to_ident_string( ) ;
157
- if name == "sort_by" || name == "sort_unstable_by" ;
158
- if let [ vec, Expr { kind: ExprKind :: Closure ( Closure { body: closure_body_id, .. } ) , .. } ] = args;
159
- if is_type_diagnostic_item( cx, cx. typeck_results( ) . expr_ty( vec) , sym:: Vec ) ;
160
- if let closure_body = cx. tcx. hir( ) . body( * closure_body_id) ;
119
+ if let Some ( method_id) = cx. typeck_results( ) . type_dependent_def_id( expr. hir_id) ;
120
+ if let Some ( impl_id) = cx. tcx. impl_of_method( method_id) ;
121
+ if cx. tcx. type_of( impl_id) . is_slice( ) ;
122
+ if let ExprKind :: Closure ( & Closure { body, .. } ) = arg. kind;
123
+ if let closure_body = cx. tcx. hir( ) . body( body) ;
161
124
if let & [
162
125
Param { pat: Pat { kind: PatKind :: Binding ( _, _, left_ident, _) , .. } , ..} ,
163
126
Param { pat: Pat { kind: PatKind :: Binding ( _, _, right_ident, _) , .. } , .. }
164
127
] = & closure_body. params;
165
- if let ExprKind :: MethodCall ( method_path, [ ref left_expr, ref right_expr] , _) = & closure_body. value. kind;
128
+ if let ExprKind :: MethodCall ( method_path, [ left_expr, right_expr] , _) = closure_body. value. kind;
166
129
if method_path. ident. name == sym:: cmp;
130
+ if is_trait_method( cx, & closure_body. value, sym:: Ord ) ;
167
131
then {
168
132
let ( closure_body, closure_arg, reverse) = if mirrored_exprs(
169
133
left_expr,
@@ -177,19 +141,18 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
177
141
} else {
178
142
return None ;
179
143
} ;
180
- let vec_name = Sugg :: hir( cx, & args[ 0 ] , ".." ) . to_string( ) ;
181
- let unstable = name == "sort_unstable_by" ;
144
+ let vec_name = Sugg :: hir( cx, recv, ".." ) . to_string( ) ;
182
145
183
146
if_chain! {
184
- if let ExprKind :: Path ( QPath :: Resolved ( _, Path {
185
- segments: [ PathSegment { ident: left_name, .. } ] , ..
186
- } ) ) = & left_expr. kind;
187
- if left_name == left_ident;
188
- if cx. tcx. get_diagnostic_item( sym:: Ord ) . map_or( false , |id| {
189
- implements_trait( cx, cx. typeck_results( ) . expr_ty( left_expr) , id, & [ ] )
190
- } ) ;
147
+ if let ExprKind :: Path ( QPath :: Resolved ( _, Path {
148
+ segments: [ PathSegment { ident: left_name, .. } ] , ..
149
+ } ) ) = & left_expr. kind;
150
+ if left_name == left_ident;
151
+ if cx. tcx. get_diagnostic_item( sym:: Ord ) . map_or( false , |id| {
152
+ implements_trait( cx, cx. typeck_results( ) . expr_ty( left_expr) , id, & [ ] )
153
+ } ) ;
191
154
then {
192
- return Some ( LintTrigger :: Sort ( SortDetection { vec_name, unstable } ) ) ;
155
+ return Some ( LintTrigger :: Sort ( SortDetection { vec_name } ) ) ;
193
156
}
194
157
}
195
158
@@ -199,7 +162,6 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
199
162
closure_arg,
200
163
closure_body,
201
164
reverse,
202
- unstable,
203
165
} ) ) ;
204
166
}
205
167
}
@@ -213,46 +175,50 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
213
175
matches ! ( ty. kind( ) , ty:: Ref ( ..) ) || ty. walk ( ) . any ( |arg| matches ! ( arg. unpack( ) , GenericArgKind :: Lifetime ( _) ) )
214
176
}
215
177
216
- impl LateLintPass < ' _ > for UnnecessarySortBy {
217
- fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
218
- match detect_lint ( cx, expr) {
219
- Some ( LintTrigger :: SortByKey ( trigger) ) => span_lint_and_sugg (
220
- cx,
221
- UNNECESSARY_SORT_BY ,
222
- expr. span ,
223
- "use Vec::sort_by_key here instead" ,
224
- "try" ,
225
- format ! (
226
- "{}.sort{}_by_key(|{}| {})" ,
227
- trigger. vec_name,
228
- if trigger. unstable { "_unstable" } else { "" } ,
229
- trigger. closure_arg,
230
- if trigger. reverse {
231
- format!( "std::cmp::Reverse({})" , trigger. closure_body)
232
- } else {
233
- trigger. closure_body. to_string( )
234
- } ,
235
- ) ,
178
+ pub ( super ) fn check < ' tcx > (
179
+ cx : & LateContext < ' tcx > ,
180
+ expr : & ' tcx Expr < ' _ > ,
181
+ recv : & ' tcx Expr < ' _ > ,
182
+ arg : & ' tcx Expr < ' _ > ,
183
+ is_unstable : bool ,
184
+ ) {
185
+ match detect_lint ( cx, expr, recv, arg) {
186
+ Some ( LintTrigger :: SortByKey ( trigger) ) => span_lint_and_sugg (
187
+ cx,
188
+ UNNECESSARY_SORT_BY ,
189
+ expr. span ,
190
+ "use Vec::sort_by_key here instead" ,
191
+ "try" ,
192
+ format ! (
193
+ "{}.sort{}_by_key(|{}| {})" ,
194
+ trigger. vec_name,
195
+ if is_unstable { "_unstable" } else { "" } ,
196
+ trigger. closure_arg,
236
197
if trigger. reverse {
237
- Applicability :: MaybeIncorrect
198
+ format! ( "std::cmp::Reverse({})" , trigger . closure_body )
238
199
} else {
239
- Applicability :: MachineApplicable
200
+ trigger . closure_body . to_string ( )
240
201
} ,
241
202
) ,
242
- Some ( LintTrigger :: Sort ( trigger) ) => span_lint_and_sugg (
243
- cx,
244
- UNNECESSARY_SORT_BY ,
245
- expr. span ,
246
- "use Vec::sort here instead" ,
247
- "try" ,
248
- format ! (
249
- "{}.sort{}()" ,
250
- trigger. vec_name,
251
- if trigger. unstable { "_unstable" } else { "" } ,
252
- ) ,
253
- Applicability :: MachineApplicable ,
203
+ if trigger. reverse {
204
+ Applicability :: MaybeIncorrect
205
+ } else {
206
+ Applicability :: MachineApplicable
207
+ } ,
208
+ ) ,
209
+ Some ( LintTrigger :: Sort ( trigger) ) => span_lint_and_sugg (
210
+ cx,
211
+ UNNECESSARY_SORT_BY ,
212
+ expr. span ,
213
+ "use Vec::sort here instead" ,
214
+ "try" ,
215
+ format ! (
216
+ "{}.sort{}()" ,
217
+ trigger. vec_name,
218
+ if is_unstable { "_unstable" } else { "" } ,
254
219
) ,
255
- None => { } ,
256
- }
220
+ Applicability :: MachineApplicable ,
221
+ ) ,
222
+ None => { } ,
257
223
}
258
224
}
0 commit comments