pattern_analysis doesn't need to know what spans are

This commit is contained in:
Nadrieril 2023-12-15 16:18:21 +01:00
parent 8c5e89907c
commit 1e89a38423
6 changed files with 39 additions and 36 deletions

View File

@ -856,21 +856,21 @@ fn report_arm_reachability<'p, 'tcx>(
for (arm, is_useful) in report.arm_usefulness.iter() { for (arm, is_useful) in report.arm_usefulness.iter() {
match is_useful { match is_useful {
Usefulness::Redundant => { Usefulness::Redundant => {
report_unreachable_pattern(*arm.pat.span(), arm.arm_data, catchall) report_unreachable_pattern(*arm.pat.data(), arm.arm_data, catchall)
} }
Usefulness::Useful(redundant_spans) if redundant_spans.is_empty() => {} Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {}
// The arm is reachable, but contains redundant subpatterns (from or-patterns). // The arm is reachable, but contains redundant subpatterns (from or-patterns).
Usefulness::Useful(redundant_spans) => { Usefulness::Useful(redundant_subpats) => {
let mut redundant_spans = redundant_spans.clone(); let mut redundant_subpats = redundant_subpats.clone();
// Emit lints in the order in which they occur in the file. // Emit lints in the order in which they occur in the file.
redundant_spans.sort_unstable(); redundant_subpats.sort_unstable_by_key(|pat| pat.data());
for span in redundant_spans { for pat in redundant_subpats {
report_unreachable_pattern(span, arm.arm_data, None); report_unreachable_pattern(*pat.data(), arm.arm_data, None);
} }
} }
} }
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) { if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
catchall = Some(*arm.pat.span()); catchall = Some(*arm.pat.data());
} }
} }
} }

View File

