@@ -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, symbol:: kw} ;
34- use std:: cmp:: Ordering ;
34+ use std:: cmp:: { max , Ordering } ;
3535use std:: collections:: hash_map:: Entry ;
3636
3737declare_clippy_lint ! {
@@ -830,12 +830,12 @@ fn report_single_match_single_pattern(
830830 ) ;
831831}
832832
833- fn check_single_match_opt_like (
834- cx : & LateContext < ' _ > ,
833+ fn check_single_match_opt_like < ' a > (
834+ cx : & LateContext < ' a > ,
835835 ex : & Expr < ' _ > ,
836836 arms : & [ Arm < ' _ > ] ,
837837 expr : & Expr < ' _ > ,
838- ty : Ty < ' _ > ,
838+ ty : Ty < ' a > ,
839839 els : Option < & Expr < ' _ > > ,
840840) {
841841 // list of candidate `Enum`s we know will never get any more members
@@ -849,25 +849,120 @@ fn check_single_match_opt_like(
849849 ( & paths:: RESULT , "Ok" ) ,
850850 ] ;
851851
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 ;
857868 }
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
859896 } ,
860- PatKind :: Binding ( BindingAnnotation :: Unannotated , .., ident, None ) => ident. to_string ( ) ,
861897 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
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 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 ,
871966 }
872967}
873968
0 commit comments