Skip to content

Commit 4d6d7ae

Browse files
Avoid all unchecked indexing in match checking
1 parent 1ce8c2b commit 4d6d7ae

File tree

1 file changed

+49
-28
lines changed

1 file changed

+49
-28
lines changed

crates/ra_hir_ty/src/_match.rs

Lines changed: 49 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -312,20 +312,16 @@ impl PatStack {
312312
Self(v)
313313
}
314314

315-
fn is_empty(&self) -> bool {
316-
self.0.is_empty()
317-
}
318-
319-
fn head(&self) -> PatIdOrWild {
320-
self.0[0]
321-
}
322-
323315
fn get_head(&self) -> Option<PatIdOrWild> {
324316
self.0.first().copied()
325317
}
326318

319+
fn tail(&self) -> &[PatIdOrWild] {
320+
self.0.get(1..).unwrap_or(&[])
321+
}
322+
327323
fn to_tail(&self) -> PatStack {
328-
Self::from_slice(&self.0[1..])
324+
Self::from_slice(self.tail())
329325
}
330326

331327
fn replace_head_with<I, T>(&self, pats: I) -> PatStack
@@ -347,7 +343,7 @@ impl PatStack {
347343
///
348344
/// See the module docs and the associated documentation in rustc for details.
349345
fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Option<PatStack> {
350-
if matches!(self.head().as_pat(cx), Pat::Wild) {
346+
if matches!(self.get_head()?.as_pat(cx), Pat::Wild) {
351347
Some(self.to_tail())
352348
} else {
353349
None
@@ -362,11 +358,12 @@ impl PatStack {
362358
cx: &MatchCheckCtx,
363359
constructor: &Constructor,
364360
) -> MatchCheckResult<Option<PatStack>> {
365-
if self.is_empty() {
366-
return Ok(None);
367-
}
361+
let head = match self.get_head() {
362+
Some(head) => head,
363+
None => return Ok(None),
364+
};
368365

369-
let head_pat = self.head().as_pat(cx);
366+
let head_pat = head.as_pat(cx);
370367
let result = match (head_pat, constructor) {
371368
(Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
372369
if ellipsis.is_some() {
@@ -394,7 +391,7 @@ impl PatStack {
394391
(Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?),
395392
(Pat::Path(_), Constructor::Enum(constructor)) => {
396393
// unit enum variants become `Pat::Path`
397-
let pat_id = self.head().as_id().expect("we know this isn't a wild");
394+
let pat_id = head.as_id().expect("we know this isn't a wild");
398395
if !enum_variant_matches(cx, pat_id, *constructor) {
399396
None
400397
} else {
@@ -405,7 +402,7 @@ impl PatStack {
405402
Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
406403
Constructor::Enum(enum_constructor),
407404
) => {
408-
let pat_id = self.head().as_id().expect("we know this isn't a wild");
405+
let pat_id = head.as_id().expect("we know this isn't a wild");
409406
if !enum_variant_matches(cx, pat_id, *enum_constructor) {
410407
None
411408
} else {
@@ -445,7 +442,7 @@ impl PatStack {
445442
}
446443
}
447444
(Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => {
448-
let pat_id = self.head().as_id().expect("we know this isn't a wild");
445+
let pat_id = head.as_id().expect("we know this isn't a wild");
449446
if !enum_variant_matches(cx, pat_id, *e) {
450447
None
451448
} else {
@@ -491,7 +488,7 @@ impl PatStack {
491488
) -> MatchCheckResult<PatStack> {
492489
assert_eq!(
493490
Pat::Wild,
494-
self.head().as_pat(cx),
491+
self.get_head().expect("expand_wildcard called on empty PatStack").as_pat(cx),
495492
"expand_wildcard must only be called on PatStack with wild at head",
496493
);
497494

@@ -509,7 +506,6 @@ impl PatStack {
509506
}
510507
}
511508

512-
#[derive(Debug)]
513509
/// A collection of PatStack.
514510
///
515511
/// This type is modeled from the struct of the same name in `rustc`.
@@ -623,13 +619,16 @@ pub(crate) fn is_useful(
623619
_ => (),
624620
}
625621

626-
if v.is_empty() {
627-
let result = if matrix.is_empty() { Usefulness::Useful } else { Usefulness::NotUseful };
622+
let head = match v.get_head() {
623+
Some(head) => head,
624+
None => {
625+
let result = if matrix.is_empty() { Usefulness::Useful } else { Usefulness::NotUseful };
628626

629-
return Ok(result);
630-
}
627+
return Ok(result);
628+
}
629+
};
631630

632-
if let Pat::Or(pat_ids) = v.head().as_pat(cx) {
631+
if let Pat::Or(pat_ids) = head.as_pat(cx) {
633632
let mut found_unimplemented = false;
634633
let any_useful = pat_ids.iter().any(|&pat_id| {
635634
let v = PatStack::from_pattern(pat_id);
@@ -653,7 +652,7 @@ pub(crate) fn is_useful(
653652
};
654653
}
655654

656-
if let Some(constructor) = pat_constructor(cx, v.head())? {
655+
if let Some(constructor) = pat_constructor(cx, head)? {
657656
let matrix = matrix.specialize_constructor(&cx, &constructor)?;
658657
let v = v
659658
.specialize_constructor(&cx, &constructor)?
@@ -854,10 +853,10 @@ mod tests {
854853
}
855854

856855
pub(super) fn check_no_diagnostic(ra_fixture: &str) {
857-
let diagnostic_count =
858-
TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
856+
let (s, diagnostic_count) =
857+
TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>();
859858

860-
assert_eq!(0, diagnostic_count, "expected no diagnostic, found one");
859+
assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s);
861860
}
862861

863862
#[test]
@@ -2014,6 +2013,28 @@ mod tests {
20142013
",
20152014
);
20162015
}
2016+
2017+
#[test]
2018+
fn or_pattern_panic_2() {
2019+
// FIXME: This is a false positive, but the code used to cause a panic in the match checker,
2020+
// so this acts as a regression test for that.
2021+
check_diagnostic(
2022+
r"
2023+
pub enum Category {
2024+
Infinity,
2025+
Zero,
2026+
}
2027+
2028+
fn panic(a: Category, b: Category) {
2029+
match (a, b) {
2030+
(Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => {}
2031+
2032+
(Category::Infinity | Category::Zero, _) => {}
2033+
}
2034+
}
2035+
",
2036+
);
2037+
}
20172038
}
20182039

20192040
#[cfg(test)]

0 commit comments

Comments
 (0)