Skip to content

Commit 174e73a

Browse files
committedJan 12, 2024
Auto merge of #119396 - Nadrieril:intersection-tracking, r=WaffleLapkin
Exhaustiveness: track overlapping ranges precisely The `overlapping_range_endpoints` lint has false positives, e.g. #117648. I expected that removing these false positives would have too much of a perf impact but never measured it. This PR is an experiment to see if the perf loss is manageable. r? `@ghost`
2 parents bfd799f + a24f4db commit 174e73a

File tree

7 files changed

+206
-135
lines changed

7 files changed

+206
-135
lines changed
 

‎compiler/rustc_pattern_analysis/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,10 @@ pub fn analyze_match<'p, 'tcx>(
124124

125125
let pat_column = PatternColumn::new(arms);
126126

127-
// Lint on ranges that overlap on their endpoints, which is likely a mistake.
128-
lint_overlapping_range_endpoints(cx, &pat_column)?;
127+
// Lint ranges that overlap on their endpoints, which is likely a mistake.
128+
if !report.overlapping_range_endpoints.is_empty() {
129+
lint_overlapping_range_endpoints(cx, &report.overlapping_range_endpoints);
130+
}
129131

130132
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
131133
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.

‎compiler/rustc_pattern_analysis/src/lints.rs

Lines changed: 22 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,14 @@
1-
use smallvec::SmallVec;
2-
3-
use rustc_data_structures::captures::Captures;
4-
use rustc_middle::ty;
51
use rustc_session::lint;
62
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
7-
use rustc_span::{ErrorGuaranteed, Span};
3+
use rustc_span::ErrorGuaranteed;
84

9-
use crate::constructor::{IntRange, MaybeInfiniteInt};
105
use crate::errors::{
11-
NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
12-
OverlappingRangeEndpoints, Uncovered,
6+
self, NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered,
137
};
148
use crate::pat::PatOrWild;
159
use crate::rustc::{
16-
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
17-
SplitConstructorSet, WitnessPat,
10+
self, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy,
11+
RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat,
1812
};
1913

2014
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
@@ -68,10 +62,6 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
6862
Ok(ctors_for_ty.split(pcx, column_ctors))
6963
}
7064

71-
fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'_> {
72-
self.patterns.iter().copied()
73-
}
74-
7565
/// Does specialization: given a constructor, this takes the patterns from the column that match
7666
/// the constructor, and outputs their fields.
7767
/// This returns one column per field of the constructor. They usually all have the same length
@@ -207,78 +197,25 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
207197
Ok(())
208198
}
209199

210-
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
211-
#[instrument(level = "debug", skip(cx))]
212200
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
213201
cx: MatchCtxt<'a, 'p, 'tcx>,
214-
column: &PatternColumn<'p, 'tcx>,
215-
) -> Result<(), ErrorGuaranteed> {
216-
let Some(ty) = column.head_ty() else {
217-
return Ok(());
218-
};
219-
let pcx = &PlaceCtxt::new_dummy(cx, ty);
220-
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
221-
222-
let set = column.analyze_ctors(pcx)?;
223-
224-
if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
225-
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
226-
let overlap_as_pat = rcx.hoist_pat_range(overlap, ty);
227-
let overlaps: Vec<_> = overlapped_spans
228-
.iter()
229-
.copied()
230-
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
231-
.collect();
232-
rcx.tcx.emit_spanned_lint(
233-
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
234-
rcx.match_lint_level,
235-
this_span,
236-
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
237-
);
238-
};
239-
240-
// If two ranges overlapped, the split set will contain their intersection as a singleton.
241-
let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range());
242-
for overlap_range in split_int_ranges.clone() {
243-
if overlap_range.is_singleton() {
244-
let overlap: MaybeInfiniteInt = overlap_range.lo;
245-
// Ranges that look like `lo..=overlap`.
246-
let mut prefixes: SmallVec<[_; 1]> = Default::default();
247-
// Ranges that look like `overlap..=hi`.
248-
let mut suffixes: SmallVec<[_; 1]> = Default::default();
249-
// Iterate on patterns that contained `overlap`.
250-
for pat in column.iter() {
251-
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
252-
let this_span = pat.data().unwrap().span;
253-
if this_range.is_singleton() {
254-
// Don't lint when one of the ranges is a singleton.
255-
continue;
256-
}
257-
if this_range.lo == overlap {
258-
// `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
259-
// ranges that look like `lo..=overlap`.
260-
if !prefixes.is_empty() {
261-
emit_lint(overlap_range, this_span, &prefixes);
262-
}
263-
suffixes.push(this_span)
264-
} else if this_range.hi == overlap.plus_one() {
265-
// `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
266-
// ranges that look like `overlap..=hi`.
267-
if !suffixes.is_empty() {
268-
emit_lint(overlap_range, this_span, &suffixes);
269-
}
270-
prefixes.push(this_span)
271-
}
272-
}
273-
}
274-
}
275-
} else {
276-
// Recurse into the fields.
277-
for ctor in set.present {
278-
for col in column.specialize(pcx, &ctor) {
279-
lint_overlapping_range_endpoints(cx, &col)?;
280-
}
281-
}
202+
overlapping_range_endpoints: &[rustc::OverlappingRanges<'p, 'tcx>],
203+
) {
204+
let rcx = cx.tycx;
205+
for overlap in overlapping_range_endpoints {
206+
let overlap_as_pat = rcx.hoist_pat_range(&overlap.overlaps_on, overlap.pat.ty());
207+
let overlaps: Vec<_> = overlap
208+
.overlaps_with
209+
.iter()
210+
.map(|pat| pat.data().unwrap().span)
211+
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
212+
.collect();
213+
let pat_span = overlap.pat.data().unwrap().span;
214+
rcx.tcx.emit_spanned_lint(
215+
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
216+
rcx.match_lint_level,
217+
pat_span,
218+
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
219+
);
282220
}
283-
Ok(())
284221
}

