@@ -572,6 +572,111 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
572
572
}
573
573
}
574
574
575
+ /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
576
+ /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
577
+ ///
578
+ /// For example, this would reject:
579
+ /// - `ref x @ Some(ref mut y)`,
580
+ /// - `ref mut x @ Some(ref y)`,
581
+ /// - `ref mut x @ Some(ref mut y)`,
582
+ /// - `ref mut? x @ Some(y)`, and
583
+ /// - `x @ Some(ref mut? y)`.
584
+ ///
585
+ /// This analysis is *not* subsumed by NLL.
586
+ fn check_borrow_conflicts_in_at_patterns < ' tcx > ( cx : & MatchVisitor < ' _ , ' _ , ' tcx > , pat : & Pat < ' tcx > ) {
587
+ // Extract `sub` in `binding @ sub`.
588
+ let PatKind :: Binding { name, mode, ty, subpattern : Some ( box ref sub) , .. } = pat. kind else {
589
+ return ;
590
+ } ;
591
+
592
+ let is_binding_by_move = |ty : Ty < ' tcx > | !ty. is_copy_modulo_regions ( cx. tcx , cx. param_env ) ;
593
+
594
+ let sess = cx. tcx . sess ;
595
+
596
+ // Get the binding move, extract the mutability if by-ref.
597
+ let mut_outer = match mode {
598
+ BindingMode :: ByValue if is_binding_by_move ( ty) => {
599
+ // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
600
+ let mut conflicts_ref = Vec :: new ( ) ;
601
+ sub. each_binding ( |_, mode, _, span| match mode {
602
+ BindingMode :: ByValue => { }
603
+ BindingMode :: ByRef ( _) => conflicts_ref. push ( span) ,
604
+ } ) ;
605
+ if !conflicts_ref. is_empty ( ) {
606
+ sess. emit_err ( BorrowOfMovedValue {
607
+ binding_span : pat. span ,
608
+ conflicts_ref,
609
+ name,
610
+ ty,
611
+ suggest_borrowing : Some ( pat. span . shrink_to_lo ( ) ) ,
612
+ } ) ;
613
+ }
614
+ return ;
615
+ }
616
+ BindingMode :: ByValue => return ,
617
+ BindingMode :: ByRef ( m) => m. mutability ( ) ,
618
+ } ;
619
+
620
+ // We now have `ref $mut_outer binding @ sub` (semantically).
621
+ // Recurse into each binding in `sub` and find mutability or move conflicts.
622
+ let mut conflicts_move = Vec :: new ( ) ;
623
+ let mut conflicts_mut_mut = Vec :: new ( ) ;
624
+ let mut conflicts_mut_ref = Vec :: new ( ) ;
625
+ sub. each_binding ( |name, mode, ty, span| {
626
+ match mode {
627
+ BindingMode :: ByRef ( mut_inner) => match ( mut_outer, mut_inner. mutability ( ) ) {
628
+ // Both sides are `ref`.
629
+ ( Mutability :: Not , Mutability :: Not ) => { }
630
+ // 2x `ref mut`.
631
+ ( Mutability :: Mut , Mutability :: Mut ) => {
632
+ conflicts_mut_mut. push ( Conflict :: Mut { span, name } )
633
+ }
634
+ ( Mutability :: Not , Mutability :: Mut ) => {
635
+ conflicts_mut_ref. push ( Conflict :: Mut { span, name } )
636
+ }
637
+ ( Mutability :: Mut , Mutability :: Not ) => {
638
+ conflicts_mut_ref. push ( Conflict :: Ref { span, name } )
639
+ }
640
+ } ,
641
+ BindingMode :: ByValue if is_binding_by_move ( ty) => {
642
+ conflicts_move. push ( Conflict :: Moved { span, name } ) // `ref mut?` + by-move conflict.
643
+ }
644
+ BindingMode :: ByValue => { } // `ref mut?` + by-copy is fine.
645
+ }
646
+ } ) ;
647
+
648
+ let report_mut_mut = !conflicts_mut_mut. is_empty ( ) ;
649
+ let report_mut_ref = !conflicts_mut_ref. is_empty ( ) ;
650
+ let report_move_conflict = !conflicts_move. is_empty ( ) ;
651
+
652
+ let mut occurrences = match mut_outer {
653
+ Mutability :: Mut => vec ! [ Conflict :: Mut { span: pat. span, name } ] ,
654
+ Mutability :: Not => vec ! [ Conflict :: Ref { span: pat. span, name } ] ,
655
+ } ;
656
+ occurrences. extend ( conflicts_mut_mut) ;
657
+ occurrences. extend ( conflicts_mut_ref) ;
658
+ occurrences. extend ( conflicts_move) ;
659
+
660
+ // Report errors if any.
661
+ if report_mut_mut {
662
+ // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
663
+ sess. emit_err ( MultipleMutBorrows { span : pat. span , occurrences } ) ;
664
+ } else if report_mut_ref {
665
+ // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
666
+ match mut_outer {
667
+ Mutability :: Mut => {
668
+ sess. emit_err ( AlreadyMutBorrowed { span : pat. span , occurrences } ) ;
669
+ }
670
+ Mutability :: Not => {
671
+ sess. emit_err ( AlreadyBorrowed { span : pat. span , occurrences } ) ;
672
+ }
673
+ } ;
674
+ } else if report_move_conflict {
675
+ // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
676
+ sess. emit_err ( MovedWhileBorrowed { span : pat. span , occurrences } ) ;
677
+ }
678
+ }
679
+
575
680
fn check_for_bindings_named_same_as_variants (
576
681
cx : & MatchVisitor < ' _ , ' _ , ' _ > ,
577
682
pat : & Pat < ' _ > ,
@@ -616,25 +721,6 @@ fn check_for_bindings_named_same_as_variants(
616
721
} ) ;
617
722
}
618
723
619
- /// Checks for common cases of "catchall" patterns that may not be intended as such.
620
- fn pat_is_catchall ( pat : & DeconstructedPat < ' _ , ' _ > ) -> bool {
621
- use Constructor :: * ;
622
- match pat. ctor ( ) {
623
- Wildcard => true ,
624
- Single => pat. iter_fields ( ) . all ( |pat| pat_is_catchall ( pat) ) ,
625
- _ => false ,
626
- }
627
- }
628
-
629
- fn unreachable_pattern ( tcx : TyCtxt < ' _ > , span : Span , id : HirId , catchall : Option < Span > ) {
630
- tcx. emit_spanned_lint (
631
- UNREACHABLE_PATTERNS ,
632
- id,
633
- span,
634
- UnreachablePattern { span : if catchall. is_some ( ) { Some ( span) } else { None } , catchall } ,
635
- ) ;
636
- }
637
-
638
724
fn irrefutable_let_patterns (
639
725
tcx : TyCtxt < ' _ > ,
640
726
id : HirId ,
@@ -680,19 +766,31 @@ fn report_arm_reachability<'p, 'tcx>(
680
766
cx : & MatchCheckCtxt < ' p , ' tcx > ,
681
767
report : & UsefulnessReport < ' p , ' tcx > ,
682
768
) {
769
+ let report_unreachable_pattern = |span, hir_id, catchall : Option < Span > | {
770
+ cx. tcx . emit_spanned_lint (
771
+ UNREACHABLE_PATTERNS ,
772
+ hir_id,
773
+ span,
774
+ UnreachablePattern {
775
+ span : if catchall. is_some ( ) { Some ( span) } else { None } ,
776
+ catchall,
777
+ } ,
778
+ ) ;
779
+ } ;
780
+
683
781
use Reachability :: * ;
684
782
let mut catchall = None ;
685
783
for ( arm, is_useful) in report. arm_usefulness . iter ( ) {
686
784
match is_useful {
687
- Unreachable => unreachable_pattern ( cx . tcx , arm. pat . span ( ) , arm. hir_id , catchall) ,
785
+ Unreachable => report_unreachable_pattern ( arm. pat . span ( ) , arm. hir_id , catchall) ,
688
786
Reachable ( unreachables) if unreachables. is_empty ( ) => { }
689
787
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
690
788
Reachable ( unreachables) => {
691
789
let mut unreachables = unreachables. clone ( ) ;
692
790
// Emit lints in the order in which they occur in the file.
693
791
unreachables. sort_unstable ( ) ;
694
792
for span in unreachables {
695
- unreachable_pattern ( cx . tcx , span, arm. hir_id , None ) ;
793
+ report_unreachable_pattern ( span, arm. hir_id , None ) ;
696
794
}
697
795
}
698
796
}
@@ -702,15 +800,14 @@ fn report_arm_reachability<'p, 'tcx>(
702
800
}
703
801
}
704
802
705
- fn collect_non_exhaustive_tys < ' tcx > (
706
- pat : & WitnessPat < ' tcx > ,
707
- non_exhaustive_tys : & mut FxHashSet < Ty < ' tcx > > ,
708
- ) {
709
- if matches ! ( pat. ctor( ) , Constructor :: NonExhaustive ) {
710
- non_exhaustive_tys. insert ( pat. ty ( ) ) ;
803
+ /// Checks for common cases of "catchall" patterns that may not be intended as such.
804
+ fn pat_is_catchall ( pat : & DeconstructedPat < ' _ , ' _ > ) -> bool {
805
+ use Constructor :: * ;
806
+ match pat. ctor ( ) {
807
+ Wildcard => true ,
808
+ Single => pat. iter_fields ( ) . all ( |pat| pat_is_catchall ( pat) ) ,
809
+ _ => false ,
711
810
}
712
- pat. iter_fields ( )
713
- . for_each ( |field_pat| collect_non_exhaustive_tys ( field_pat, non_exhaustive_tys) )
714
811
}
715
812
716
813
/// Report that a match is not exhaustive.
@@ -748,7 +845,14 @@ fn non_exhaustive_match<'p, 'tcx>(
748
845
sp,
749
846
format ! ( "non-exhaustive patterns: {joined_patterns} not covered" ) ,
750
847
) ;
751
- err. span_label ( sp, pattern_not_covered_label ( & witnesses, & joined_patterns) ) ;
848
+ err. span_label (
849
+ sp,
850
+ format ! (
851
+ "pattern{} {} not covered" ,
852
+ rustc_errors:: pluralize!( witnesses. len( ) ) ,
853
+ joined_patterns
854
+ ) ,
855
+ ) ;
752
856
patterns_len = witnesses. len ( ) ;
753
857
pattern = if witnesses. len ( ) < 4 {
754
858
witnesses
@@ -895,7 +999,7 @@ fn non_exhaustive_match<'p, 'tcx>(
895
999
err. emit ( )
896
1000
}
897
1001
898
- pub ( crate ) fn joined_uncovered_patterns < ' p , ' tcx > (
1002
+ fn joined_uncovered_patterns < ' p , ' tcx > (
899
1003
cx : & MatchCheckCtxt < ' p , ' tcx > ,
900
1004
witnesses : & [ WitnessPat < ' tcx > ] ,
901
1005
) -> String {
@@ -916,11 +1020,15 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
916
1020
}
917
1021
}
918
1022
919
- pub ( crate ) fn pattern_not_covered_label (
920
- witnesses : & [ WitnessPat < ' _ > ] ,
921
- joined_patterns : & str ,
922
- ) -> String {
923
- format ! ( "pattern{} {} not covered" , rustc_errors:: pluralize!( witnesses. len( ) ) , joined_patterns)
1023
+ fn collect_non_exhaustive_tys < ' tcx > (
1024
+ pat : & WitnessPat < ' tcx > ,
1025
+ non_exhaustive_tys : & mut FxHashSet < Ty < ' tcx > > ,
1026
+ ) {
1027
+ if matches ! ( pat. ctor( ) , Constructor :: NonExhaustive ) {
1028
+ non_exhaustive_tys. insert ( pat. ty ( ) ) ;
1029
+ }
1030
+ pat. iter_fields ( )
1031
+ . for_each ( |field_pat| collect_non_exhaustive_tys ( field_pat, non_exhaustive_tys) )
924
1032
}
925
1033
926
1034
/// Point at the definition of non-covered `enum` variants.
@@ -982,108 +1090,3 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
982
1090
}
983
1091
covered
984
1092
}
985
-
986
- /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
987
- /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
988
- ///
989
- /// For example, this would reject:
990
- /// - `ref x @ Some(ref mut y)`,
991
- /// - `ref mut x @ Some(ref y)`,
992
- /// - `ref mut x @ Some(ref mut y)`,
993
- /// - `ref mut? x @ Some(y)`, and
994
- /// - `x @ Some(ref mut? y)`.
995
- ///
996
- /// This analysis is *not* subsumed by NLL.
997
- fn check_borrow_conflicts_in_at_patterns < ' tcx > ( cx : & MatchVisitor < ' _ , ' _ , ' tcx > , pat : & Pat < ' tcx > ) {
998
- // Extract `sub` in `binding @ sub`.
999
- let PatKind :: Binding { name, mode, ty, subpattern : Some ( box ref sub) , .. } = pat. kind else {
1000
- return ;
1001
- } ;
1002
-
1003
- let is_binding_by_move = |ty : Ty < ' tcx > | !ty. is_copy_modulo_regions ( cx. tcx , cx. param_env ) ;
1004
-
1005
- let sess = cx. tcx . sess ;
1006
-
1007
- // Get the binding move, extract the mutability if by-ref.
1008
- let mut_outer = match mode {
1009
- BindingMode :: ByValue if is_binding_by_move ( ty) => {
1010
- // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
1011
- let mut conflicts_ref = Vec :: new ( ) ;
1012
- sub. each_binding ( |_, mode, _, span| match mode {
1013
- BindingMode :: ByValue => { }
1014
- BindingMode :: ByRef ( _) => conflicts_ref. push ( span) ,
1015
- } ) ;
1016
- if !conflicts_ref. is_empty ( ) {
1017
- sess. emit_err ( BorrowOfMovedValue {
1018
- binding_span : pat. span ,
1019
- conflicts_ref,
1020
- name,
1021
- ty,
1022
- suggest_borrowing : Some ( pat. span . shrink_to_lo ( ) ) ,
1023
- } ) ;
1024
- }
1025
- return ;
1026
- }
1027
- BindingMode :: ByValue => return ,
1028
- BindingMode :: ByRef ( m) => m. mutability ( ) ,
1029
- } ;
1030
-
1031
- // We now have `ref $mut_outer binding @ sub` (semantically).
1032
- // Recurse into each binding in `sub` and find mutability or move conflicts.
1033
- let mut conflicts_move = Vec :: new ( ) ;
1034
- let mut conflicts_mut_mut = Vec :: new ( ) ;
1035
- let mut conflicts_mut_ref = Vec :: new ( ) ;
1036
- sub. each_binding ( |name, mode, ty, span| {
1037
- match mode {
1038
- BindingMode :: ByRef ( mut_inner) => match ( mut_outer, mut_inner. mutability ( ) ) {
1039
- // Both sides are `ref`.
1040
- ( Mutability :: Not , Mutability :: Not ) => { }
1041
- // 2x `ref mut`.
1042
- ( Mutability :: Mut , Mutability :: Mut ) => {
1043
- conflicts_mut_mut. push ( Conflict :: Mut { span, name } )
1044
- }
1045
- ( Mutability :: Not , Mutability :: Mut ) => {
1046
- conflicts_mut_ref. push ( Conflict :: Mut { span, name } )
1047
- }
1048
- ( Mutability :: Mut , Mutability :: Not ) => {
1049
- conflicts_mut_ref. push ( Conflict :: Ref { span, name } )
1050
- }
1051
- } ,
1052
- BindingMode :: ByValue if is_binding_by_move ( ty) => {
1053
- conflicts_move. push ( Conflict :: Moved { span, name } ) // `ref mut?` + by-move conflict.
1054
- }
1055
- BindingMode :: ByValue => { } // `ref mut?` + by-copy is fine.
1056
- }
1057
- } ) ;
1058
-
1059
- let report_mut_mut = !conflicts_mut_mut. is_empty ( ) ;
1060
- let report_mut_ref = !conflicts_mut_ref. is_empty ( ) ;
1061
- let report_move_conflict = !conflicts_move. is_empty ( ) ;
1062
-
1063
- let mut occurrences = match mut_outer {
1064
- Mutability :: Mut => vec ! [ Conflict :: Mut { span: pat. span, name } ] ,
1065
- Mutability :: Not => vec ! [ Conflict :: Ref { span: pat. span, name } ] ,
1066
- } ;
1067
- occurrences. extend ( conflicts_mut_mut) ;
1068
- occurrences. extend ( conflicts_mut_ref) ;
1069
- occurrences. extend ( conflicts_move) ;
1070
-
1071
- // Report errors if any.
1072
- if report_mut_mut {
1073
- // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
1074
- sess. emit_err ( MultipleMutBorrows { span : pat. span , occurrences } ) ;
1075
- } else if report_mut_ref {
1076
- // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
1077
- match mut_outer {
1078
- Mutability :: Mut => {
1079
- sess. emit_err ( AlreadyMutBorrowed { span : pat. span , occurrences } ) ;
1080
- }
1081
- Mutability :: Not => {
1082
- sess. emit_err ( AlreadyBorrowed { span : pat. span , occurrences } ) ;
1083
- }
1084
- } ;
1085
- } else if report_move_conflict {
1086
- // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
1087
- sess. emit_err ( MovedWhileBorrowed { span : pat. span , occurrences } ) ;
1088
- }
1089
- }
0 commit comments