|
2 | 2 |
|
3 | 3 | use std::iter;
|
4 | 4 |
|
5 |
| -use itertools::Itertools; |
6 |
| - |
7 | 5 | use hir::{Adt, HasSource, Semantics};
|
| 6 | +use itertools::Itertools; |
8 | 7 | use ra_ide_db::RootDatabase;
|
9 | 8 |
|
10 | 9 | use crate::{Assist, AssistCtx, AssistId};
|
@@ -61,20 +60,29 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
|
61 | 60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
|
62 | 61 | .collect()
|
63 | 62 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
|
64 |
| - // partial fill not currently supported for tuple of enums |
| 63 | + // Partial fill not currently supported for tuple of enums. |
65 | 64 | if !arms.is_empty() {
|
66 | 65 | return None;
|
67 | 66 | }
|
68 | 67 |
|
| 68 | + // We do not currently support filling match arms for a tuple |
| 69 | + // containing a single enum. |
| 70 | + if enum_defs.len() < 2 { |
| 71 | + return None; |
| 72 | + } |
| 73 | + |
| 74 | + // When calculating the match arms for a tuple of enums, we want |
| 75 | + // to create a match arm for each possible combination of enum |
| 76 | + // values. The `multi_cartesian_product` method transforms |
| 77 | + // Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)> |
| 78 | + // where each tuple represents a proposed match arm. |
69 | 79 | enum_defs
|
70 | 80 | .into_iter()
|
71 | 81 | .map(|enum_def| enum_def.variants(ctx.db))
|
72 | 82 | .multi_cartesian_product()
|
73 | 83 | .map(|variants| {
|
74 |
| - let patterns = variants |
75 |
| - .into_iter() |
76 |
| - .filter_map(|variant| build_pat(ctx.db, module, variant)) |
77 |
| - .collect::<Vec<_>>(); |
| 84 | + let patterns = |
| 85 | + variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant)); |
78 | 86 | ast::Pat::from(make::tuple_pat(patterns))
|
79 | 87 | })
|
80 | 88 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
|
@@ -130,16 +138,19 @@ fn resolve_tuple_of_enum_def(
|
130 | 138 | sema: &Semantics<RootDatabase>,
|
131 | 139 | expr: &ast::Expr,
|
132 | 140 | ) -> Option<Vec<hir::Enum>> {
|
133 |
| - Some( |
134 |
| - sema.type_of_expr(&expr)? |
135 |
| - .tuple_fields(sema.db) |
136 |
| - .iter() |
137 |
| - .map(|ty| match ty.as_adt() { |
138 |
| - Some(Adt::Enum(e)) => e, |
139 |
| - _ => panic!("handle the case of tuple containing non-enum"), |
| 141 | + sema.type_of_expr(&expr)? |
| 142 | + .tuple_fields(sema.db) |
| 143 | + .iter() |
| 144 | + .map(|ty| { |
| 145 | + ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
| 146 | + Some(Adt::Enum(e)) => Some(e), |
| 147 | + // For now we only handle expansion for a tuple of enums. Here |
| 148 | + // we map non-enum items to None and rely on `collect` to |
| 149 | + // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. |
| 150 | + _ => None, |
140 | 151 | })
|
141 |
| - .collect(), |
142 |
| - ) |
| 152 | + }) |
| 153 | + .collect() |
143 | 154 | }
|
144 | 155 |
|
145 | 156 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
|
@@ -189,6 +200,21 @@ mod tests {
|
189 | 200 | );
|
190 | 201 | }
|
191 | 202 |
|
| 203 | + #[test] |
| 204 | + fn tuple_of_non_enum() { |
| 205 | + // for now this case is not handled, although it potentially could be |
| 206 | + // in the future |
| 207 | + check_assist_not_applicable( |
| 208 | + fill_match_arms, |
| 209 | + r#" |
| 210 | + fn main() { |
| 211 | + match (0, false)<|> { |
| 212 | + } |
| 213 | + } |
| 214 | + "#, |
| 215 | + ); |
| 216 | + } |
| 217 | + |
192 | 218 | #[test]
|
193 | 219 | fn partial_fill_record_tuple() {
|
194 | 220 | check_assist(
|
@@ -389,6 +415,50 @@ mod tests {
|
389 | 415 | );
|
390 | 416 | }
|
391 | 417 |
|
| 418 | + #[test] |
| 419 | + fn fill_match_arms_tuple_of_enum_ref() { |
| 420 | + check_assist( |
| 421 | + fill_match_arms, |
| 422 | + r#" |
| 423 | + enum A { |
| 424 | + One, |
| 425 | + Two, |
| 426 | + } |
| 427 | + enum B { |
| 428 | + One, |
| 429 | + Two, |
| 430 | + } |
| 431 | +
|
| 432 | + fn main() { |
| 433 | + let a = A::One; |
| 434 | + let b = B::One; |
| 435 | + match (&a<|>, &b) {} |
| 436 | + } |
| 437 | + "#, |
| 438 | + r#" |
| 439 | + enum A { |
| 440 | + One, |
| 441 | + Two, |
| 442 | + } |
| 443 | + enum B { |
| 444 | + One, |
| 445 | + Two, |
| 446 | + } |
| 447 | +
|
| 448 | + fn main() { |
| 449 | + let a = A::One; |
| 450 | + let b = B::One; |
| 451 | + match <|>(&a, &b) { |
| 452 | + (A::One, B::One) => (), |
| 453 | + (A::One, B::Two) => (), |
| 454 | + (A::Two, B::One) => (), |
| 455 | + (A::Two, B::Two) => (), |
| 456 | + } |
| 457 | + } |
| 458 | + "#, |
| 459 | + ); |
| 460 | + } |
| 461 | + |
392 | 462 | #[test]
|
393 | 463 | fn fill_match_arms_tuple_of_enum_partial() {
|
394 | 464 | check_assist_not_applicable(
|
@@ -442,6 +512,28 @@ mod tests {
|
442 | 512 | );
|
443 | 513 | }
|
444 | 514 |
|
| 515 | + #[test] |
| 516 | + fn fill_match_arms_single_element_tuple_of_enum() { |
| 517 | + // For now we don't hande the case of a single element tuple, but |
| 518 | + // we could handle this in the future if `make::tuple_pat` allowed |
| 519 | + // creating a tuple with a single pattern. |
| 520 | + check_assist_not_applicable( |
| 521 | + fill_match_arms, |
| 522 | + r#" |
| 523 | + enum A { |
| 524 | + One, |
| 525 | + Two, |
| 526 | + } |
| 527 | +
|
| 528 | + fn main() { |
| 529 | + let a = A::One; |
| 530 | + match (a<|>, ) { |
| 531 | + } |
| 532 | + } |
| 533 | + "#, |
| 534 | + ); |
| 535 | + } |
| 536 | + |
445 | 537 | #[test]
|
446 | 538 | fn test_fill_match_arm_refs() {
|
447 | 539 | check_assist(
|
|
0 commit comments