aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs124
-rw-r--r--crates/ra_syntax/src/ast/make.rs7
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
3use std::iter; 3use std::iter;
4 4
5use itertools::Itertools;
6
7use hir::{Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, Semantics};
6use itertools::Itertools;
8use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
9 8
10use crate::{Assist, AssistCtx, AssistId}; 9use 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
145fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { 156fn 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
139pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { 144pub 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 {