Skip to content

Commit

Permalink
Lint for redundant branches in or-patterns
Browse files Browse the repository at this point in the history
  • Loading branch information
Nadrieril committed Dec 2, 2019
1 parent 00ccadf commit 5c7bd52
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 19 deletions.
40 changes: 29 additions & 11 deletions src/librustc_mir/hair/pattern/_match.rs
Expand Up @@ -455,6 +455,7 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> {
}

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

impl<'p, 'tcx> Matrix<'p, 'tcx> {
Expand Down Expand Up @@ -1025,17 +1026,19 @@ impl<'tcx> Constructor<'tcx> {
}

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

impl<'tcx> Usefulness<'tcx> {
impl<'tcx, 'p> Usefulness<'tcx, 'p> {
fn new_useful(preference: WitnessPreference) -> Self {
match preference {
ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
LeaveOutWitness => Useful,
LeaveOutWitness => Useful(vec![]),
}
}

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

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

// If the first pattern is an or-pattern, expand it.
if let Some(vs) = v.expand_or_pat() {
return vs
.into_iter()
.map(|v| is_useful(cx, matrix, &v, witness_preference, hir_id))
.find(|result| result.is_useful())
.unwrap_or(NotUseful);
// We need to push the already-seen patterns into the matrix in order to detect redundant
// branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns.
let mut matrix = matrix.clone();
let mut unreachable_pats = Vec::new();
let mut any_is_useful = false;
for v in vs {
let res = is_useful(cx, &matrix, &v, witness_preference, hir_id);
match res {
Useful(pats) => {
any_is_useful = true;
unreachable_pats.extend(pats);
}
NotUseful => unreachable_pats.push(v.head()),
UsefulWithWitness(_) => {
bug!("Encountered or-pat in `v` during exhaustiveness checking")
}
}
matrix.push(v);
}
return if any_is_useful { Useful(unreachable_pats) } else { NotUseful };
}

let (ty, span) = matrix
Expand Down Expand Up @@ -1768,7 +1786,7 @@ fn is_useful_specialized<'p, 'tcx>(
lty: Ty<'tcx>,
witness_preference: WitnessPreference,
hir_id: HirId,
) -> Usefulness<'tcx> {
) -> Usefulness<'tcx, 'p> {
debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty);

let ctor_wild_subpatterns =
Expand Down
13 changes: 11 additions & 2 deletions src/librustc_mir/hair/pattern/check_match.rs
Expand Up @@ -468,7 +468,16 @@ fn check_arms<'p, 'tcx>(
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
}
}
Useful => (),
Useful(unreachable_subpatterns) => {
for pat in unreachable_subpatterns {
cx.tcx.lint_hir(
lint::builtin::UNREACHABLE_PATTERNS,
hir_pat.hir_id,
pat.span,
"unreachable pattern",
);
}
}
UsefulWithWitness(_) => bug!(),
}
if guard.is_none() {
Expand Down Expand Up @@ -496,7 +505,7 @@ fn check_not_useful<'p, 'tcx>(
} else {
pats.into_iter().map(|w| w.single_pattern()).collect()
}),
Useful => bug!(),
Useful(_) => bug!(),
}
}

Expand Down
15 changes: 10 additions & 5 deletions src/test/ui/or-patterns/exhaustiveness-pass.rs
Expand Up @@ -43,26 +43,31 @@ fn main() {
_ => {}
}

// FIXME(or_patterns): Redundancies not detected for now.
match (0,) {
(1 | 1,) => {}
(1
| 1,) => {} //~ ERROR unreachable
_ => {}
}
match [0; 2] {
[0 | 0, 0 | 0] => {}
[0
| 0 //~ ERROR unreachable
, 0
| 0] => {} //~ ERROR unreachable
_ => {}
}
match &[][..] {
[0] => {}
[0, _] => {}
[0, _, _] => {}
[1, ..] => {}
[1 | 2, ..] => {}
[1 //~ ERROR unreachable
| 2, ..] => {}
_ => {}
}
match Some(0) {
Some(0) => {}
Some(0 | 1) => {}
Some(0 //~ ERROR unreachable
| 1) => {}
_ => {}
}
}
38 changes: 37 additions & 1 deletion src/test/ui/or-patterns/exhaustiveness-pass.stderr
@@ -1,8 +1,44 @@
error: unreachable pattern
--> $DIR/exhaustiveness-pass.rs:48:12
|
LL | | 1,) => {}
| ^
|
note: lint level defined here
--> $DIR/exhaustiveness-pass.rs:4:9
|
LL | #![deny(unreachable_patterns)]
| ^^^^^^^^^^^^^^^^^^^^

error: unreachable pattern
--> $DIR/exhaustiveness-pass.rs:55:15
|
LL | | 0] => {}
| ^

error: unreachable pattern
--> $DIR/exhaustiveness-pass.rs:53:15
|
LL | | 0
| ^

error: unreachable pattern
--> $DIR/exhaustiveness-pass.rs:63:10
|
LL | [1
| ^

error: unreachable pattern
--> $DIR/exhaustiveness-pass.rs:69:14
|
LL | Some(0
| ^

error: or-patterns are not fully implemented yet
--> $DIR/exhaustiveness-pass.rs:10:10
|
LL | (0 | _,) => {}
| ^^^^^

error: aborting due to previous error
error: aborting due to 6 previous errors

0 comments on commit 5c7bd52

Please sign in to comment.