@ -37,14 +37,17 @@ use crate::rustc::RustcMatchCheckCtxt;
use crate::usefulness::{compute_match_usefulness, ValidityConstraint}; use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
pub trait MatchCx: Sized + Clone + fmt::Debug { pub trait MatchCx: Sized + Clone + fmt::Debug {
/// The type of a pattern.
type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy
type Span: Clone + Default;
/// The index of an enum variant. /// The index of an enum variant.
type VariantIdx: Clone + Idx; type VariantIdx: Clone + Idx;
/// A string literal /// A string literal
type StrLit: Clone + PartialEq + fmt::Debug; type StrLit: Clone + PartialEq + fmt::Debug;
/// Extra data to store on a match arm. /// Extra data to store on a match arm.
type ArmData: Copy + Clone + fmt::Debug; type ArmData: Copy + Clone + fmt::Debug;
/// Extra data to store on a pattern. `Default` needed when we create fictitious wildcard
/// patterns during analysis.
type PatData: Clone + Default;
fn is_opaque_ty(ty: Self::Ty) -> bool; fn is_opaque_ty(ty: Self::Ty) -> bool;
fn is_exhaustive_patterns_feature_on(&self) -> bool; fn is_exhaustive_patterns_feature_on(&self) -> bool;

View File

@ -213,7 +213,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
}; };
use rustc_errors::DecorateLint; use rustc_errors::DecorateLint;
let mut err = cx.tcx.sess.struct_span_warn(*arm.pat.span(), ""); let mut err = cx.tcx.sess.struct_span_warn(*arm.pat.data(), "");
err.set_primary_message(decorator.msg()); err.set_primary_message(decorator.msg());
decorator.decorate_lint(&mut err); decorator.decorate_lint(&mut err);
err.emit(); err.emit();
@ -263,7 +263,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
let mut suffixes: SmallVec<[_; 1]> = Default::default(); let mut suffixes: SmallVec<[_; 1]> = Default::default();
// Iterate on patterns that contained `overlap`. // Iterate on patterns that contained `overlap`.
for pat in column.iter() { for pat in column.iter() {
let this_span = *pat.span(); let this_span = *pat.data();
let Constructor::IntRange(this_range) = pat.ctor() else { continue }; let Constructor::IntRange(this_range) = pat.ctor() else { continue };
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.

View File

@ -26,23 +26,23 @@ pub struct DeconstructedPat<'p, Cx: MatchCx> {
ctor: Constructor<Cx>, ctor: Constructor<Cx>,
fields: &'p [DeconstructedPat<'p, Cx>], fields: &'p [DeconstructedPat<'p, Cx>],
ty: Cx::Ty, ty: Cx::Ty,
span: Cx::Span, data: Cx::PatData,
/// Whether removing this arm would change the behavior of the match expression. /// Whether removing this arm would change the behavior of the match expression.
useful: Cell<bool>, useful: Cell<bool>,
} }
impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> { impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> {
pub fn wildcard(ty: Cx::Ty, span: Cx::Span) -> Self { pub fn wildcard(ty: Cx::Ty, data: Cx::PatData) -> Self {
Self::new(Wildcard, &[], ty, span) Self::new(Wildcard, &[], ty, data)
} }
pub fn new( pub fn new(
ctor: Constructor<Cx>, ctor: Constructor<Cx>,
fields: &'p [DeconstructedPat<'p, Cx>], fields: &'p [DeconstructedPat<'p, Cx>],
ty: Cx::Ty, ty: Cx::Ty,
span: Cx::Span, data: Cx::PatData,
) -> Self { ) -> Self {
DeconstructedPat { ctor, fields, ty, span, useful: Cell::new(false) } DeconstructedPat { ctor, fields, ty, data, useful: Cell::new(false) }
} }
pub(crate) fn is_or_pat(&self) -> bool { pub(crate) fn is_or_pat(&self) -> bool {
@ -63,8 +63,8 @@ impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> {
pub fn ty(&self) -> Cx::Ty { pub fn ty(&self) -> Cx::Ty {
self.ty self.ty
} }
pub fn span(&self) -> &Cx::Span { pub fn data(&self) -> &Cx::PatData {
&self.span &self.data
} }
pub fn iter_fields<'a>( pub fn iter_fields<'a>(
@ -83,7 +83,7 @@ impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> {
let wildcard_sub_tys = || { let wildcard_sub_tys = || {
let tys = pcx.cx.ctor_sub_tys(other_ctor, pcx.ty); let tys = pcx.cx.ctor_sub_tys(other_ctor, pcx.ty);
tys.iter() tys.iter()
.map(|ty| DeconstructedPat::wildcard(*ty, Cx::Span::default())) .map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default()))
.map(|pat| pcx.wildcard_arena.alloc(pat) as &_) .map(|pat| pcx.wildcard_arena.alloc(pat) as &_)
.collect() .collect()
}; };
@ -113,8 +113,8 @@ impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> {
} }
} }
/// We keep track for each pattern if it was ever useful during the analysis. This is used /// We keep track for each pattern if it was ever useful during the analysis. This is used with
/// with `redundant_spans` to report redundant subpatterns arising from or patterns. /// `redundant_subpatterns` to report redundant subpatterns arising from or patterns.
pub(crate) fn set_useful(&self) { pub(crate) fn set_useful(&self) {
self.useful.set(true) self.useful.set(true)
} }
@ -132,19 +132,19 @@ impl<'p, Cx: MatchCx> DeconstructedPat<'p, Cx> {
} }
} }
/// Report the spans of subpatterns that were not useful, if any. /// Report the subpatterns that were not useful, if any.
pub(crate) fn redundant_spans(&self) -> Vec<Cx::Span> { pub(crate) fn redundant_subpatterns(&self) -> Vec<&Self> {
let mut spans = Vec::new(); let mut subpats = Vec::new();
self.collect_redundant_spans(&mut spans); self.collect_redundant_subpatterns(&mut subpats);
spans subpats
} }
fn collect_redundant_spans(&self, spans: &mut Vec<Cx::Span>) { fn collect_redundant_subpatterns<'a>(&'a self, subpats: &mut Vec<&'a Self>) {
// We don't look at subpatterns if we already reported the whole pattern as redundant. // We don't look at subpatterns if we already reported the whole pattern as redundant.
if !self.is_useful() { if !self.is_useful() {
spans.push(self.span.clone()); subpats.push(self);
} else { } else {
for p in self.iter_fields() { for p in self.iter_fields() {
p.collect_redundant_spans(spans); p.collect_redundant_subpatterns(subpats);
} }
} }
} }

View File

