aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists')
-rw-r--r--crates/ide_assists/src/handlers/fill_match_arms.rs128
1 files changed, 100 insertions, 28 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..a30c4d04e 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;
@@ -7,7 +8,7 @@ use itertools::Itertools;
7use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; 8use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
8 9
9use crate::{ 10use crate::{
10 utils::{does_pat_match_variant, render_snippet, Cursor}, 11 utils::{self, render_snippet, Cursor},
11 AssistContext, AssistId, AssistKind, Assists, 12 AssistContext, AssistId, AssistKind, Assists,
12}; 13};
13 14
@@ -48,6 +49,18 @@ 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 // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129.
61 .filter(|pat| !matches!(pat, Pat::WildcardPat(_)))
62 .collect();
63
51 let module = ctx.sema.scope(expr.syntax()).module()?; 64 let module = ctx.sema.scope(expr.syntax()).module()?;
52 65
53 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { 66 let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
@@ -56,7 +69,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
56 let mut variants = variants 69 let mut variants = variants
57 .into_iter() 70 .into_iter()
58 .filter_map(|variant| build_pat(ctx.db(), module, variant)) 71 .filter_map(|variant| build_pat(ctx.db(), module, variant))
59 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 72 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
60 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 73 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
61 .collect::<Vec<_>>(); 74 .collect::<Vec<_>>();
62 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { 75 if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() {
@@ -66,11 +79,6 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
66 } 79 }
67 variants 80 variants
68 } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { 81 } 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 82 // When calculating the match arms for a tuple of enums, we want
75 // to create a match arm for each possible combination of enum 83 // to create a match arm for each possible combination of enum
76 // values. The `multi_cartesian_product` method transforms 84 // values. The `multi_cartesian_product` method transforms
@@ -85,7 +93,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)); 93 variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
86 ast::Pat::from(make::tuple_pat(patterns)) 94 ast::Pat::from(make::tuple_pat(patterns))
87 }) 95 })
88 .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) 96 .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
89 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) 97 .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
90 .collect() 98 .collect()
91 } else { 99 } else {
@@ -128,16 +136,19 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
128 ) 136 )
129} 137}
130 138
131fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { 139fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
132 existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| { 140 !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
133 // Special casee OrPat as separate top-level pats 141}
134 let top_level_pats: Vec<Pat> = match pat {
135 Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
136 _ => vec![pat],
137 };
138 142
139 !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var)) 143// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
140 }) 144fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
145 match (pat, var) {
146 (Pat::WildcardPat(_), _) => true,
147 (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
148 tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
149 }
150 _ => utils::does_pat_match_variant(pat, var),
151 }
141} 152}
142 153
143fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { 154fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
@@ -467,20 +478,81 @@ fn main() {
467 478
468 #[test] 479 #[test]
469 fn fill_match_arms_tuple_of_enum_partial() { 480 fn fill_match_arms_tuple_of_enum_partial() {
470 check_assist_not_applicable( 481 check_assist(
471 fill_match_arms, 482 fill_match_arms,
472 r#" 483 r#"
473 enum A { One, Two } 484enum A { One, Two }
474 enum B { One, Two } 485enum B { One, Two }
475 486
476 fn main() { 487fn main() {
477 let a = A::One; 488 let a = A::One;
478 let b = B::One; 489 let b = B::One;
479 match (a$0, b) { 490 match (a$0, b) {
480 (A::Two, B::One) => {} 491 (A::Two, B::One) => {}
481 } 492 }
482 } 493}
483 "#, 494"#,
495 r#"
496enum A { One, Two }
497enum B { One, Two }
498
499fn main() {
500 let a = A::One;
501 let b = B::One;
502 match (a, b) {
503 (A::Two, B::One) => {}
504 $0(A::One, B::One) => {}
505 (A::One, B::Two) => {}
506 (A::Two, B::Two) => {}
507 }
508}
509"#,
510 );
511 }
512
513 #[test]
514 fn fill_match_arms_tuple_of_enum_partial_with_wildcards() {
515 let ra_fixture = r#"
516fn main() {
517 let a = Some(1);
518 let b = Some(());
519 match (a$0, b) {
520 (Some(_), _) => {}
521 (None, Some(_)) => {}
522 }
523}
524"#;
525 check_assist(
526 fill_match_arms,
527 &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE),
528 r#"
529fn main() {
530 let a = Some(1);
531 let b = Some(());
532 match (a, b) {
533 (Some(_), _) => {}
534 (None, Some(_)) => {}
535 $0(None, None) => {}
536 }
537}
538"#,
539 );
540 }
541
542 #[test]
543 fn fill_match_arms_partial_with_deep_pattern() {
544 // Fixme: cannot handle deep patterns
545 let ra_fixture = r#"
546fn main() {
547 match $0Some(true) {
548 Some(true) => {}
549 None => {}
550 }
551}
552"#;
553 check_assist_not_applicable(
554 fill_match_arms,
555 &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE),
484 ); 556 );
485 } 557 }
486 558