Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 4ef4be6

Browse files
committed
better range analysis: do overlap check after splitting
1 parent 76ea6a6 commit 4ef4be6

File tree

4 files changed

+138
-92
lines changed

4 files changed

+138
-92
lines changed

compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs

Lines changed: 51 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -177,23 +177,6 @@ impl IntRange {
177177
}
178178
}
179179

180-
fn suspicious_intersection(&self, other: &Self) -> bool {
181-
// `false` in the following cases:
182-
// 1 ---- // 1 ---------- // 1 ---- // 1 ----
183-
// 2 ---------- // 2 ---- // 2 ---- // 2 ----
184-
//
185-
// The following are currently `false`, but could be `true` in the future (#64007):
186-
// 1 --------- // 1 ---------
187-
// 2 ---------- // 2 ----------
188-
//
189-
// `true` in the following cases:
190-
// 1 ------- // 1 -------
191-
// 2 -------- // 2 -------
192-
let (lo, hi) = self.boundaries();
193-
let (other_lo, other_hi) = other.boundaries();
194-
(lo == other_hi || hi == other_lo) && !self.is_singleton() && !other.is_singleton()
195-
}
196-
197180
/// Partition a range of integers into disjoint subranges. This does constructor splitting for
198181
/// integer ranges as explained at the top of the file.
199182
///
@@ -320,44 +303,63 @@ impl IntRange {
320303
pub(super) fn lint_overlapping_range_endpoints<'a, 'p: 'a, 'tcx: 'a>(
321304
&self,
322305
pcx: &PatCtxt<'_, 'p, 'tcx>,
323-
pats: impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>>,
306+
pats: impl Iterator<Item = (&'a DeconstructedPat<'p, 'tcx>, bool)>,
324307
column_count: usize,
325308
lint_root: HirId,
326309
) {
327-
if self.is_singleton() {
310+
// FIXME: for now, only check for overlapping ranges on non-nested range patterns. Otherwise
311+
// with the current logic the following is detected as overlapping:
312+
// ```
313+
// match (0u8, true) {
314+
// (0 ..= 125, false) => {}
315+
// (125 ..= 255, true) => {}
316+
// _ => {}
317+
// }
318+
// ```
319+
if !self.is_singleton() || column_count != 1 {
328320
return;
329321
}
330322

331-
if column_count != 1 {
332-
// FIXME: for now, only check for overlapping ranges on simple range
333-
// patterns. Otherwise with the current logic the following is detected
334-
// as overlapping:
335-
// ```
336-
// match (0u8, true) {
337-
// (0 ..= 125, false) => {}
338-
// (125 ..= 255, true) => {}
339-
// _ => {}
340-
// }
341-
// ```
342-
return;
343-
}
344-
345-
let overlap: Vec<_> = pats
346-
.filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span())))
347-
.filter(|(range, _)| self.suspicious_intersection(range))
348-
.map(|(range, span)| Overlap {
349-
range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty),
350-
span,
351-
})
352-
.collect();
353-
354-
if !overlap.is_empty() {
355-
pcx.cx.tcx.emit_spanned_lint(
356-
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
357-
lint_root,
358-
pcx.span,
359-
OverlappingRangeEndpoints { overlap, range: pcx.span },
360-
);
323+
let overlap: u128 = self.boundaries().0;
324+
// Spans of ranges that start or end with the overlap.
325+
let mut prefixes: SmallVec<[_; 1]> = Default::default();
326+
let mut suffixes: SmallVec<[_; 1]> = Default::default();
327+
// Iterate on rows that contained `overlap`.
328+
for (range, this_span, is_under_guard) in pats.filter_map(|(pat, under_guard)| {
329+
Some((pat.ctor().as_int_range()?, pat.span(), under_guard))
330+
}) {
331+
if range.is_singleton() {
332+
continue;
333+
}
334+
let mut overlaps: SmallVec<[_; 1]> = Default::default();
335+
if *range.range.start() == overlap {
336+
if !prefixes.is_empty() {
337+
overlaps = prefixes.clone();
338+
}
339+
if !is_under_guard {
340+
suffixes.push(this_span)
341+
}
342+
} else if *range.range.end() == overlap {
343+
if !suffixes.is_empty() {
344+
overlaps = suffixes.clone();
345+
}
346+
if !is_under_guard {
347+
prefixes.push(this_span)
348+
}
349+
}
350+
if !overlaps.is_empty() {
351+
let overlap_as_pat = self.to_pat(pcx.cx.tcx, pcx.ty);
352+
let overlaps: Vec<_> = overlaps
353+
.into_iter()
354+
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
355+
.collect();
356+
pcx.cx.tcx.emit_spanned_lint(
357+
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
358+
lint_root,
359+
this_span,
360+
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
361+
);
362+
}
361363
}
362364
}
363365
}

compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

Lines changed: 22 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,8 @@
308308
use super::deconstruct_pat::{Constructor, ConstructorSet, DeconstructedPat, WitnessPat};
309309
use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
310310

