@@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{
55use clippy_utils:: macros:: { is_panic, root_macro_call} ;
66use clippy_utils:: source:: { expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability} ;
77use clippy_utils:: sugg:: Sugg ;
8- use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item, match_type , peel_mid_ty_refs} ;
8+ use clippy_utils:: ty:: { implements_trait, is_type_diagnostic_item, peel_mid_ty_refs} ;
99use clippy_utils:: visitors:: is_local_used;
1010use clippy_utils:: {
1111 get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
@@ -31,7 +31,7 @@ use rustc_semver::RustcVersion;
3131use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
3232use rustc_span:: source_map:: { Span , Spanned } ;
3333use rustc_span:: sym;
34- use std:: cmp:: Ordering ;
34+ use std:: cmp:: { max , Ordering } ;
3535use std:: collections:: hash_map:: Entry ;
3636
3737declare_clippy_lint ! {
@@ -741,7 +741,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
741741 let ty = cx. typeck_results ( ) . expr_ty ( ex) ;
742742 if * ty. kind ( ) != ty:: Bool || is_lint_allowed ( cx, MATCH_BOOL , ex. hir_id ) {
743743 check_single_match_single_pattern ( cx, ex, arms, expr, els) ;
744- check_single_match_opt_like ( cx, ex, arms, expr, ty , els) ;
744+ check_single_match_opt_like ( cx, ex, arms, expr, els) ;
745745 }
746746 }
747747}
@@ -835,7 +835,6 @@ fn check_single_match_opt_like(
835835 ex : & Expr < ' _ > ,
836836 arms : & [ Arm < ' _ > ] ,
837837 expr : & Expr < ' _ > ,
838- ty : Ty < ' _ > ,
839838 els : Option < & Expr < ' _ > > ,
840839) {
841840 // list of candidate `Enum`s we know will never get any more members
@@ -849,25 +848,135 @@ fn check_single_match_opt_like(
849848 ( & paths:: RESULT , "Ok" ) ,
850849 ] ;
851850
852- let path = match arms[ 1 ] . pat . kind {
853- PatKind :: TupleStruct ( ref path, inner, _) => {
854- // Contains any non wildcard patterns (e.g., `Err(err)`)?
855- if !inner. iter ( ) . all ( is_wild) {
856- return ;
851+ // We want to suggest to exclude an arm that contains only wildcards or forms the ehaustive
852+ // match with the second branch.
853+ if !contains_only_wilds ( arms[ 1 ] . pat ) && !form_exhaustive_tuples ( arms[ 0 ] . pat , arms[ 1 ] . pat ) {
854+ return ;
855+ }
856+
857+ let mut paths = Vec :: new ( ) ;
858+ if !collect_pat_paths ( & mut paths, arms[ 1 ] . pat ) {
859+ return ;
860+ }
861+
862+ let in_candidate_enum = |path : & String | -> bool {
863+ for & ( _, pat_path) in candidates {
864+ if path == pat_path {
865+ return true ;
866+ }
867+ }
868+ false
869+ } ;
870+ if paths. iter ( ) . all ( in_candidate_enum) {
871+ report_single_match_single_pattern ( cx, ex, arms, expr, els) ;
872+ }
873+ }
874+
875+ /// Collects paths from the given paths. Returns true if the given pattern could be simplified,
876+ /// false otherwise.
877+ fn collect_pat_paths ( acc : & mut Vec < String > , pat : & Pat < ' _ > ) -> bool {
878+ match pat. kind {
879+ PatKind :: Wild => true ,
880+ PatKind :: Tuple ( inner, _) => {
881+ for p in inner {
882+ if !collect_pat_paths ( acc, p) {
883+ return false ;
884+ }
857885 }
858- rustc_hir_pretty:: to_string ( rustc_hir_pretty:: NO_ANN , |s| s. print_qpath ( path, false ) )
886+ true
887+ } ,
888+ PatKind :: TupleStruct ( ref path, ..) => {
889+ acc. push ( rustc_hir_pretty:: to_string ( rustc_hir_pretty:: NO_ANN , |s| {
890+ s. print_qpath ( path, false ) ;
891+ } ) ) ;
892+ true
893+ } ,
894+ PatKind :: Binding ( BindingAnnotation :: Unannotated , .., ident, None ) => {
895+ acc. push ( ident. to_string ( ) ) ;
896+ true
859897 } ,
860- PatKind :: Binding ( BindingAnnotation :: Unannotated , .., ident, None ) => ident. to_string ( ) ,
861898 PatKind :: Path ( ref path) => {
862- rustc_hir_pretty:: to_string ( rustc_hir_pretty:: NO_ANN , |s| s. print_qpath ( path, false ) )
899+ acc. push ( rustc_hir_pretty:: to_string ( rustc_hir_pretty:: NO_ANN , |s| {
900+ s. print_qpath ( path, false ) ;
901+ } ) ) ;
902+ true
863903 } ,
864- _ => return ,
865- } ;
904+ _ => false ,
905+ }
906+ }
866907
867- for & ( ty_path, pat_path) in candidates {
868- if path == * pat_path && match_type ( cx, ty, ty_path) {
869- report_single_match_single_pattern ( cx, ex, arms, expr, els) ;
870- }
908+ /// Returns true if the given arm of pattern matching contains wildcard patterns.
909+ fn contains_only_wilds ( pat : & Pat < ' _ > ) -> bool {
910+ match pat. kind {
911+ PatKind :: Wild => true ,
912+ PatKind :: Tuple ( inner, _) | PatKind :: TupleStruct ( _, inner, ..) => inner. iter ( ) . all ( contains_only_wilds) ,
913+ _ => false ,
914+ }
915+ }
916+
917+ /// Returns true if the given patterns form the tuples that exhaustively matches all possible
918+ /// parameters.
919+ ///
920+ /// Here are some examples:
921+ ///
922+ /// ```
923+ /// // Doesn't form exhaustive match, because the first arm may contain not only E::V.
924+ /// match x {
925+ /// (Some(E::V), _) => todo!(),
926+ /// (None, _) => {}
927+ /// }
928+ ///
929+ /// // Forms exhaustive match, because the patterns cover all possible cases at the same positions.
930+ /// match x {
931+ /// (Some(_), _) => todo!(),
932+ /// (None, _) => {}
933+ /// }
934+ /// ```
935+ fn form_exhaustive_tuples ( left : & Pat < ' _ > , right : & Pat < ' _ > ) -> bool {
936+ match ( & left. kind , & right. kind ) {
937+ ( PatKind :: Wild , _) | ( _, PatKind :: Wild ) => true ,
938+ ( PatKind :: Tuple ( left_in, left_pos) , PatKind :: Tuple ( right_in, right_pos) ) => {
939+ // We don't actually know the position and presence of the `..` (dotdot) operator in
940+ // the arms, so we need to evaluate the correct offsets here in order to iterate in
941+ // both arms at the same time.
942+ let len = max (
943+ left_in. len ( ) + {
944+ if left_pos. is_some ( ) { 1 } else { 0 }
945+ } ,
946+ right_in. len ( ) + {
947+ if right_pos. is_some ( ) { 1 } else { 0 }
948+ } ,
949+ ) ;
950+ let mut left_pos = left_pos. unwrap_or ( usize:: MAX ) ;
951+ let mut right_pos = right_pos. unwrap_or ( usize:: MAX ) ;
952+ let mut left_span = 0 ;
953+ let mut right_span = 0 ;
954+ for i in 0 ..len {
955+ let mut found_dotdot = false ;
956+ if i == left_pos {
957+ left_span += 1 ;
958+ if left_span < len - left_in. len ( ) {
959+ left_pos += 1 ;
960+ }
961+ found_dotdot = true ;
962+ }
963+ if i == right_pos {
964+ right_span += 1 ;
965+ if right_span < len - right_in. len ( ) {
966+ right_pos += 1 ;
967+ }
968+ found_dotdot = true ;
969+ }
970+ if found_dotdot {
971+ continue ;
972+ }
973+ if !contains_only_wilds ( & left_in[ i - left_span] ) && !contains_only_wilds ( & right_in[ i - right_span] ) {
974+ return false ;
975+ }
976+ }
977+ true
978+ } ,
979+ _ => false ,
871980 }
872981}
873982
0 commit comments