‎compiler/rustc_pattern_analysis/src/rustc.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub type DeconstructedPat<'p, 'tcx> =
3434
crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3535
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3636
pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
37+
pub type OverlappingRanges<'p, 'tcx> =
38+
crate::usefulness::OverlappingRanges<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3739
pub(crate) type PlaceCtxt<'a, 'p, 'tcx> =
3840
crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
3941
pub(crate) type SplitConstructorSet<'p, 'tcx> =

‎compiler/rustc_pattern_analysis/src/usefulness.rs

Lines changed: 159 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -712,10 +712,11 @@
712712
//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific
713713
//! reason not to, for example if they crucially depend on a particular feature like `or_patterns`.
714714
715+
use rustc_index::bit_set::BitSet;
715716
use smallvec::{smallvec, SmallVec};
716717
use std::fmt;
717718

718-
use crate::constructor::{Constructor, ConstructorSet};
719+
use crate::constructor::{Constructor, ConstructorSet, IntRange};
719720
use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat};
720721
use crate::{Captures, MatchArm, MatchCtxt, TypeCx};
721722

@@ -911,6 +912,11 @@ struct MatrixRow<'p, Cx: TypeCx> {
911912
/// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
912913
/// This is reset to `false` when specializing.
913914
useful: bool,
915+
/// Tracks which rows above this one have an intersection with this one, i.e. such that there is
916+
/// a value that matches both rows.
917+
/// Note: Because of relevancy we may miss some intersections. The intersections we do find are
918+
/// correct.
919+
intersects: BitSet<usize>,
914920
}
915921

916922
impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
@@ -938,6 +944,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
938944
parent_row: self.parent_row,
939945
is_under_guard: self.is_under_guard,
940946
useful: false,
947+
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
941948
})
942949
}
943950

@@ -955,6 +962,7 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
955962
parent_row,
956963
is_under_guard: self.is_under_guard,
957964
useful: false,
965+
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
958966
}
959967
}
960968
}
@@ -993,13 +1001,15 @@ struct Matrix<'p, Cx: TypeCx> {
9931001
impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
9941002
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
9951003
/// expands it. Internal method, prefer [`Matrix::new`].
996-
fn expand_and_push(&mut self, row: MatrixRow<'p, Cx>) {
1004+
fn expand_and_push(&mut self, mut row: MatrixRow<'p, Cx>) {
9971005
if !row.is_empty() && row.head().is_or_pat() {
9981006
// Expand nested or-patterns.
999-
for new_row in row.expand_or_pat() {
1007+
for mut new_row in row.expand_or_pat() {
1008+
new_row.intersects = BitSet::new_empty(self.rows.len());
10001009
self.rows.push(new_row);
10011010
}
10021011
} else {
1012+
row.intersects = BitSet::new_empty(self.rows.len());
10031013
self.rows.push(row);
10041014
}
10051015
}
@@ -1019,9 +1029,10 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
10191029
for (row_id, arm) in arms.iter().enumerate() {
10201030
let v = MatrixRow {
10211031
pats: PatStack::from_pattern(arm.pat),
1022-
parent_row: row_id, // dummy, we won't read it
1032+
parent_row: row_id, // dummy, we don't read it
10231033
is_under_guard: arm.has_guard,
10241034
useful: false,
1035+
intersects: BitSet::new_empty(0), // Initialized in `Matrix::expand_and_push`.
10251036
};
10261037
matrix.expand_and_push(v);
10271038
}
@@ -1317,6 +1328,83 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13171328
}
13181329
}
13191330

