@@ -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, symbol:: kw} ;
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 ! {
@@ -830,12 +830,12 @@ fn report_single_match_single_pattern(
830
830
) ;
831
831
}
832
832
833
- fn check_single_match_opt_like (
834
- cx : & LateContext < ' _ > ,
833
+ fn check_single_match_opt_like < ' a > (
834
+ cx : & LateContext < ' a > ,
835
835
ex : & Expr < ' _ > ,
836
836
arms : & [ Arm < ' _ > ] ,
837
837
expr : & Expr < ' _ > ,
838
- ty : Ty < ' _ > ,
838
+ ty : Ty < ' a > ,
839
839
els : Option < & Expr < ' _ > > ,
840
840
) {
841
841
// list of candidate `Enum`s we know will never get any more members
@@ -849,25 +849,120 @@ fn check_single_match_opt_like(
849
849
( & paths:: RESULT , "Ok" ) ,
850
850
] ;
851
851
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 ;
852
+ // We want to suggest to exclude an arm that contains only wildcards or forms the exhaustive
853
+ // match with the second branch, without enum variants in matches.
854
+ if !contains_only_wilds ( arms[ 1 ] . pat ) && !form_exhaustive_matches ( arms[ 0 ] . pat , arms[ 1 ] . pat ) {
855
+ return ;
856
+ }
857
+
858
+ let mut paths_and_types = Vec :: new ( ) ;
859
+ if !collect_pat_paths ( & mut paths_and_types, cx, arms[ 1 ] . pat , ty) {
860
+ return ;
861
+ }
862
+
863
+ let in_candidate_enum = |path_info : & ( String , & TyS < ' _ > ) | -> bool {
864
+ let ( path, ty) = path_info;
865
+ for & ( ty_path, pat_path) in candidates {
866
+ if path == pat_path && match_type ( cx, ty, ty_path) {
867
+ return true ;
857
868
}
858
- rustc_hir_pretty:: to_string ( rustc_hir_pretty:: NO_ANN , |s| s. print_qpath ( path, false ) )
869
+ }
870
+ false
871
+ } ;
872
+ if paths_and_types. iter ( ) . all ( in_candidate_enum) {
873
+ report_single_match_single_pattern ( cx, ex, arms, expr, els) ;
874
+ }
875
+ }
876
+
877
+ /// Collects paths and their types from the given patterns. Returns true if the given pattern could
878
+ /// be simplified, false otherwise.
879
+ fn collect_pat_paths < ' a > ( acc : & mut Vec < ( String , Ty < ' a > ) > , cx : & LateContext < ' a > , pat : & Pat < ' _ > , ty : Ty < ' a > ) -> bool {
880
+ match pat. kind {
881
+ PatKind :: Wild => true ,
882
+ PatKind :: Tuple ( inner, _) => inner. iter ( ) . all ( |p| {
883
+ let p_ty = cx. typeck_results ( ) . pat_ty ( p) ;
884
+ collect_pat_paths ( acc, cx, p, p_ty)
885
+ } ) ,
886
+ PatKind :: TupleStruct ( ref path, ..) => {
887
+ let path = rustc_hir_pretty:: to_string ( rustc_hir_pretty:: NO_ANN , |s| {
888
+ s. print_qpath ( path, false ) ;
889
+ } ) ;
890
+ acc. push ( ( path, ty) ) ;
891
+ true
892
+ } ,
893
+ PatKind :: Binding ( BindingAnnotation :: Unannotated , .., ident, None ) => {
894
+ acc. push ( ( ident. to_string ( ) , ty) ) ;
895
+ true
859
896
} ,
860
- PatKind :: Binding ( BindingAnnotation :: Unannotated , .., ident, None ) => ident. to_string ( ) ,
861
897
PatKind :: Path ( ref path) => {
862
- rustc_hir_pretty:: to_string ( rustc_hir_pretty:: NO_ANN , |s| s. print_qpath ( path, false ) )
898
+ let path = rustc_hir_pretty:: to_string ( rustc_hir_pretty:: NO_ANN , |s| {
899
+ s. print_qpath ( path, false ) ;
900
+ } ) ;
901
+ acc. push ( ( path, ty) ) ;
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 forms only exhaustive matches that don't contain enum
918
+ /// patterns without a wildcard.
919
+ fn form_exhaustive_matches ( left : & Pat < ' _ > , right : & Pat < ' _ > ) -> bool {
920
+ match ( & left. kind , & right. kind ) {
921
+ ( PatKind :: Wild , _) | ( _, PatKind :: Wild ) => true ,
922
+ ( PatKind :: Tuple ( left_in, left_pos) , PatKind :: Tuple ( right_in, right_pos) ) => {
923
+ // We don't actually know the position and the presence of the `..` (dotdot) operator
924
+ // in the arms, so we need to evaluate the correct offsets here in order to iterate in
925
+ // both arms at the same time.
926
+ let len = max (
927
+ left_in. len ( ) + {
928
+ if left_pos. is_some ( ) { 1 } else { 0 }
929
+ } ,
930
+ right_in. len ( ) + {
931
+ if right_pos. is_some ( ) { 1 } else { 0 }
932
+ } ,
933
+ ) ;
934
+ let mut left_pos = left_pos. unwrap_or ( usize:: MAX ) ;
935
+ let mut right_pos = right_pos. unwrap_or ( usize:: MAX ) ;
936
+ let mut left_dot_space = 0 ;
937
+ let mut right_dot_space = 0 ;
938
+ for i in 0 ..len {
939
+ let mut found_dotdot = false ;
940
+ if i == left_pos {
941
+ left_dot_space += 1 ;
942
+ if left_dot_space < len - left_in. len ( ) {
943
+ left_pos += 1 ;
944
+ }
945
+ found_dotdot = true ;
946
+ }
947
+ if i == right_pos {
948
+ right_dot_space += 1 ;
949
+ if right_dot_space < len - right_in. len ( ) {
950
+ right_pos += 1 ;
951
+ }
952
+ found_dotdot = true ;
953
+ }
954
+ if found_dotdot {
955
+ continue ;
956
+ }
957
+ if !contains_only_wilds ( & left_in[ i - left_dot_space] )
958
+ && !contains_only_wilds ( & right_in[ i - right_dot_space] )
959
+ {
960
+ return false ;
961
+ }
962
+ }
963
+ true
964
+ } ,
965
+ _ => false ,
871
966
}
872
967
}
873
968
0 commit comments