@@ -3,15 +3,14 @@ use clippy_utils::diagnostics::{
3
3
multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
4
4
} ;
5
5
use clippy_utils:: macros:: { is_panic, root_macro_call} ;
6
- use clippy_utils:: paths;
7
6
use clippy_utils:: peel_blocks_with_stmt;
8
7
use clippy_utils:: source:: { expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability} ;
9
8
use clippy_utils:: sugg:: Sugg ;
10
- use clippy_utils:: ty:: { implements_trait , is_type_diagnostic_item, match_type , peel_mid_ty_refs } ;
9
+ use clippy_utils:: ty:: is_type_diagnostic_item;
11
10
use clippy_utils:: visitors:: is_local_used;
12
11
use clippy_utils:: {
13
- get_parent_expr, is_lang_ctor, is_lint_allowed , is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs,
14
- path_to_local_id , peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs , recurse_or_patterns, strip_pat_refs,
12
+ get_parent_expr, is_lang_ctor, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, path_to_local_id ,
13
+ peel_blocks, peel_hir_pat_refs, recurse_or_patterns, strip_pat_refs,
15
14
} ;
16
15
use core:: iter:: once;
17
16
use if_chain:: if_chain;
@@ -20,19 +19,20 @@ use rustc_errors::Applicability;
20
19
use rustc_hir:: def:: { CtorKind , DefKind , Res } ;
21
20
use rustc_hir:: LangItem :: { OptionNone , OptionSome } ;
22
21
use rustc_hir:: {
23
- self as hir, Arm , BindingAnnotation , Block , BorrowKind , Expr , ExprKind , Local , MatchSource , Mutability , Node , Pat ,
22
+ self as hir, Arm , BindingAnnotation , BorrowKind , Expr , ExprKind , Local , MatchSource , Mutability , Node , Pat ,
24
23
PatKind , PathSegment , QPath , RangeEnd , TyKind ,
25
24
} ;
26
25
use rustc_lint:: { LateContext , LateLintPass } ;
27
- use rustc_middle:: ty:: { self , Ty , TyS , VariantDef } ;
26
+ use rustc_middle:: ty:: { self , Ty , VariantDef } ;
28
27
use rustc_semver:: RustcVersion ;
29
28
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
30
29
use rustc_span:: { sym, symbol:: kw, Span } ;
31
- use std:: cmp:: { max , Ordering } ;
30
+ use std:: cmp:: Ordering ;
32
31
33
32
mod match_like_matches;
34
33
mod match_same_arms;
35
34
mod redundant_pattern_match;
35
+ mod single_match;
36
36
37
37
declare_clippy_lint ! {
38
38
/// ### What it does
@@ -630,7 +630,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
630
630
}
631
631
632
632
if let ExprKind :: Match ( ex, arms, MatchSource :: Normal ) = expr. kind {
633
- check_single_match ( cx, ex, arms, expr) ;
633
+ single_match :: check ( cx, ex, arms, expr) ;
634
634
check_match_bool ( cx, ex, arms, expr) ;
635
635
check_overlapping_arms ( cx, ex, arms) ;
636
636
check_wild_err_arm ( cx, ex, arms) ;
@@ -710,262 +710,6 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
710
710
extract_msrv_attr ! ( LateContext ) ;
711
711
}
712
712
713
- #[ rustfmt:: skip]
714
- fn check_single_match ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
715
- if arms. len ( ) == 2 && arms[ 0 ] . guard . is_none ( ) && arms[ 1 ] . guard . is_none ( ) {
716
- if expr. span . from_expansion ( ) {
717
- // Don't lint match expressions present in
718
- // macro_rules! block
719
- return ;
720
- }
721
- if let PatKind :: Or ( ..) = arms[ 0 ] . pat . kind {
722
- // don't lint for or patterns for now, this makes
723
- // the lint noisy in unnecessary situations
724
- return ;
725
- }
726
- let els = arms[ 1 ] . body ;
727
- let els = if is_unit_expr ( peel_blocks ( els) ) {
728
- None
729
- } else if let ExprKind :: Block ( Block { stmts, expr : block_expr, .. } , _) = els. kind {
730
- if stmts. len ( ) == 1 && block_expr. is_none ( ) || stmts. is_empty ( ) && block_expr. is_some ( ) {
731
- // single statement/expr "else" block, don't lint
732
- return ;
733
- }
734
- // block with 2+ statements or 1 expr and 1+ statement
735
- Some ( els)
736
- } else {
737
- // not a block, don't lint
738
- return ;
739
- } ;
740
-
741
- let ty = cx. typeck_results ( ) . expr_ty ( ex) ;
742
- if * ty. kind ( ) != ty:: Bool || is_lint_allowed ( cx, MATCH_BOOL , ex. hir_id ) {
743
- check_single_match_single_pattern ( cx, ex, arms, expr, els) ;
744
- check_single_match_opt_like ( cx, ex, arms, expr, ty, els) ;
745
- }
746
- }
747
- }
748
-
749
- fn check_single_match_single_pattern (
750
- cx : & LateContext < ' _ > ,
751
- ex : & Expr < ' _ > ,
752
- arms : & [ Arm < ' _ > ] ,
753
- expr : & Expr < ' _ > ,
754
- els : Option < & Expr < ' _ > > ,
755
- ) {
756
- if is_wild ( arms[ 1 ] . pat ) {
757
- report_single_match_single_pattern ( cx, ex, arms, expr, els) ;
758
- }
759
- }
760
-
761
- fn report_single_match_single_pattern (
762
- cx : & LateContext < ' _ > ,
763
- ex : & Expr < ' _ > ,
764
- arms : & [ Arm < ' _ > ] ,
765
- expr : & Expr < ' _ > ,
766
- els : Option < & Expr < ' _ > > ,
767
- ) {
768
- let lint = if els. is_some ( ) { SINGLE_MATCH_ELSE } else { SINGLE_MATCH } ;
769
- let els_str = els. map_or ( String :: new ( ) , |els| {
770
- format ! ( " else {}" , expr_block( cx, els, None , ".." , Some ( expr. span) ) )
771
- } ) ;
772
-
773
- let ( pat, pat_ref_count) = peel_hir_pat_refs ( arms[ 0 ] . pat ) ;
774
- let ( msg, sugg) = if_chain ! {
775
- if let PatKind :: Path ( _) | PatKind :: Lit ( _) = pat. kind;
776
- let ( ty, ty_ref_count) = peel_mid_ty_refs( cx. typeck_results( ) . expr_ty( ex) ) ;
777
- if let Some ( spe_trait_id) = cx. tcx. lang_items( ) . structural_peq_trait( ) ;
778
- if let Some ( pe_trait_id) = cx. tcx. lang_items( ) . eq_trait( ) ;
779
- if ty. is_integral( ) || ty. is_char( ) || ty. is_str( )
780
- || ( implements_trait( cx, ty, spe_trait_id, & [ ] )
781
- && implements_trait( cx, ty, pe_trait_id, & [ ty. into( ) ] ) ) ;
782
- then {
783
- // scrutinee derives PartialEq and the pattern is a constant.
784
- let pat_ref_count = match pat. kind {
785
- // string literals are already a reference.
786
- PatKind :: Lit ( Expr { kind: ExprKind :: Lit ( lit) , .. } ) if lit. node. is_str( ) => pat_ref_count + 1 ,
787
- _ => pat_ref_count,
788
- } ;
789
- // References are only implicitly added to the pattern, so no overflow here.
790
- // e.g. will work: match &Some(_) { Some(_) => () }
791
- // will not: match Some(_) { &Some(_) => () }
792
- let ref_count_diff = ty_ref_count - pat_ref_count;
793
-
794
- // Try to remove address of expressions first.
795
- let ( ex, removed) = peel_n_hir_expr_refs( ex, ref_count_diff) ;
796
- let ref_count_diff = ref_count_diff - removed;
797
-
798
- let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`" ;
799
- let sugg = format!(
800
- "if {} == {}{} {}{}" ,
801
- snippet( cx, ex. span, ".." ) ,
802
- // PartialEq for different reference counts may not exist.
803
- "&" . repeat( ref_count_diff) ,
804
- snippet( cx, arms[ 0 ] . pat. span, ".." ) ,
805
- expr_block( cx, arms[ 0 ] . body, None , ".." , Some ( expr. span) ) ,
806
- els_str,
807
- ) ;
808
- ( msg, sugg)
809
- } else {
810
- let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`" ;
811
- let sugg = format!(
812
- "if let {} = {} {}{}" ,
813
- snippet( cx, arms[ 0 ] . pat. span, ".." ) ,
814
- snippet( cx, ex. span, ".." ) ,
815
- expr_block( cx, arms[ 0 ] . body, None , ".." , Some ( expr. span) ) ,
816
- els_str,
817
- ) ;
818
- ( msg, sugg)
819
- }
820
- } ;
821
-
822
- span_lint_and_sugg (
823
- cx,
824
- lint,
825
- expr. span ,
826
- msg,
827
- "try this" ,
828
- sugg,
829
- Applicability :: HasPlaceholders ,
830
- ) ;
831
- }
832
-
833
- fn check_single_match_opt_like < ' a > (
834
- cx : & LateContext < ' a > ,
835
- ex : & Expr < ' _ > ,
836
- arms : & [ Arm < ' _ > ] ,
837
- expr : & Expr < ' _ > ,
838
- ty : Ty < ' a > ,
839
- els : Option < & Expr < ' _ > > ,
840
- ) {
841
- // list of candidate `Enum`s we know will never get any more members
842
- let candidates = & [
843
- ( & paths:: COW , "Borrowed" ) ,
844
- ( & paths:: COW , "Cow::Borrowed" ) ,
845
- ( & paths:: COW , "Cow::Owned" ) ,
846
- ( & paths:: COW , "Owned" ) ,
847
- ( & paths:: OPTION , "None" ) ,
848
- ( & paths:: RESULT , "Err" ) ,
849
- ( & paths:: RESULT , "Ok" ) ,
850
- ] ;
851
-
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 ;
868
- }
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
896
- } ,
897
- PatKind :: Path ( ref path) => {
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
903
- } ,
904
- _ => false ,
905
- }
906
- }
907
-
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 ,
966
- }
967
- }
968
-
969
713
fn check_match_bool ( cx : & LateContext < ' _ > , ex : & Expr < ' _ > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
970
714
// Type of expression is `bool`.
971
715
if * cx. typeck_results ( ) . expr_ty ( ex) . kind ( ) == ty:: Bool {
0 commit comments