1331+
/// Collect ranges that overlap like `lo..=overlap`/`overlap..=hi`. Must be called during
1332+
/// exhaustiveness checking, if we find a singleton range after constructor splitting. This reuses
1333+
/// row intersection information to only detect ranges that truly overlap.
1334+
///
1335+
/// If two ranges overlapped, the split set will contain their intersection as a singleton.
1336+
/// Specialization will then select rows that match the overlap, and exhaustiveness will compute
1337+
/// which rows have an intersection that includes the overlap. That gives us all the info we need to
1338+
/// compute overlapping ranges without false positives.
1339+
///
1340+
/// We can however get false negatives because exhaustiveness does not explore all cases. See the
1341+
/// section on relevancy at the top of the file.
1342+
fn collect_overlapping_range_endpoints<'p, Cx: TypeCx>(
1343+
overlap_range: IntRange,
1344+
matrix: &Matrix<'p, Cx>,
1345+
specialized_matrix: &Matrix<'p, Cx>,
1346+
overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>,
1347+
) {
1348+
let overlap = overlap_range.lo;
1349+
// Ranges that look like `lo..=overlap`.
1350+
let mut prefixes: SmallVec<[_; 1]> = Default::default();
1351+
// Ranges that look like `overlap..=hi`.
1352+
let mut suffixes: SmallVec<[_; 1]> = Default::default();
1353+
// Iterate on patterns that contained `overlap`. We iterate on `specialized_matrix` which
1354+
// contains only rows that matched the current `ctor` as well as accurate intersection
1355+
// information. It doesn't contain the column that contains the range; that can be found in
1356+
// `matrix`.
1357+
for (child_row_id, child_row) in specialized_matrix.rows().enumerate() {
1358+
let PatOrWild::Pat(pat) = matrix.rows[child_row.parent_row].head() else { continue };
1359+
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
1360+
// Don't lint when one of the ranges is a singleton.
1361+
if this_range.is_singleton() {
1362+
continue;
1363+
}
1364+
if this_range.lo == overlap {
1365+
// `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
1366+
// ranges that look like `lo..=overlap`.
1367+
if !prefixes.is_empty() {
1368+
let overlaps_with: Vec<_> = prefixes
1369+
.iter()
1370+
.filter(|&&(other_child_row_id, _)| {
1371+
child_row.intersects.contains(other_child_row_id)
1372+
})
1373+
.map(|&(_, pat)| pat)
1374+
.collect();
1375+
if !overlaps_with.is_empty() {
1376+
overlapping_range_endpoints.push(OverlappingRanges {
1377+
pat,
1378+
overlaps_on: overlap_range,
1379+
overlaps_with,
1380+
});
1381+
}
1382+
}
1383+
suffixes.push((child_row_id, pat))
1384+
} else if this_range.hi == overlap.plus_one() {
1385+
// `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
1386+
// ranges that look like `overlap..=hi`.
1387+
if !suffixes.is_empty() {
1388+
let overlaps_with: Vec<_> = suffixes
1389+
.iter()
1390+
.filter(|&&(other_child_row_id, _)| {
1391+
child_row.intersects.contains(other_child_row_id)
1392+
})
1393+
.map(|&(_, pat)| pat)
1394+
.collect();
1395+
if !overlaps_with.is_empty() {
1396+
overlapping_range_endpoints.push(OverlappingRanges {
1397+
pat,
1398+
overlaps_on: overlap_range,
1399+
overlaps_with,
1400+
});
1401+
}
1402+
}
1403+
prefixes.push((child_row_id, pat))
1404+
}
1405+
}
1406+
}
1407+
13201408
/// The core of the algorithm.
13211409
///
13221410
/// This recursively computes witnesses of the non-exhaustiveness of `matrix` (if any). Also tracks
@@ -1335,6 +1423,7 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
13351423
fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
13361424
mcx: MatchCtxt<'a, 'p, Cx>,
13371425
matrix: &mut Matrix<'p, Cx>,
1426+
overlapping_range_endpoints: &mut Vec<OverlappingRanges<'p, Cx>>,
13381427
is_top_level: bool,
13391428
) -> Result<WitnessMatrix<Cx>, Cx::Error> {
13401429
debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count()));
@@ -1349,21 +1438,19 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
13491438
let Some(ty) = matrix.head_ty() else {
13501439
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
13511440
// A row is useful iff it has no (unguarded) rows above it.
1352-
for row in matrix.rows_mut() {
1353-
// All rows are useful until they're not.
1354-
row.useful = true;
1355-
// When there's an unguarded row, the match is exhaustive and any subsequent row is not
1356-
// useful.
1357-
if !row.is_under_guard {
1358-
return Ok(WitnessMatrix::empty());
1359-
}
1441+
let mut useful = true; // Whether the next row is useful.
1442+
for (i, row) in matrix.rows_mut().enumerate() {
1443+
row.useful = useful;
1444+
row.intersects.insert_range(0..i);
1445+
// The next rows stays useful if this one is under a guard.
1446+
useful &= row.is_under_guard;
13601447
}
1361-
// No (unguarded) rows, so the match is not exhaustive. We return a new witness unless
1362-
// irrelevant.
1363-
return if matrix.wildcard_row_is_relevant {
1448+
return if useful && matrix.wildcard_row_is_relevant {
1449+
// The wildcard row is useful; the match is non-exhaustive.
13641450
Ok(WitnessMatrix::unit_witness())
13651451
} else {
1366-
// We choose to not report anything here; see at the top for details.
1452+
// Either the match is exhaustive, or we choose not to report anything because of
1453+
// relevancy. See at the top for details.
13671454
Ok(WitnessMatrix::empty())
13681455
};
13691456
};
@@ -1416,18 +1503,47 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
14161503
let ctor_is_relevant = matches!(ctor, Constructor::Missing) || missing_ctors.is_empty();
14171504
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant);
14181505
let mut witnesses = ensure_sufficient_stack(|| {
1419-
compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix, false)
1506+
compute_exhaustiveness_and_usefulness(
1507+
mcx,
1508+
&mut spec_matrix,
1509+
overlapping_range_endpoints,
1510+
false,
1511+
)
14201512
})?;
14211513

