aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Mcguigan <[email protected]>2020-03-23 05:42:32 +0000
committerJosh Mcguigan <[email protected]>2020-03-23 06:28:25 +0000
commit2afccbe47727d9d2787f76efd67f5b5d9ff1d55a (patch)
tree01cd96ea1bbcd492df1082907a1106b89aab7eaa
parentbaa11d52f45fed76a65863c64938e43443bb9bb8 (diff)
implement fill match arm assist for tuple of enums
-rw-r--r--Cargo.lock1
-rw-r--r--crates/ra_assists/Cargo.toml1
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs163
-rw-r--r--crates/ra_syntax/src/ast/make.rs9
4 files changed, 160 insertions, 14 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f8d26192d..eb5247b69 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -876,6 +876,7 @@ name = "ra_assists"
876version = "0.1.0" 876version = "0.1.0"
877dependencies = [ 877dependencies = [
878 "format-buf", 878 "format-buf",
879 "itertools",
879 "join_to_string", 880 "join_to_string",
880 "ra_db", 881 "ra_db",
881 "ra_fmt", 882 "ra_fmt",
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..d207c3307 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -2,6 +2,8 @@
2 2
3use std::iter; 3use std::iter;
4 4
5use itertools::Itertools;
6
5use hir::{Adt, HasSource, Semantics}; 7use hir::{Adt, HasSource, Semantics};
6use ra_ide_db::RootDatabase; 8use ra_ide_db::RootDatabase;
7 9
@@ -39,13 +41,6 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
39 let match_arm_list = match_expr.match_arm_list()?; 41 let match_arm_list = match_expr.match_arm_list()?;
40 42
41 let expr = match_expr.expr()?; 43 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 44
50 let mut arms: Vec<MatchArm> = match_arm_list.arms().collect(); 45 let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
51 if arms.len() == 1 { 46 if arms.len() == 1 {
@@ -54,13 +49,40 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
54 } 49 }
55 } 50 }
56 51
57 let db = ctx.db; 52 let module = ctx.sema.scope(expr.syntax()).module()?;
58 let missing_arms: Vec<MatchArm> = variants 53
59 .into_iter() 54 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)) 55 let variants = enum_def.variants(ctx.db);
61 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 56
62 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) 57 variants
63 .collect(); 58 .into_iter()
59 .filter_map(|variant| build_pat(ctx.db, module, variant))
60 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
61 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
62 .collect()
63 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
64 // partial fill not currently supported for tuple of enums
65 if !arms.is_empty() {
66 return None;
67 }
68
69 enum_defs
70 .into_iter()
71 .map(|enum_def| enum_def.variants(ctx.db))
72 .multi_cartesian_product()
73 .map(|variants| {
74 let patterns = variants
75 .into_iter()
76 .filter_map(|variant| build_pat(ctx.db, module, variant))
77 .collect::<Vec<_>>();
78 ast::Pat::from(make::tuple_pat(patterns))
79 })
80 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
81 .map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
82 .collect()
83 } else {
84 return None;
85 };
64 86
65 if missing_arms.is_empty() { 87 if missing_arms.is_empty() {
66 return None; 88 return None;
@@ -104,6 +126,22 @@ fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
104 }) 126 })
105} 127}
106 128
129fn resolve_tuple_of_enum_def(
130 sema: &Semantics<RootDatabase>,
131 expr: &ast::Expr,
132) -> 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"),
140 })
141 .collect(),
142 )
143}
144
107fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { 145fn 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())?); 146 let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?);
109 147
@@ -308,6 +346,103 @@ mod tests {
308 } 346 }
309 347
310 #[test] 348 #[test]
349 fn fill_match_arms_tuple_of_enum() {
350 check_assist(
351 fill_match_arms,
352 r#"
353 enum A {
354 One,
355 Two,
356 }
357 enum B {
358 One,
359 Two,
360 }
361
362 fn main() {
363 let a = A::One;
364 let b = B::One;
365 match (a<|>, b) {}
366 }
367 "#,
368 r#"
369 enum A {
370 One,
371 Two,
372 }
373 enum B {
374 One,
375 Two,
376 }
377
378 fn main() {
379 let a = A::One;
380 let b = B::One;
381 match <|>(a, b) {
382 (A::One, B::One) => (),
383 (A::One, B::Two) => (),
384 (A::Two, B::One) => (),
385 (A::Two, B::Two) => (),
386 }
387 }
388 "#,
389 );
390 }
391
392 #[test]
393 fn fill_match_arms_tuple_of_enum_partial() {
394 check_assist_not_applicable(
395 fill_match_arms,
396 r#"
397 enum A {
398 One,
399 Two,
400 }
401 enum B {
402 One,
403 Two,
404 }
405
406 fn main() {
407 let a = A::One;
408 let b = B::One;
409 match (a<|>, b) {
410 (A::Two, B::One) => (),
411 }
412 }
413 "#,
414 );
415 }
416
417 #[test]
418 fn fill_match_arms_tuple_of_enum_not_applicable() {
419 check_assist_not_applicable(
420 fill_match_arms,
421 r#"
422 enum A {
423 One,
424 Two,
425 }
426 enum B {
427 One,
428 Two,
429 }
430
431 fn main() {
432 let a = A::One;
433 let b = B::One;
434 match (a<|>, b) {
435 (A::Two, B::One) => (),
436 (A::One, B::One) => (),
437 (A::One, B::Two) => (),
438 (A::Two, B::Two) => (),
439 }
440 }
441 "#,
442 );
443 }
444
445 #[test]
311 fn test_fill_match_arm_refs() { 446 fn test_fill_match_arm_refs() {
312 check_assist( 447 check_assist(
313 fill_match_arms, 448 fill_match_arms,
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 9f6f1cc53..9d8ed6238 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -136,6 +136,15 @@ pub fn placeholder_pat() -> ast::PlaceholderPat {
136 } 136 }
137} 137}
138 138
139pub 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(", ");
141 return from_text(&format!("({})", pats_str));
142
143 fn from_text(text: &str) -> ast::TuplePat {
144 ast_from_text(&format!("fn f({}: ())", text))
145 }
146}
147
139pub fn tuple_struct_pat( 148pub fn tuple_struct_pat(
140 path: ast::Path, 149 path: ast::Path,
141 pats: impl IntoIterator<Item = ast::Pat>, 150 pats: impl IntoIterator<Item = ast::Pat>,