11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2+ use clippy_utils:: is_trait_method;
23use clippy_utils:: sugg:: Sugg ;
3- use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item } ;
4+ use clippy_utils:: ty:: implements_trait;
45use if_chain:: if_chain;
56use rustc_errors:: Applicability ;
67use rustc_hir:: { Closure , Expr , ExprKind , Mutability , Param , Pat , PatKind , Path , PathSegment , QPath } ;
7- use rustc_lint:: { LateContext , LateLintPass } ;
8+ use rustc_lint:: LateContext ;
89use rustc_middle:: ty:: { self , subst:: GenericArgKind } ;
9- use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1010use rustc_span:: sym;
1111use rustc_span:: symbol:: Ident ;
1212use std:: iter;
1313
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 ;
4915
5016enum LintTrigger {
5117 Sort ( SortDetection ) ,
@@ -54,15 +20,13 @@ enum LintTrigger {
5420
5521struct SortDetection {
5622 vec_name : String ,
57- unstable : bool ,
5823}
5924
6025struct SortByKeyDetection {
6126 vec_name : String ,
6227 closure_arg : String ,
6328 closure_body : String ,
6429 reverse : bool ,
65- unstable : bool ,
6630}
6731
6832/// 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
150114 }
151115}
152116
153- fn detect_lint ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Option < LintTrigger > {
117+ fn detect_lint ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , recv : & Expr < ' _ > , arg : & Expr < ' _ > ) -> Option < LintTrigger > {
154118 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) ;
161124 if let & [
162125 Param { pat: Pat { kind: PatKind :: Binding ( _, _, left_ident, _) , .. } , ..} ,
163126 Param { pat: Pat { kind: PatKind :: Binding ( _, _, right_ident, _) , .. } , .. }
164127 ] = & 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;
166129 if method_path. ident. name == sym:: cmp;
130+ if is_trait_method( cx, & closure_body. value, sym:: Ord ) ;
167131 then {
168132 let ( closure_body, closure_arg, reverse) = if mirrored_exprs(
169133 left_expr,
@@ -177,19 +141,18 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
177141 } else {
178142 return None ;
179143 } ;
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( ) ;
182145
183146 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+ } ) ;
191154 then {
192- return Some ( LintTrigger :: Sort ( SortDetection { vec_name, unstable } ) ) ;
155+ return Some ( LintTrigger :: Sort ( SortDetection { vec_name } ) ) ;
193156 }
194157 }
195158
@@ -199,7 +162,6 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
199162 closure_arg,
200163 closure_body,
201164 reverse,
202- unstable,
203165 } ) ) ;
204166 }
205167 }
@@ -213,46 +175,50 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
213175 matches ! ( ty. kind( ) , ty:: Ref ( ..) ) || ty. walk ( ) . any ( |arg| matches ! ( arg. unpack( ) , GenericArgKind :: Lifetime ( _) ) )
214176}
215177
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,
236197 if trigger. reverse {
237- Applicability :: MaybeIncorrect
198+ format! ( "std::cmp::Reverse({})" , trigger . closure_body )
238199 } else {
239- Applicability :: MachineApplicable
200+ trigger . closure_body . to_string ( )
240201 } ,
241202 ) ,
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 { "" } ,
254219 ) ,
255- None => { } ,
256- }
220+ Applicability :: MachineApplicable ,
221+ ) ,
222+ None => { } ,
257223 }
258224}
0 commit comments