plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
f9467e7
— Peter John
2026-10-25T16:02:16+05:30
fix(checker): refutable payload patterns don't cover a tag in exhaustiveness
- plum-checker/src/lib.rs +12 -2
- plum-checker/tests/checker_tests.rs +54 -0
plum-checker/src/lib.rs
CHANGED
|
@@ -1262,8 +1262,18 @@ fn checkMatch(
|
|
|
1262
1262
|
catch_all = true; // a binding
|
|
1263
1263
|
}
|
|
1264
1264
|
}
|
|
1265
|
-
Some(ast::CasePattern::Class { name,
|
|
1265
|
+
Some(ast::CasePattern::Class { name, fields }) => {
|
|
1266
|
+
// `B(0)` is refutable and does not cover `B`.
|
|
1267
|
+
let irrefutable = |p: &ast::CasePattern| match p {
|
|
1268
|
+
ast::CasePattern::Wildcard => true,
|
|
1269
|
+
ast::CasePattern::Name(n) => {
|
|
1270
|
+
!n.chars().next().is_some_and(|c| c.is_uppercase())
|
|
1271
|
+
}
|
|
1272
|
+
_ => false,
|
|
1273
|
+
};
|
|
1274
|
+
if fields.iter().all(irrefutable) {
|
|
1266
|
-
|
|
1275
|
+
covered.insert(name.as_str());
|
|
1276
|
+
}
|
|
1267
1277
|
}
|
|
1268
1278
|
_ => {}
|
|
1269
1279
|
}
|
plum-checker/tests/checker_tests.rs
CHANGED
|
@@ -1626,3 +1626,57 @@ fun f() -> Try[Int, [Mapped]] =
|
|
|
1626
1626
|
);
|
|
1627
1627
|
assert!(r.is_ok(), "got {:?}", r.err());
|
|
1628
1628
|
}
|
|
1629
|
+
|
|
1630
|
+
#[test]
|
|
1631
|
+
fn refutable_payload_pattern_does_not_cover_tag() {
|
|
1632
|
+
let r = errText(checkTry(
|
|
1633
|
+
"\
|
|
1634
|
+
fun f(t: [A, B(Int)]) -> Int =
|
|
1635
|
+
match t
|
|
1636
|
+
A => 1
|
|
1637
|
+
B(0) => 2
|
|
1638
|
+
",
|
|
1639
|
+
));
|
|
1640
|
+
assert!(r.contains("non-exhaustive") && r.contains("B"), "got: {}", r);
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
#[test]
|
|
1644
|
+
fn question_mark_wider_inner_than_outer_with_payload() {
|
|
1645
|
+
let r = errText(checkTry(
|
|
1646
|
+
"\
|
|
1647
|
+
fun g() -> Try[Int, [A, B(Str)]] =
|
|
1648
|
+
Err(A)
|
|
1649
|
+
|
|
1650
|
+
fun f() -> Try[Int, [A, B(Int)]] =
|
|
1651
|
+
x := g()?
|
|
1652
|
+
Ok(x)
|
|
1653
|
+
",
|
|
1654
|
+
));
|
|
1655
|
+
assert!(r.contains("not part of"), "got: {}", r);
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
#[test]
|
|
1659
|
+
fn tag_literal_does_not_shadow_declared_enum_variant() {
|
|
1660
|
+
let r = checkTry(
|
|
1661
|
+
"\
|
|
1662
|
+
enum Color =
|
|
1663
|
+
| Red
|
|
1664
|
+
| Blue
|
|
1665
|
+
|
|
1666
|
+
fun f() -> Color =
|
|
1667
|
+
Red
|
|
1668
|
+
",
|
|
1669
|
+
);
|
|
1670
|
+
assert!(r.is_ok(), "got {:?}", r.err());
|
|
1671
|
+
let bad = checkTry(
|
|
1672
|
+
"\
|
|
1673
|
+
enum Color =
|
|
1674
|
+
| Red
|
|
1675
|
+
| Blue
|
|
1676
|
+
|
|
1677
|
+
fun f() -> Try[Int, [Red]] =
|
|
1678
|
+
Err(Red)
|
|
1679
|
+
",
|
|
1680
|
+
);
|
|
1681
|
+
assert!(bad.is_err());
|
|
1682
|
+
}
|