14221514
// Transform witnesses for `spec_matrix` into witnesses for `matrix`.
14231515
witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_individual_missing_ctors);
14241516
// Accumulate the found witnesses.
14251517
ret.extend(witnesses);
14261518

1427-
// A parent row is useful if any of its children is.
14281519
for child_row in spec_matrix.rows() {
1429-
let parent_row = &mut matrix.rows[child_row.parent_row];
1430-
parent_row.useful = parent_row.useful || child_row.useful;
1520+
let parent_row_id = child_row.parent_row;
1521+
let parent_row = &mut matrix.rows[parent_row_id];
1522+
// A parent row is useful if any of its children is.
1523+
parent_row.useful |= child_row.useful;
1524+
for child_intersection in child_row.intersects.iter() {
1525+
// Convert the intersecting ids into ids for the parent matrix.
1526+
let parent_intersection = spec_matrix.rows[child_intersection].parent_row;
1527+
// Note: self-intersection can happen with or-patterns.
1528+
if parent_intersection != parent_row_id {
1529+
parent_row.intersects.insert(parent_intersection);
1530+
}
1531+
}
1532+
}
1533+
1534+
// Detect ranges that overlap on their endpoints.
1535+
if let Constructor::IntRange(overlap_range) = ctor {
1536+
if overlap_range.is_singleton()
1537+
&& spec_matrix.rows.len() >= 2
1538+
&& spec_matrix.rows.iter().any(|row| !row.intersects.is_empty())
1539+
{
1540+
collect_overlapping_range_endpoints(
1541+
overlap_range,
1542+
matrix,
1543+
&spec_matrix,
1544+
overlapping_range_endpoints,
1545+
);
1546+
}
14311547
}
14321548
}
14331549

@@ -1453,13 +1569,23 @@ pub enum Usefulness<'p, Cx: TypeCx> {
14531569
Redundant,
14541570
}
14551571

1572+
/// Indicates that the range `pat` overlapped with all the ranges in `overlaps_with`, where the
1573+
/// range they overlapped over is `overlaps_on`. We only detect singleton overlaps.
1574+
#[derive(Clone, Debug)]
1575+
pub struct OverlappingRanges<'p, Cx: TypeCx> {
1576+
pub pat: &'p DeconstructedPat<'p, Cx>,
1577+
pub overlaps_on: IntRange,
1578+
pub overlaps_with: Vec<&'p DeconstructedPat<'p, Cx>>,
1579+
}
1580+
14561581
/// The output of checking a match for exhaustiveness and arm usefulness.
14571582
pub struct UsefulnessReport<'p, Cx: TypeCx> {
14581583
/// For each arm of the input, whether that arm is useful after the arms above it.
14591584
pub arm_usefulness: Vec<(MatchArm<'p, Cx>, Usefulness<'p, Cx>)>,
14601585
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
14611586
/// exhaustiveness.
14621587
pub non_exhaustiveness_witnesses: Vec<WitnessPat<Cx>>,
1588+
pub overlapping_range_endpoints: Vec<OverlappingRanges<'p, Cx>>,
14631589
}
14641590

