@@ -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,22 +800,14 @@ fn report_arm_reachability<'p, 'tcx>(
702
800
}
703
801
}
704
802
705
- fn collect_non_exhaustive_tys < ' tcx > (
706
- tcx : TyCtxt < ' tcx > ,
707
- pat : & WitnessPat < ' tcx > ,
708
- non_exhaustive_tys : & mut FxHashSet < Ty < ' tcx > > ,
709
- ) {
710
- if matches ! ( pat. ctor( ) , Constructor :: NonExhaustive ) {
711
- non_exhaustive_tys. insert ( pat. ty ( ) ) ;
712
- }
713
- if let Constructor :: IntRange ( range) = pat. ctor ( ) {
714
- if range. is_beyond_boundaries ( pat. ty ( ) , tcx) {
715
- // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
716
- non_exhaustive_tys. insert ( pat. ty ( ) ) ;
717
- }
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 ,
718
810
}
719
- pat. iter_fields ( )
720
- . for_each ( |field_pat| collect_non_exhaustive_tys ( tcx, field_pat, non_exhaustive_tys) )
721
811
}
722
812
723
813
/// Report that a match is not exhaustive.
@@ -755,7 +845,14 @@ fn non_exhaustive_match<'p, 'tcx>(
755
845
sp,
756
846
format ! ( "non-exhaustive patterns: {joined_patterns} not covered" ) ,
757
847
) ;
758
- 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
+ ) ;
759
856
patterns_len = witnesses. len ( ) ;
760
857
pattern = if witnesses. len ( ) < 4 {
761
858
witnesses
@@ -910,7 +1007,7 @@ fn non_exhaustive_match<'p, 'tcx>(
910
1007
err. emit ( )
911
1008
}
912
1009
913
- pub ( crate ) fn joined_uncovered_patterns < ' p , ' tcx > (
1010
+ fn joined_uncovered_patterns < ' p , ' tcx > (
914
1011
cx : & MatchCheckCtxt < ' p , ' tcx > ,
915
1012
witnesses : & [ WitnessPat < ' tcx > ] ,
916
1013
) -> String {
@@ -931,11 +1028,22 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
931
1028
}
932
1029
}
933
1030
934
- pub ( crate ) fn pattern_not_covered_label (
935
- witnesses : & [ WitnessPat < ' _ > ] ,
936
- joined_patterns : & str ,
937
- ) -> String {
938
- format ! ( "pattern{} {} not covered" , rustc_errors:: pluralize!( witnesses. len( ) ) , joined_patterns)
1031
+ fn collect_non_exhaustive_tys < ' tcx > (
1032
+ tcx : TyCtxt < ' tcx > ,
1033
+ pat : & WitnessPat < ' tcx > ,
1034
+ non_exhaustive_tys : & mut FxHashSet < Ty < ' tcx > > ,
1035
+ ) {
1036
+ if matches ! ( pat. ctor( ) , Constructor :: NonExhaustive ) {
1037
+ non_exhaustive_tys. insert ( pat. ty ( ) ) ;
1038
+ }
1039
+ if let Constructor :: IntRange ( range) = pat. ctor ( ) {
1040
+ if range. is_beyond_boundaries ( pat. ty ( ) , tcx) {
1041
+ // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
1042
+ non_exhaustive_tys. insert ( pat. ty ( ) ) ;
1043
+ }
1044
+ }
1045
+ pat. iter_fields ( )
1046
+ . for_each ( |field_pat| collect_non_exhaustive_tys ( tcx, field_pat, non_exhaustive_tys) )
939
1047
}
940
1048
941
1049
/// Point at the definition of non-covered `enum` variants.
@@ -997,108 +1105,3 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
997
1105
}
998
1106
covered
999
1107
}
1000
-
1001
- /// Check if a by-value binding is by-value. That is, check if the binding's type is not `Copy`.
1002
- /// Check that there are no borrow or move conflicts in `binding @ subpat` patterns.
1003
- ///
1004
- /// For example, this would reject:
1005
- /// - `ref x @ Some(ref mut y)`,
1006
- /// - `ref mut x @ Some(ref y)`,
1007
- /// - `ref mut x @ Some(ref mut y)`,
1008
- /// - `ref mut? x @ Some(y)`, and
1009
- /// - `x @ Some(ref mut? y)`.
1010
- ///
1011
- /// This analysis is *not* subsumed by NLL.
1012
- fn check_borrow_conflicts_in_at_patterns < ' tcx > ( cx : & MatchVisitor < ' _ , ' _ , ' tcx > , pat : & Pat < ' tcx > ) {
1013
- // Extract `sub` in `binding @ sub`.
1014
- let PatKind :: Binding { name, mode, ty, subpattern : Some ( box ref sub) , .. } = pat. kind else {
1015
- return ;
1016
- } ;
1017
-
1018
- let is_binding_by_move = |ty : Ty < ' tcx > | !ty. is_copy_modulo_regions ( cx. tcx , cx. param_env ) ;
1019
-
1020
- let sess = cx. tcx . sess ;
1021
-
1022
- // Get the binding move, extract the mutability if by-ref.
1023
- let mut_outer = match mode {
1024
- BindingMode :: ByValue if is_binding_by_move ( ty) => {
1025
- // We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
1026
- let mut conflicts_ref = Vec :: new ( ) ;
1027
- sub. each_binding ( |_, mode, _, span| match mode {
1028
- BindingMode :: ByValue => { }
1029
- BindingMode :: ByRef ( _) => conflicts_ref. push ( span) ,
1030
- } ) ;
1031
- if !conflicts_ref. is_empty ( ) {
1032
- sess. emit_err ( BorrowOfMovedValue {
1033
- binding_span : pat. span ,
1034
- conflicts_ref,
1035
- name,
1036
- ty,
1037
- suggest_borrowing : Some ( pat. span . shrink_to_lo ( ) ) ,
1038
- } ) ;
1039
- }
1040
- return ;
1041
- }
1042
- BindingMode :: ByValue => return ,
1043
- BindingMode :: ByRef ( m) => m. mutability ( ) ,
1044
- } ;
1045
-
1046
- // We now have `ref $mut_outer binding @ sub` (semantically).
1047
- // Recurse into each binding in `sub` and find mutability or move conflicts.
1048
- let mut conflicts_move = Vec :: new ( ) ;
1049
- let mut conflicts_mut_mut = Vec :: new ( ) ;
1050
- let mut conflicts_mut_ref = Vec :: new ( ) ;
1051
- sub. each_binding ( |name, mode, ty, span| {
1052
- match mode {
1053
- BindingMode :: ByRef ( mut_inner) => match ( mut_outer, mut_inner. mutability ( ) ) {
1054
- // Both sides are `ref`.
1055
- ( Mutability :: Not , Mutability :: Not ) => { }
1056
- // 2x `ref mut`.
1057
- ( Mutability :: Mut , Mutability :: Mut ) => {
1058
- conflicts_mut_mut. push ( Conflict :: Mut { span, name } )
1059
- }
1060
- ( Mutability :: Not , Mutability :: Mut ) => {
1061
- conflicts_mut_ref. push ( Conflict :: Mut { span, name } )
1062
- }
1063
- ( Mutability :: Mut , Mutability :: Not ) => {
1064
- conflicts_mut_ref. push ( Conflict :: Ref { span, name } )
1065
- }
1066
- } ,
1067
- BindingMode :: ByValue if is_binding_by_move ( ty) => {
1068
- conflicts_move. push ( Conflict :: Moved { span, name } ) // `ref mut?` + by-move conflict.
1069
- }
1070
- BindingMode :: ByValue => { } // `ref mut?` + by-copy is fine.
1071
- }
1072
- } ) ;
1073
-
1074
- let report_mut_mut = !conflicts_mut_mut. is_empty ( ) ;
1075
- let report_mut_ref = !conflicts_mut_ref. is_empty ( ) ;
1076
- let report_move_conflict = !conflicts_move. is_empty ( ) ;
1077
-
1078
- let mut occurrences = match mut_outer {
1079
- Mutability :: Mut => vec ! [ Conflict :: Mut { span: pat. span, name } ] ,
1080
- Mutability :: Not => vec ! [ Conflict :: Ref { span: pat. span, name } ] ,
1081
- } ;
1082
- occurrences. extend ( conflicts_mut_mut) ;
1083
- occurrences. extend ( conflicts_mut_ref) ;
1084
- occurrences. extend ( conflicts_move) ;
1085
-
1086
- // Report errors if any.
1087
- if report_mut_mut {
1088
- // Report mutability conflicts for e.g. `ref mut x @ Some(ref mut y)`.
1089
- sess. emit_err ( MultipleMutBorrows { span : pat. span , occurrences } ) ;
1090
- } else if report_mut_ref {
1091
- // Report mutability conflicts for e.g. `ref x @ Some(ref mut y)` or the converse.
1092
- match mut_outer {
1093
- Mutability :: Mut => {
1094
- sess. emit_err ( AlreadyMutBorrowed { span : pat. span , occurrences } ) ;
1095
- }
1096
- Mutability :: Not => {
1097
- sess. emit_err ( AlreadyBorrowed { span : pat. span , occurrences } ) ;
1098
- }
1099
- } ;
1100
- } else if report_move_conflict {
1101
- // Report by-ref and by-move conflicts, e.g. `ref x @ y`.
1102
- sess. emit_err ( MovedWhileBorrowed { span : pat. span , occurrences } ) ;
1103
- }
1104
- }
0 commit comments