308308use self :: ArmType :: * ;
309309use self :: Usefulness :: * ;
310310use super :: deconstruct_pat:: {
311- Constructor , ConstructorSet , DeconstructedPat , SplitConstructorSet , WitnessPat ,
311+ Constructor , ConstructorSet , DeconstructedPat , IntRange , SplitConstructorSet , WitnessPat ,
312312} ;
313- use crate :: errors:: { NonExhaustiveOmittedPattern , Uncovered } ;
313+ use crate :: errors:: { NonExhaustiveOmittedPattern , Overlap , OverlappingRangeEndpoints , Uncovered } ;
314314
315315use rustc_data_structures:: captures:: Captures ;
316316
@@ -319,6 +319,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
319319use rustc_hir:: def_id:: DefId ;
320320use rustc_hir:: HirId ;
321321use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
322+ use rustc_session:: lint;
322323use rustc_session:: lint:: builtin:: NON_EXHAUSTIVE_OMITTED_PATTERNS ;
323324use rustc_span:: { Span , DUMMY_SP } ;
324325
@@ -475,11 +476,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
475476 Matrix { patterns : vec ! [ ] }
476477 }
477478
478- /// Number of columns of this matrix. `None` is the matrix is empty.
479- pub ( super ) fn column_count ( & self ) -> Option < usize > {
480- self . patterns . get ( 0 ) . map ( |r| r. len ( ) )
481- }
482-
483479 /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
484480 /// expands it.
485481 fn push ( & mut self , row : PatStack < ' p , ' tcx > ) {
@@ -835,15 +831,6 @@ fn is_useful<'p, 'tcx>(
835831
836832 let v_ctor = v. head ( ) . ctor ( ) ;
837833 debug ! ( ?v_ctor) ;
838- if let Constructor :: IntRange ( ctor_range) = & v_ctor {
839- // Lint on likely incorrect range patterns (#63987)
840- ctor_range. lint_overlapping_range_endpoints (
841- pcx,
842- matrix. heads ( ) ,
843- matrix. column_count ( ) . unwrap_or ( 0 ) ,
844- lint_root,
845- )
846- }
847834 // We split the head constructor of `v`.
848835 let split_ctors = v_ctor. split ( pcx, matrix. heads ( ) . map ( DeconstructedPat :: ctor) ) ;
849836 // For each constructor, we compute whether there's a value that starts with it that would
@@ -903,6 +890,9 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
903890 let column_ctors = self . patterns . iter ( ) . map ( |p| p. ctor ( ) ) ;
904891 ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, column_ctors)
905892 }
893+ fn iter < ' a > ( & ' a self ) -> impl Iterator < Item = & ' p DeconstructedPat < ' p , ' tcx > > + Captures < ' a > {
894+ self . patterns . iter ( ) . copied ( )
895+ }
906896
907897 /// Does specialization: given a constructor, this takes the patterns from the column that match
908898 /// the constructor, and outputs their fields.
@@ -992,6 +982,81 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
992982 witnesses
993983}
994984
985+ /// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
986+ #[ instrument( level = "debug" , skip( cx, lint_root) ) ]
987+ fn lint_overlapping_range_endpoints < ' p , ' tcx > (
988+ cx : & MatchCheckCtxt < ' p , ' tcx > ,
989+ column : & PatternColumn < ' p , ' tcx > ,
990+ lint_root : HirId ,
991+ ) {
992+ let Some ( ty) = column. head_ty ( ) else {
993+ return ;
994+ } ;
995+ let pcx = & PatCtxt { cx, ty, span : DUMMY_SP , is_top_level : false } ;
996+
997+ let set = column. analyze_ctors ( pcx) ;
998+
999+ if IntRange :: is_integral ( ty) {
1000+ let emit_lint = |overlap : & IntRange , this_span : Span , overlapped_spans : & [ Span ] | {
1001+ let overlap_as_pat = overlap. to_pat ( cx. tcx , ty) ;
1002+ let overlaps: Vec < _ > = overlapped_spans
1003+ . iter ( )
1004+ . copied ( )
1005+ . map ( |span| Overlap { range : overlap_as_pat. clone ( ) , span } )
1006+ . collect ( ) ;
1007+ cx. tcx . emit_spanned_lint (
1008+ lint:: builtin:: OVERLAPPING_RANGE_ENDPOINTS ,
1009+ lint_root,
1010+ this_span,
1011+ OverlappingRangeEndpoints { overlap : overlaps, range : this_span } ,
1012+ ) ;
1013+ } ;
1014+
1015+ // If two ranges overlapped, the split set will contain their intersection as a singleton.
1016+ let split_int_ranges = set. present . iter ( ) . filter_map ( |c| c. as_int_range ( ) ) ;
1017+ for overlap_range in split_int_ranges. clone ( ) {
1018+ if overlap_range. is_singleton ( ) {
1019+ let overlap: u128 = overlap_range. boundaries ( ) . 0 ;
1020+ // Spans of ranges that start or end with the overlap.
1021+ let mut prefixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1022+ let mut suffixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1023+ // Iterate on patterns that contained `overlap`.
1024+ for pat in column. iter ( ) {
1025+ let this_span = pat. span ( ) ;
1026+ let Constructor :: IntRange ( this_range) = pat. ctor ( ) else { continue } ;
1027+ if this_range. is_singleton ( ) {
1028+ // Don't lint when one of the ranges is a singleton.
1029+ continue ;
1030+ }
1031+ let ( start, end) = this_range. boundaries ( ) ;
1032+ if start == overlap {
1033+ // `this_range` looks like `overlap..=end`; it overlaps with any ranges that
1034+ // look like `start..=overlap`.
1035+ if !prefixes. is_empty ( ) {
1036+ emit_lint ( overlap_range, this_span, & prefixes) ;
1037+ }
1038+ suffixes. push ( this_span)
1039+ } else if end == overlap {
1040+ // `this_range` looks like `start..=overlap`; it overlaps with any ranges
1041+ // that look like `overlap..=end`.
1042+ if !suffixes. is_empty ( ) {
1043+ emit_lint ( overlap_range, this_span, & suffixes) ;
1044+ }
1045+ prefixes. push ( this_span)
1046+ }
1047+ }
1048+ }
1049+ }
1050+ } else {
1051+ // Recurse into the fields.
1052+ for ctor in set. present {
1053+ for col in column. specialize ( pcx, & ctor) {
1054+ lint_overlapping_range_endpoints ( cx, & col, lint_root) ;
1055+ }
1056+ }
1057+ }
1058+ }
1059+
9951060/// The arm of a match expression.
9961061#[ derive( Clone , Copy , Debug ) ]
9971062pub ( crate ) struct MatchArm < ' p , ' tcx > {
@@ -1062,6 +1127,10 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
10621127 NoWitnesses { .. } => bug ! ( ) ,
10631128 } ;
10641129
1130+ let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1131+ let pat_column = PatternColumn :: new ( pat_column) ;
1132+ lint_overlapping_range_endpoints ( cx, & pat_column, lint_root) ;
1133+
10651134 // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
10661135 // `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
10671136 if cx. refutable
@@ -1071,10 +1140,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
10711140 rustc_session:: lint:: Level :: Allow
10721141 )
10731142 {
1074- let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1075- let pat_column = PatternColumn :: new ( pat_column) ;
10761143 let witnesses = collect_nonexhaustive_missing_variants ( cx, & pat_column) ;
1077-
10781144 if !witnesses. is_empty ( ) {
10791145 // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
10801146 // is not exhaustive enough.
0 commit comments