14651591
/// Computes whether a match is exhaustive and which of its arms are useful.
@@ -1470,9 +1596,14 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
14701596
scrut_ty: Cx::Ty,
14711597
scrut_validity: ValidityConstraint,
14721598
) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
1599+
let mut overlapping_range_endpoints = Vec::new();
14731600
let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity);
1474-
let non_exhaustiveness_witnesses =
1475-
compute_exhaustiveness_and_usefulness(cx, &mut matrix, true)?;
1601+
let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(
1602+
cx,
1603+
&mut matrix,
1604+
&mut overlapping_range_endpoints,
1605+
true,
1606+
)?;
14761607

14771608
let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
14781609
let arm_usefulness: Vec<_> = arms
@@ -1489,5 +1620,10 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
14891620
(arm, usefulness)
14901621
})
14911622
.collect();
1492-
Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses })
1623+
1624+
Ok(UsefulnessReport {
1625+
arm_usefulness,
1626+
non_exhaustiveness_witnesses,
1627+
overlapping_range_endpoints,
1628+
})
14931629
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// check-pass
2+
fn main() {
3+
match (0i8, 0i8) {
4+
(0, _) => {}
5+
(..=-1, ..=0) => {}
6+
(1.., 0..) => {}
7+
(1.., ..=-1) | (..=-1, 1..) => {}
8+
}
9+
}

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,25 @@ fn main() {
4444
match (0u8, true) {
4545
(0..=10, true) => {}
4646
(10..20, true) => {} //~ ERROR multiple patterns overlap on their endpoints
47-
(10..20, false) => {} //~ ERROR multiple patterns overlap on their endpoints
47+
(10..20, false) => {}
4848
_ => {}
4949
}
5050
match (true, 0u8) {
5151
(true, 0..=10) => {}
5252
(true, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints
53-
(false, 10..20) => {} //~ ERROR multiple patterns overlap on their endpoints
53+
(false, 10..20) => {}
5454
_ => {}
5555
}
5656
match Some(0u8) {
5757
Some(0..=10) => {}
5858
Some(10..20) => {} //~ ERROR multiple patterns overlap on their endpoints
5959
_ => {}
6060
}
61+
62+
// The lint has false negatives when we skip some cases because of relevancy.
63+
match (true, true, 0u8) {
64+
(true, _, 0..=10) => {}
65+
(_, true, 10..20) => {}
66+
_ => {}
67+
}
6168
}

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

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,6 @@ LL | (10..20, true) => {}
8484
|
8585
= note: you likely meant to write mutually exclusive ranges
8686

87-
error: multiple patterns overlap on their endpoints
88-
--> $DIR/overlapping_range_endpoints.rs:47:10
89-
|
90-
LL | (0..=10, true) => {}
91-
| ------ this range overlaps on `10_u8`...
92-
LL | (10..20, true) => {}
93-
LL | (10..20, false) => {}
94-
| ^^^^^^ ... with this range
95-
|
96-
= note: you likely meant to write mutually exclusive ranges
97-
9887
error: multiple patterns overlap on their endpoints
9988
--> $DIR/overlapping_range_endpoints.rs:52:16
10089
|
@@ -105,17 +94,6 @@ LL | (true, 10..20) => {}
10594
|
10695
= note: you likely meant to write mutually exclusive ranges
10796

108-
error: multiple patterns overlap on their endpoints
109-
--> $DIR/overlapping_range_endpoints.rs:53:17
110-
|
111-
LL | (true, 0..=10) => {}
112-
| ------ this range overlaps on `10_u8`...
113-
LL | (true, 10..20) => {}
114-
LL | (false, 10..20) => {}
115-
| ^^^^^^ ... with this range
116-
|
117-
= note: you likely meant to write mutually exclusive ranges
118-
11997
error: multiple patterns overlap on their endpoints
12098
--> $DIR/overlapping_range_endpoints.rs:58:14
12199
|
@@ -126,5 +104,5 @@ LL | Some(10..20) => {}
126104
|
127105
= note: you likely meant to write mutually exclusive ranges
128106

129-
error: aborting due to 12 previous errors
107+
error: aborting due to 10 previous errors
130108

0 commit comments

Comments
 (0)
Please sign in to comment.