311-
use rustc_data_structures::captures::Captures;
312-
313311
use rustc_arena::TypedArena;
312+
use rustc_data_structures::captures::Captures;
314313
use rustc_data_structures::stack::ensure_sufficient_stack;
315314
use rustc_hir::def_id::DefId;
316315
use rustc_hir::HirId;
@@ -471,10 +470,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
471470
Matrix { rows: vec![], wildcard_row }
472471
}
473472

474-
fn len(&self) -> usize {
475-
self.rows.len()
476-
}
477-
478473
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
479474
/// expands it.
480475
fn push(&mut self, row: PatStack<'p, 'tcx>) {
@@ -813,33 +808,10 @@ fn compute_usefulness<'p, 'tcx>(
813808
let report_when_all_missing =
814809
is_top_level && !super::deconstruct_pat::IntRange::is_integral(pcx.ty);
815810

816-
if super::deconstruct_pat::IntRange::is_integral(ty) {
817-
for row_id in 0..matrix.len() {
818-
let v = &matrix.rows[row_id];
819-
if let Constructor::IntRange(ctor_range) = v.head().ctor() {
820-
if ctor_range.is_singleton() {
821-
continue;
822-
}
823-
// Lint on likely incorrect range patterns (#63987)
824-
let pcx = &PatCtxt { span: v.head().span(), ..*pcx };
825-
let compare_against = matrix
826-
.rows()
827-
.take(row_id)
828-
.filter(|row| !row.is_under_guard)
829-
.map(|row| row.head());
830-
ctor_range.lint_overlapping_range_endpoints(
831-
pcx,
832-
compare_against,
833-
v.len(),
834-
lint_root,
835-
)
836-
}
837-
}
838-
}
839-
840811
// For each constructor, we compute whether there's a value that starts with it that would
841812
// witness the usefulness of `v`.
842813
let mut ret = WitnessMatrix::new_empty();
814+
let orig_column_count = matrix.column_count();
843815
for ctor in split_ctors {
844816
debug!("specialize({:?})", ctor);
845817
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor);
@@ -848,6 +820,26 @@ fn compute_usefulness<'p, 'tcx>(
848820
witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_when_all_missing);
849821
ret.extend(witnesses);
850822

823+
// Lint on likely incorrect range patterns (#63987)
824+
if spec_matrix.rows().len() >= 2 {
825+
if let Constructor::IntRange(overlap_range) = ctor {
826+
// If two ranges overlap on their boundaries, that boundary will be found as a singleton
827+
// range after splitting.
828+
// We limit to a single column for now, see `lint_overlapping_range_endpoints`.
829+
if overlap_range.is_singleton() && orig_column_count == 1 {
830+
overlap_range.lint_overlapping_range_endpoints(
831+
pcx,
832+
spec_matrix
833+
.rows()
834+
.map(|child_row| &matrix.rows[child_row.parent_row])
835+
.map(|parent_row| (parent_row.head(), parent_row.is_under_guard)),
836+
orig_column_count,
837+
lint_root,
838+
);
839+
}
840+
}
841+
}
842+
851843
for child_row in spec_matrix.rows() {
852844
let parent_row = &mut matrix.rows[child_row.parent_row];
853845
parent_row.is_useful = parent_row.is_useful || child_row.is_useful;

tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ macro_rules! m {
88
$t2 => {}
99
_ => {}
1010
}
11-
}
11+
};
1212
}
1313

