Move usefulness-specific pattern computations to usefulness
This commit is contained in:
parent
8ace7ea1f7
commit
cb3ce6645f
2 changed files with 42 additions and 34 deletions
|
@ -29,7 +29,7 @@ pub struct DeconstructedPat<Cx: TypeCx> {
|
||||||
/// correspond to a user-supplied pattern.
|
/// correspond to a user-supplied pattern.
|
||||||
data: Option<Cx::PatData>,
|
data: Option<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>,
|
pub(crate) useful: Cell<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
||||||
|
@ -112,34 +112,17 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
||||||
pub(crate) fn set_useful(&self) {
|
pub(crate) fn set_useful(&self) {
|
||||||
self.useful.set(true)
|
self.useful.set(true)
|
||||||
}
|
}
|
||||||
pub(crate) fn is_useful(&self) -> bool {
|
|
||||||
if self.useful.get() {
|
/// Walk top-down and call `it` in each place where a pattern occurs
|
||||||
true
|
/// starting with the root pattern `walk` is called on. If `it` returns
|
||||||
} else if self.is_or_pat() && self.iter_fields().any(|f| f.is_useful()) {
|
/// false then we will descend no further but siblings will be processed.
|
||||||
// We always expand or patterns in the matrix, so we will never see the actual
|
pub fn walk<'a>(&'a self, it: &mut impl FnMut(&'a Self) -> bool) {
|
||||||
// or-pattern (the one with constructor `Or`) in the column. As such, it will not be
|
if !it(self) {
|
||||||
// marked as useful itself, only its children will. We recover this information here.
|
return;
|
||||||
self.set_useful();
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Report the subpatterns that were not useful, if any.
|
|
||||||
pub(crate) fn redundant_subpatterns(&self) -> Vec<&Self> {
|
|
||||||
let mut subpats = Vec::new();
|
|
||||||
self.collect_redundant_subpatterns(&mut subpats);
|
|
||||||
subpats
|
|
||||||
}
|
|
||||||
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.
|
|
||||||
if !self.is_useful() {
|
|
||||||
subpats.push(self);
|
|
||||||
} else {
|
|
||||||
for p in self.iter_fields() {
|
for p in self.iter_fields() {
|
||||||
p.collect_redundant_subpatterns(subpats);
|
p.walk(it)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1599,6 +1599,36 @@ pub enum Usefulness<'p, Cx: TypeCx> {
|
||||||
Redundant,
|
Redundant,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Report whether this pattern was found useful, and its subpatterns that were not useful if any.
|
||||||
|
fn collect_pattern_usefulness<'p, Cx: TypeCx>(pat: &'p DeconstructedPat<Cx>) -> Usefulness<'p, Cx> {
|
||||||
|
fn pat_is_useful<'p, Cx: TypeCx>(pat: &'p DeconstructedPat<Cx>) -> bool {
|
||||||
|
if pat.useful.get() {
|
||||||
|
true
|
||||||
|
} else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(f)) {
|
||||||
|
// We always expand or patterns in the matrix, so we will never see the actual
|
||||||
|
// or-pattern (the one with constructor `Or`) in the column. As such, it will not be
|
||||||
|
// marked as useful itself, only its children will. We recover this information here.
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut redundant_subpats = Vec::new();
|
||||||
|
pat.walk(&mut |p| {
|
||||||
|
if pat_is_useful(p) {
|
||||||
|
// The pattern is useful, so we recurse to find redundant subpatterns.
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// The pattern is redundant.
|
||||||
|
redundant_subpats.push(p);
|
||||||
|
false // stop recursing
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if pat_is_useful(pat) { Usefulness::Useful(redundant_subpats) } else { Usefulness::Redundant }
|
||||||
|
}
|
||||||
|
|
||||||
/// 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.
|
||||||
|
@ -1626,12 +1656,7 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
|
||||||
.copied()
|
.copied()
|
||||||
.map(|arm| {
|
.map(|arm| {
|
||||||
debug!(?arm);
|
debug!(?arm);
|
||||||
// We warn when a pattern is not useful.
|
let usefulness = collect_pattern_usefulness(arm.pat);
|
||||||
let usefulness = if arm.pat.is_useful() {
|
|
||||||
Usefulness::Useful(arm.pat.redundant_subpatterns())
|
|
||||||
} else {
|
|
||||||
Usefulness::Redundant
|
|
||||||
};
|
|
||||||
(arm, usefulness)
|
(arm, usefulness)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue