Skip to content

Commit 5c7bd52

Browse files
committed
Lint for redundant branches in or-patterns
1 parent 00ccadf commit 5c7bd52

File tree

4 files changed

+87
-19
lines changed

4 files changed

+87
-19
lines changed

src/librustc_mir/hair/pattern/_match.rs

+29-11
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
455455
}
456456

457457
/// A 2D matrix.
458+
#[derive(Clone)]
458459
pub struct Matrix<'p, 'tcx>(Vec<PatStack<'p, 'tcx>>);
459460

460461
impl<'p, 'tcx> Matrix<'p, 'tcx> {
@@ -1025,17 +1026,19 @@ impl<'tcx> Constructor<'tcx> {
10251026
}
10261027

10271028
#[derive(Clone, Debug)]
1028-
pub enum Usefulness<'tcx> {
1029-
Useful,
1029+
pub enum Usefulness<'tcx, 'p> {
1030+
/// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns.
1031+
Useful(Vec<&'p Pat<'tcx>>),
1032+
/// Carries a list of witnesses of non-exhaustiveness.
10301033
UsefulWithWitness(Vec<Witness<'tcx>>),
10311034
NotUseful,
10321035
}
10331036

1034-
impl<'tcx> Usefulness<'tcx> {
1037+
impl<'tcx, 'p> Usefulness<'tcx, 'p> {
10351038
fn new_useful(preference: WitnessPreference) -> Self {
10361039
match preference {
10371040
ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
1038-
LeaveOutWitness => Useful,
1041+
LeaveOutWitness => Useful(vec![]),
10391042
}
10401043
}
10411044

@@ -1602,7 +1605,7 @@ pub fn is_useful<'p, 'tcx>(
16021605
v: &PatStack<'p, 'tcx>,
16031606
witness_preference: WitnessPreference,
16041607
hir_id: HirId,
1605-
) -> Usefulness<'tcx> {
1608+
) -> Usefulness<'tcx, 'p> {
16061609
let &Matrix(ref rows) = matrix;
16071610
debug!("is_useful({:#?}, {:#?})", matrix, v);
16081611

@@ -1623,11 +1626,26 @@ pub fn is_useful<'p, 'tcx>(
16231626

16241627
// If the first pattern is an or-pattern, expand it.
16251628
if let Some(vs) = v.expand_or_pat() {
1626-
return vs
1627-
.into_iter()
1628-
.map(|v| is_useful(cx, matrix, &v, witness_preference, hir_id))
1629-
.find(|result| result.is_useful())
1630-
.unwrap_or(NotUseful);
1629+
// We need to push the already-seen patterns into the matrix in order to detect redundant
1630+
// branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns.
1631+
let mut matrix = matrix.clone();
1632+
let mut unreachable_pats = Vec::new();
1633+
let mut any_is_useful = false;
1634+
for v in vs {
1635+
let res = is_useful(cx, &matrix, &v, witness_preference, hir_id);
1636+
match res {
1637+
Useful(pats) => {
1638+
any_is_useful = true;
1639+
unreachable_pats.extend(pats);
1640+
}
1641+
NotUseful => unreachable_pats.push(v.head()),
1642+
UsefulWithWitness(_) => {
1643+
bug!("Encountered or-pat in `v` during exhaustiveness checking")
1644+
}
1645+
}
1646+
matrix.push(v);
1647+
}
1648+
return if any_is_useful { Useful(unreachable_pats) } else { NotUseful };
16311649
}
16321650

16331651
let (ty, span) = matrix
@@ -1768,7 +1786,7 @@ fn is_useful_specialized<'p, 'tcx>(
17681786
lty: Ty<'tcx>,
17691787
witness_preference: WitnessPreference,
17701788
hir_id: HirId,
1771-
) -> Usefulness<'tcx> {
1789+
) -> Usefulness<'tcx, 'p> {
17721790
debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty);
17731791

17741792
let ctor_wild_subpatterns =

src/librustc_mir/hair/pattern/check_match.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,16 @@ fn check_arms<'p, 'tcx>(
468468
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
469469
}
470470
}
471-
Useful => (),
471+
Useful(unreachable_subpatterns) => {
472+
for pat in unreachable_subpatterns {
473+
cx.tcx.lint_hir(
474+
lint::builtin::UNREACHABLE_PATTERNS,
475+
hir_pat.hir_id,
476+
pat.span,
477+
"unreachable pattern",
478+
);
479+
}
480+
}
472481
UsefulWithWitness(_) => bug!(),
473482
}
474483
if guard.is_none() {
@@ -496,7 +505,7 @@ fn check_not_useful<'p, 'tcx>(
496505
} else {
497506
pats.into_iter().map(|w| w.single_pattern()).collect()
498507
}),
499-
Useful => bug!(),
508+
Useful(_) => bug!(),
500509
}
501510
}
502511

src/test/ui/or-patterns/exhaustiveness-pass.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -43,26 +43,31 @@ fn main() {
4343
_ => {}
4444
}
4545

46-
// FIXME(or_patterns): Redundancies not detected for now.
4746
match (0,) {
48-
(1 | 1,) => {}
47+
(1
48+
| 1,) => {} //~ ERROR unreachable
4949
_ => {}
5050
}
5151
match [0; 2] {
52-
[0 | 0, 0 | 0] => {}
52+
[0
53+
| 0 //~ ERROR unreachable
54+
, 0
55+
| 0] => {} //~ ERROR unreachable
5356
_ => {}
5457
}
5558
match &[][..] {
5659
[0] => {}
5760
[0, _] => {}
5861
[0, _, _] => {}
5962
[1, ..] => {}
60-
[1 | 2, ..] => {}
63+
[1 //~ ERROR unreachable
64+
| 2, ..] => {}
6165
_ => {}
6266
}
6367
match Some(0) {
6468
Some(0) => {}
65-
Some(0 | 1) => {}
69+
Some(0 //~ ERROR unreachable
70+
| 1) => {}
6671
_ => {}
6772
}
6873
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,44 @@
1+
error: unreachable pattern
2+
--> $DIR/exhaustiveness-pass.rs:48:12
3+
|
4+
LL | | 1,) => {}
5+
| ^
6+
|
7+
note: lint level defined here
8+
--> $DIR/exhaustiveness-pass.rs:4:9
9+
|
10+
LL | #![deny(unreachable_patterns)]
11+
| ^^^^^^^^^^^^^^^^^^^^
12+
13+
error: unreachable pattern
14+
--> $DIR/exhaustiveness-pass.rs:55:15
15+
|
16+
LL | | 0] => {}
17+
| ^
18+
19+
error: unreachable pattern
20+
--> $DIR/exhaustiveness-pass.rs:53:15
21+
|
22+
LL | | 0
23+
| ^
24+
25+
error: unreachable pattern
26+
--> $DIR/exhaustiveness-pass.rs:63:10
27+
|
28+
LL | [1
29+
| ^
30+
31+
error: unreachable pattern
32+
--> $DIR/exhaustiveness-pass.rs:69:14
33+
|
34+
LL | Some(0
35+
| ^
36+
137
error: or-patterns are not fully implemented yet
238
--> $DIR/exhaustiveness-pass.rs:10:10
339
|
440
LL | (0 | _,) => {}
541
| ^^^^^
642

7-
error: aborting due to previous error
43+
error: aborting due to 6 previous errors
844

0 commit comments

Comments
 (0)