aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/Cargo.toml1
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs255
-rw-r--r--crates/ra_syntax/src/ast/make.rs14
3 files changed, 256 insertions, 14 deletions
diff --git a/crates/ra_assists/Cargo.toml b/crates/ra_assists/Cargo.toml
index d314dc8e6..85adddb5b 100644
--- a/crates/ra_assists/Cargo.toml
+++ b/crates/ra_assists/Cargo.toml
@@ -11,6 +11,7 @@ doctest = false
11format-buf = "1.0.0" 11format-buf = "1.0.0"
12join_to_string = "0.1.3" 12join_to_string = "0.1.3"
13rustc-hash = "1.1.0" 13rustc-hash = "1.1.0"
14itertools = "0.8.2"
14 15
15ra_syntax = { path = "../ra_syntax" } 16ra_syntax = { path = "../ra_syntax" }
16ra_text_edit = { path = "../ra_text_edit" } 17ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index fbd6a3ec3..7463b2af7 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -3,6 +3,7 @@
3use std::iter; 3use std::iter;
4 4
5use hir::{Adt, HasSource, Semantics}; 5use hir::{Adt, HasSource, Semantics};
6use itertools::Itertools;
6use ra_ide_db::RootDatabase; 7use ra_ide_db::RootDatabase;
7 8
8use crate::{Assist, AssistCtx, AssistId}; 9use crate::{Assist, AssistCtx, AssistId};
@@ -39,13 +40,6 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
39 let match_arm_list = match_expr.match_arm_list()?; 40 let match_arm_list = match_expr.match_arm_list()?;
40 41
41 let expr = match_expr.expr()?; 42 let expr = match_expr.expr()?;
42 let enum_def = resolve_enum_def(&ctx.sema, &expr)?;
43 let module = ctx.sema.scope(expr.syntax()).module()?;
44
45 let variants = enum_def.variants(ctx.db);
46 if variants.is_empty() {
47 return None;
48 }
49 43
50 let mut arms: Vec<MatchArm> = match_arm_list.arms().collect(); 44 let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
51 if arms.len() == 1 { 45 if arms.len() == 1 {
@@ -54,13 +48,49 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
54 } 48 }
55 } 49 }
56 50
57 let db = ctx.db; 51 let module = ctx.sema.scope(expr.syntax()).module()?;
58 let missing_arms: Vec<MatchArm> = variants 52
59 .into_iter() 53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
60 .filter_map(|variant| build_pat(db, module, variant)) 54 let variants = enum_def.variants(ctx.db);
61 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 55
62 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) 56 variants
63 .collect(); 57 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db, module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
61 .collect()
62 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
63 // Partial fill not currently supported for tuple of enums.
64 if !arms.is_empty() {
65 return None;
66 }
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.
79 enum_defs
80 .into_iter()
81 .map(|enum_def| enum_def.variants(ctx.db))
82 .multi_cartesian_product()
83 .map(|variants| {
84 let patterns =
85 variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant));
86 ast::Pat::from(make::tuple_pat(patterns))
87 })
88 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
89 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
90 .collect()
91 } else {
92 return None;
93 };
64 94
65 if missing_arms.is_empty() { 95 if missing_arms.is_empty() {
66 return None; 96 return None;
@@ -104,6 +134,25 @@ fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
104 }) 134 })
105} 135}
106 136
137fn resolve_tuple_of_enum_def(
138 sema: &Semantics<RootDatabase>,
139 expr: &ast::Expr,
140) -> Option<Vec<hir::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,
151 })
152 })
153 .collect()
154}
155
107fn 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> {
108 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?); 157 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?);
109 158
@@ -152,6 +201,21 @@ mod tests {
152 } 201 }
153 202
154 #[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]
155 fn partial_fill_record_tuple() { 219 fn partial_fill_record_tuple() {
156 check_assist( 220 check_assist(
157 fill_match_arms, 221 fill_match_arms,
@@ -308,6 +372,169 @@ mod tests {
308 } 372 }
309 373
310 #[test] 374 #[test]
375 fn fill_match_arms_tuple_of_enum() {
376 check_assist(
377 fill_match_arms,
378 r#"
379 enum A {
380 One,
381 Two,
382 }
383 enum B {
384 One,
385 Two,
386 }
387
388 fn main() {
389 let a = A::One;
390 let b = B::One;
391 match (a<|>, b) {}
392 }
393 "#,
394 r#"
395 enum A {
396 One,
397 Two,
398 }
399 enum B {
400 One,
401 Two,
402 }
403
404 fn main() {
405 let a = A::One;
406 let b = B::One;
407 match <|>(a, b) {
408 (A::One, B::One) => (),
409 (A::One, B::Two) => (),
410 (A::Two, B::One) => (),
411 (A::Two, B::Two) => (),
412 }
413 }
414 "#,
415 );
416 }
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
462 #[test]
463 fn fill_match_arms_tuple_of_enum_partial() {
464 check_assist_not_applicable(
465 fill_match_arms,
466 r#"
467 enum A {
468 One,
469 Two,
470 }
471 enum B {
472 One,
473 Two,
474 }
475
476 fn main() {
477 let a = A::One;
478 let b = B::One;
479 match (a<|>, b) {
480 (A::Two, B::One) => (),
481 }
482 }
483 "#,
484 );
485 }
486
487 #[test]
488 fn fill_match_arms_tuple_of_enum_not_applicable() {
489 check_assist_not_applicable(
490 fill_match_arms,
491 r#"
492 enum A {
493 One,
494 Two,
495 }
496 enum B {
497 One,
498 Two,
499 }
500
501 fn main() {
502 let a = A::One;
503 let b = B::One;
504 match (a<|>, b) {
505 (A::Two, B::One) => (),
506 (A::One, B::One) => (),
507 (A::One, B::Two) => (),
508 (A::Two, B::Two) => (),
509 }
510 }
511 "#,
512 );
513 }
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
537 #[test]
311 fn test_fill_match_arm_refs() { 538 fn test_fill_match_arm_refs() {
312 check_assist( 539 check_assist(
313 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 9f6f1cc53..9257ccd1a 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -136,6 +136,20 @@ 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
144pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat {
145 let pats_str = pats.into_iter().map(|p| p.to_string()).join(", ");
146 return from_text(&format!("({})", pats_str));
147
148 fn from_text(text: &str) -> ast::TuplePat {
149 ast_from_text(&format!("fn f({}: ())", text))
150 }
151}
152
139pub fn tuple_struct_pat( 153pub fn tuple_struct_pat(
140 path: ast::Path, 154 path: ast::Path,
141 pats: impl IntoIterator<Item = ast::Pat>, 155 pats: impl IntoIterator<Item = ast::Pat>,