1- use crate :: utils:: { match_type , span_lint_and_sugg } ;
1+ use crate :: utils;
22use crate :: utils:: paths;
33use crate :: utils:: sugg:: Sugg ;
44use if_chain:: if_chain;
55use rustc_errors:: Applicability ;
6- use rustc_lint:: { LateLintPass , LateContext } ;
7- use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
86use rustc_hir:: * ;
7+ use rustc_lint:: { LateContext , LateLintPass } ;
8+ use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
9+ use rustc_span:: symbol:: Ident ;
910
1011declare_clippy_lint ! {
1112 /// **What it does:**
@@ -40,18 +41,122 @@ struct LintTrigger {
4041 unstable : bool ,
4142}
4243
44+ /// Detect if the two expressions are mirrored (identical, except one
45+ /// contains a and the other replaces it with b)
46+ fn mirrored_exprs ( cx : & LateContext < ' _ , ' _ > , a_expr : & Expr < ' _ > , a_ident : & Ident , b_expr : & Expr < ' _ > , b_ident : & Ident ) -> bool {
47+ match ( & a_expr. kind , & b_expr. kind ) {
48+ // Two boxes with mirrored contents
49+ ( ExprKind :: Box ( left_expr) , ExprKind :: Box ( right_expr) ) => mirrored_exprs ( cx, left_expr, a_ident, right_expr, b_ident) ,
50+ // Two arrays with mirrored contents
51+ ( ExprKind :: Array ( left_exprs) , ExprKind :: Array ( right_exprs) )
52+ => left_exprs. iter ( ) . zip ( right_exprs. iter ( ) ) . all ( |( left, right) | mirrored_exprs ( cx, left, a_ident, right, b_ident) ) ,
53+ // The two exprs are function calls.
54+ // Check to see that the function itself and its arguments are mirrored
55+ ( ExprKind :: Call ( left_expr, left_args) , ExprKind :: Call ( right_expr, right_args) )
56+ => mirrored_exprs ( cx, left_expr, a_ident, right_expr, b_ident)
57+ && left_args. iter ( ) . zip ( right_args. iter ( ) ) . all ( |( left, right) | mirrored_exprs ( cx, left, a_ident, right, b_ident) ) ,
58+ // The two exprs are method calls.
59+ // Check to see that the function is the same and the arguments are mirrored
60+ // This is enough because the receiver of the method is listed in the arguments
61+ ( ExprKind :: MethodCall ( left_segment, _, left_args) , ExprKind :: MethodCall ( right_segment, _, right_args) )
62+ => left_segment. ident == right_segment. ident
63+ && left_args. iter ( ) . zip ( right_args. iter ( ) ) . all ( |( left, right) | mirrored_exprs ( cx, left, a_ident, right, b_ident) ) ,
64+ // Two tuples with mirrored contents
65+ ( ExprKind :: Tup ( left_exprs) , ExprKind :: Tup ( right_exprs) )
66+ => left_exprs. iter ( ) . zip ( right_exprs. iter ( ) ) . all ( |( left, right) | mirrored_exprs ( cx, left, a_ident, right, b_ident) ) ,
67+ // Two binary ops, which are the same operation and which have mirrored arguments
68+ ( ExprKind :: Binary ( left_op, left_left, left_right) , ExprKind :: Binary ( right_op, right_left, right_right) )
69+ => left_op. node == right_op. node
70+ && mirrored_exprs ( cx, left_left, a_ident, right_left, b_ident)
71+ && mirrored_exprs ( cx, left_right, a_ident, right_right, b_ident) ,
72+ // Two unary ops, which are the same operation and which have the same argument
73+ ( ExprKind :: Unary ( left_op, left_expr) , ExprKind :: Unary ( right_op, right_expr) )
74+ => left_op == right_op && mirrored_exprs ( cx, left_expr, a_ident, right_expr, b_ident) ,
75+ // The two exprs are literals of some kind
76+ ( ExprKind :: Lit ( left_lit) , ExprKind :: Lit ( right_lit) ) => left_lit. node == right_lit. node ,
77+ ( ExprKind :: Cast ( left_expr, _) , ExprKind :: Cast ( right_expr, _) )
78+ => mirrored_exprs ( cx, left_expr, a_ident, right_expr, b_ident) ,
79+ ( ExprKind :: DropTemps ( left) , ExprKind :: DropTemps ( right) ) => mirrored_exprs ( cx, left, a_ident, right, b_ident) ,
80+ ( ExprKind :: Block ( left, _) , ExprKind :: Block ( right, _) ) => mirrored_blocks ( cx, left, a_ident, right, b_ident) ,
81+ ( ExprKind :: Field ( left_expr, left_ident) , ExprKind :: Field ( right_expr, right_ident) )
82+ => left_ident. name == right_ident. name && mirrored_exprs ( cx, left_expr, a_ident, right_expr, right_ident) ,
83+ // The two exprs are `a` and `b`, directly
84+ ( ExprKind :: Path ( QPath :: Resolved ( _, Path { segments : & [ PathSegment { ident : left_ident, .. } ] , .. } , ) ) ,
85+ ExprKind :: Path ( QPath :: Resolved ( _, Path { segments : & [ PathSegment { ident : right_ident, .. } ] , .. } , ) ) ,
86+ ) => & left_ident == a_ident && & right_ident == b_ident,
87+ // The two exprs are Paths to the same name (which is neither a nor b)
88+ ( ExprKind :: Path ( QPath :: Resolved ( _, Path { segments : left_segments, .. } ) ) ,
89+ ExprKind :: Path ( QPath :: Resolved ( _, Path { segments : right_segments, .. } ) ) )
90+ => left_segments. iter ( ) . zip ( right_segments. iter ( ) ) . all ( |( left, right) | left. ident == right. ident )
91+ && left_segments. iter ( ) . all ( |seg| & seg. ident != a_ident && & seg. ident != b_ident) ,
92+ // Matching expressions, but one or both is borrowed
93+ ( ExprKind :: AddrOf ( left_kind, Mutability :: Not , left_expr) , ExprKind :: AddrOf ( right_kind, Mutability :: Not , right_expr) )
94+ => left_kind == right_kind && mirrored_exprs ( cx, left_expr, a_ident, right_expr, b_ident) ,
95+ ( _, ExprKind :: AddrOf ( _, Mutability :: Not , right_expr) )
96+ => mirrored_exprs ( cx, a_expr, a_ident, right_expr, b_ident) ,
97+ ( ExprKind :: AddrOf ( _, Mutability :: Not , left_expr) , _)
98+ => mirrored_exprs ( cx, left_expr, a_ident, b_expr, b_ident) ,
99+ // _ => false,
100+ ( left, right) => {
101+ println ! ( "{:?}\n {:?}" , left, right) ;
102+ false
103+ } ,
104+ }
105+ }
106+
107+ /// Detect if the two blocks are mirrored (identical, except one
108+ /// contains a and the other replaces it with b)
109+ fn mirrored_blocks ( cx : & LateContext < ' _ , ' _ > , a_block : & Block < ' _ > , a_ident : & Ident , b_block : & Block < ' _ > , b_ident : & Ident ) -> bool {
110+ match ( a_block, b_block) {
111+ ( Block { stmts : left_stmts, expr : left_expr, .. } ,
112+ Block { stmts : right_stmts, expr : right_expr, .. } )
113+ => left_stmts. iter ( ) . zip ( right_stmts. iter ( ) ) . all ( |( left, right) | match ( & left. kind , & right. kind ) {
114+ ( StmtKind :: Expr ( left_expr) , StmtKind :: Expr ( right_expr) ) => mirrored_exprs ( cx, left_expr, a_ident, right_expr, b_ident) ,
115+ ( StmtKind :: Semi ( left_expr) , StmtKind :: Semi ( right_expr) ) => mirrored_exprs ( cx, left_expr, a_ident, right_expr, b_ident) ,
116+ ( StmtKind :: Item ( left_item) , StmtKind :: Item ( right_item) ) => left_item. id == right_item. id ,
117+ ( StmtKind :: Local ( left) , StmtKind :: Local ( right) ) => mirrored_locals ( cx, left, a_ident, right, b_ident) ,
118+ _ => false ,
119+ } ) && match ( left_expr, right_expr) {
120+ ( None , None ) => true ,
121+ ( Some ( left) , Some ( right) ) => mirrored_exprs ( cx, left, a_ident, right, b_ident) ,
122+ _ => false ,
123+ } ,
124+ }
125+ }
126+
127+ /// Check that the two "Local"s (let statements) are equal
128+ fn mirrored_locals ( cx : & LateContext < ' _ , ' _ > , a_local : & Local < ' _ > , a_ident : & Ident , b_local : & Local < ' _ > , b_ident : & Ident ) -> bool {
129+ match ( a_local, b_local) {
130+ ( Local { pat : left_pat, init : left_expr, .. } , Local { pat : right_pat, init : right_expr, .. } )
131+ => match ( left_expr, right_expr) {
132+ ( None , None ) => true ,
133+ ( Some ( left) , Some ( right) ) => mirrored_exprs ( cx, left, a_ident, right, b_ident) ,
134+ _ => false ,
135+ } ,
136+ }
137+ }
138+
43139fn detect_lint ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > ) -> Option < LintTrigger > {
44140 if_chain ! {
45141 if let ExprKind :: MethodCall ( name_ident, _, args) = & expr. kind;
46142 if let name = name_ident. ident. name. to_ident_string( ) ;
47143 if name == "sort_by" || name == "sort_unstable_by" ;
48- if let [ vec, Expr { kind: ExprKind :: Closure ( _, closure_decl, closure_body_id, _, _) , .. } ] = args;
49- if closure_decl. inputs. len( ) == 2 ;
50- if match_type( cx, & cx. tables. expr_ty( vec) , & paths:: VEC ) ;
144+ if let [ vec, Expr { kind: ExprKind :: Closure ( _, _, closure_body_id, _, _) , .. } ] = args;
145+ if utils:: match_type( cx, & cx. tables. expr_ty( vec) , & paths:: VEC ) ;
146+ if let closure_body = cx. tcx. hir( ) . body( * closure_body_id) ;
147+ if let & [
148+ Param { pat: Pat { kind: PatKind :: Binding ( _, _, a_ident, _) , .. } , ..} ,
149+ Param { pat: Pat { kind: PatKind :: Binding ( _, _, b_ident, _) , .. } , .. }
150+ ] = & closure_body. params;
151+ if let ExprKind :: MethodCall ( method_path, _, [ ref b_expr, ref a_expr] ) = & closure_body. value. kind;
152+ if method_path. ident. name. to_ident_string( ) == "cmp" ;
153+ if mirrored_exprs( & cx, & a_expr, & a_ident, & b_expr, & b_ident) ;
51154 then {
52155 let vec_name = Sugg :: hir( cx, & args[ 0 ] , ".." ) . to_string( ) ;
53156 let unstable = name == "sort_unstable_by" ;
54- Some ( LintTrigger { vec_name, unstable, closure_arg: "e" . to_string( ) , closure_reverse_body: "e" . to_string( ) } )
157+ let closure_arg = a_ident. name. to_ident_string( ) ;
158+ let closure_reverse_body = Sugg :: hir( cx, & a_expr, ".." ) . to_string( ) ;
159+ Some ( LintTrigger { vec_name, unstable, closure_arg, closure_reverse_body } )
55160 } else {
56161 None
57162 }
@@ -60,18 +165,8 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<LintTrigger>
60165
61166impl LateLintPass < ' _ , ' _ > for SortByKeyReverse {
62167 fn check_expr ( & mut self , cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > ) {
63- println ! ( "{:?}" , expr) ;
64- span_lint_and_sugg (
65- cx,
66- SORT_BY_KEY_REVERSE ,
67- expr. span ,
68- "use Vec::sort_by_key here instead" ,
69- "try" ,
70- String :: from ( "being a better person" ) ,
71- Applicability :: MachineApplicable ,
72- ) ;
73168 if let Some ( trigger) = detect_lint ( cx, expr) {
74- span_lint_and_sugg (
169+ utils :: span_lint_and_sugg (
75170 cx,
76171 SORT_BY_KEY_REVERSE ,
77172 expr. span ,
0 commit comments