Factor out collection of overlapping ranges
This commit is contained in:
parent
89d01babe6
commit
6f6ba2571d
4 changed files with 62 additions and 31 deletions
|
@ -124,7 +124,7 @@ pub fn analyze_match<'p, 'tcx>(
|
||||||
|
|
||||||
let pat_column = PatternColumn::new(arms);
|
let pat_column = PatternColumn::new(arms);
|
||||||
|
|
||||||
// Lint on ranges that overlap on their endpoints, which is likely a mistake.
|
// Lint ranges that overlap on their endpoints, which is likely a mistake.
|
||||||
lint_overlapping_range_endpoints(cx, &pat_column)?;
|
lint_overlapping_range_endpoints(cx, &pat_column)?;
|
||||||
|
|
||||||
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
|
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
|
||||||
|
|
|
@ -4,18 +4,18 @@ use rustc_data_structures::captures::Captures;
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_session::lint;
|
use rustc_session::lint;
|
||||||
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||||
use rustc_span::{ErrorGuaranteed, Span};
|
use rustc_span::ErrorGuaranteed;
|
||||||
|
|
||||||
use crate::constructor::{IntRange, MaybeInfiniteInt};
|
use crate::constructor::MaybeInfiniteInt;
|
||||||
use crate::errors::{
|
use crate::errors::{
|
||||||
NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
|
self, NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Uncovered,
|
||||||
OverlappingRangeEndpoints, Uncovered,
|
|
||||||
};
|
};
|
||||||
use crate::pat::PatOrWild;
|
use crate::pat::PatOrWild;
|
||||||
use crate::rustc::{
|
use crate::rustc::{
|
||||||
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
|
self, Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy,
|
||||||
SplitConstructorSet, WitnessPat,
|
RustcMatchCheckCtxt, SplitConstructorSet, WitnessPat,
|
||||||
};
|
};
|
||||||
|
use crate::usefulness::OverlappingRanges;
|
||||||
|
|
||||||
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
|
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
|
||||||
/// inspect the same subvalue/place".
|
/// inspect the same subvalue/place".
|
||||||
|
@ -209,34 +209,19 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||||
|
|
||||||
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
|
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
|
||||||
#[instrument(level = "debug", skip(cx))]
|
#[instrument(level = "debug", skip(cx))]
|
||||||
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
pub(crate) fn collect_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||||
column: &PatternColumn<'p, 'tcx>,
|
column: &PatternColumn<'p, 'tcx>,
|
||||||
|
overlapping_range_endpoints: &mut Vec<rustc::OverlappingRanges<'p, 'tcx>>,
|
||||||
) -> Result<(), ErrorGuaranteed> {
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
let Some(ty) = column.head_ty() else {
|
let Some(ty) = column.head_ty() else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||||
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
|
|
||||||
|
|
||||||
let set = column.analyze_ctors(pcx)?;
|
let set = column.analyze_ctors(pcx)?;
|
||||||
|
|
||||||
if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
|
if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
|
||||||
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
|
|
||||||
let overlap_as_pat = rcx.hoist_pat_range(overlap, ty);
|
|
||||||
let overlaps: Vec<_> = overlapped_spans
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
|
|
||||||
.collect();
|
|
||||||
rcx.tcx.emit_spanned_lint(
|
|
||||||
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
|
|
||||||
rcx.match_lint_level,
|
|
||||||
this_span,
|
|
||||||
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// If two ranges overlapped, the split set will contain their intersection as a singleton.
|
// If two ranges overlapped, the split set will contain their intersection as a singleton.
|
||||||
let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range());
|
let split_int_ranges = set.present.iter().filter_map(|c| c.as_int_range());
|
||||||
for overlap_range in split_int_ranges.clone() {
|
for overlap_range in split_int_ranges.clone() {
|
||||||
|
@ -249,7 +234,6 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||||
// Iterate on patterns that contained `overlap`.
|
// Iterate on patterns that contained `overlap`.
|
||||||
for pat in column.iter() {
|
for pat in column.iter() {
|
||||||
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
|
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
|
||||||
let this_span = pat.data().unwrap().span;
|
|
||||||
if this_range.is_singleton() {
|
if this_range.is_singleton() {
|
||||||
// Don't lint when one of the ranges is a singleton.
|
// Don't lint when one of the ranges is a singleton.
|
||||||
continue;
|
continue;
|
||||||
|
@ -258,16 +242,24 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||||
// `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
|
// `this_range` looks like `overlap..=this_range.hi`; it overlaps with any
|
||||||
// ranges that look like `lo..=overlap`.
|
// ranges that look like `lo..=overlap`.
|
||||||
if !prefixes.is_empty() {
|
if !prefixes.is_empty() {
|
||||||
emit_lint(overlap_range, this_span, &prefixes);
|
overlapping_range_endpoints.push(OverlappingRanges {
|
||||||
|
pat,
|
||||||
|
overlaps_on: *overlap_range,
|
||||||
|
overlaps_with: prefixes.as_slice().to_vec(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
suffixes.push(this_span)
|
suffixes.push(pat)
|
||||||
} else if this_range.hi == overlap.plus_one() {
|
} else if this_range.hi == overlap.plus_one() {
|
||||||
// `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
|
// `this_range` looks like `this_range.lo..=overlap`; it overlaps with any
|
||||||
// ranges that look like `overlap..=hi`.
|
// ranges that look like `overlap..=hi`.
|
||||||
if !suffixes.is_empty() {
|
if !suffixes.is_empty() {
|
||||||
emit_lint(overlap_range, this_span, &suffixes);
|
overlapping_range_endpoints.push(OverlappingRanges {
|
||||||
|
pat,
|
||||||
|
overlaps_on: *overlap_range,
|
||||||
|
overlaps_with: suffixes.as_slice().to_vec(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
prefixes.push(this_span)
|
prefixes.push(pat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -276,9 +268,37 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||||
// Recurse into the fields.
|
// Recurse into the fields.
|
||||||
for ctor in set.present {
|
for ctor in set.present {
|
||||||
for col in column.specialize(pcx, &ctor) {
|
for col in column.specialize(pcx, &ctor) {
|
||||||
lint_overlapping_range_endpoints(cx, &col)?;
|
collect_overlapping_range_endpoints(cx, &col, overlapping_range_endpoints)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(cx))]
|
||||||
|
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||||
|
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||||
|
column: &PatternColumn<'p, 'tcx>,
|
||||||
|
) -> Result<(), ErrorGuaranteed> {
|
||||||
|
let mut overlapping_range_endpoints = Vec::new();
|
||||||
|
collect_overlapping_range_endpoints(cx, column, &mut overlapping_range_endpoints)?;
|
||||||
|
|
||||||
|
let rcx = cx.tycx;
|
||||||
|
for overlap in overlapping_range_endpoints {
|
||||||
|
let overlap_as_pat = rcx.hoist_pat_range(&overlap.overlaps_on, overlap.pat.ty());
|
||||||
|
let overlaps: Vec<_> = overlap
|
||||||
|
.overlaps_with
|
||||||
|
.iter()
|
||||||
|
.map(|pat| pat.data().unwrap().span)
|
||||||
|
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
|
||||||
|
.collect();
|
||||||
|
let pat_span = overlap.pat.data().unwrap().span;
|
||||||
|
rcx.tcx.emit_spanned_lint(
|
||||||
|
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
|
||||||
|
rcx.match_lint_level,
|
||||||
|
pat_span,
|
||||||
|
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,8 @@ pub type DeconstructedPat<'p, 'tcx> =
|
||||||
crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
|
pub type OverlappingRanges<'p, 'tcx> =
|
||||||
|
crate::usefulness::OverlappingRanges<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
pub(crate) type PlaceCtxt<'a, 'p, 'tcx> =
|
pub(crate) type PlaceCtxt<'a, 'p, 'tcx> =
|
||||||
crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||||
pub(crate) type SplitConstructorSet<'p, 'tcx> =
|
pub(crate) type SplitConstructorSet<'p, 'tcx> =
|
||||||
|
|
|
@ -716,7 +716,7 @@ use rustc_index::bit_set::BitSet;
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use crate::constructor::{Constructor, ConstructorSet};
|
use crate::constructor::{Constructor, ConstructorSet, IntRange};
|
||||||
use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat};
|
use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat};
|
||||||
use crate::{Captures, MatchArm, MatchCtxt, TypeCx};
|
use crate::{Captures, MatchArm, MatchCtxt, TypeCx};
|
||||||
|
|
||||||
|
@ -1471,6 +1471,15 @@ pub enum Usefulness<'p, Cx: TypeCx> {
|
||||||
Redundant,
|
Redundant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Indicates that the range `pat` overlapped with all the ranges in `overlaps_with`, where the
|
||||||
|
/// range they overlapped over is `overlaps_on`. We only detect singleton overlaps.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct OverlappingRanges<'p, Cx: TypeCx> {
|
||||||
|
pub pat: &'p DeconstructedPat<'p, Cx>,
|
||||||
|
pub overlaps_on: IntRange,
|
||||||
|
pub overlaps_with: Vec<&'p DeconstructedPat<'p, Cx>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// The output of checking a match for exhaustiveness and arm usefulness.
|
/// The output of checking a match for exhaustiveness and arm usefulness.
|
||||||
pub struct UsefulnessReport<'p, Cx: TypeCx> {
|
pub struct UsefulnessReport<'p, Cx: TypeCx> {
|
||||||
/// For each arm of the input, whether that arm is useful after the arms above it.
|
/// For each arm of the input, whether that arm is useful after the arms above it.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue