1
- use crate :: utils:: { eq_expr_value, in_macro, SpanlessEq , SpanlessHash } ;
2
- use crate :: utils:: { get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then} ;
3
- use rustc_data_structures:: fx:: FxHashMap ;
4
- use rustc_hir:: { Arm , Block , Expr , ExprKind , MatchSource , Pat , PatKind } ;
1
+ use crate :: utils:: { eq_expr_value, in_macro, search_same, SpanlessEq , SpanlessHash } ;
2
+ use crate :: utils:: { get_parent_expr, higher, if_sequence, span_lint_and_note} ;
3
+ use rustc_hir:: { Block , Expr } ;
5
4
use rustc_lint:: { LateContext , LateLintPass } ;
6
- use rustc_middle:: ty:: { Ty , TyS } ;
7
5
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
8
- use rustc_span:: symbol:: Symbol ;
9
- use std:: collections:: hash_map:: Entry ;
10
- use std:: hash:: BuildHasherDefault ;
11
6
12
7
declare_clippy_lint ! {
13
8
/// **What it does:** Checks for consecutive `if`s with the same condition.
@@ -108,48 +103,7 @@ declare_clippy_lint! {
108
103
"`if` with the same `then` and `else` blocks"
109
104
}
110
105
111
- declare_clippy_lint ! {
112
- /// **What it does:** Checks for `match` with identical arm bodies.
113
- ///
114
- /// **Why is this bad?** This is probably a copy & paste error. If arm bodies
115
- /// are the same on purpose, you can factor them
116
- /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
117
- ///
118
- /// **Known problems:** False positive possible with order dependent `match`
119
- /// (see issue
120
- /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
121
- ///
122
- /// **Example:**
123
- /// ```rust,ignore
124
- /// match foo {
125
- /// Bar => bar(),
126
- /// Quz => quz(),
127
- /// Baz => bar(), // <= oops
128
- /// }
129
- /// ```
130
- ///
131
- /// This should probably be
132
- /// ```rust,ignore
133
- /// match foo {
134
- /// Bar => bar(),
135
- /// Quz => quz(),
136
- /// Baz => baz(), // <= fixed
137
- /// }
138
- /// ```
139
- ///
140
- /// or if the original code was not a typo:
141
- /// ```rust,ignore
142
- /// match foo {
143
- /// Bar | Baz => bar(), // <= shows the intent better
144
- /// Quz => quz(),
145
- /// }
146
- /// ```
147
- pub MATCH_SAME_ARMS ,
148
- pedantic,
149
- "`match` with identical arm bodies"
150
- }
151
-
152
- declare_lint_pass ! ( CopyAndPaste => [ IFS_SAME_COND , SAME_FUNCTIONS_IN_IF_CONDITION , IF_SAME_THEN_ELSE , MATCH_SAME_ARMS ] ) ;
106
+ declare_lint_pass ! ( CopyAndPaste => [ IFS_SAME_COND , SAME_FUNCTIONS_IN_IF_CONDITION , IF_SAME_THEN_ELSE ] ) ;
153
107
154
108
impl < ' tcx > LateLintPass < ' tcx > for CopyAndPaste {
155
109
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
@@ -167,7 +121,6 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
167
121
lint_same_then_else ( cx, & blocks) ;
168
122
lint_same_cond ( cx, & conds) ;
169
123
lint_same_fns_in_if_cond ( cx, & conds) ;
170
- lint_match_arms ( cx, expr) ;
171
124
}
172
125
}
173
126
}
@@ -243,122 +196,6 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
243
196
}
244
197
}
245
198
246
- /// Implementation of `MATCH_SAME_ARMS`.
247
- fn lint_match_arms < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' _ > ) {
248
- fn same_bindings < ' tcx > ( lhs : & FxHashMap < Symbol , Ty < ' tcx > > , rhs : & FxHashMap < Symbol , Ty < ' tcx > > ) -> bool {
249
- lhs. len ( ) == rhs. len ( )
250
- && lhs
251
- . iter ( )
252
- . all ( |( name, l_ty) | rhs. get ( name) . map_or ( false , |r_ty| TyS :: same_type ( l_ty, r_ty) ) )
253
- }
254
-
255
- if let ExprKind :: Match ( _, ref arms, MatchSource :: Normal ) = expr. kind {
256
- let hash = |& ( _, arm) : & ( usize , & Arm < ' _ > ) | -> u64 {
257
- let mut h = SpanlessHash :: new ( cx) ;
258
- h. hash_expr ( & arm. body ) ;
259
- h. finish ( )
260
- } ;
261
-
262
- let eq = |& ( lindex, lhs) : & ( usize , & Arm < ' _ > ) , & ( rindex, rhs) : & ( usize , & Arm < ' _ > ) | -> bool {
263
- let min_index = usize:: min ( lindex, rindex) ;
264
- let max_index = usize:: max ( lindex, rindex) ;
265
-
266
- // Arms with a guard are ignored, those can’t always be merged together
267
- // This is also the case for arms in-between each there is an arm with a guard
268
- ( min_index..=max_index) . all ( |index| arms[ index] . guard . is_none ( ) ) &&
269
- SpanlessEq :: new ( cx) . eq_expr ( & lhs. body , & rhs. body ) &&
270
- // all patterns should have the same bindings
271
- same_bindings ( & bindings ( cx, & lhs. pat ) , & bindings ( cx, & rhs. pat ) )
272
- } ;
273
-
274
- let indexed_arms: Vec < ( usize , & Arm < ' _ > ) > = arms. iter ( ) . enumerate ( ) . collect ( ) ;
275
- for ( & ( _, i) , & ( _, j) ) in search_same ( & indexed_arms, hash, eq) {
276
- span_lint_and_then (
277
- cx,
278
- MATCH_SAME_ARMS ,
279
- j. body . span ,
280
- "this `match` has identical arm bodies" ,
281
- |diag| {
282
- diag. span_note ( i. body . span , "same as this" ) ;
283
-
284
- // Note: this does not use `span_suggestion` on purpose:
285
- // there is no clean way
286
- // to remove the other arm. Building a span and suggest to replace it to ""
287
- // makes an even more confusing error message. Also in order not to make up a
288
- // span for the whole pattern, the suggestion is only shown when there is only
289
- // one pattern. The user should know about `|` if they are already using it…
290
-
291
- let lhs = snippet ( cx, i. pat . span , "<pat1>" ) ;
292
- let rhs = snippet ( cx, j. pat . span , "<pat2>" ) ;
293
-
294
- if let PatKind :: Wild = j. pat . kind {
295
- // if the last arm is _, then i could be integrated into _
296
- // note that i.pat cannot be _, because that would mean that we're
297
- // hiding all the subsequent arms, and rust won't compile
298
- diag. span_note (
299
- i. body . span ,
300
- & format ! (
301
- "`{}` has the same arm body as the `_` wildcard, consider removing it" ,
302
- lhs
303
- ) ,
304
- ) ;
305
- } else {
306
- diag. span_help ( i. pat . span , & format ! ( "consider refactoring into `{} | {}`" , lhs, rhs) ) ;
307
- }
308
- } ,
309
- ) ;
310
- }
311
- }
312
- }
313
-
314
- /// Returns the list of bindings in a pattern.
315
- fn bindings < ' tcx > ( cx : & LateContext < ' tcx > , pat : & Pat < ' _ > ) -> FxHashMap < Symbol , Ty < ' tcx > > {
316
- fn bindings_impl < ' tcx > ( cx : & LateContext < ' tcx > , pat : & Pat < ' _ > , map : & mut FxHashMap < Symbol , Ty < ' tcx > > ) {
317
- match pat. kind {
318
- PatKind :: Box ( ref pat) | PatKind :: Ref ( ref pat, _) => bindings_impl ( cx, pat, map) ,
319
- PatKind :: TupleStruct ( _, pats, _) => {
320
- for pat in pats {
321
- bindings_impl ( cx, pat, map) ;
322
- }
323
- } ,
324
- PatKind :: Binding ( .., ident, ref as_pat) => {
325
- if let Entry :: Vacant ( v) = map. entry ( ident. name ) {
326
- v. insert ( cx. typeck_results ( ) . pat_ty ( pat) ) ;
327
- }
328
- if let Some ( ref as_pat) = * as_pat {
329
- bindings_impl ( cx, as_pat, map) ;
330
- }
331
- } ,
332
- PatKind :: Or ( fields) | PatKind :: Tuple ( fields, _) => {
333
- for pat in fields {
334
- bindings_impl ( cx, pat, map) ;
335
- }
336
- } ,
337
- PatKind :: Struct ( _, fields, _) => {
338
- for pat in fields {
339
- bindings_impl ( cx, & pat. pat , map) ;
340
- }
341
- } ,
342
- PatKind :: Slice ( lhs, ref mid, rhs) => {
343
- for pat in lhs {
344
- bindings_impl ( cx, pat, map) ;
345
- }
346
- if let Some ( ref mid) = * mid {
347
- bindings_impl ( cx, mid, map) ;
348
- }
349
- for pat in rhs {
350
- bindings_impl ( cx, pat, map) ;
351
- }
352
- } ,
353
- PatKind :: Lit ( ..) | PatKind :: Range ( ..) | PatKind :: Wild | PatKind :: Path ( ..) => ( ) ,
354
- }
355
- }
356
-
357
- let mut result = FxHashMap :: default ( ) ;
358
- bindings_impl ( cx, pat, & mut result) ;
359
- result
360
- }
361
-
362
199
fn search_same_sequenced < T , Eq > ( exprs : & [ T ] , eq : Eq ) -> Option < ( & T , & T ) >
363
200
where
364
201
Eq : Fn ( & T , & T ) -> bool ,
@@ -370,47 +207,3 @@ where
370
207
}
371
208
None
372
209
}
373
-
374
- fn search_common_cases < ' a , T , Eq > ( exprs : & ' a [ T ] , eq : & Eq ) -> Option < ( & ' a T , & ' a T ) >
375
- where
376
- Eq : Fn ( & T , & T ) -> bool ,
377
- {
378
- if exprs. len ( ) == 2 && eq ( & exprs[ 0 ] , & exprs[ 1 ] ) {
379
- Some ( ( & exprs[ 0 ] , & exprs[ 1 ] ) )
380
- } else {
381
- None
382
- }
383
- }
384
-
385
- fn search_same < T , Hash , Eq > ( exprs : & [ T ] , hash : Hash , eq : Eq ) -> Vec < ( & T , & T ) >
386
- where
387
- Hash : Fn ( & T ) -> u64 ,
388
- Eq : Fn ( & T , & T ) -> bool ,
389
- {
390
- if let Some ( expr) = search_common_cases ( & exprs, & eq) {
391
- return vec ! [ expr] ;
392
- }
393
-
394
- let mut match_expr_list: Vec < ( & T , & T ) > = Vec :: new ( ) ;
395
-
396
- let mut map: FxHashMap < _ , Vec < & _ > > =
397
- FxHashMap :: with_capacity_and_hasher ( exprs. len ( ) , BuildHasherDefault :: default ( ) ) ;
398
-
399
- for expr in exprs {
400
- match map. entry ( hash ( expr) ) {
401
- Entry :: Occupied ( mut o) => {
402
- for o in o. get ( ) {
403
- if eq ( o, expr) {
404
- match_expr_list. push ( ( o, expr) ) ;
405
- }
406
- }
407
- o. get_mut ( ) . push ( expr) ;
408
- } ,
409
- Entry :: Vacant ( v) => {
410
- v. insert ( vec ! [ expr] ) ;
411
- } ,
412
- }
413
- }
414
-
415
- match_expr_list
416
- }
0 commit comments