1
1
use rustc:: lint:: * ;
2
+ use rustc:: middle:: ty;
2
3
use rustc_front:: hir:: * ;
3
4
use std:: collections:: HashMap ;
4
5
use std:: collections:: hash_map:: Entry ;
6
+ use syntax:: parse:: token:: InternedString ;
5
7
use utils:: { SpanlessEq , SpanlessHash } ;
6
8
use utils:: { get_parent_expr, in_macro, span_note_and_lint} ;
7
9
@@ -33,14 +35,34 @@ declare_lint! {
33
35
"if with the same *then* and *else* blocks"
34
36
}
35
37
38
+ /// **What it does:** This lint checks for `match` with identical arm bodies.
39
+ ///
40
+ /// **Why is this bad?** This is probably a copy & paste error.
41
+ ///
42
+ /// **Known problems:** Hopefully none.
43
+ ///
44
+ /// **Example:**
45
+ /// ```rust,ignore
46
+ /// match foo {
47
+ /// Bar => bar(),
48
+ /// Quz => quz(),
49
+ /// Baz => bar(), // <= oups
50
+ /// ```
51
+ declare_lint ! {
52
+ pub MATCH_SAME_ARMS ,
53
+ Warn ,
54
+ "`match` with identical arm bodies"
55
+ }
56
+
36
57
#[ derive( Copy , Clone , Debug ) ]
37
58
pub struct CopyAndPaste ;
38
59
39
60
impl LintPass for CopyAndPaste {
40
61
fn get_lints ( & self ) -> LintArray {
41
62
lint_array ! [
42
63
IFS_SAME_COND ,
43
- IF_SAME_THEN_ELSE
64
+ IF_SAME_THEN_ELSE ,
65
+ MATCH_SAME_ARMS
44
66
]
45
67
}
46
68
}
@@ -58,39 +80,63 @@ impl LateLintPass for CopyAndPaste {
58
80
let ( conds, blocks) = if_sequence ( expr) ;
59
81
lint_same_then_else ( cx, & blocks) ;
60
82
lint_same_cond ( cx, & conds) ;
83
+ lint_match_arms ( cx, expr) ;
61
84
}
62
85
}
63
86
}
64
87
65
88
/// Implementation of `IF_SAME_THEN_ELSE`.
66
89
fn lint_same_then_else ( cx : & LateContext , blocks : & [ & Block ] ) {
67
- let hash = |block| -> u64 {
90
+ let hash : & Fn ( & & Block ) -> u64 = & |block| -> u64 {
68
91
let mut h = SpanlessHash :: new ( cx) ;
69
92
h. hash_block ( block) ;
70
93
h. finish ( )
71
94
} ;
72
- let eq = |lhs, rhs| -> bool {
95
+
96
+ let eq : & Fn ( & & Block , & & Block ) -> bool = & |& lhs, & rhs| -> bool {
73
97
SpanlessEq :: new ( cx) . eq_block ( lhs, rhs)
74
98
} ;
75
99
76
100
if let Some ( ( i, j) ) = search_same ( blocks, hash, eq) {
77
- span_note_and_lint ( cx, IF_SAME_THEN_ELSE , j. span , "this if has identical blocks" , i. span , "same as this" ) ;
101
+ span_note_and_lint ( cx, IF_SAME_THEN_ELSE , j. span , "this `if` has identical blocks" , i. span , "same as this" ) ;
78
102
}
79
103
}
80
104
81
105
/// Implementation of `IFS_SAME_COND`.
82
106
fn lint_same_cond ( cx : & LateContext , conds : & [ & Expr ] ) {
83
- let hash = |expr| -> u64 {
107
+ let hash : & Fn ( & & Expr ) -> u64 = & |expr| -> u64 {
84
108
let mut h = SpanlessHash :: new ( cx) ;
85
109
h. hash_expr ( expr) ;
86
110
h. finish ( )
87
111
} ;
88
- let eq = |lhs, rhs| -> bool {
112
+
113
+ let eq : & Fn ( & & Expr , & & Expr ) -> bool = & |& lhs, & rhs| -> bool {
89
114
SpanlessEq :: new ( cx) . ignore_fn ( ) . eq_expr ( lhs, rhs)
90
115
} ;
91
116
92
117
if let Some ( ( i, j) ) = search_same ( conds, hash, eq) {
93
- span_note_and_lint ( cx, IFS_SAME_COND , j. span , "this if has the same condition as a previous if" , i. span , "same as this" ) ;
118
+ span_note_and_lint ( cx, IFS_SAME_COND , j. span , "this `if` has the same condition as a previous if" , i. span , "same as this" ) ;
119
+ }
120
+ }
121
+
122
+ /// Implementation if `MATCH_SAME_ARMS`.
123
+ fn lint_match_arms ( cx : & LateContext , expr : & Expr ) {
124
+ let hash = |arm : & Arm | -> u64 {
125
+ let mut h = SpanlessHash :: new ( cx) ;
126
+ h. hash_expr ( & arm. body ) ;
127
+ h. finish ( )
128
+ } ;
129
+
130
+ let eq = |lhs : & Arm , rhs : & Arm | -> bool {
131
+ SpanlessEq :: new ( cx) . eq_expr ( & lhs. body , & rhs. body ) &&
132
+ // all patterns should have the same bindings
133
+ bindings ( cx, & lhs. pats [ 0 ] ) == bindings ( cx, & rhs. pats [ 0 ] )
134
+ } ;
135
+
136
+ if let ExprMatch ( _, ref arms, MatchSource :: Normal ) = expr. node {
137
+ if let Some ( ( i, j) ) = search_same ( & * * arms, hash, eq) {
138
+ span_note_and_lint ( cx, MATCH_SAME_ARMS , j. body . span , "this `match` has identical arm bodies" , i. body . span , "same as this" ) ;
139
+ }
94
140
}
95
141
}
96
142
@@ -123,11 +169,59 @@ fn if_sequence(mut expr: &Expr) -> (Vec<&Expr>, Vec<&Block>) {
123
169
( conds, blocks)
124
170
}
125
171
126
- fn search_same < ' a , T , Hash , Eq > ( exprs : & [ & ' a T ] ,
127
- hash : Hash ,
128
- eq : Eq ) -> Option < ( & ' a T , & ' a T ) >
129
- where Hash : Fn ( & ' a T ) -> u64 ,
130
- Eq : Fn ( & ' a T , & ' a T ) -> bool {
172
+ /// Return the list of bindings in a pattern.
173
+ fn bindings < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , pat : & Pat ) -> HashMap < InternedString , ty:: Ty < ' tcx > > {
174
+ fn bindings_impl < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , pat : & Pat , map : & mut HashMap < InternedString , ty:: Ty < ' tcx > > ) {
175
+ match pat. node {
176
+ PatBox ( ref pat) | PatRegion ( ref pat, _) => bindings_impl ( cx, pat, map) ,
177
+ PatEnum ( _, Some ( ref pats) ) => {
178
+ for pat in pats {
179
+ bindings_impl ( cx, pat, map) ;
180
+ }
181
+ }
182
+ PatIdent ( _, ref ident, ref as_pat) => {
183
+ if let Entry :: Vacant ( v) = map. entry ( ident. node . name . as_str ( ) ) {
184
+ v. insert ( cx. tcx . pat_ty ( pat) ) ;
185
+ }
186
+ if let Some ( ref as_pat) = * as_pat {
187
+ bindings_impl ( cx, as_pat, map) ;
188
+ }
189
+ } ,
190
+ PatStruct ( _, ref fields, _) => {
191
+ for pat in fields {
192
+ bindings_impl ( cx, & pat. node . pat , map) ;
193
+ }
194
+ }
195
+ PatTup ( ref fields) => {
196
+ for pat in fields {
197
+ bindings_impl ( cx, pat, map) ;
198
+ }
199
+ }
200
+ PatVec ( ref lhs, ref mid, ref rhs) => {
201
+ for pat in lhs {
202
+ bindings_impl ( cx, pat, map) ;
203
+ }
204
+ if let Some ( ref mid) = * mid {
205
+ bindings_impl ( cx, mid, map) ;
206
+ }
207
+ for pat in rhs {
208
+ bindings_impl ( cx, pat, map) ;
209
+ }
210
+ }
211
+ PatEnum ( ..) | PatLit ( ..) | PatQPath ( ..) | PatRange ( ..) | PatWild => ( ) ,
212
+ }
213
+ }
214
+
215
+ let mut result = HashMap :: new ( ) ;
216
+ bindings_impl ( cx, pat, & mut result) ;
217
+ result
218
+ }
219
+
220
+ fn search_same < T , Hash , Eq > ( exprs : & [ T ] ,
221
+ hash : Hash ,
222
+ eq : Eq ) -> Option < ( & T , & T ) >
223
+ where Hash : Fn ( & T ) -> u64 ,
224
+ Eq : Fn ( & T , & T ) -> bool {
131
225
// common cases
132
226
if exprs. len ( ) < 2 {
133
227
return None ;
@@ -141,14 +235,14 @@ where Hash: Fn(&'a T) -> u64,
141
235
}
142
236
}
143
237
144
- let mut map : HashMap < _ , Vec < & ' a _ > > = HashMap :: with_capacity ( exprs. len ( ) ) ;
238
+ let mut map : HashMap < _ , Vec < & _ > > = HashMap :: with_capacity ( exprs. len ( ) ) ;
145
239
146
- for & expr in exprs {
240
+ for expr in exprs {
147
241
match map. entry ( hash ( expr) ) {
148
242
Entry :: Occupied ( o) => {
149
243
for o in o. get ( ) {
150
- if eq ( o, expr) {
151
- return Some ( ( o, expr) )
244
+ if eq ( & o, expr) {
245
+ return Some ( ( & o, expr) )
152
246
}
153
247
}
154
248
}
0 commit comments