1414
fn main() {
1515
m!(0u8, 20..=30, 30..=40); //~ ERROR multiple patterns overlap on their endpoints
1616
m!(0u8, 30..=40, 20..=30); //~ ERROR multiple patterns overlap on their endpoints
1717
m!(0u8, 20..=30, 31..=40);
1818
m!(0u8, 20..=30, 29..=40);
19-
m!(0u8, 20.. 30, 29..=40); //~ ERROR multiple patterns overlap on their endpoints
20-
m!(0u8, 20.. 30, 28..=40);
21-
m!(0u8, 20.. 30, 30..=40);
19+
m!(0u8, 20..30, 29..=40); //~ ERROR multiple patterns overlap on their endpoints
20+
m!(0u8, 20..30, 28..=40);
21+
m!(0u8, 20..30, 30..=40);
2222
m!(0u8, 20..=30, 30..=30);
2323
m!(0u8, 20..=30, 30..=31); //~ ERROR multiple patterns overlap on their endpoints
2424
m!(0u8, 20..=30, 29..=30);
@@ -28,15 +28,28 @@ fn main() {
2828
m!(0u8, 20..=30, 20);
2929
m!(0u8, 20..=30, 25);
3030
m!(0u8, 20..=30, 30);
31-
m!(0u8, 20.. 30, 29);
31+
m!(0u8, 20..30, 29);
3232
m!(0u8, 20, 20..=30);
3333
m!(0u8, 25, 20..=30);
3434
m!(0u8, 30, 20..=30);
3535

3636
match 0u8 {
37+
1..=10 => {}
3738
0..=10 => {}
3839
20..=30 => {}
39-
10..=20 => {} //~ ERROR multiple patterns overlap on their endpoints
40+
10..=20 => {}
41+
//~^ ERROR multiple patterns overlap on their endpoints
42+
//~| ERROR multiple patterns overlap on their endpoints
43+
_ => {}
44+
}
45+
match 0u8 {
46+
0..=10 => {}
47+
10..=20 if true => {} //~ ERROR multiple patterns overlap on their endpoints
48+
_ => {}
49+
}
50+
match 0u8 {
51+
0..=10 if true => {}
52+
10..=20 => {}
4053
_ => {}
4154
}
4255
match (0u8, true) {
@@ -51,6 +64,13 @@ fn main() {
5164
(false, 10..20) => {}
5265
_ => {}
5366
}
67+
// this probably shouldn't lint
68+
match (true, 0u8) {
69+
(true, 0..=10) => {}
70+
(false, _) => {}
71+
(_, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints
72+
_ => {}
73+
}
5474
match Some(0u8) {
5575
Some(0..=10) => {}
5676
Some(10..20) => {} //~ ERROR multiple patterns overlap on their endpoints

tests/ui/pattern/usefulness/integer-ranges/overlapping_range_endpoints.stderr

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ LL | m!(0u8, 30..=40, 20..=30);
2424
= note: you likely meant to write mutually exclusive ranges
2525

2626
error: multiple patterns overlap on their endpoints
27-
--> $DIR/overlapping_range_endpoints.rs:19:22
27+
--> $DIR/overlapping_range_endpoints.rs:19:21
2828
|
29-
LL | m!(0u8, 20.. 30, 29..=40);
30-
| ------- ^^^^^^^ ... with this range
29+
LL | m!(0u8, 20..30, 29..=40);
30+
| ------ ^^^^^^^ ... with this range
3131
| |
3232
| this range overlaps on `29_u8`...
3333
|
@@ -54,10 +54,21 @@ LL | m!(0u8, 20..=30, 19..=20);
5454
= note: you likely meant to write mutually exclusive ranges
5555

5656
error: multiple patterns overlap on their endpoints
57-
--> $DIR/overlapping_range_endpoints.rs:39:9
57+
--> $DIR/overlapping_range_endpoints.rs:40:9
5858
|
59+
LL | 1..=10 => {}
60+
| ------ this range overlaps on `10_u8`...
5961
LL | 0..=10 => {}
6062
| ------ this range overlaps on `10_u8`...
63+
LL | 20..=30 => {}
64+
LL | 10..=20 => {}
65+
| ^^^^^^^ ... with this range
66+
|
67+
= note: you likely meant to write mutually exclusive ranges
68+
69+
error: multiple patterns overlap on their endpoints
70+
--> $DIR/overlapping_range_endpoints.rs:40:9
71+
|
6172
LL | 20..=30 => {}
6273
| ------- this range overlaps on `20_u8`...
6374
LL | 10..=20 => {}
@@ -66,7 +77,17 @@ LL | 10..=20 => {}
6677
= note: you likely meant to write mutually exclusive ranges
6778

6879
error: multiple patterns overlap on their endpoints
69-
--> $DIR/overlapping_range_endpoints.rs:50:16
80+
--> $DIR/overlapping_range_endpoints.rs:47:9
81+
|
82+
LL | 0..=10 => {}
83+
| ------ this range overlaps on `10_u8`...
84+
LL | 10..=20 if true => {}
85+
| ^^^^^^^ ... with this range
86+
|
87+
= note: you likely meant to write mutually exclusive ranges
88+
89+
error: multiple patterns overlap on their endpoints
90+
--> $DIR/overlapping_range_endpoints.rs:63:16
7091
|
7192
LL | (true, 0..=10) => {}
7293
| ------ this range overlaps on `10_u8`...
@@ -76,7 +97,18 @@ LL | (true, 10..20) => {}
7697
= note: you likely meant to write mutually exclusive ranges
7798

7899
error: multiple patterns overlap on their endpoints
79-
--> $DIR/overlapping_range_endpoints.rs:56:14
100+
--> $DIR/overlapping_range_endpoints.rs:71:13
101+
|
102+
LL | (true, 0..=10) => {}
103+
| ------ this range overlaps on `10_u8`...
104+
LL | (false, _) => {}
105+
LL | (_, 10..20) => {}
106+
| ^^^^^^ ... with this range
107+
|
108+
= note: you likely meant to write mutually exclusive ranges
109+
110+
error: multiple patterns overlap on their endpoints
111+
--> $DIR/overlapping_range_endpoints.rs:76:14
80112
|
81113
LL | Some(0..=10) => {}
82114
| ------ this range overlaps on `10_u8`...
@@ -85,5 +117,5 @@ LL | Some(10..20) => {}
85117
|
86118
= note: you likely meant to write mutually exclusive ranges
87119

88-
error: aborting due to 8 previous errors
120+
error: aborting due to 11 previous errors
89121

0 commit comments

Comments
 (0)