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 } ;
54use rustc_lint:: { LateContext , LateLintPass } ;
6- use rustc_middle:: ty:: { Ty , TyS } ;
75use 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 ;
116
127declare_clippy_lint ! {
138 /// **What it does:** Checks for consecutive `if`s with the same condition.
@@ -108,48 +103,7 @@ declare_clippy_lint! {
108103 "`if` with the same `then` and `else` blocks"
109104}
110105
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 ] ) ;
153107
154108impl < ' tcx > LateLintPass < ' tcx > for CopyAndPaste {
155109 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
@@ -167,7 +121,6 @@ impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
167121 lint_same_then_else ( cx, & blocks) ;
168122 lint_same_cond ( cx, & conds) ;
169123 lint_same_fns_in_if_cond ( cx, & conds) ;
170- lint_match_arms ( cx, expr) ;
171124 }
172125 }
173126}
@@ -243,122 +196,6 @@ fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
243196 }
244197}
245198
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-
362199fn search_same_sequenced < T , Eq > ( exprs : & [ T ] , eq : Eq ) -> Option < ( & T , & T ) >
363200where
364201 Eq : Fn ( & T , & T ) -> bool ,
@@ -370,47 +207,3 @@ where
370207 }
371208 None
372209}
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