aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_hir_ty
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_hir_ty')
-rw-r--r--crates/ra_hir_ty/src/_match.rs350
-rw-r--r--crates/ra_hir_ty/src/expr.rs13
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs6
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs3
4 files changed, 340 insertions, 32 deletions
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
index 9e9a9d047..a64be9848 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -289,7 +289,7 @@ impl PatStack {
289 Self::from_slice(&self.0[1..]) 289 Self::from_slice(&self.0[1..])
290 } 290 }
291 291
292 fn replace_head_with(&self, pat_ids: &[PatId]) -> PatStack { 292 fn replace_head_with<T: Into<PatIdOrWild> + Copy>(&self, pat_ids: &[T]) -> PatStack {
293 let mut patterns: PatStackInner = smallvec![]; 293 let mut patterns: PatStackInner = smallvec![];
294 for pat in pat_ids { 294 for pat in pat_ids {
295 patterns.push((*pat).into()); 295 patterns.push((*pat).into());
@@ -320,12 +320,14 @@ impl PatStack {
320 constructor: &Constructor, 320 constructor: &Constructor,
321 ) -> MatchCheckResult<Option<PatStack>> { 321 ) -> MatchCheckResult<Option<PatStack>> {
322 let result = match (self.head().as_pat(cx), constructor) { 322 let result = match (self.head().as_pat(cx), constructor) {
323 (Pat::Tuple(ref pat_ids), Constructor::Tuple { arity }) => { 323 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
324 debug_assert_eq!( 324 if ellipsis.is_some() {
325 pat_ids.len(), 325 // If there are ellipsis here, we should add the correct number of
326 *arity, 326 // Pat::Wild patterns to `pat_ids`. We should be able to use the
327 "we type check before calling this code, so we should never hit this case", 327 // constructors arity for this, but at the time of writing we aren't
328 ); 328 // correctly calculating this arity when ellipsis are present.
329 return Err(MatchCheckErr::NotImplemented);
330 }
329 331
330 Some(self.replace_head_with(pat_ids)) 332 Some(self.replace_head_with(pat_ids))
331 } 333 }
@@ -351,19 +353,47 @@ impl PatStack {
351 Some(self.to_tail()) 353 Some(self.to_tail())
352 } 354 }
353 } 355 }
354 (Pat::TupleStruct { args: ref pat_ids, .. }, Constructor::Enum(enum_constructor)) => { 356 (
357 Pat::TupleStruct { args: ref pat_ids, ellipsis, .. },
358 Constructor::Enum(enum_constructor),
359 ) => {
355 let pat_id = self.head().as_id().expect("we know this isn't a wild"); 360 let pat_id = self.head().as_id().expect("we know this isn't a wild");
356 if !enum_variant_matches(cx, pat_id, *enum_constructor) { 361 if !enum_variant_matches(cx, pat_id, *enum_constructor) {
357 None 362 None
358 } else { 363 } else {
359 // If the enum variant matches, then we need to confirm 364 let constructor_arity = constructor.arity(cx)?;
360 // that the number of patterns aligns with the expected 365 if let Some(ellipsis_position) = ellipsis {
361 // number of patterns for that enum variant. 366 // If there are ellipsis in the pattern, the ellipsis must take the place
362 if pat_ids.len() != constructor.arity(cx)? { 367 // of at least one sub-pattern, so `pat_ids` should be smaller than the
363 return Err(MatchCheckErr::MalformedMatchArm); 368 // constructor arity.
369 if pat_ids.len() < constructor_arity {
370 let mut new_patterns: Vec<PatIdOrWild> = vec![];
371
372 for pat_id in &pat_ids[0..ellipsis_position] {
373 new_patterns.push((*pat_id).into());
374 }
375
376 for _ in 0..(constructor_arity - pat_ids.len()) {
377 new_patterns.push(PatIdOrWild::Wild);
378 }
379
380 for pat_id in &pat_ids[ellipsis_position..pat_ids.len()] {
381 new_patterns.push((*pat_id).into());
382 }
383
384 Some(self.replace_head_with(&new_patterns))
385 } else {
386 return Err(MatchCheckErr::MalformedMatchArm);
387 }
388 } else {
389 // If there is no ellipsis in the tuple pattern, the number
390 // of patterns must equal the constructor arity.
391 if pat_ids.len() == constructor_arity {
392 Some(self.replace_head_with(pat_ids))
393 } else {
394 return Err(MatchCheckErr::MalformedMatchArm);
395 }
364 } 396 }
365
366 Some(self.replace_head_with(pat_ids))
367 } 397 }
368 } 398 }
369 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented), 399 (Pat::Or(_), _) => return Err(MatchCheckErr::NotImplemented),
@@ -644,7 +674,11 @@ impl Constructor {
644fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { 674fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> {
645 let res = match pat.as_pat(cx) { 675 let res = match pat.as_pat(cx) {
646 Pat::Wild => None, 676 Pat::Wild => None,
647 Pat::Tuple(pats) => Some(Constructor::Tuple { arity: pats.len() }), 677 // FIXME somehow create the Tuple constructor with the proper arity. If there are
678 // ellipsis, the arity is not equal to the number of patterns.
679 Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => {
680 Some(Constructor::Tuple { arity: pats.len() })
681 }
648 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { 682 Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] {
649 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), 683 Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)),
650 _ => return Err(MatchCheckErr::NotImplemented), 684 _ => return Err(MatchCheckErr::NotImplemented),
@@ -973,6 +1007,47 @@ mod tests {
973 } 1007 }
974 1008
975 #[test] 1009 #[test]
1010 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
1011 let content = r"
1012 fn test_fn() {
1013 match (false, true, false) {
1014 (false, ..) => {},
1015 (true, ..) => {},
1016 }
1017 }
1018 ";
1019
1020 check_no_diagnostic(content);
1021 }
1022
1023 #[test]
1024 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
1025 let content = r"
1026 fn test_fn() {
1027 match (false, true, false) {
1028 (.., false) => {},
1029 (.., true) => {},
1030 }
1031 }
1032 ";
1033
1034 check_no_diagnostic(content);
1035 }
1036
1037 #[test]
1038 fn tuple_of_bools_with_ellipsis_no_diagnostic() {
1039 let content = r"
1040 fn test_fn() {
1041 match (false, true, false) {
1042 (..) => {},
1043 }
1044 }
1045 ";
1046
1047 check_no_diagnostic(content);
1048 }
1049
1050 #[test]
976 fn tuple_of_tuple_and_bools_no_arms() { 1051 fn tuple_of_tuple_and_bools_no_arms() {
977 let content = r" 1052 let content = r"
978 fn test_fn() { 1053 fn test_fn() {
@@ -1315,8 +1390,9 @@ mod tests {
1315 } 1390 }
1316 "; 1391 ";
1317 1392
1318 // Match arms with the incorrect type are filtered out. 1393 // Match statements with arms that don't match the
1319 check_diagnostic(content); 1394 // expression pattern do not fire this diagnostic.
1395 check_no_diagnostic(content);
1320 } 1396 }
1321 1397
1322 #[test] 1398 #[test]
@@ -1330,8 +1406,9 @@ mod tests {
1330 } 1406 }
1331 "; 1407 ";
1332 1408
1333 // Match arms with the incorrect type are filtered out. 1409 // Match statements with arms that don't match the
1334 check_diagnostic(content); 1410 // expression pattern do not fire this diagnostic.
1411 check_no_diagnostic(content);
1335 } 1412 }
1336 1413
1337 #[test] 1414 #[test]
@@ -1344,8 +1421,9 @@ mod tests {
1344 } 1421 }
1345 "; 1422 ";
1346 1423
1347 // Match arms with the incorrect type are filtered out. 1424 // Match statements with arms that don't match the
1348 check_diagnostic(content); 1425 // expression pattern do not fire this diagnostic.
1426 check_no_diagnostic(content);
1349 } 1427 }
1350 1428
1351 #[test] 1429 #[test]
@@ -1383,6 +1461,163 @@ mod tests {
1383 // we don't create a diagnostic). 1461 // we don't create a diagnostic).
1384 check_no_diagnostic(content); 1462 check_no_diagnostic(content);
1385 } 1463 }
1464
1465 #[test]
1466 fn expr_diverges() {
1467 let content = r"
1468 enum Either {
1469 A,
1470 B,
1471 }
1472 fn test_fn() {
1473 match loop {} {
1474 Either::A => (),
1475 Either::B => (),
1476 }
1477 }
1478 ";
1479
1480 check_no_diagnostic(content);
1481 }
1482
1483 #[test]
1484 fn expr_loop_with_break() {
1485 let content = r"
1486 enum Either {
1487 A,
1488 B,
1489 }
1490 fn test_fn() {
1491 match loop { break Foo::A } {
1492 Either::A => (),
1493 Either::B => (),
1494 }
1495 }
1496 ";
1497
1498 check_no_diagnostic(content);
1499 }
1500
1501 #[test]
1502 fn expr_partially_diverges() {
1503 let content = r"
1504 enum Either<T> {
1505 A(T),
1506 B,
1507 }
1508 fn foo() -> Either<!> {
1509 Either::B
1510 }
1511 fn test_fn() -> u32 {
1512 match foo() {
1513 Either::A(val) => val,
1514 Either::B => 0,
1515 }
1516 }
1517 ";
1518
1519 check_no_diagnostic(content);
1520 }
1521
1522 #[test]
1523 fn enum_tuple_partial_ellipsis_no_diagnostic() {
1524 let content = r"
1525 enum Either {
1526 A(bool, bool, bool, bool),
1527 B,
1528 }
1529 fn test_fn() {
1530 match Either::B {
1531 Either::A(true, .., true) => {},
1532 Either::A(true, .., false) => {},
1533 Either::A(false, .., true) => {},
1534 Either::A(false, .., false) => {},
1535 Either::B => {},
1536 }
1537 }
1538 ";
1539
1540 check_no_diagnostic(content);
1541 }
1542
1543 #[test]
1544 fn enum_tuple_partial_ellipsis_2_no_diagnostic() {
1545 let content = r"
1546 enum Either {
1547 A(bool, bool, bool, bool),
1548 B,
1549 }
1550 fn test_fn() {
1551 match Either::B {
1552 Either::A(true, .., true) => {},
1553 Either::A(true, .., false) => {},
1554 Either::A(.., true) => {},
1555 Either::A(.., false) => {},
1556 Either::B => {},
1557 }
1558 }
1559 ";
1560
1561 check_no_diagnostic(content);
1562 }
1563
1564 #[test]
1565 fn enum_tuple_partial_ellipsis_missing_arm() {
1566 let content = r"
1567 enum Either {
1568 A(bool, bool, bool, bool),
1569 B,
1570 }
1571 fn test_fn() {
1572 match Either::B {
1573 Either::A(true, .., true) => {},
1574 Either::A(true, .., false) => {},
1575 Either::A(false, .., false) => {},
1576 Either::B => {},
1577 }
1578 }
1579 ";
1580
1581 check_diagnostic(content);
1582 }
1583
1584 #[test]
1585 fn enum_tuple_partial_ellipsis_2_missing_arm() {
1586 let content = r"
1587 enum Either {
1588 A(bool, bool, bool, bool),
1589 B,
1590 }
1591 fn test_fn() {
1592 match Either::B {
1593 Either::A(true, .., true) => {},
1594 Either::A(true, .., false) => {},
1595 Either::A(.., true) => {},
1596 Either::B => {},
1597 }
1598 }
1599 ";
1600
1601 check_diagnostic(content);
1602 }
1603
1604 #[test]
1605 fn enum_tuple_ellipsis_no_diagnostic() {
1606 let content = r"
1607 enum Either {
1608 A(bool, bool, bool, bool),
1609 B,
1610 }
1611 fn test_fn() {
1612 match Either::B {
1613 Either::A(..) => {},
1614 Either::B => {},
1615 }
1616 }
1617 ";
1618
1619 check_no_diagnostic(content);
1620 }
1386} 1621}
1387 1622
1388#[cfg(test)] 1623#[cfg(test)]
@@ -1452,4 +1687,75 @@ mod false_negatives {
1452 // We do not currently handle patterns with internal `or`s. 1687 // We do not currently handle patterns with internal `or`s.
1453 check_no_diagnostic(content); 1688 check_no_diagnostic(content);
1454 } 1689 }
1690
1691 #[test]
1692 fn expr_diverges_missing_arm() {
1693 let content = r"
1694 enum Either {
1695 A,
1696 B,
1697 }
1698 fn test_fn() {
1699 match loop {} {
1700 Either::A => (),
1701 }
1702 }
1703 ";
1704
1705 // This is a false negative.
1706 // Even though the match expression diverges, rustc fails
1707 // to compile here since `Either::B` is missing.
1708 check_no_diagnostic(content);
1709 }
1710
1711 #[test]
1712 fn expr_loop_missing_arm() {
1713 let content = r"
1714 enum Either {
1715 A,
1716 B,
1717 }
1718 fn test_fn() {
1719 match loop { break Foo::A } {
1720 Either::A => (),
1721 }
1722 }
1723 ";
1724
1725 // This is a false negative.
1726 // We currently infer the type of `loop { break Foo::A }` to `!`, which
1727 // causes us to skip the diagnostic since `Either::A` doesn't type check
1728 // with `!`.
1729 check_no_diagnostic(content);
1730 }
1731
1732 #[test]
1733 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
1734 let content = r"
1735 fn test_fn() {
1736 match (false, true, false) {
1737 (false, ..) => {},
1738 }
1739 }
1740 ";
1741
1742 // This is a false negative.
1743 // We don't currently handle tuple patterns with ellipsis.
1744 check_no_diagnostic(content);
1745 }
1746
1747 #[test]
1748 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
1749 let content = r"
1750 fn test_fn() {
1751 match (false, true, false) {
1752 (.., false) => {},
1753 }
1754 }
1755 ";
1756
1757 // This is a false negative.
1758 // We don't currently handle tuple patterns with ellipsis.
1759 check_no_diagnostic(content);
1760 }
1455} 1761}
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index 69b527f74..21abbcf1e 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -161,12 +161,6 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
161 161
162 let mut seen = Matrix::empty(); 162 let mut seen = Matrix::empty();
163 for pat in pats { 163 for pat in pats {
164 // We skip any patterns whose type we cannot resolve.
165 //
166 // This could lead to false positives in this diagnostic, so
167 // it might be better to skip the entire diagnostic if we either
168 // cannot resolve a match arm or determine that the match arm has
169 // the wrong type.
170 if let Some(pat_ty) = infer.type_of_pat.get(pat) { 164 if let Some(pat_ty) = infer.type_of_pat.get(pat) {
171 // We only include patterns whose type matches the type 165 // We only include patterns whose type matches the type
172 // of the match expression. If we had a InvalidMatchArmPattern 166 // of the match expression. If we had a InvalidMatchArmPattern
@@ -189,8 +183,15 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
189 // to the matrix here. 183 // to the matrix here.
190 let v = PatStack::from_pattern(pat); 184 let v = PatStack::from_pattern(pat);
191 seen.push(&cx, v); 185 seen.push(&cx, v);
186 continue;
192 } 187 }
193 } 188 }
189
190 // If we can't resolve the type of a pattern, or the pattern type doesn't
191 // fit the match expression, we skip this diagnostic. Skipping the entire
192 // diagnostic rather than just not including this match arm is preferred
193 // to avoid the chance of false positives.
194 return;
194 } 195 }
195 196
196 match is_useful(&cx, &seen, &PatStack::from_wild()) { 197 match is_useful(&cx, &seen, &PatStack::from_wild()) {
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index 078476f76..8ec4d4ace 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -85,7 +85,7 @@ impl<'a> InferenceContext<'a> {
85 let body = Arc::clone(&self.body); // avoid borrow checker problem 85 let body = Arc::clone(&self.body); // avoid borrow checker problem
86 86
87 let is_non_ref_pat = match &body[pat] { 87 let is_non_ref_pat = match &body[pat] {
88 Pat::Tuple(..) 88 Pat::Tuple { .. }
89 | Pat::Or(..) 89 | Pat::Or(..)
90 | Pat::TupleStruct { .. } 90 | Pat::TupleStruct { .. }
91 | Pat::Record { .. } 91 | Pat::Record { .. }
@@ -116,7 +116,7 @@ impl<'a> InferenceContext<'a> {
116 let expected = expected; 116 let expected = expected;
117 117
118 let ty = match &body[pat] { 118 let ty = match &body[pat] {
119 Pat::Tuple(ref args) => { 119 Pat::Tuple { ref args, .. } => {
120 let expectations = match expected.as_tuple() { 120 let expectations = match expected.as_tuple() {
121 Some(parameters) => &*parameters.0, 121 Some(parameters) => &*parameters.0,
122 _ => &[], 122 _ => &[],
@@ -155,7 +155,7 @@ impl<'a> InferenceContext<'a> {
155 let subty = self.infer_pat(*pat, expectation, default_bm); 155 let subty = self.infer_pat(*pat, expectation, default_bm);
156 Ty::apply_one(TypeCtor::Ref(*mutability), subty) 156 Ty::apply_one(TypeCtor::Ref(*mutability), subty)
157 } 157 }
158 Pat::TupleStruct { path: p, args: subpats } => { 158 Pat::TupleStruct { path: p, args: subpats, .. } => {
159 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) 159 self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat)
160 } 160 }
161 Pat::Record { path: p, args: fields, ellipsis: _ } => { 161 Pat::Record { path: p, args: fields, ellipsis: _ } => {
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 6e5d2247c..07cbc521a 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -1,7 +1,8 @@
1use super::{infer, infer_with_mismatches};
2use insta::assert_snapshot; 1use insta::assert_snapshot;
3use test_utils::covers; 2use test_utils::covers;
4 3
4use super::{infer, infer_with_mismatches};
5
5#[test] 6#[test]
6fn infer_pattern() { 7fn infer_pattern() {
7 assert_snapshot!( 8 assert_snapshot!(