diff options
Diffstat (limited to 'crates/hir_ty')
-rw-r--r-- | crates/hir_ty/src/diagnostics/match_check.rs | 248 | ||||
-rw-r--r-- | crates/hir_ty/src/infer/pat.rs | 46 | ||||
-rw-r--r-- | crates/hir_ty/src/tests/patterns.rs | 95 |
3 files changed, 304 insertions, 85 deletions
diff --git a/crates/hir_ty/src/diagnostics/match_check.rs b/crates/hir_ty/src/diagnostics/match_check.rs index 5bd03f2ac..62c329731 100644 --- a/crates/hir_ty/src/diagnostics/match_check.rs +++ b/crates/hir_ty/src/diagnostics/match_check.rs | |||
@@ -216,14 +216,14 @@ | |||
216 | //! U(P, p) := U(P, (r_1, p_2, .., p_n)) | 216 | //! U(P, p) := U(P, (r_1, p_2, .., p_n)) |
217 | //! || U(P, (r_2, p_2, .., p_n)) | 217 | //! || U(P, (r_2, p_2, .., p_n)) |
218 | //! ``` | 218 | //! ``` |
219 | use std::sync::Arc; | 219 | use std::{iter, sync::Arc}; |
220 | 220 | ||
221 | use arena::Idx; | 221 | use arena::Idx; |
222 | use hir_def::{ | 222 | use hir_def::{ |
223 | adt::VariantData, | 223 | adt::VariantData, |
224 | body::Body, | 224 | body::Body, |
225 | expr::{Expr, Literal, Pat, PatId}, | 225 | expr::{Expr, Literal, Pat, PatId}, |
226 | AdtId, EnumVariantId, VariantId, | 226 | AdtId, EnumVariantId, StructId, VariantId, |
227 | }; | 227 | }; |
228 | use smallvec::{smallvec, SmallVec}; | 228 | use smallvec::{smallvec, SmallVec}; |
229 | 229 | ||
@@ -366,16 +366,17 @@ impl PatStack { | |||
366 | 366 | ||
367 | let head_pat = head.as_pat(cx); | 367 | let head_pat = head.as_pat(cx); |
368 | let result = match (head_pat, constructor) { | 368 | let result = match (head_pat, constructor) { |
369 | (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { | 369 | (Pat::Tuple { args: pat_ids, ellipsis }, &Constructor::Tuple { arity }) => { |
370 | if ellipsis.is_some() { | 370 | if let Some(ellipsis) = ellipsis { |
371 | // If there are ellipsis here, we should add the correct number of | 371 | let (pre, post) = pat_ids.split_at(ellipsis); |
372 | // Pat::Wild patterns to `pat_ids`. We should be able to use the | 372 | let n_wild_pats = arity.saturating_sub(pat_ids.len()); |
373 | // constructors arity for this, but at the time of writing we aren't | 373 | let pre_iter = pre.iter().map(Into::into); |
374 | // correctly calculating this arity when ellipsis are present. | 374 | let wildcards = iter::repeat(PatIdOrWild::Wild).take(n_wild_pats); |
375 | return Err(MatchCheckErr::NotImplemented); | 375 | let post_iter = post.iter().map(Into::into); |
376 | Some(self.replace_head_with(pre_iter.chain(wildcards).chain(post_iter))) | ||
377 | } else { | ||
378 | Some(self.replace_head_with(pat_ids.iter())) | ||
376 | } | 379 | } |
377 | |||
378 | Some(self.replace_head_with(pat_ids.iter())) | ||
379 | } | 380 | } |
380 | (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { | 381 | (Pat::Lit(lit_expr), Constructor::Bool(constructor_val)) => { |
381 | match cx.body.exprs[lit_expr] { | 382 | match cx.body.exprs[lit_expr] { |
@@ -390,21 +391,28 @@ impl PatStack { | |||
390 | } | 391 | } |
391 | } | 392 | } |
392 | (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), | 393 | (Pat::Wild, constructor) => Some(self.expand_wildcard(cx, constructor)?), |
393 | (Pat::Path(_), Constructor::Enum(constructor)) => { | 394 | (Pat::Path(_), constructor) => { |
394 | // unit enum variants become `Pat::Path` | 395 | // unit enum variants become `Pat::Path` |
395 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 396 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
396 | if !enum_variant_matches(cx, pat_id, *constructor) { | 397 | let variant_id: VariantId = match constructor { |
398 | &Constructor::Enum(e) => e.into(), | ||
399 | &Constructor::Struct(s) => s.into(), | ||
400 | _ => return Err(MatchCheckErr::NotImplemented), | ||
401 | }; | ||
402 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
397 | None | 403 | None |
398 | } else { | 404 | } else { |
399 | Some(self.to_tail()) | 405 | Some(self.to_tail()) |
400 | } | 406 | } |
401 | } | 407 | } |
402 | ( | 408 | (Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, constructor) => { |
403 | Pat::TupleStruct { args: ref pat_ids, ellipsis, .. }, | ||
404 | Constructor::Enum(enum_constructor), | ||
405 | ) => { | ||
406 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 409 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
407 | if !enum_variant_matches(cx, pat_id, *enum_constructor) { | 410 | let variant_id: VariantId = match constructor { |
411 | &Constructor::Enum(e) => e.into(), | ||
412 | &Constructor::Struct(s) => s.into(), | ||
413 | _ => return Err(MatchCheckErr::MalformedMatchArm), | ||
414 | }; | ||
415 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
408 | None | 416 | None |
409 | } else { | 417 | } else { |
410 | let constructor_arity = constructor.arity(cx)?; | 418 | let constructor_arity = constructor.arity(cx)?; |
@@ -442,12 +450,22 @@ impl PatStack { | |||
442 | } | 450 | } |
443 | } | 451 | } |
444 | } | 452 | } |
445 | (Pat::Record { args: ref arg_patterns, .. }, Constructor::Enum(e)) => { | 453 | (Pat::Record { args: ref arg_patterns, .. }, constructor) => { |
446 | let pat_id = head.as_id().expect("we know this isn't a wild"); | 454 | let pat_id = head.as_id().expect("we know this isn't a wild"); |
447 | if !enum_variant_matches(cx, pat_id, *e) { | 455 | let (variant_id, variant_data) = match constructor { |
456 | &Constructor::Enum(e) => ( | ||
457 | e.into(), | ||
458 | cx.db.enum_data(e.parent).variants[e.local_id].variant_data.clone(), | ||
459 | ), | ||
460 | &Constructor::Struct(s) => { | ||
461 | (s.into(), cx.db.struct_data(s).variant_data.clone()) | ||
462 | } | ||
463 | _ => return Err(MatchCheckErr::MalformedMatchArm), | ||
464 | }; | ||
465 | if Some(variant_id) != cx.infer.variant_resolution_for_pat(pat_id) { | ||
448 | None | 466 | None |
449 | } else { | 467 | } else { |
450 | match cx.db.enum_data(e.parent).variants[e.local_id].variant_data.as_ref() { | 468 | match variant_data.as_ref() { |
451 | VariantData::Record(struct_field_arena) => { | 469 | VariantData::Record(struct_field_arena) => { |
452 | // Here we treat any missing fields in the record as the wild pattern, as | 470 | // Here we treat any missing fields in the record as the wild pattern, as |
453 | // if the record has ellipsis. We want to do this here even if the | 471 | // if the record has ellipsis. We want to do this here even if the |
@@ -726,6 +744,7 @@ enum Constructor { | |||
726 | Bool(bool), | 744 | Bool(bool), |
727 | Tuple { arity: usize }, | 745 | Tuple { arity: usize }, |
728 | Enum(EnumVariantId), | 746 | Enum(EnumVariantId), |
747 | Struct(StructId), | ||
729 | } | 748 | } |
730 | 749 | ||
731 | impl Constructor { | 750 | impl Constructor { |
@@ -740,6 +759,11 @@ impl Constructor { | |||
740 | VariantData::Unit => 0, | 759 | VariantData::Unit => 0, |
741 | } | 760 | } |
742 | } | 761 | } |
762 | &Constructor::Struct(s) => match cx.db.struct_data(s).variant_data.as_ref() { | ||
763 | VariantData::Tuple(struct_field_data) => struct_field_data.len(), | ||
764 | VariantData::Record(struct_field_data) => struct_field_data.len(), | ||
765 | VariantData::Unit => 0, | ||
766 | }, | ||
743 | }; | 767 | }; |
744 | 768 | ||
745 | Ok(arity) | 769 | Ok(arity) |
@@ -748,7 +772,7 @@ impl Constructor { | |||
748 | fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> { | 772 | fn all_constructors(&self, cx: &MatchCheckCtx) -> Vec<Constructor> { |
749 | match self { | 773 | match self { |
750 | Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)], | 774 | Constructor::Bool(_) => vec![Constructor::Bool(true), Constructor::Bool(false)], |
751 | Constructor::Tuple { .. } => vec![*self], | 775 | Constructor::Tuple { .. } | Constructor::Struct(_) => vec![*self], |
752 | Constructor::Enum(e) => cx | 776 | Constructor::Enum(e) => cx |
753 | .db | 777 | .db |
754 | .enum_data(e.parent) | 778 | .enum_data(e.parent) |
@@ -767,10 +791,11 @@ impl Constructor { | |||
767 | fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { | 791 | fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Option<Constructor>> { |
768 | let res = match pat.as_pat(cx) { | 792 | let res = match pat.as_pat(cx) { |
769 | Pat::Wild => None, | 793 | Pat::Wild => None, |
770 | // FIXME somehow create the Tuple constructor with the proper arity. If there are | 794 | Pat::Tuple { .. } => { |
771 | // ellipsis, the arity is not equal to the number of patterns. | 795 | let pat_id = pat.as_id().expect("we already know this pattern is not a wild"); |
772 | Pat::Tuple { args: pats, ellipsis } if ellipsis.is_none() => { | 796 | Some(Constructor::Tuple { |
773 | Some(Constructor::Tuple { arity: pats.len() }) | 797 | arity: cx.infer.type_of_pat[pat_id].as_tuple().ok_or(MatchCheckErr::Unknown)?.len(), |
798 | }) | ||
774 | } | 799 | } |
775 | Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { | 800 | Pat::Lit(lit_expr) => match cx.body.exprs[lit_expr] { |
776 | Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), | 801 | Expr::Literal(Literal::Bool(val)) => Some(Constructor::Bool(val)), |
@@ -784,6 +809,7 @@ fn pat_constructor(cx: &MatchCheckCtx, pat: PatIdOrWild) -> MatchCheckResult<Opt | |||
784 | VariantId::EnumVariantId(enum_variant_id) => { | 809 | VariantId::EnumVariantId(enum_variant_id) => { |
785 | Some(Constructor::Enum(enum_variant_id)) | 810 | Some(Constructor::Enum(enum_variant_id)) |
786 | } | 811 | } |
812 | VariantId::StructId(struct_id) => Some(Constructor::Struct(struct_id)), | ||
787 | _ => return Err(MatchCheckErr::NotImplemented), | 813 | _ => return Err(MatchCheckErr::NotImplemented), |
788 | } | 814 | } |
789 | } | 815 | } |
@@ -828,13 +854,13 @@ fn all_constructors_covered( | |||
828 | 854 | ||
829 | false | 855 | false |
830 | }), | 856 | }), |
857 | &Constructor::Struct(s) => used_constructors.iter().any(|constructor| match constructor { | ||
858 | &Constructor::Struct(sid) => sid == s, | ||
859 | _ => false, | ||
860 | }), | ||
831 | } | 861 | } |
832 | } | 862 | } |
833 | 863 | ||
834 | fn enum_variant_matches(cx: &MatchCheckCtx, pat_id: PatId, enum_variant_id: EnumVariantId) -> bool { | ||
835 | Some(enum_variant_id.into()) == cx.infer.variant_resolution_for_pat(pat_id) | ||
836 | } | ||
837 | |||
838 | #[cfg(test)] | 864 | #[cfg(test)] |
839 | mod tests { | 865 | mod tests { |
840 | use crate::diagnostics::tests::check_diagnostics; | 866 | use crate::diagnostics::tests::check_diagnostics; |
@@ -846,8 +872,8 @@ mod tests { | |||
846 | fn main() { | 872 | fn main() { |
847 | match () { } | 873 | match () { } |
848 | //^^ Missing match arm | 874 | //^^ Missing match arm |
849 | match (()) { } | 875 | match (()) { } |
850 | //^^^^ Missing match arm | 876 | //^^^^ Missing match arm |
851 | 877 | ||
852 | match () { _ => (), } | 878 | match () { _ => (), } |
853 | match () { () => (), } | 879 | match () { () => (), } |
@@ -1352,6 +1378,123 @@ fn main() { | |||
1352 | ); | 1378 | ); |
1353 | } | 1379 | } |
1354 | 1380 | ||
1381 | #[test] | ||
1382 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
1383 | check_diagnostics( | ||
1384 | r#" | ||
1385 | fn main() { | ||
1386 | match (false, true, false) { | ||
1387 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1388 | (false, ..) => (), | ||
1389 | } | ||
1390 | }"#, | ||
1391 | ); | ||
1392 | } | ||
1393 | |||
1394 | #[test] | ||
1395 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
1396 | check_diagnostics( | ||
1397 | r#" | ||
1398 | fn main() { | ||
1399 | match (false, true, false) { | ||
1400 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1401 | (.., false) => (), | ||
1402 | } | ||
1403 | }"#, | ||
1404 | ); | ||
1405 | } | ||
1406 | |||
1407 | #[test] | ||
1408 | fn tuple_of_bools_with_ellipsis_in_middle_missing_arm() { | ||
1409 | check_diagnostics( | ||
1410 | r#" | ||
1411 | fn main() { | ||
1412 | match (false, true, false) { | ||
1413 | //^^^^^^^^^^^^^^^^^^^^ Missing match arm | ||
1414 | (true, .., false) => (), | ||
1415 | } | ||
1416 | }"#, | ||
1417 | ); | ||
1418 | } | ||
1419 | |||
1420 | #[test] | ||
1421 | fn record_struct() { | ||
1422 | check_diagnostics( | ||
1423 | r#"struct Foo { a: bool } | ||
1424 | fn main(f: Foo) { | ||
1425 | match f {} | ||
1426 | //^ Missing match arm | ||
1427 | match f { Foo { a: true } => () } | ||
1428 | //^ Missing match arm | ||
1429 | match &f { Foo { a: true } => () } | ||
1430 | //^^ Missing match arm | ||
1431 | match f { Foo { a: _ } => () } | ||
1432 | match f { | ||
1433 | Foo { a: true } => (), | ||
1434 | Foo { a: false } => (), | ||
1435 | } | ||
1436 | match &f { | ||
1437 | Foo { a: true } => (), | ||
1438 | Foo { a: false } => (), | ||
1439 | } | ||
1440 | } | ||
1441 | "#, | ||
1442 | ); | ||
1443 | } | ||
1444 | |||
1445 | #[test] | ||
1446 | fn tuple_struct() { | ||
1447 | check_diagnostics( | ||
1448 | r#"struct Foo(bool); | ||
1449 | fn main(f: Foo) { | ||
1450 | match f {} | ||
1451 | //^ Missing match arm | ||
1452 | match f { Foo(true) => () } | ||
1453 | //^ Missing match arm | ||
1454 | match f { | ||
1455 | Foo(true) => (), | ||
1456 | Foo(false) => (), | ||
1457 | } | ||
1458 | } | ||
1459 | "#, | ||
1460 | ); | ||
1461 | } | ||
1462 | |||
1463 | #[test] | ||
1464 | fn unit_struct() { | ||
1465 | check_diagnostics( | ||
1466 | r#"struct Foo; | ||
1467 | fn main(f: Foo) { | ||
1468 | match f {} | ||
1469 | //^ Missing match arm | ||
1470 | match f { Foo => () } | ||
1471 | } | ||
1472 | "#, | ||
1473 | ); | ||
1474 | } | ||
1475 | |||
1476 | #[test] | ||
1477 | fn record_struct_ellipsis() { | ||
1478 | check_diagnostics( | ||
1479 | r#"struct Foo { foo: bool, bar: bool } | ||
1480 | fn main(f: Foo) { | ||
1481 | match f { Foo { foo: true, .. } => () } | ||
1482 | //^ Missing match arm | ||
1483 | match f { | ||
1484 | //^ Missing match arm | ||
1485 | Foo { foo: true, .. } => (), | ||
1486 | Foo { bar: false, .. } => () | ||
1487 | } | ||
1488 | match f { Foo { .. } => () } | ||
1489 | match f { | ||
1490 | Foo { foo: true, .. } => (), | ||
1491 | Foo { foo: false, .. } => () | ||
1492 | } | ||
1493 | } | ||
1494 | "#, | ||
1495 | ); | ||
1496 | } | ||
1497 | |||
1355 | mod false_negatives { | 1498 | mod false_negatives { |
1356 | //! The implementation of match checking here is a work in progress. As we roll this out, we | 1499 | //! The implementation of match checking here is a work in progress. As we roll this out, we |
1357 | //! prefer false negatives to false positives (ideally there would be no false positives). This | 1500 | //! prefer false negatives to false positives (ideally there would be no false positives). This |
@@ -1393,46 +1536,5 @@ fn main() { | |||
1393 | "#, | 1536 | "#, |
1394 | ); | 1537 | ); |
1395 | } | 1538 | } |
1396 | |||
1397 | #[test] | ||
1398 | fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { | ||
1399 | // We don't currently handle tuple patterns with ellipsis. | ||
1400 | check_diagnostics( | ||
1401 | r#" | ||
1402 | fn main() { | ||
1403 | match (false, true, false) { | ||
1404 | (false, ..) => (), | ||
1405 | } | ||
1406 | } | ||
1407 | "#, | ||
1408 | ); | ||
1409 | } | ||
1410 | |||
1411 | #[test] | ||
1412 | fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { | ||
1413 | // We don't currently handle tuple patterns with ellipsis. | ||
1414 | check_diagnostics( | ||
1415 | r#" | ||
1416 | fn main() { | ||
1417 | match (false, true, false) { | ||
1418 | (.., false) => (), | ||
1419 | } | ||
1420 | } | ||
1421 | "#, | ||
1422 | ); | ||
1423 | } | ||
1424 | |||
1425 | #[test] | ||
1426 | fn struct_missing_arm() { | ||
1427 | // We don't currently handle structs. | ||
1428 | check_diagnostics( | ||
1429 | r#" | ||
1430 | struct Foo { a: bool } | ||
1431 | fn main(f: Foo) { | ||
1432 | match f { Foo { a: true } => () } | ||
1433 | } | ||
1434 | "#, | ||
1435 | ); | ||
1436 | } | ||
1437 | } | 1539 | } |
1438 | } | 1540 | } |
diff --git a/crates/hir_ty/src/infer/pat.rs b/crates/hir_ty/src/infer/pat.rs index cde2ab82b..b70ec55eb 100644 --- a/crates/hir_ty/src/infer/pat.rs +++ b/crates/hir_ty/src/infer/pat.rs | |||
@@ -23,6 +23,7 @@ impl<'a> InferenceContext<'a> { | |||
23 | expected: &Ty, | 23 | expected: &Ty, |
24 | default_bm: BindingMode, | 24 | default_bm: BindingMode, |
25 | id: PatId, | 25 | id: PatId, |
26 | ellipsis: Option<usize>, | ||
26 | ) -> Ty { | 27 | ) -> Ty { |
27 | let (ty, def) = self.resolve_variant(path); | 28 | let (ty, def) = self.resolve_variant(path); |
28 | let var_data = def.map(|it| variant_data(self.db.upcast(), it)); | 29 | let var_data = def.map(|it| variant_data(self.db.upcast(), it)); |
@@ -34,8 +35,15 @@ impl<'a> InferenceContext<'a> { | |||
34 | let substs = ty.substs().unwrap_or_else(Substs::empty); | 35 | let substs = ty.substs().unwrap_or_else(Substs::empty); |
35 | 36 | ||
36 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); | 37 | let field_tys = def.map(|it| self.db.field_types(it)).unwrap_or_default(); |
38 | let (pre, post) = match ellipsis { | ||
39 | Some(idx) => subpats.split_at(idx), | ||
40 | None => (&subpats[..], &[][..]), | ||
41 | }; | ||
42 | let post_idx_offset = field_tys.iter().count() - post.len(); | ||
37 | 43 | ||
38 | for (i, &subpat) in subpats.iter().enumerate() { | 44 | let pre_iter = pre.iter().enumerate(); |
45 | let post_iter = (post_idx_offset..).zip(post.iter()); | ||
46 | for (i, &subpat) in pre_iter.chain(post_iter) { | ||
39 | let expected_ty = var_data | 47 | let expected_ty = var_data |
40 | .as_ref() | 48 | .as_ref() |
41 | .and_then(|d| d.field(&Name::new_tuple_field(i))) | 49 | .and_then(|d| d.field(&Name::new_tuple_field(i))) |
@@ -111,20 +119,29 @@ impl<'a> InferenceContext<'a> { | |||
111 | let expected = expected; | 119 | let expected = expected; |
112 | 120 | ||
113 | let ty = match &body[pat] { | 121 | let ty = match &body[pat] { |
114 | Pat::Tuple { ref args, .. } => { | 122 | &Pat::Tuple { ref args, ellipsis } => { |
115 | let expectations = match expected.as_tuple() { | 123 | let expectations = match expected.as_tuple() { |
116 | Some(parameters) => &*parameters.0, | 124 | Some(parameters) => &*parameters.0, |
117 | _ => &[], | 125 | _ => &[], |
118 | }; | 126 | }; |
119 | let expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown)); | ||
120 | 127 | ||
121 | let inner_tys = args | 128 | let (pre, post) = match ellipsis { |
122 | .iter() | 129 | Some(idx) => args.split_at(idx), |
123 | .zip(expectations_iter) | 130 | None => (&args[..], &[][..]), |
124 | .map(|(&pat, ty)| self.infer_pat(pat, ty, default_bm)) | 131 | }; |
125 | .collect(); | 132 | let n_uncovered_patterns = expectations.len().saturating_sub(args.len()); |
133 | let mut expectations_iter = expectations.iter().chain(repeat(&Ty::Unknown)); | ||
134 | let mut infer_pat = |(&pat, ty)| self.infer_pat(pat, ty, default_bm); | ||
135 | |||
136 | let mut inner_tys = Vec::with_capacity(n_uncovered_patterns + args.len()); | ||
137 | inner_tys.extend(pre.iter().zip(expectations_iter.by_ref()).map(&mut infer_pat)); | ||
138 | inner_tys.extend(expectations_iter.by_ref().take(n_uncovered_patterns).cloned()); | ||
139 | inner_tys.extend(post.iter().zip(expectations_iter).map(infer_pat)); | ||
126 | 140 | ||
127 | Ty::apply(TypeCtor::Tuple { cardinality: args.len() as u16 }, Substs(inner_tys)) | 141 | Ty::apply( |
142 | TypeCtor::Tuple { cardinality: inner_tys.len() as u16 }, | ||
143 | Substs(inner_tys.into()), | ||
144 | ) | ||
128 | } | 145 | } |
129 | Pat::Or(ref pats) => { | 146 | Pat::Or(ref pats) => { |
130 | if let Some((first_pat, rest)) = pats.split_first() { | 147 | if let Some((first_pat, rest)) = pats.split_first() { |
@@ -150,9 +167,14 @@ impl<'a> InferenceContext<'a> { | |||
150 | let subty = self.infer_pat(*pat, expectation, default_bm); | 167 | let subty = self.infer_pat(*pat, expectation, default_bm); |
151 | Ty::apply_one(TypeCtor::Ref(*mutability), subty) | 168 | Ty::apply_one(TypeCtor::Ref(*mutability), subty) |
152 | } | 169 | } |
153 | Pat::TupleStruct { path: p, args: subpats, .. } => { | 170 | Pat::TupleStruct { path: p, args: subpats, ellipsis } => self.infer_tuple_struct_pat( |
154 | self.infer_tuple_struct_pat(p.as_ref(), subpats, expected, default_bm, pat) | 171 | p.as_ref(), |
155 | } | 172 | subpats, |
173 | expected, | ||
174 | default_bm, | ||
175 | pat, | ||
176 | *ellipsis, | ||
177 | ), | ||
156 | Pat::Record { path: p, args: fields, ellipsis: _ } => { | 178 | Pat::Record { path: p, args: fields, ellipsis: _ } => { |
157 | self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) | 179 | self.infer_record_pat(p.as_ref(), fields, expected, default_bm, pat) |
158 | } | 180 | } |
diff --git a/crates/hir_ty/src/tests/patterns.rs b/crates/hir_ty/src/tests/patterns.rs index 6a965ac4f..5a5f48fd0 100644 --- a/crates/hir_ty/src/tests/patterns.rs +++ b/crates/hir_ty/src/tests/patterns.rs | |||
@@ -679,3 +679,98 @@ fn box_pattern() { | |||
679 | "#]], | 679 | "#]], |
680 | ); | 680 | ); |
681 | } | 681 | } |
682 | |||
683 | #[test] | ||
684 | fn tuple_ellipsis_pattern() { | ||
685 | check_infer( | ||
686 | r#" | ||
687 | fn foo(tuple: (u8, i16, f32)) { | ||
688 | match tuple { | ||
689 | (.., b, c) => {}, | ||
690 | (a, .., c) => {}, | ||
691 | (a, b, ..) => {}, | ||
692 | (a, b) => {/*too short*/} | ||
693 | (a, b, c, d) => {/*too long*/} | ||
694 | _ => {} | ||
695 | } | ||
696 | }"#, | ||
697 | expect![[r#" | ||
698 | 7..12 'tuple': (u8, i16, f32) | ||
699 | 30..224 '{ ... } }': () | ||
700 | 36..222 'match ... }': () | ||
701 | 42..47 'tuple': (u8, i16, f32) | ||
702 | 58..68 '(.., b, c)': (u8, i16, f32) | ||
703 | 63..64 'b': i16 | ||
704 | 66..67 'c': f32 | ||
705 | 72..74 '{}': () | ||
706 | 84..94 '(a, .., c)': (u8, i16, f32) | ||
707 | 85..86 'a': u8 | ||
708 | 92..93 'c': f32 | ||
709 | 98..100 '{}': () | ||
710 | 110..120 '(a, b, ..)': (u8, i16, f32) | ||
711 | 111..112 'a': u8 | ||
712 | 114..115 'b': i16 | ||
713 | 124..126 '{}': () | ||
714 | 136..142 '(a, b)': (u8, i16, f32) | ||
715 | 137..138 'a': u8 | ||
716 | 140..141 'b': i16 | ||
717 | 146..161 '{/*too short*/}': () | ||
718 | 170..182 '(a, b, c, d)': (u8, i16, f32, {unknown}) | ||
719 | 171..172 'a': u8 | ||
720 | 174..175 'b': i16 | ||
721 | 177..178 'c': f32 | ||
722 | 180..181 'd': {unknown} | ||
723 | 186..200 '{/*too long*/}': () | ||
724 | 209..210 '_': (u8, i16, f32) | ||
725 | 214..216 '{}': () | ||
726 | "#]], | ||
727 | ); | ||
728 | } | ||
729 | |||
730 | #[test] | ||
731 | fn tuple_struct_ellipsis_pattern() { | ||
732 | check_infer( | ||
733 | r#" | ||
734 | struct Tuple(u8, i16, f32); | ||
735 | fn foo(tuple: Tuple) { | ||
736 | match tuple { | ||
737 | Tuple(.., b, c) => {}, | ||
738 | Tuple(a, .., c) => {}, | ||
739 | Tuple(a, b, ..) => {}, | ||
740 | Tuple(a, b) => {/*too short*/} | ||
741 | Tuple(a, b, c, d) => {/*too long*/} | ||
742 | _ => {} | ||
743 | } | ||
744 | }"#, | ||
745 | expect![[r#" | ||
746 | 35..40 'tuple': Tuple | ||
747 | 49..268 '{ ... } }': () | ||
748 | 55..266 'match ... }': () | ||
749 | 61..66 'tuple': Tuple | ||
750 | 77..92 'Tuple(.., b, c)': Tuple | ||
751 | 87..88 'b': i16 | ||
752 | 90..91 'c': f32 | ||
753 | 96..98 '{}': () | ||
754 | 108..123 'Tuple(a, .., c)': Tuple | ||
755 | 114..115 'a': u8 | ||
756 | 121..122 'c': f32 | ||
757 | 127..129 '{}': () | ||
758 | 139..154 'Tuple(a, b, ..)': Tuple | ||
759 | 145..146 'a': u8 | ||
760 | 148..149 'b': i16 | ||
761 | 158..160 '{}': () | ||
762 | 170..181 'Tuple(a, b)': Tuple | ||
763 | 176..177 'a': u8 | ||
764 | 179..180 'b': i16 | ||
765 | 185..200 '{/*too short*/}': () | ||
766 | 209..226 'Tuple(... c, d)': Tuple | ||
767 | 215..216 'a': u8 | ||
768 | 218..219 'b': i16 | ||
769 | 221..222 'c': f32 | ||
770 | 224..225 'd': {unknown} | ||
771 | 230..244 '{/*too long*/}': () | ||
772 | 253..254 '_': Tuple | ||
773 | 258..260 '{}': () | ||
774 | "#]], | ||
775 | ); | ||
776 | } | ||