@ -35,7 +35,7 @@ pub(crate) type PatCtxt<'a, 'p, 'tcx> =
crate::usefulness::PatCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>; crate::usefulness::PatCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
pub(crate) type SplitConstructorSet<'p, 'tcx> = pub(crate) type SplitConstructorSet<'p, 'tcx> =
crate::constructor::SplitConstructorSet<RustcMatchCheckCtxt<'p, 'tcx>>; crate::constructor::SplitConstructorSet<RustcMatchCheckCtxt<'p, 'tcx>>;
pub type Usefulness = crate::usefulness::Usefulness<Span>; pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
pub type UsefulnessReport<'p, 'tcx> = pub type UsefulnessReport<'p, 'tcx> =
crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>; crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcMatchCheckCtxt<'p, 'tcx>>; pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcMatchCheckCtxt<'p, 'tcx>>;
@ -864,10 +864,10 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
impl<'p, 'tcx> MatchCx for RustcMatchCheckCtxt<'p, 'tcx> { impl<'p, 'tcx> MatchCx for RustcMatchCheckCtxt<'p, 'tcx> {
type Ty = Ty<'tcx>; type Ty = Ty<'tcx>;
type Span = Span;
type VariantIdx = VariantIdx; type VariantIdx = VariantIdx;
type StrLit = Const<'tcx>; type StrLit = Const<'tcx>;
type ArmData = HirId; type ArmData = HirId;
type PatData = Span;
fn is_exhaustive_patterns_feature_on(&self) -> bool { fn is_exhaustive_patterns_feature_on(&self) -> bool {
self.tcx.features().exhaustive_patterns self.tcx.features().exhaustive_patterns

View File

@ -849,7 +849,7 @@ impl<'a, 'p, Cx: MatchCx> Matrix<'a, 'p, Cx> {
scrut_validity: ValidityConstraint, scrut_validity: ValidityConstraint,
) -> Self { ) -> Self {
let wild_pattern = let wild_pattern =
wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Cx::Span::default())); wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Default::default()));
let wildcard_row = PatStack::from_pattern(wild_pattern); let wildcard_row = PatStack::from_pattern(wild_pattern);
let mut matrix = Matrix { let mut matrix = Matrix {
rows: Vec::with_capacity(arms.len()), rows: Vec::with_capacity(arms.len()),
@ -1287,11 +1287,11 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: MatchCx>(
/// Indicates whether or not a given arm is useful. /// Indicates whether or not a given arm is useful.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Usefulness<Span> { pub enum Usefulness<'p, Cx: MatchCx> {
/// The arm is useful. This additionally carries a set of or-pattern branches that have been /// The arm is useful. This additionally carries a set of or-pattern branches that have been
/// found to be redundant despite the overall arm being useful. Used only in the presence of /// found to be redundant despite the overall arm being useful. Used only in the presence of
/// or-patterns, otherwise it stays empty. /// or-patterns, otherwise it stays empty.
Useful(Vec<Span>), Useful(Vec<&'p DeconstructedPat<'p, Cx>>),
/// The arm is redundant and can be removed without changing the behavior of the match /// The arm is redundant and can be removed without changing the behavior of the match
/// expression. /// expression.
Redundant, Redundant,
@ -1300,7 +1300,7 @@ pub enum Usefulness<Span> {
/// 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: MatchCx> { pub struct UsefulnessReport<'p, Cx: MatchCx> {
/// 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.
pub arm_usefulness: Vec<(MatchArm<'p, Cx>, Usefulness<Cx::Span>)>, pub arm_usefulness: Vec<(MatchArm<'p, Cx>, Usefulness<'p, Cx>)>,
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
/// exhaustiveness. /// exhaustiveness.
pub non_exhaustiveness_witnesses: Vec<WitnessPat<Cx>>, pub non_exhaustiveness_witnesses: Vec<WitnessPat<Cx>>,
@ -1327,7 +1327,7 @@ pub fn compute_match_usefulness<'p, Cx: MatchCx>(
debug!(?arm); debug!(?arm);
// We warn when a pattern is not useful. // We warn when a pattern is not useful.
let usefulness = if arm.pat.is_useful() { let usefulness = if arm.pat.is_useful() {
Usefulness::Useful(arm.pat.redundant_spans()) Usefulness::Useful(arm.pat.redundant_subpatterns())
} else { } else {
Usefulness::Redundant Usefulness::Redundant
}; };