aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
authorDawer <[email protected]>2021-04-16 21:09:09 +0100
committerDawer <[email protected]>2021-04-16 21:09:09 +0100
commitedbb1797fb16523f3d49b048b8d8ee8fe1c48c99 (patch)
treeb3f82be2a6ab671710b07a6ede308fd00c1cb52d /crates
parente53919a425bf062056a23e825fb30a51a639385c (diff)
Fill partial match arms for a tuple of enums
Diffstat (limited to 'crates')
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs54
1 files changed, 37 insertions, 17 deletions
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs
index 80bd1b7e8..e4794f17c 100644
--- a/crates/ide_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ide_assists/src/handlers/fill_match_arms.rs
@@ -1,5 +1,6 @@
1use std::iter; 1use std::iter;
2 2
3use either::Either;
3use hir::{Adt, HasSource, ModuleDef, Semantics}; 4use hir::{Adt, HasSource, ModuleDef, Semantics};
4use ide_db::helpers::{mod_path_to_ast, FamousDefs}; 5use ide_db::helpers::{mod_path_to_ast, FamousDefs};
5use ide_db::RootDatabase; 6use ide_db::RootDatabase;
@@ -48,6 +49,16 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
48 } 49 }
49 } 50 }
50 51
52 let top_lvl_pats: Vec<_> = arms
53 .iter()
54 .filter_map(ast::MatchArm::pat)
55 .flat_map(|pat| match pat {
56 // Special casee OrPat as separate top-level pats
57 Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
58 _ => Either::Right(iter::once(pat)),
59 })
60 .collect();
61
51 let module = ctx.sema.scope(expr.syntax()).module()?; 62 let module = ctx.sema.scope(expr.syntax()).module()?;
52 63
53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { 64 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
@@ -56,7 +67,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
56 let mut variants = variants 67 let mut variants = variants
57 .into_iter() 68 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db(), module, variant)) 69 .filter_map(|variant| build_pat(ctx.db(), module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 70 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 71 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect::<Vec<_>>(); 72 .collect::<Vec<_>>();
62 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 73 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() {
@@ -66,11 +77,6 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
66 } 77 }
67 variants 78 variants
68 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { 79 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
69 // Partial fill not currently supported for tuple of enums.
70 if !arms.is_empty() {
71 return None;
72 }
73
74 // When calculating the match arms for a tuple of enums, we want 80 // When calculating the match arms for a tuple of enums, we want
75 // to create a match arm for each possible combination of enum 81 // to create a match arm for each possible combination of enum
76 // values. The `multi_cartesian_product` method transforms 82 // values. The `multi_cartesian_product` method transforms
@@ -85,7 +91,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
85 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant)); 91 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
86 ast::Pat::from(make::tuple_pat(patterns)) 92 ast::Pat::from(make::tuple_pat(patterns))
87 }) 93 })
88 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 94 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
89 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 95 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
90 .collect() 96 .collect()
91 } else { 97 } else {
@@ -128,15 +134,14 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
128 ) 134 )
129} 135}
130 136
131fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { 137fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
132 existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| { 138 !existing_pats.iter().any(|pat| match (pat, var) {
133 // Special casee OrPat as separate top-level pats 139 (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
134 let top_level_pats: Vec<Pat> = match pat { 140 // `does_pat_match_variant` gives false positives for tuple patterns
135 Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(), 141 // Fixme: this is still somewhat limited
136 _ => vec![pat], 142 tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
137 }; 143 }
138 144 _ => does_pat_match_variant(pat, var),
139 !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var))
140 }) 145 })
141} 146}
142 147
@@ -467,7 +472,7 @@ fn main() {
467 472
468 #[test] 473 #[test]
469 fn fill_match_arms_tuple_of_enum_partial() { 474 fn fill_match_arms_tuple_of_enum_partial() {
470 check_assist_not_applicable( 475 check_assist(
471 fill_match_arms, 476 fill_match_arms,
472 r#" 477 r#"
473 enum A { One, Two } 478 enum A { One, Two }
@@ -481,6 +486,21 @@ fn main() {
481 } 486 }
482 } 487 }
483 "#, 488 "#,
489 r#"
490 enum A { One, Two }
491 enum B { One, Two }
492
493 fn main() {
494 let a = A::One;
495 let b = B::One;
496 match (a, b) {
497 (A::Two, B::One) => {}
498 $0(A::One, B::One) => {}
499 (A::One, B::Two) => {}
500 (A::Two, B::Two) => {}
501 }
502 }
503 "#,
484 ); 504 );
485 } 505 }
486 506