Skip to content

Commit 6f6ba25

Browse files
committed
Factor out collection of overlapping ranges
1 parent 89d01ba commit 6f6ba25

File tree

4 files changed

+62
-31
lines changed

4 files changed

+62
-31
lines changed

compiler/rustc_pattern_analysis/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ 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.
127+
// Lint ranges that overlap on their endpoints, which is likely a mistake.
128128
lint_overlapping_range_endpoints(cx, &pat_column)?;
129129

130130
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting

compiler/rustc_pattern_analysis/src/lints.rs

+49-29
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ use rustc_data_structures::captures::Captures;
44
use rustc_middle::ty;
55
use rustc_session::lint;
66
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
7-
use rustc_span::{ErrorGuaranteed, Span};
7+
use rustc_span::ErrorGuaranteed;
88

9-
use crate::constructor::{IntRange, MaybeInfiniteInt};
9+
use crate::constructor::MaybeInfiniteInt;
1010
use crate::errors::{
11-
NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
12-
OverlappingRangeEndpoints, Uncovered,
11+
self, NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered,
1312
};
1413
use crate::pat::PatOrWild;
1514
use crate::rustc::{
16-
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
17-
SplitConstructorSet, WitnessPat,
15+
self, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy,
16+
RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat,
1817
};
18+
use crate::usefulness::OverlappingRanges;
1919

2020
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
2121
/// inspect the same subvalue/place".
@@ -209,34 +209,19 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
209209

210210
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
211211
#[instrument(level = "debug", skip(cx))]
212-
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
212+
pub(crate) fn collect_overlapping_range_endpoints<'a, 'p, 'tcx>(
213213
cx: MatchCtxt<'a, 'p, 'tcx>,
214214
column: &PatternColumn<'p, 'tcx>,
215+
overlapping_range_endpoints: &mut Vec<rustc::OverlappingRanges<'p, 'tcx>>,
215216
) -> Result<(), ErrorGuaranteed> {
216217
let Some(ty) = column.head_ty() else {
217218
return Ok(());
218219
};
219220
let pcx = &PlaceCtxt::new_dummy(cx, ty);
220-
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
221221

222222
let set = column.analyze_ctors(pcx)?;
223223

224224
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-
240225
// If two ranges overlapped, the split set will contain their intersection as a singleton.
241226
let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range());
242227
for overlap_range in split_int_ranges.clone() {
@@ -249,7 +234,6 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
249234
// Iterate on patterns that contained `overlap`.
250235
for pat in column.iter() {
251236
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
252-
let this_span = pat.data().unwrap().span;
253237
if this_range.is_singleton() {
254238
// Don't lint when one of the ranges is a singleton.
255239
continue;
@@ -258,16 +242,24 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
258242
// `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
259243
// ranges that look like `lo..=overlap`.
260244
if !prefixes.is_empty() {
261-
emit_lint(overlap_range, this_span, &prefixes);
245+
overlapping_range_endpoints.push(OverlappingRanges {
246+
pat,
247+
overlaps_on: *overlap_range,
248+
overlaps_with: prefixes.as_slice().to_vec(),
249+
});
262250
}
263-
suffixes.push(this_span)
251+
suffixes.push(pat)
264252
} else if this_range.hi == overlap.plus_one() {
265253
// `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
266254
// ranges that look like `overlap..=hi`.
267255
if !suffixes.is_empty() {
268-
emit_lint(overlap_range, this_span, &suffixes);
256+
overlapping_range_endpoints.push(OverlappingRanges {
257+
pat,
258+
overlaps_on: *overlap_range,
259+
overlaps_with: suffixes.as_slice().to_vec(),
260+
});
269261
}
270-
prefixes.push(this_span)
262+
prefixes.push(pat)
271263
}
272264
}
273265
}
@@ -276,9 +268,37 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
276268
// Recurse into the fields.
277269
for ctor in set.present {
278270
for col in column.specialize(pcx, &ctor) {
279-
lint_overlapping_range_endpoints(cx, &col)?;
271+
collect_overlapping_range_endpoints(cx, &col, overlapping_range_endpoints)?;
280272
}
281273
}
282274
}
283275
Ok(())
284276
}
277+
278+
#[instrument(level = "debug", skip(cx))]
279+
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
280+
cx: MatchCtxt<'a, 'p, 'tcx>,
281+
column: &PatternColumn<'p, 'tcx>,
282+
) -> Result<(), ErrorGuaranteed> {
283+
let mut overlapping_range_endpoints = Vec::new();
284+
collect_overlapping_range_endpoints(cx, column, &mut overlapping_range_endpoints)?;
285+
286+
let rcx = cx.tycx;
287+
for overlap in overlapping_range_endpoints {
288+
let overlap_as_pat = rcx.hoist_pat_range(&overlap.overlaps_on, overlap.pat.ty());
289+
let overlaps: Vec<_> = overlap
290+
.overlaps_with
291+
.iter()
292+
.map(|pat| pat.data().unwrap().span)
293+
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
294+
.collect();
295+
let pat_span = overlap.pat.data().unwrap().span;
296+
rcx.tcx.emit_spanned_lint(
297+
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
298+
rcx.match_lint_level,
299+
pat_span,
300+
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
301+
);
302+
}
303+
Ok(())
304+
}

compiler/rustc_pattern_analysis/src/rustc.rs

+2
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

+10-1
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ use rustc_index::bit_set::BitSet;
716716
use smallvec::{smallvec, SmallVec};
717717
use std::fmt;
718718

719-
use crate::constructor::{Constructor, ConstructorSet};
719+
use crate::constructor::{Constructor, ConstructorSet, IntRange};
720720
use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat};
721721
use crate::{Captures, MatchArm, MatchCtxt, TypeCx};
722722

@@ -1471,6 +1471,15 @@ pub enum Usefulness<'p, Cx: TypeCx> {
14711471
Redundant,
14721472
}
14731473

1474+
/// Indicates that the range `pat` overlapped with all the ranges in `overlaps_with`, where the
1475+
/// range they overlapped over is `overlaps_on`. We only detect singleton overlaps.
1476+
#[derive(Clone, Debug)]
1477+
pub struct OverlappingRanges<'p, Cx: TypeCx> {
1478+
pub pat: &'p DeconstructedPat<'p, Cx>,
1479+
pub overlaps_on: IntRange,
1480+
pub overlaps_with: Vec<&'p DeconstructedPat<'p, Cx>>,
1481+
}
1482+
14741483
/// The output of checking a match for exhaustiveness and arm usefulness.
14751484
pub struct UsefulnessReport<'p, Cx: TypeCx> {
14761485
/// For each arm of the input, whether that arm is useful after the arms above it.

0 commit comments

Comments
 (0)