mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
Merge #3938
3938: fix missing match arm false positive r=flodiebold a=JoshMcguigan This fixes #3932 by skipping the missing match arm diagnostic in the case any of the match arms don't type check properly against the match expression. I think this is the appropriate behavior for this diagnostic, since `is_useful` relies on all match arms being well formed, and the case of a malformed match arm should probably be handled by a different diagnostic. Co-authored-by: Josh Mcguigan <joshmcg88@gmail.com>
This commit is contained in:
commit
268b798729
2 changed files with 253 additions and 12 deletions
|
@ -972,6 +972,47 @@ mod tests {
|
||||||
check_no_diagnostic(content);
|
check_no_diagnostic(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
|
||||||
|
let content = r"
|
||||||
|
fn test_fn() {
|
||||||
|
match (false, true, false) {
|
||||||
|
(false, ..) => {},
|
||||||
|
(true, ..) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
|
||||||
|
let content = r"
|
||||||
|
fn test_fn() {
|
||||||
|
match (false, true, false) {
|
||||||
|
(.., false) => {},
|
||||||
|
(.., true) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tuple_of_bools_with_ellipsis_no_diagnostic() {
|
||||||
|
let content = r"
|
||||||
|
fn test_fn() {
|
||||||
|
match (false, true, false) {
|
||||||
|
(..) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tuple_of_tuple_and_bools_no_arms() {
|
fn tuple_of_tuple_and_bools_no_arms() {
|
||||||
let content = r"
|
let content = r"
|
||||||
|
@ -1315,8 +1356,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
// Match arms with the incorrect type are filtered out.
|
// Match statements with arms that don't match the
|
||||||
check_diagnostic(content);
|
// expression pattern do not fire this diagnostic.
|
||||||
|
check_no_diagnostic(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1330,8 +1372,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
// Match arms with the incorrect type are filtered out.
|
// Match statements with arms that don't match the
|
||||||
check_diagnostic(content);
|
// expression pattern do not fire this diagnostic.
|
||||||
|
check_no_diagnostic(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1344,8 +1387,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
";
|
";
|
||||||
|
|
||||||
// Match arms with the incorrect type are filtered out.
|
// Match statements with arms that don't match the
|
||||||
check_diagnostic(content);
|
// expression pattern do not fire this diagnostic.
|
||||||
|
check_no_diagnostic(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1383,6 +1427,102 @@ mod tests {
|
||||||
// we don't create a diagnostic).
|
// we don't create a diagnostic).
|
||||||
check_no_diagnostic(content);
|
check_no_diagnostic(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expr_diverges() {
|
||||||
|
let content = r"
|
||||||
|
enum Either {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
fn test_fn() {
|
||||||
|
match loop {} {
|
||||||
|
Either::A => (),
|
||||||
|
Either::B => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expr_loop_with_break() {
|
||||||
|
let content = r"
|
||||||
|
enum Either {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
fn test_fn() {
|
||||||
|
match loop { break Foo::A } {
|
||||||
|
Either::A => (),
|
||||||
|
Either::B => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expr_partially_diverges() {
|
||||||
|
let content = r"
|
||||||
|
enum Either<T> {
|
||||||
|
A(T),
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
fn foo() -> Either<!> {
|
||||||
|
Either::B
|
||||||
|
}
|
||||||
|
fn test_fn() -> u32 {
|
||||||
|
match foo() {
|
||||||
|
Either::A(val) => val,
|
||||||
|
Either::B => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enum_tuple_partial_ellipsis_no_diagnostic() {
|
||||||
|
let content = r"
|
||||||
|
enum Either {
|
||||||
|
A(bool, bool, bool, bool),
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
fn test_fn() {
|
||||||
|
match Either::B {
|
||||||
|
Either::A(true, .., true) => {},
|
||||||
|
Either::A(true, .., false) => {},
|
||||||
|
Either::A(false, .., true) => {},
|
||||||
|
Either::A(false, .., false) => {},
|
||||||
|
Either::B => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enum_tuple_ellipsis_no_diagnostic() {
|
||||||
|
let content = r"
|
||||||
|
enum Either {
|
||||||
|
A(bool, bool, bool, bool),
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
fn test_fn() {
|
||||||
|
match Either::B {
|
||||||
|
Either::A(..) => {},
|
||||||
|
Either::B => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1452,4 +1592,104 @@ mod false_negatives {
|
||||||
// We do not currently handle patterns with internal `or`s.
|
// We do not currently handle patterns with internal `or`s.
|
||||||
check_no_diagnostic(content);
|
check_no_diagnostic(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expr_diverges_missing_arm() {
|
||||||
|
let content = r"
|
||||||
|
enum Either {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
fn test_fn() {
|
||||||
|
match loop {} {
|
||||||
|
Either::A => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// This is a false negative.
|
||||||
|
// Even though the match expression diverges, rustc fails
|
||||||
|
// to compile here since `Either::B` is missing.
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expr_loop_missing_arm() {
|
||||||
|
let content = r"
|
||||||
|
enum Either {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
fn test_fn() {
|
||||||
|
match loop { break Foo::A } {
|
||||||
|
Either::A => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// This is a false negative.
|
||||||
|
// We currently infer the type of `loop { break Foo::A }` to `!`, which
|
||||||
|
// causes us to skip the diagnostic since `Either::A` doesn't type check
|
||||||
|
// with `!`.
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
|
||||||
|
let content = r"
|
||||||
|
fn test_fn() {
|
||||||
|
match (false, true, false) {
|
||||||
|
(false, ..) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// This is a false negative.
|
||||||
|
// The `..` pattern is currently lowered to a single `Pat::Wild`
|
||||||
|
// no matter how many fields the `..` pattern is covering. This
|
||||||
|
// causes the match arm in this test not to type check against
|
||||||
|
// the match expression, which causes this diagnostic not to
|
||||||
|
// fire.
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
|
||||||
|
let content = r"
|
||||||
|
fn test_fn() {
|
||||||
|
match (false, true, false) {
|
||||||
|
(.., false) => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// This is a false negative.
|
||||||
|
// See comments on `tuple_of_bools_with_ellipsis_at_end_missing_arm`.
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn enum_tuple_partial_ellipsis_missing_arm() {
|
||||||
|
let content = r"
|
||||||
|
enum Either {
|
||||||
|
A(bool, bool, bool, bool),
|
||||||
|
B,
|
||||||
|
}
|
||||||
|
fn test_fn() {
|
||||||
|
match Either::B {
|
||||||
|
Either::A(true, .., true) => {},
|
||||||
|
Either::A(true, .., false) => {},
|
||||||
|
Either::A(false, .., false) => {},
|
||||||
|
Either::B => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
|
||||||
|
// This is a false negative.
|
||||||
|
// The `..` pattern is currently lowered to a single `Pat::Wild`
|
||||||
|
// no matter how many fields the `..` pattern is covering. This
|
||||||
|
// causes us to return a `MatchCheckErr::MalformedMatchArm` in
|
||||||
|
// `Pat::specialize_constructor`.
|
||||||
|
check_no_diagnostic(content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,12 +161,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
|
|
||||||
let mut seen = Matrix::empty();
|
let mut seen = Matrix::empty();
|
||||||
for pat in pats {
|
for pat in pats {
|
||||||
// We skip any patterns whose type we cannot resolve.
|
|
||||||
//
|
|
||||||
// This could lead to false positives in this diagnostic, so
|
|
||||||
// it might be better to skip the entire diagnostic if we either
|
|
||||||
// cannot resolve a match arm or determine that the match arm has
|
|
||||||
// the wrong type.
|
|
||||||
if let Some(pat_ty) = infer.type_of_pat.get(pat) {
|
if let Some(pat_ty) = infer.type_of_pat.get(pat) {
|
||||||
// We only include patterns whose type matches the type
|
// We only include patterns whose type matches the type
|
||||||
// of the match expression. If we had a InvalidMatchArmPattern
|
// of the match expression. If we had a InvalidMatchArmPattern
|
||||||
|
@ -189,8 +183,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
|
||||||
// to the matrix here.
|
// to the matrix here.
|
||||||
let v = PatStack::from_pattern(pat);
|
let v = PatStack::from_pattern(pat);
|
||||||
seen.push(&cx, v);
|
seen.push(&cx, v);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we can't resolve the type of a pattern, or the pattern type doesn't
|
||||||
|
// fit the match expression, we skip this diagnostic. Skipping the entire
|
||||||
|
// diagnostic rather than just not including this match arm is preferred
|
||||||
|
// to avoid the chance of false positives.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match is_useful(&cx, &seen, &PatStack::from_wild()) {
|
match is_useful(&cx, &seen, &PatStack::from_wild()) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue