307
307
308
308
use self :: ArmType :: * ;
309
309
use self :: Usefulness :: * ;
310
- use super :: deconstruct_pat:: { Constructor , ConstructorSet , DeconstructedPat , WitnessPat } ;
311
- use crate :: errors:: { NonExhaustiveOmittedPattern , Uncovered } ;
310
+ use super :: deconstruct_pat:: { Constructor , ConstructorSet , DeconstructedPat , IntRange , WitnessPat } ;
311
+ use crate :: errors:: { NonExhaustiveOmittedPattern , Overlap , OverlappingRangeEndpoints , Uncovered } ;
312
312
313
313
use rustc_data_structures:: captures:: Captures ;
314
314
@@ -317,6 +317,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
317
317
use rustc_hir:: def_id:: DefId ;
318
318
use rustc_hir:: HirId ;
319
319
use rustc_middle:: ty:: { self , Ty , TyCtxt } ;
320
+ use rustc_session:: lint;
320
321
use rustc_session:: lint:: builtin:: NON_EXHAUSTIVE_OMITTED_PATTERNS ;
321
322
use rustc_span:: { Span , DUMMY_SP } ;
322
323
@@ -473,11 +474,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
473
474
Matrix { patterns : vec ! [ ] }
474
475
}
475
476
476
- /// Number of columns of this matrix. `None` is the matrix is empty.
477
- pub ( super ) fn column_count ( & self ) -> Option < usize > {
478
- self . patterns . get ( 0 ) . map ( |r| r. len ( ) )
479
- }
480
-
481
477
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
482
478
/// expands it.
483
479
fn push ( & mut self , row : PatStack < ' p , ' tcx > ) {
@@ -870,15 +866,6 @@ fn is_useful<'p, 'tcx>(
870
866
871
867
let v_ctor = v. head ( ) . ctor ( ) ;
872
868
debug ! ( ?v_ctor) ;
873
- if let Constructor :: IntRange ( ctor_range) = & v_ctor {
874
- // Lint on likely incorrect range patterns (#63987)
875
- ctor_range. lint_overlapping_range_endpoints (
876
- pcx,
877
- matrix. heads ( ) ,
878
- matrix. column_count ( ) . unwrap_or ( 0 ) ,
879
- lint_root,
880
- )
881
- }
882
869
// We split the head constructor of `v`.
883
870
let split_ctors = v_ctor. split ( pcx, matrix. heads ( ) . map ( DeconstructedPat :: ctor) ) ;
884
871
// For each constructor, we compute whether there's a value that starts with it that would
@@ -924,7 +911,8 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
924
911
let ty = column[ 0 ] . ty ( ) ;
925
912
let pcx = & PatCtxt { cx, ty, span : DUMMY_SP , is_top_level : false } ;
926
913
927
- let set = ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, column. iter ( ) . map ( |p| p. ctor ( ) ) ) ;
914
+ let column_ctors = column. iter ( ) . map ( |p| p. ctor ( ) ) ;
915
+ let set = ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, column_ctors) ;
928
916
if set. present . is_empty ( ) {
929
917
// We can't consistently handle the case where no constructors are present (since this would
930
918
// require digging deep through any type in case there's a non_exhaustive enum somewhere),
@@ -986,6 +974,102 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
986
974
witnesses
987
975
}
988
976
977
+ /// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
978
+ /// This traverses patterns column-by-column, where a column is the intuitive notion of "subpatterns
979
+ /// that inspect the same subvalue". Despite similarities with `is_useful`, this traversal is
980
+ /// different. Notably this is linear in the depth of patterns, whereas `is_useful` is worst-case
981
+ /// exponential (exhaustiveness is NP-complete).
982
+ fn lint_overlapping_range_endpoints < ' p , ' tcx > (
983
+ cx : & MatchCheckCtxt < ' p , ' tcx > ,
984
+ column : & [ & DeconstructedPat < ' p , ' tcx > ] ,
985
+ lint_root : HirId ,
986
+ ) {
987
+ if column. is_empty ( ) {
988
+ return ;
989
+ }
990
+ let ty = column[ 0 ] . ty ( ) ;
991
+ let pcx = & PatCtxt { cx, ty, span : DUMMY_SP , is_top_level : false } ;
992
+
993
+ let column_ctors = column. iter ( ) . map ( |p| p. ctor ( ) ) ;
994
+ let set = ConstructorSet :: for_ty ( pcx. cx , pcx. ty ) . split ( pcx, column_ctors) ;
995
+
996
+ if IntRange :: is_integral ( ty) {
997
+ // If two ranges overlapped, the split set will contain their intersection as a singleton.
998
+ let split_int_ranges = set. present . iter ( ) . filter_map ( |c| c. as_int_range ( ) ) ;
999
+ for overlap_range in split_int_ranges. clone ( ) {
1000
+ if overlap_range. is_singleton ( ) {
1001
+ let overlap: u128 = overlap_range. boundaries ( ) . 0 ;
1002
+ // Spans of ranges that start or end with the overlap.
1003
+ let mut prefixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1004
+ let mut suffixes: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1005
+ // Iterate on patterns that contained `overlap`.
1006
+ for pat in column {
1007
+ let this_span = pat. span ( ) ;
1008
+ let Constructor :: IntRange ( this_range) = pat. ctor ( ) else { continue } ;
1009
+ if this_range. is_singleton ( ) {
1010
+ // Don't lint when one of the ranges is a singleton.
1011
+ continue ;
1012
+ }
1013
+ let mut this_overlaps: SmallVec < [ _ ; 1 ] > = Default :: default ( ) ;
1014
+ let ( start, end) = this_range. boundaries ( ) ;
1015
+ if start == overlap {
1016
+ if !prefixes. is_empty ( ) {
1017
+ this_overlaps = prefixes. clone ( ) ;
1018
+ }
1019
+ suffixes. push ( this_span)
1020
+ } else if end == overlap {
1021
+ if !suffixes. is_empty ( ) {
1022
+ this_overlaps = suffixes. clone ( ) ;
1023
+ }
1024
+ prefixes. push ( this_span)
1025
+ }
1026
+ if !this_overlaps. is_empty ( ) {
1027
+ let overlap_as_pat = overlap_range. to_pat ( pcx. cx . tcx , pcx. ty ) ;
1028
+ let overlaps: Vec < _ > = this_overlaps
1029
+ . into_iter ( )
1030
+ . map ( |span| Overlap { range : overlap_as_pat. clone ( ) , span } )
1031
+ . collect ( ) ;
1032
+ pcx. cx . tcx . emit_spanned_lint (
1033
+ lint:: builtin:: OVERLAPPING_RANGE_ENDPOINTS ,
1034
+ lint_root,
1035
+ this_span,
1036
+ OverlappingRangeEndpoints { overlap : overlaps, range : this_span } ,
1037
+ ) ;
1038
+ }
1039
+ }
1040
+ }
1041
+ }
1042
+ }
1043
+
1044
+ // Recurse into the fields.
1045
+ for ctor in set. present {
1046
+ let arity = ctor. arity ( pcx) ;
1047
+ if arity == 0 {
1048
+ continue ;
1049
+ }
1050
+
1051
+ // We specialize the column by `ctor`. This gives us `arity`-many columns of patterns. These
1052
+ // columns may have different lengths in the presence of or-patterns (this is why we can't
1053
+ // reuse `Matrix`).
1054
+ let mut specialized_columns: Vec < Vec < _ > > = ( 0 ..arity) . map ( |_| Vec :: new ( ) ) . collect ( ) ;
1055
+ let relevant_patterns = column. iter ( ) . filter ( |pat| ctor. is_covered_by ( pcx, pat. ctor ( ) ) ) ;
1056
+ for pat in relevant_patterns {
1057
+ let specialized = pat. specialize ( pcx, & ctor) ;
1058
+ for ( subpat, sub_column) in specialized. iter ( ) . zip ( & mut specialized_columns) {
1059
+ if subpat. is_or_pat ( ) {
1060
+ sub_column. extend ( subpat. iter_fields ( ) )
1061
+ } else {
1062
+ sub_column. push ( subpat)
1063
+ }
1064
+ }
1065
+ }
1066
+
1067
+ for col in specialized_columns. iter ( ) {
1068
+ lint_overlapping_range_endpoints ( cx, col. as_slice ( ) , lint_root) ;
1069
+ }
1070
+ }
1071
+ }
1072
+
989
1073
/// The arm of a match expression.
990
1074
#[ derive( Clone , Copy , Debug ) ]
991
1075
pub ( crate ) struct MatchArm < ' p , ' tcx > {
@@ -1056,6 +1140,9 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
1056
1140
NoWitnesses { .. } => bug ! ( ) ,
1057
1141
} ;
1058
1142
1143
+ let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1144
+ lint_overlapping_range_endpoints ( cx, & pat_column, lint_root) ;
1145
+
1059
1146
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
1060
1147
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
1061
1148
if cx. refutable
@@ -1065,9 +1152,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
1065
1152
rustc_session:: lint:: Level :: Allow
1066
1153
)
1067
1154
{
1068
- let pat_column = arms. iter ( ) . flat_map ( |arm| arm. pat . flatten_or_pat ( ) ) . collect :: < Vec < _ > > ( ) ;
1069
1155
let witnesses = collect_nonexhaustive_missing_variants ( cx, & pat_column) ;
1070
-
1071
1156
if !witnesses. is_empty ( ) {
1072
1157
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
1073
1158
// is not exhaustive enough.
0 commit comments