Fix subtle bug in pattern rendering

When rendering missing or redundant patterns, linked-list would
  wrongly suggest the last nil constructor as a pattern on non-empty
  list.

  For example, before this commit, the exhaustivness checker would yield:

  ```
  [(_, True), []]
  ```

  as a suggestion, for being the result of being a list pattern with a
  single argument being `(_, True) :: Nil`. Blindly following the
  compiler suggestion here would cause a type unification error (since
  `[]` doesn't unify with a 2-tuple).

  Indeed, we mustn't render the Nil constructor when rendering non-empty
  lists! So the correct suggestion should be:

  ```
  [(_, True)]
  ```
This commit is contained in:
KtorZ 2023-08-02 10:31:35 +02:00
parent 00b255e960
commit 4f7f39292d
No known key found for this signature in database
GPG Key ID: 33173CB6F77F4277
2 changed files with 123 additions and 4 deletions

View File

@ -493,10 +493,124 @@ fn exhaustiveness_complex() {
unmatched, unmatched,
.. ..
} }
)) if unmatched[0] == "((Yes, _), (Yes, [_, ..]))" && unmatched[1] == "((No { idk, thing }, _), (Yes, _))" )) if unmatched[0] == "((Yes, _), (Yes, [_, ..]))" && unmatched[1] == "((No { idk, thing }, _), (Yes, _))"
)) ))
} }
#[test]
fn exhaustiveness_tuple() {
let source_code = r#"
fn foo() {
when (14, True) is {
(14, True) -> Void
}
}
"#;
assert!(matches!(
check(parse(source_code)),
Err((
_,
Error::NotExhaustivePatternMatch {
unmatched,
..
}
)) if unmatched[0] == "(_, _)"
))
}
#[test]
fn exhaustiveness_nested_list_and_tuples() {
fn assert_step(step: &str, expected: &str) {
let result = check(parse(step));
println!("{result:#?}");
assert!(matches!(
result,
Err((
_,
Error::NotExhaustivePatternMatch {
unmatched,
..
}
)) if unmatched[0] == expected
));
}
assert_step(
r#"
fn foo() {
let xs : List<(List<(Int, Bool)>, Int)> = [([(14, True)], 42)]
when xs is {
[ ] -> Void
[([(14, True)], 42), ..] -> Void
}
}
"#,
"[([], _), ..]",
);
assert_step(
r#"
fn foo() {
let xs : List<(List<(Int, Bool)>, Int)> = [([(14, True)], 42)]
when xs is {
[ ] -> Void
[([(_, True)], 42), ..] -> Void
[([ ], _), ..] -> Void
}
}
"#,
"[([(_, False), ..], _), ..]",
);
assert_step(
r#"
fn foo() {
let xs : List<(List<(Int, Bool)>, Int)> = [([(14, True)], 42)]
when xs is {
[ ] -> Void
[([(_, True ) ], 42), ..] -> Void
[([ ], _), ..] -> Void
[([(_, False), ..], _), ..] -> Void
}
}
"#,
"[([(_, True), _, ..], _), ..]",
);
assert_step(
r#"
fn foo() {
let xs : List<(List<(Int, Bool)>, Int)> = [([(14, True)], 42)]
when xs is {
[ ] -> Void
[([(_, True ) ], 42), ..] -> Void
[([ ], _), ..] -> Void
[([(_, False) , ..], _), ..] -> Void
[([(_, True ), _, ..], _), ..] -> Void
}
}
"#,
"[([(_, True)], _), ..]",
);
let source_code = r#"
fn foo() {
let xs : List<(List<(Int, Bool)>, Int)> = [([(14, True)], 42)]
when xs is {
[ ] -> Void
[([(_, True ) ], 42), ..] -> Void
[([ ], _), ..] -> Void
[([(_, False) , ..], _), ..] -> Void
[([(_, True ), _, ..], _), ..] -> Void
[([(_, True ) ], _), ..] -> Void
}
}
"#;
assert!(matches!(check(parse(source_code)), Ok(_)))
}
#[test] #[test]
fn expect_sugar_correct_type() { fn expect_sugar_correct_type() {
let source_code = r#" let source_code = r#"

View File

@ -405,11 +405,16 @@ impl Pattern {
let args = args let args = args
.into_iter() .into_iter()
.enumerate() .enumerate()
.map(|(index, p)| { .filter_map(|(index, p)| {
if index == 1 { if index == 1 {
pretty_tail(p) let tail = pretty_tail(p);
if tail == "[]" {
None
} else {
Some(tail)
}
} else { } else {
p.pretty() Some(p.pretty())
} }
}) })
.join(", "); .join(", ");