diff options
-rw-r--r-- | crates/ra_assists/src/handlers/fill_match_arms.rs | 124 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 7 |
2 files changed, 114 insertions, 17 deletions
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index d207c3307..7463b2af7 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs | |||
@@ -2,9 +2,8 @@ | |||
2 | 2 | ||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use itertools::Itertools; | ||
6 | |||
7 | use hir::{Adt, HasSource, Semantics}; | 5 | use hir::{Adt, HasSource, Semantics}; |
6 | use itertools::Itertools; | ||
8 | use ra_ide_db::RootDatabase; | 7 | use ra_ide_db::RootDatabase; |
9 | 8 | ||
10 | use crate::{Assist, AssistCtx, AssistId}; | 9 | use crate::{Assist, AssistCtx, AssistId}; |
@@ -61,20 +60,29 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> { | |||
61 | .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) | 60 | .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) |
62 | .collect() | 61 | .collect() |
63 | } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { | 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 | if !arms.is_empty() { | 64 | if !arms.is_empty() { |
66 | return None; | 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 | enum_defs | 79 | enum_defs |
70 | .into_iter() | 80 | .into_iter() |
71 | .map(|enum_def| enum_def.variants(ctx.db)) | 81 | .map(|enum_def| enum_def.variants(ctx.db)) |
72 | .multi_cartesian_product() | 82 | .multi_cartesian_product() |
73 | .map(|variants| { | 83 | .map(|variants| { |
74 | let patterns = variants | 84 | let patterns = |
75 | .into_iter() | 85 | variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant)); |
76 | .filter_map(|variant| build_pat(ctx.db, module, variant)) | ||
77 | .collect::<Vec<_>>(); | ||
78 | ast::Pat::from(make::tuple_pat(patterns)) | 86 | ast::Pat::from(make::tuple_pat(patterns)) |
79 | }) | 87 | }) |
80 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) | 88 | .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) |
@@ -130,16 +138,19 @@ fn resolve_tuple_of_enum_def( | |||
130 | sema: &Semantics<RootDatabase>, | 138 | sema: &Semantics<RootDatabase>, |
131 | expr: &ast::Expr, | 139 | expr: &ast::Expr, |
132 | ) -> Option<Vec<hir::Enum>> { | 140 | ) -> Option<Vec<hir::Enum>> { |
133 | Some( | 141 | sema.type_of_expr(&expr)? |
134 | sema.type_of_expr(&expr)? | 142 | .tuple_fields(sema.db) |
135 | .tuple_fields(sema.db) | 143 | .iter() |
136 | .iter() | 144 | .map(|ty| { |
137 | .map(|ty| match ty.as_adt() { | 145 | ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { |
138 | Some(Adt::Enum(e)) => e, | 146 | Some(Adt::Enum(e)) => Some(e), |
139 | _ => panic!("handle the case of tuple containing non-enum"), | 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(), | 152 | }) |
142 | ) | 153 | .collect() |
143 | } | 154 | } |
144 | 155 | ||
145 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { | 156 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { |
@@ -190,6 +201,21 @@ mod tests { | |||
190 | } | 201 | } |
191 | 202 | ||
192 | #[test] | 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 | |||
218 | #[test] | ||
193 | fn partial_fill_record_tuple() { | 219 | fn partial_fill_record_tuple() { |
194 | check_assist( | 220 | check_assist( |
195 | fill_match_arms, | 221 | fill_match_arms, |
@@ -390,6 +416,50 @@ mod tests { | |||
390 | } | 416 | } |
391 | 417 | ||
392 | #[test] | 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 | |||
462 | #[test] | ||
393 | fn fill_match_arms_tuple_of_enum_partial() { | 463 | fn fill_match_arms_tuple_of_enum_partial() { |
394 | check_assist_not_applicable( | 464 | check_assist_not_applicable( |
395 | fill_match_arms, | 465 | fill_match_arms, |
@@ -443,6 +513,28 @@ mod tests { | |||
443 | } | 513 | } |
444 | 514 | ||
445 | #[test] | 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 | |||
537 | #[test] | ||
446 | fn test_fill_match_arm_refs() { | 538 | fn test_fill_match_arm_refs() { |
447 | check_assist( | 539 | check_assist( |
448 | fill_match_arms, | 540 | fill_match_arms, |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 9d8ed6238..9257ccd1a 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -136,8 +136,13 @@ pub fn placeholder_pat() -> ast::PlaceholderPat { | |||
136 | } | 136 | } |
137 | } | 137 | } |
138 | 138 | ||
139 | /// Creates a tuple of patterns from an interator of patterns. | ||
140 | /// | ||
141 | /// Invariant: `pats` must be length > 1 | ||
142 | /// | ||
143 | /// FIXME handle `pats` length == 1 | ||
139 | pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { | 144 | pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { |
140 | let pats_str = pats.into_iter().map(|p| p.syntax().to_string()).join(", "); | 145 | let pats_str = pats.into_iter().map(|p| p.to_string()).join(", "); |
141 | return from_text(&format!("({})", pats_str)); | 146 | return from_text(&format!("({})", pats_str)); |
142 | 147 | ||
143 | fn from_text(text: &str) -> ast::TuplePat { | 148 | fn from_text(text: &str) -> ast::TuplePat { |