@@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{
5
5
use clippy_utils:: macros:: { is_panic, root_macro_call} ;
6
6
use clippy_utils:: source:: { expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability} ;
7
7
use 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} ;
9
9
use clippy_utils:: visitors:: is_local_used;
10
10
use clippy_utils:: {
11
11
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;
31
31
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
32
32
use rustc_span:: source_map:: { Span , Spanned } ;
33
33
use rustc_span:: sym;
34
- use std:: cmp:: Ordering ;
34
+ use std:: cmp:: { max , Ordering } ;
35
35
use std:: collections:: hash_map:: Entry ;
36
36
37
37
declare_clippy_lint ! {
@@ -741,7 +741,7 @@ fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], exp
741
741
let ty = cx. typeck_results ( ) . expr_ty ( ex) ;
742
742
if * ty. kind ( ) != ty:: Bool || is_lint_allowed ( cx, MATCH_BOOL , ex. hir_id ) {
743
743
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) ;
745
745
}
746
746
}
747
747
}
@@ -835,7 +835,6 @@ fn check_single_match_opt_like(
835
835
ex : & Expr < ' _ > ,
836
836
arms : & [ Arm < ' _ > ] ,
837
837
expr : & Expr < ' _ > ,
838
- ty : Ty < ' _ > ,
839
838
els : Option < & Expr < ' _ > > ,
840
839
) {
841
840
// list of candidate `Enum`s we know will never get any more members
@@ -849,25 +848,135 @@ fn check_single_match_opt_like(
849
848
( & paths:: RESULT , "Ok" ) ,
850
849
] ;
851
850
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
+ }
857
885
}
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
859
897
} ,
860
- PatKind :: Binding ( BindingAnnotation :: Unannotated , .., ident, None ) => ident. to_string ( ) ,
861
898
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
863
903
} ,
864
- _ => return ,
865
- } ;
904
+ _ => false ,
905
+ }
906
+ }
866
907
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 ,
871
980
}
872
981
}
873
982
0 commit comments