diff options
author | Josh Mcguigan <[email protected]> | 2020-03-23 05:42:32 +0000 |
---|---|---|
committer | Josh Mcguigan <[email protected]> | 2020-03-23 06:28:25 +0000 |
commit | 2afccbe47727d9d2787f76efd67f5b5d9ff1d55a (patch) | |
tree | 01cd96ea1bbcd492df1082907a1106b89aab7eaa /crates | |
parent | baa11d52f45fed76a65863c64938e43443bb9bb8 (diff) |
implement fill match arm assist for tuple of enums
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_assists/src/handlers/fill_match_arms.rs | 163 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 9 |
3 files changed, 159 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 | |||
11 | format-buf = "1.0.0" | 11 | format-buf = "1.0.0" |
12 | join_to_string = "0.1.3" | 12 | join_to_string = "0.1.3" |
13 | rustc-hash = "1.1.0" | 13 | rustc-hash = "1.1.0" |
14 | itertools = "0.8.2" | ||
14 | 15 | ||
15 | ra_syntax = { path = "../ra_syntax" } | 16 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_text_edit = { path = "../ra_text_edit" } | 17 | ra_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 | ||
3 | use std::iter; | 3 | use std::iter; |
4 | 4 | ||
5 | use itertools::Itertools; | ||
6 | |||
5 | use hir::{Adt, HasSource, Semantics}; | 7 | use hir::{Adt, HasSource, Semantics}; |
6 | use ra_ide_db::RootDatabase; | 8 | use 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 | ||
129 | fn 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 | |||
107 | fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> { | 145 | fn 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 | ||
139 | 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(", "); | ||
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 | |||
139 | pub fn tuple_struct_pat( | 148 | pub 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>, |