@@ -572,6 +572,111 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
572572 }
573573}
574574
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+
575680fn check_for_bindings_named_same_as_variants (
576681 cx : & MatchVisitor < ' _ , ' _ , ' _ > ,
577682 pat : & Pat < ' _ > ,
@@ -616,25 +721,6 @@ fn check_for_bindings_named_same_as_variants(
616721 } ) ;
617722}
618723
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-
638724fn irrefutable_let_patterns (
639725 tcx : TyCtxt < ' _ > ,
640726 id : HirId ,
@@ -680,19 +766,31 @@ fn report_arm_reachability<'p, 'tcx>(
680766 cx : & MatchCheckCtxt < ' p , ' tcx > ,
681767 report : & UsefulnessReport < ' p , ' tcx > ,
682768) {
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+
683781 use Reachability :: * ;
684782 let mut catchall = None ;
685783 for ( arm, is_useful) in report. arm_usefulness . iter ( ) {
686784 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) ,
688786 Reachable ( unreachables) if unreachables. is_empty ( ) => { }
689787 // The arm is reachable, but contains unreachable subpatterns (from or-patterns).
690788 Reachable ( unreachables) => {
691789 let mut unreachables = unreachables. clone ( ) ;
692790 // Emit lints in the order in which they occur in the file.
693791 unreachables. sort_unstable ( ) ;
694792 for span in unreachables {
695- unreachable_pattern ( cx . tcx , span, arm. hir_id , None ) ;
793+ report_unreachable_pattern ( span, arm. hir_id , None ) ;
696794 }
697795 }
698796 }
@@ -702,22 +800,14 @@ fn report_arm_reachability<'p, 'tcx>(
702800 }
703801}
704802
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 ,
718810 }
719- pat. iter_fields ( )
720- . for_each ( |field_pat| collect_non_exhaustive_tys ( tcx, field_pat, non_exhaustive_tys) )
721811}
722812
723813/// Report that a match is not exhaustive.
@@ -755,7 +845,14 @@ fn non_exhaustive_match<'p, 'tcx>(
755845 sp,
756846 format ! ( "non-exhaustive patterns: {joined_patterns} not covered" ) ,
757847 ) ;
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+ ) ;
759856 patterns_len = witnesses. len ( ) ;
760857 pattern = if witnesses. len ( ) < 4 {
761858 witnesses
@@ -910,7 +1007,7 @@ fn non_exhaustive_match<'p, 'tcx>(
9101007 err. emit ( )
9111008}
9121009
913- pub ( crate ) fn joined_uncovered_patterns < ' p , ' tcx > (
1010+ fn joined_uncovered_patterns < ' p , ' tcx > (
9141011 cx : & MatchCheckCtxt < ' p , ' tcx > ,
9151012 witnesses : & [ WitnessPat < ' tcx > ] ,
9161013) -> String {
@@ -931,11 +1028,22 @@ pub(crate) fn joined_uncovered_patterns<'p, 'tcx>(
9311028 }
9321029}
9331030
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) )
9391047}
9401048
9411049/// Point at the definition of non-covered `enum` variants.
@@ -997,108 +1105,3 @@ fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'a>(
9971105 }
9981106 covered
9991107}
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