mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 21:05:02 +00:00
Merge #4930
4930: Avoid all unchecked indexing in match checking r=flodiebold a=jonas-schievink Fixes https://github.com/rust-analyzer/rust-analyzer/issues/4416, but replaces it with a false positive. r? @flodiebold Co-authored-by: Jonas Schievink <jonasschievink@gmail.com>
This commit is contained in:
commit
902a9c6da7
1 changed files with 49 additions and 28 deletions
|
@ -312,20 +312,16 @@ impl PatStack {
|
||||||
Self(v)
|
Self(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_empty(&self) -> bool {
|
|
||||||
self.0.is_empty()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn head(&self) -> PatIdOrWild {
|
|
||||||
self.0[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_head(&self) -> Option<PatIdOrWild> {
|
fn get_head(&self) -> Option<PatIdOrWild> {
|
||||||
self.0.first().copied()
|
self.0.first().copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tail(&self) -> &[PatIdOrWild] {
|
||||||
|
self.0.get(1..).unwrap_or(&[])
|
||||||
|
}
|
||||||
|
|
||||||
fn to_tail(&self) -> PatStack {
|
fn to_tail(&self) -> PatStack {
|
||||||
Self::from_slice(&self.0[1..])
|
Self::from_slice(self.tail())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_head_with<I, T>(&self, pats: I) -> PatStack
|
fn replace_head_with<I, T>(&self, pats: I) -> PatStack
|
||||||
|
@ -347,7 +343,7 @@ impl PatStack {
|
||||||
///
|
///
|
||||||
/// See the module docs and the associated documentation in rustc for details.
|
/// See the module docs and the associated documentation in rustc for details.
|
||||||
fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Option<PatStack> {
|
fn specialize_wildcard(&self, cx: &MatchCheckCtx) -> Option<PatStack> {
|
||||||
if matches!(self.head().as_pat(cx), Pat::Wild) {
|
if matches!(self.get_head()?.as_pat(cx), Pat::Wild) {
|
||||||
Some(self.to_tail())
|
Some(self.to_tail())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -362,11 +358,12 @@ impl PatStack {
|
||||||
cx: &MatchCheckCtx,
|
cx: &MatchCheckCtx,
|
||||||
constructor: &Constructor,
|
constructor: &Constructor,
|
||||||
) -> MatchCheckResult<Option<PatStack>> {
|
) -> MatchCheckResult<Option<PatStack>> {
|
||||||
if self.is_empty() {
|
let head = match self.get_head() {
|
||||||
return Ok(None);
|
Some(head) => head,
|
||||||
}
|
None => return Ok(None),
|
||||||
|
};
|
||||||
|
|
||||||
let head_pat = self.head().as_pat(cx);
|
let head_pat = head.as_pat(cx);
|
||||||
let result = match (head_pat, constructor) {
|
let result = match (head_pat, constructor) {
|
||||||
(Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
|
(Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
|
||||||
if ellipsis.is_some() {
|
if ellipsis.is_some() {
|
||||||
|
@ -394,7 +391,7 @@ impl PatStack {
|
||||||
(Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?),
|
(Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?),
|
||||||
(Pat::Path(_), Constructor::Enum(constructor)) => {
|
(Pat::Path(_), Constructor::Enum(constructor)) => {
|
||||||
// unit enum variants become `Pat::Path`
|
// unit enum variants become `Pat::Path`
|
||||||
let pat_id = self.head().as_id().expect("we know this isn't a wild");
|
let pat_id = head.as_id().expect("we know this isn't a wild");
|
||||||
if !enum_variant_matches(cx, pat_id, *constructor) {
|
if !enum_variant_matches(cx, pat_id, *constructor) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -405,7 +402,7 @@ impl PatStack {
|
||||||
Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
|
Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
|
||||||
Constructor::Enum(enum_constructor),
|
Constructor::Enum(enum_constructor),
|
||||||
) => {
|
) => {
|
||||||
let pat_id = self.head().as_id().expect("we know this isn't a wild");
|
let pat_id = head.as_id().expect("we know this isn't a wild");
|
||||||
if !enum_variant_matches(cx, pat_id, *enum_constructor) {
|
if !enum_variant_matches(cx, pat_id, *enum_constructor) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -445,7 +442,7 @@ impl PatStack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => {
|
(Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => {
|
||||||
let pat_id = self.head().as_id().expect("we know this isn't a wild");
|
let pat_id = head.as_id().expect("we know this isn't a wild");
|
||||||
if !enum_variant_matches(cx, pat_id, *e) {
|
if !enum_variant_matches(cx, pat_id, *e) {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -491,7 +488,7 @@ impl PatStack {
|
||||||
) -> MatchCheckResult<PatStack> {
|
) -> MatchCheckResult<PatStack> {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Pat::Wild,
|
Pat::Wild,
|
||||||
self.head().as_pat(cx),
|
self.get_head().expect("expand_wildcard called on empty PatStack").as_pat(cx),
|
||||||
"expand_wildcard must only be called on PatStack with wild at head",
|
"expand_wildcard must only be called on PatStack with wild at head",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -509,7 +506,6 @@ impl PatStack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
/// A collection of PatStack.
|
/// A collection of PatStack.
|
||||||
///
|
///
|
||||||
/// This type is modeled from the struct of the same name in `rustc`.
|
/// This type is modeled from the struct of the same name in `rustc`.
|
||||||
|
@ -623,13 +619,16 @@ pub(crate) fn is_useful(
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.is_empty() {
|
let head = match v.get_head() {
|
||||||
let result = if matrix.is_empty() { Usefulness::Useful } else { Usefulness::NotUseful };
|
Some(head) => head,
|
||||||
|
None => {
|
||||||
|
let result = if matrix.is_empty() { Usefulness::Useful } else { Usefulness::NotUseful };
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if let Pat::Or(pat_ids) = v.head().as_pat(cx) {
|
if let Pat::Or(pat_ids) = head.as_pat(cx) {
|
||||||
let mut found_unimplemented = false;
|
let mut found_unimplemented = false;
|
||||||
let any_useful = pat_ids.iter().any(|&pat_id| {
|
let any_useful = pat_ids.iter().any(|&pat_id| {
|
||||||
let v = PatStack::from_pattern(pat_id);
|
let v = PatStack::from_pattern(pat_id);
|
||||||
|
@ -653,7 +652,7 @@ pub(crate) fn is_useful(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(constructor) = pat_constructor(cx, v.head())? {
|
if let Some(constructor) = pat_constructor(cx, head)? {
|
||||||
let matrix = matrix.specialize_constructor(&cx, &constructor)?;
|
let matrix = matrix.specialize_constructor(&cx, &constructor)?;
|
||||||
let v = v
|
let v = v
|
||||||
.specialize_constructor(&cx, &constructor)?
|
.specialize_constructor(&cx, &constructor)?
|
||||||
|
@ -854,10 +853,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn check_no_diagnostic(ra_fixture: &str) {
|
pub(super) fn check_no_diagnostic(ra_fixture: &str) {
|
||||||
let diagnostic_count =
|
let (s, diagnostic_count) =
|
||||||
TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
|
TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>();
|
||||||
|
|
||||||
assert_eq!(0, diagnostic_count, "expected no diagnostic, found one");
|
assert_eq!(0, diagnostic_count, "expected no diagnostic, found one: {}", s);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2014,6 +2013,28 @@ mod tests {
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn or_pattern_panic_2() {
|
||||||
|
// FIXME: This is a false positive, but the code used to cause a panic in the match checker,
|
||||||
|
// so this acts as a regression test for that.
|
||||||
|
check_diagnostic(
|
||||||
|
r"
|
||||||
|
pub enum Category {
|
||||||
|
Infinity,
|
||||||
|
Zero,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn panic(a: Category, b: Category) {
|
||||||
|
match (a, b) {
|
||||||
|
(Category::Infinity, Category::Infinity) | (Category::Zero, Category::Zero) => {}
|
||||||
|
|
||||||
|
(Category::Infinity | Category::Zero, _) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue