Skip to content

Commit cfd618d

Browse files
committed
Lint self-overlapping or-patterns under guard
1 parent 4fd3181 commit cfd618d

File tree

3 files changed

+43
-1
lines changed

3 files changed

+43
-1
lines changed

compiler/rustc_pattern_analysis/src/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,16 @@ pub trait PatCx: Sized + fmt::Debug {
129129
deref_pat: &DeconstructedPat<Self>,
130130
normal_pat: &DeconstructedPat<Self>,
131131
) -> Self::Error;
132+
133+
/// Lint that an or-pattern will cause a guard to be tried several times because there's a value
134+
/// that matches several of the or-alternatives.
135+
/// The default implementation does nothing.
136+
fn lint_overlapping_alternatives_under_guard(
137+
&self,
138+
_pat1: &DeconstructedPat<Self>,
139+
_pat2: &DeconstructedPat<Self>,
140+
) {
141+
}
132142
}
133143

134144
/// The arm of a match expression.

compiler/rustc_pattern_analysis/src/rustc.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,6 +1039,21 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
10391039
normal_constructor_label,
10401040
})
10411041
}
1042+
1043+
fn lint_overlapping_alternatives_under_guard(
1044+
&self,
1045+
pat1: &crate::pat::DeconstructedPat<Self>,
1046+
pat2: &crate::pat::DeconstructedPat<Self>,
1047+
) {
1048+
self.tcx
1049+
.dcx()
1050+
.struct_span_err(
1051+
pat1.data().span,
1052+
format!("pattern overlaps with or-alternative under a guard"),
1053+
)
1054+
.with_span_label(pat2.data().span, "overlaps with")
1055+
.emit();
1056+
}
10421057
}
10431058

10441059
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.

compiler/rustc_pattern_analysis/src/usefulness.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1106,14 +1106,20 @@ impl<'p, Cx: PatCx> fmt::Debug for PatStack<'p, Cx> {
11061106
/// A row of the matrix.
11071107
#[derive(Clone)]
11081108
struct MatrixRow<'p, Cx: PatCx> {
1109-
// The patterns in the row.
1109+
/// The patterns in the row.
11101110
pats: PatStack<'p, Cx>,
1111+
/// A (sub)pattern this row comes from. When expanding or-patterns, this tracks the last
1112+
/// alternative expanded, e.g. in `(0|1, 2|3)` we'd keep `3` for the last row. Used only for
1113+
/// diagnostics.
1114+
origin: &'p DeconstructedPat<Cx>,
11111115
/// Whether the original arm had a guard. This is inherited when specializing.
11121116
is_under_guard: bool,
11131117
/// When we specialize, we remember which row of the original matrix produced a given row of the
11141118
/// specialized matrix. When we unspecialize, we use this to propagate usefulness back up the
11151119
/// callstack. On creation, this stores the index of the original match arm.
11161120
parent_row: usize,
1121+
/// Remember the match arm this came from.
1122+
arm_id: usize,
11171123
/// False when the matrix is just built. This is set to `true` by
11181124
/// [`compute_exhaustiveness_and_usefulness`] if the arm is found to be useful.
11191125
/// This is reset to `false` when specializing.
@@ -1143,7 +1149,9 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
11431149
fn new(arm: &MatchArm<'p, Cx>, arm_id: usize) -> Self {
11441150
MatrixRow {
11451151
pats: PatStack::from_pattern(arm.pat),
1152+
origin: arm.pat,
11461153
parent_row: arm_id,
1154+
arm_id,
11471155
is_under_guard: arm.has_guard,
11481156
useful: false,
11491157
intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`.
@@ -1168,8 +1176,10 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
11681176
fn expand_or_pat(&self, parent_row: usize) -> impl Iterator<Item = MatrixRow<'p, Cx>> {
11691177
let is_or_pat = self.pats.head().is_or_pat();
11701178
self.pats.expand_or_pat().map(move |patstack| MatrixRow {
1179+
origin: if is_or_pat { patstack.head().as_pat().unwrap() } else { self.origin },
11711180
pats: patstack,
11721181
parent_row,
1182+
arm_id: self.arm_id,
11731183
is_under_guard: self.is_under_guard,
11741184
useful: false,
11751185
intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`.
@@ -1189,7 +1199,9 @@ impl<'p, Cx: PatCx> MatrixRow<'p, Cx> {
11891199
) -> Result<MatrixRow<'p, Cx>, Cx::Error> {
11901200
Ok(MatrixRow {
11911201
pats: self.pats.pop_head_constructor(cx, ctor, ctor_arity, ctor_is_relevant)?,
1202+
origin: self.origin,
11921203
parent_row,
1204+
arm_id: self.arm_id,
11931205
is_under_guard: self.is_under_guard,
11941206
useful: false,
11951207
intersects_at_least: DenseBitSet::new_empty(0), // Initialized in `Matrix::push`.
@@ -1725,6 +1737,11 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: PatCx>(
17251737
// The next rows stays useful if this one is under a guard.
17261738
useful &= row.is_under_guard;
17271739
}
1740+
for (row1, row2) in matrix.rows().zip(matrix.rows().skip(1)) {
1741+
if row1.arm_id == row2.arm_id && row1.is_under_guard {
1742+
mcx.tycx.lint_overlapping_alternatives_under_guard(row1.origin, row2.origin);
1743+
}
1744+
}
17281745
return if useful && matrix.wildcard_row_is_relevant {
17291746
// The wildcard row is useful; the match is non-exhaustive.
17301747
Ok(WitnessMatrix::unit_witness())

0 commit comments

Comments
 (0)