Skip to content

Commit edbb179

Browse files
committed
Fill partial match arms for a tuple of enums
1 parent e53919a commit edbb179

File tree

1 file changed

+37
-17
lines changed

1 file changed

+37
-17
lines changed

crates/ide_assists/src/handlers/fill_match_arms.rs

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::iter;
22

3+
use either::Either;
34
use hir::{Adt, HasSource, ModuleDef, Semantics};
45
use ide_db::helpers::{mod_path_to_ast, FamousDefs};
56
use ide_db::RootDatabase;
@@ -48,6 +49,16 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
4849
}
4950
}
5051

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+
5162
let module = ctx.sema.scope(expr.syntax()).module()?;
5263

5364
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<
5667
let mut variants = variants
5768
.into_iter()
5869
.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))
6071
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
6172
.collect::<Vec<_>>();
6273
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<
6677
}
6778
variants
6879
} 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-
7480
// When calculating the match arms for a tuple of enums, we want
7581
// to create a match arm for each possible combination of enum
7682
// values. The `multi_cartesian_product` method transforms
@@ -85,7 +91,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
8591
variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
8692
ast::Pat::from(make::tuple_pat(patterns))
8793
})
88-
.filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
94+
.filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
8995
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
9096
.collect()
9197
} else {
@@ -128,15 +134,14 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
128134
)
129135
}
130136

131-
fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
132-
existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| {
133-
// Special casee OrPat as separate top-level pats
134-
let top_level_pats: Vec<Pat> = match pat {
135-
Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(),
136-
_ => vec![pat],
137-
};
138-
139-
!top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var))
137+
fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
138+
!existing_pats.iter().any(|pat| match (pat, var) {
139+
(Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => {
140+
// `does_pat_match_variant` gives false positives for tuple patterns
141+
// Fixme: this is still somewhat limited
142+
tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v))
143+
}
144+
_ => does_pat_match_variant(pat, var),
140145
})
141146
}
142147

@@ -467,7 +472,7 @@ fn main() {
467472

468473
#[test]
469474
fn fill_match_arms_tuple_of_enum_partial() {
470-
check_assist_not_applicable(
475+
check_assist(
471476
fill_match_arms,
472477
r#"
473478
enum A { One, Two }
@@ -481,6 +486,21 @@ fn main() {
481486
}
482487
}
483488
"#,
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+
"#,
484504
);
485505
}
486506

0 commit comments

Comments
 (0)