1
- use crate :: utils:: { match_type , span_lint_and_sugg } ;
1
+ use crate :: utils;
2
2
use crate :: utils:: paths;
3
3
use crate :: utils:: sugg:: Sugg ;
4
4
use if_chain:: if_chain;
5
5
use rustc_errors:: Applicability ;
6
- use rustc_lint:: { LateLintPass , LateContext } ;
7
- use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
8
6
use rustc_hir:: * ;
7
+ use rustc_lint:: { LateContext , LateLintPass } ;
8
+ use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
9
+ use rustc_span:: symbol:: Ident ;
9
10
10
11
declare_clippy_lint ! {
11
12
/// **What it does:**
@@ -40,18 +41,122 @@ struct LintTrigger {
40
41
unstable : bool ,
41
42
}
42
43
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
+
43
139
fn detect_lint ( cx : & LateContext < ' _ , ' _ > , expr : & Expr < ' _ > ) -> Option < LintTrigger > {
44
140
if_chain ! {
45
141
if let ExprKind :: MethodCall ( name_ident, _, args) = & expr. kind;
46
142
if let name = name_ident. ident. name. to_ident_string( ) ;
47
143
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) ;
51
154
then {
52
155
let vec_name = Sugg :: hir( cx, & args[ 0 ] , ".." ) . to_string( ) ;
53
156
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 } )
55
160
} else {
56
161
None
57
162
}
@@ -60,18 +165,8 @@ fn detect_lint(cx: &LateContext<'_, '_>, expr: &Expr<'_>) -> Option<LintTrigger>
60
165
61
166
impl LateLintPass < ' _ , ' _ > for SortByKeyReverse {
62
167
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
- ) ;
73
168
if let Some ( trigger) = detect_lint ( cx, expr) {
74
- span_lint_and_sugg (
169
+ utils :: span_lint_and_sugg (
75
170
cx,
76
171
SORT_BY_KEY_REVERSE ,
77
172
expr. span ,
0 commit comments