@@ -3,19 +3,19 @@ use crate::utils::sugg::Sugg;
33use crate :: utils:: visitors:: LocalUsedVisitor ;
44use crate :: utils:: {
55 expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
6- is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local_id ,
7- peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block, snippet_opt ,
8- snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then ,
9- strip_pat_refs,
6+ is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local ,
7+ path_to_local_id , peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block,
8+ snippet_opt , snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
9+ span_lint_and_then , strip_pat_refs,
1010} ;
1111use crate :: utils:: { paths, search_same, SpanlessEq , SpanlessHash } ;
1212use if_chain:: if_chain;
1313use rustc_ast:: ast:: LitKind ;
14- use rustc_data_structures:: fx:: FxHashMap ;
14+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
1515use rustc_errors:: Applicability ;
1616use rustc_hir:: def:: CtorKind ;
1717use rustc_hir:: {
18- Arm , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , Guard , Local , MatchSource , Mutability , Node , Pat ,
18+ Arm , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , Guard , HirId , Local , MatchSource , Mutability , Node , Pat ,
1919 PatKind , QPath , RangeEnd ,
2020} ;
2121use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
@@ -24,7 +24,7 @@ use rustc_middle::ty::{self, Ty, TyS};
2424use rustc_semver:: RustcVersion ;
2525use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
2626use rustc_span:: source_map:: { Span , Spanned } ;
27- use rustc_span:: { sym, Symbol } ;
27+ use rustc_span:: sym;
2828use std:: cmp:: Ordering ;
2929use std:: collections:: hash_map:: Entry ;
3030use std:: collections:: Bound ;
@@ -1873,13 +1873,6 @@ fn test_overlapping() {
18731873
18741874/// Implementation of `MATCH_SAME_ARMS`.
18751875fn lint_match_arms < ' tcx > ( cx : & LateContext < ' tcx > , expr : & Expr < ' _ > ) {
1876- fn same_bindings < ' tcx > ( lhs : & FxHashMap < Symbol , Ty < ' tcx > > , rhs : & FxHashMap < Symbol , Ty < ' tcx > > ) -> bool {
1877- lhs. len ( ) == rhs. len ( )
1878- && lhs
1879- . iter ( )
1880- . all ( |( name, l_ty) | rhs. get ( name) . map_or ( false , |r_ty| TyS :: same_type ( l_ty, r_ty) ) )
1881- }
1882-
18831876 if let ExprKind :: Match ( _, ref arms, MatchSource :: Normal ) = expr. kind {
18841877 let hash = |& ( _, arm) : & ( usize , & Arm < ' _ > ) | -> u64 {
18851878 let mut h = SpanlessHash :: new ( cx) ;
@@ -1891,12 +1884,38 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
18911884 let min_index = usize:: min ( lindex, rindex) ;
18921885 let max_index = usize:: max ( lindex, rindex) ;
18931886
1887+ let mut local_map: FxHashMap < HirId , HirId > = FxHashMap :: default ( ) ;
1888+ let eq_fallback = |a : & Expr < ' _ > , b : & Expr < ' _ > | {
1889+ if_chain ! {
1890+ if let Some ( a_id) = path_to_local( a) ;
1891+ if let Some ( b_id) = path_to_local( b) ;
1892+ let entry = match local_map. entry( a_id) {
1893+ Entry :: Vacant ( entry) => entry,
1894+ // check if using the same bindings as before
1895+ Entry :: Occupied ( entry) => return * entry. get( ) == b_id,
1896+ } ;
1897+ // the names technically don't have to match; this makes the lint more conservative
1898+ if cx. tcx. hir( ) . name( a_id) == cx. tcx. hir( ) . name( b_id) ;
1899+ if TyS :: same_type( cx. typeck_results( ) . expr_ty( a) , cx. typeck_results( ) . expr_ty( b) ) ;
1900+ if pat_contains_local( lhs. pat, a_id) ;
1901+ if pat_contains_local( rhs. pat, b_id) ;
1902+ then {
1903+ entry. insert( b_id) ;
1904+ true
1905+ } else {
1906+ false
1907+ }
1908+ }
1909+ } ;
18941910 // Arms with a guard are ignored, those can’t always be merged together
18951911 // This is also the case for arms in-between each there is an arm with a guard
1896- ( min_index..=max_index) . all ( |index| arms[ index] . guard . is_none ( ) ) &&
1897- SpanlessEq :: new ( cx) . eq_expr ( & lhs. body , & rhs. body ) &&
1898- // all patterns should have the same bindings
1899- same_bindings ( & bindings ( cx, & lhs. pat ) , & bindings ( cx, & rhs. pat ) )
1912+ ( min_index..=max_index) . all ( |index| arms[ index] . guard . is_none ( ) )
1913+ && SpanlessEq :: new ( cx)
1914+ . expr_fallback ( eq_fallback)
1915+ . eq_expr ( & lhs. body , & rhs. body )
1916+ // these checks could be removed to allow unused bindings
1917+ && bindings_eq ( lhs. pat , local_map. keys ( ) . copied ( ) . collect ( ) )
1918+ && bindings_eq ( rhs. pat , local_map. values ( ) . copied ( ) . collect ( ) )
19001919 } ;
19011920
19021921 let indexed_arms: Vec < ( usize , & Arm < ' _ > ) > = arms. iter ( ) . enumerate ( ) . collect ( ) ;
@@ -1939,50 +1958,18 @@ fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
19391958 }
19401959}
19411960
1942- /// Returns the list of bindings in a pattern.
1943- fn bindings < ' tcx > ( cx : & LateContext < ' tcx > , pat : & Pat < ' _ > ) -> FxHashMap < Symbol , Ty < ' tcx > > {
1944- fn bindings_impl < ' tcx > ( cx : & LateContext < ' tcx > , pat : & Pat < ' _ > , map : & mut FxHashMap < Symbol , Ty < ' tcx > > ) {
1945- match pat. kind {
1946- PatKind :: Box ( ref pat) | PatKind :: Ref ( ref pat, _) => bindings_impl ( cx, pat, map) ,
1947- PatKind :: TupleStruct ( _, pats, _) => {
1948- for pat in pats {
1949- bindings_impl ( cx, pat, map) ;
1950- }
1951- } ,
1952- PatKind :: Binding ( .., ident, ref as_pat) => {
1953- if let Entry :: Vacant ( v) = map. entry ( ident. name ) {
1954- v. insert ( cx. typeck_results ( ) . pat_ty ( pat) ) ;
1955- }
1956- if let Some ( ref as_pat) = * as_pat {
1957- bindings_impl ( cx, as_pat, map) ;
1958- }
1959- } ,
1960- PatKind :: Or ( fields) | PatKind :: Tuple ( fields, _) => {
1961- for pat in fields {
1962- bindings_impl ( cx, pat, map) ;
1963- }
1964- } ,
1965- PatKind :: Struct ( _, fields, _) => {
1966- for pat in fields {
1967- bindings_impl ( cx, & pat. pat , map) ;
1968- }
1969- } ,
1970- PatKind :: Slice ( lhs, ref mid, rhs) => {
1971- for pat in lhs {
1972- bindings_impl ( cx, pat, map) ;
1973- }
1974- if let Some ( ref mid) = * mid {
1975- bindings_impl ( cx, mid, map) ;
1976- }
1977- for pat in rhs {
1978- bindings_impl ( cx, pat, map) ;
1979- }
1980- } ,
1981- PatKind :: Lit ( ..) | PatKind :: Range ( ..) | PatKind :: Wild | PatKind :: Path ( ..) => ( ) ,
1982- }
1983- }
1984-
1985- let mut result = FxHashMap :: default ( ) ;
1986- bindings_impl ( cx, pat, & mut result) ;
1961+ fn pat_contains_local ( pat : & Pat < ' _ > , id : HirId ) -> bool {
1962+ let mut result = false ;
1963+ pat. walk_short ( |p| {
1964+ result |= matches ! ( p. kind, PatKind :: Binding ( _, binding_id, ..) if binding_id == id) ;
1965+ !result
1966+ } ) ;
19871967 result
19881968}
1969+
1970+ /// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
1971+ fn bindings_eq ( pat : & Pat < ' _ > , mut ids : FxHashSet < HirId > ) -> bool {
1972+ let mut result = true ;
1973+ pat. each_binding_or_first ( & mut |_, id, _, _| result &= ids. remove ( & id) ) ;
1974+ result && ids. is_empty ( )
1975+ }
0 commit comments