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.
@@ -993,6 +983,82 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
993983 witnesses
994984}
995985
986+ /// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
987+ #[ instrument( level = "debug" , skip( cx, lint_root) ) ]
988+ fn lint_overlapping_range_endpoints < ' p , ' tcx > (
989+ cx : & MatchCheckCtxt < ' p , ' tcx > ,
990+ column : & PatternColumn < ' p , ' tcx > ,
991+ lint_root : HirId ,
992+ ) {
993+ if column. is_empty ( ) {
994+ return ;
995+ }
996+ let ty = column. head_ty ( ) ;
997+ let pcx = & PatCtxt { cx, ty, span : DUMMY_SP , is_top_level : false } ;
998+
999+ let set = column. analyze_ctors ( pcx) ;
1000+
1001+ if IntRange :: is_integral ( ty) {
1002+ let emit_lint = |overlap : & IntRange , this_span : Span , overlapped_spans : & [ Span ] | {
1003+ let overlap_as_pat = overlap. to_pat ( cx. tcx , ty) ;
1004+ let overlaps: Vec < _ > = overlapped_spans
1005+ . iter ( )
1006+ . copied ( )
1007+ . map ( |span| Overlap { range : overlap_as_pat. clone ( ) , span } )
1008+ . collect ( ) ;
1009+ cx. tcx . emit_spanned_lint (
1010+ lint:: builtin:: OVERLAPPING_RANGE_ENDPOINTS ,
1011+ lint_root,
1012+ this_span,
1013+ OverlappingRangeEndpoints { overlap : overlaps, range : this_span } ,
1014+ ) ;
1015+ } ;
1016+
1017+ // If two ranges overlapped, the split set will contain their intersection as a singleton.
1018+ let split_int_ranges = set. present . iter ( ) . filter_map ( |c| c. as_int_range ( ) ) ;
1019+ for overlap_range in split_int_ranges. clone ( ) {
1020+ if overlap_range. is_singleton ( ) {
1021+ let overlap: u128 = overlap_range. boundaries ( ) . 0 ;
1022+ // Spans of ranges that start or end with the overlap.
1023+ let mut prefixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1024+ let mut suffixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1025+ // Iterate on patterns that contained `overlap`.
1026+ for pat in column. iter ( ) {
1027+ let this_span = pat. span ( ) ;
1028+ let Constructor :: IntRange ( this_range) = pat. ctor ( ) else { continue } ;
1029+ if this_range. is_singleton ( ) {
1030+ // Don't lint when one of the ranges is a singleton.
1031+ continue ;
1032+ }
1033+ let ( start, end) = this_range. boundaries ( ) ;
1034+ if start == overlap {
1035+ // `this_range` looks like `overlap..=end`; it overlaps with any ranges that
1036+ // look like `start..=overlap`.
1037+ if !prefixes. is_empty ( ) {
1038+ emit_lint ( overlap_range, this_span, & prefixes) ;
1039+ }
1040+ suffixes. push ( this_span)
1041+ } else if end == overlap {
1042+ // `this_range` looks like `start..=overlap`; it overlaps with any ranges
1043+ // that look like `overlap..=end`.
1044+ if !suffixes. is_empty ( ) {
1045+ emit_lint ( overlap_range, this_span, & suffixes) ;
1046+ }
1047+ prefixes. push ( this_span)
1048+ }
1049+ }
1050+ }
1051+ }
1052+ } else {
1053+ // Recurse into the fields.
1054+ for ctor in set. present {
1055+ for col in column. specialize ( pcx, & ctor) {
1056+ lint_overlapping_range_endpoints ( cx, & col, lint_root) ;
1057+ }
1058+ }
1059+ }
1060+ }
1061+
9961062/// The arm of a match expression.
9971063#[ derive( Clone , Copy , Debug ) ]
9981064pub ( crate ) struct MatchArm < ' p , ' tcx > {
@@ -1063,6 +1129,10 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
10631129 NoWitnesses { .. } => bug ! ( ) ,
10641130 } ;
10651131
1132+ let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1133+ let pat_column = PatternColumn :: new ( pat_column) ;
1134+ lint_overlapping_range_endpoints ( cx, & pat_column, lint_root) ;
1135+
10661136 // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
10671137 // `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
10681138 if cx. refutable
@@ -1072,10 +1142,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
10721142 rustc_session:: lint:: Level :: Allow
10731143 )
10741144 {
1075- let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1076- let pat_column = PatternColumn :: new ( pat_column) ;
10771145 let witnesses = collect_nonexhaustive_missing_variants ( cx, & pat_column) ;
1078-
10791146 if !witnesses. is_empty ( ) {
10801147 // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
10811148 // is not exhaustive enough.
0 commit comments