308
308
use self :: ArmType :: * ;
309
309
use self :: Usefulness :: * ;
310
310
use super :: deconstruct_pat:: {
311
- Constructor , ConstructorSet , DeconstructedPat , SplitConstructorSet , WitnessPat ,
311
+ Constructor , ConstructorSet , DeconstructedPat , IntRange , SplitConstructorSet , WitnessPat ,
312
312
} ;
313
- use crate :: errors:: { NonExhaustiveOmittedPattern , Uncovered } ;
313
+ use crate :: errors:: { NonExhaustiveOmittedPattern , Overlap , OverlappingRangeEndpoints , Uncovered } ;
314
314
315
315
use rustc_data_structures:: captures:: Captures ;
316
316
@@ -319,6 +319,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
319
319
use rustc_hir:: def_id:: DefId ;
320
320
use rustc_hir:: HirId ;
321
321
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
322
+ use rustc_session:: lint;
322
323
use rustc_session:: lint:: builtin:: NON_EXHAUSTIVE_OMITTED_PATTERNS ;
323
324
use rustc_span:: { Span , DUMMY_SP } ;
324
325
@@ -475,11 +476,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
475
476
Matrix { patterns : vec ! [ ] }
476
477
}
477
478
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
-
483
479
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
484
480
/// expands it.
485
481
fn push ( & mut self , row : PatStack < ' p , ' tcx > ) {
@@ -835,15 +831,6 @@ fn is_useful<'p, 'tcx>(
835
831
836
832
let v_ctor = v. head ( ) . ctor ( ) ;
837
833
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
- }
847
834
// We split the head constructor of `v`.
848
835
let split_ctors = v_ctor. split ( pcx, matrix. heads ( ) . map ( DeconstructedPat :: ctor) ) ;
849
836
// 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> {
903
890
let column_ctors = self . patterns . iter ( ) . map ( |p| p. ctor ( ) ) ;
904
891
ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, column_ctors)
905
892
}
893
+ fn iter < ' a > ( & ' a self ) -> impl Iterator < Item = & ' p DeconstructedPat < ' p , ' tcx > > + Captures < ' a > {
894
+ self . patterns . iter ( ) . copied ( )
895
+ }
906
896
907
897
/// Does specialization: given a constructor, this takes the patterns from the column that match
908
898
/// the constructor, and outputs their fields.
@@ -992,6 +982,81 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
992
982
witnesses
993
983
}
994
984
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
+
995
1060
/// The arm of a match expression.
996
1061
#[ derive( Clone , Copy , Debug ) ]
997
1062
pub ( crate ) struct MatchArm < ' p , ' tcx > {
@@ -1062,6 +1127,10 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
1062
1127
NoWitnesses { .. } => bug ! ( ) ,
1063
1128
} ;
1064
1129
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
+
1065
1134
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
1066
1135
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
1067
1136
if cx. refutable
@@ -1071,10 +1140,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
1071
1140
rustc_session:: lint:: Level :: Allow
1072
1141
)
1073
1142
{
1074
- let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1075
- let pat_column = PatternColumn :: new ( pat_column) ;
1076
1143
let witnesses = collect_nonexhaustive_missing_variants ( cx, & pat_column) ;
1077
-
1078
1144
if !witnesses. is_empty ( ) {
1079
1145
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
1080
1146
// is not exhaustive enough.
0 commit comments