Skip to content

Commit 65dd942

Browse files
bors[bot]iDawer
andauthored
Merge #8565
8565: Fill match arms assist: add remaining arms for tuple of enums r=iDawer a=iDawer Fix for #8493 However, the assist is still flaky and does not use `hir_ty::diagnostics::match_check` Co-authored-by: Dawer <7803845+iDawer@users.noreply.github.com>
2 parents 75bf832 + 9222d3b commit 65dd942

File tree

1 file changed

+100
-28
lines changed

1 file changed

+100
-28
lines changed

crates/ide_assists/src/handlers/fill_match_arms.rs

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
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;
67
use itertools::Itertools;
78
use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
89

910
use crate::{
10-
utils::{does_pat_match_variant, render_snippet, Cursor},
11+
utils::{self, render_snippet, Cursor},
1112
AssistContext, AssistId, AssistKind, Assists,
1213
};
1314

@@ -48,6 +49,18 @@ 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+
// 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+
5164
let module = ctx.sema.scope(expr.syntax()).module()?;
5265

5366
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<
5669
let mut variants = variants
5770
.into_iter()
5871
.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))
6073
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
6174
.collect::<Vec<_>>();
6275
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<
6679
}
6780
variants
6881
} 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-
7482
// When calculating the match arms for a tuple of enums, we want
7583
// to create a match arm for each possible combination of enum
7684
// values. The `multi_cartesian_product` method transforms
@@ -85,7 +93,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
8593
variants.into_iter().filter_map(|variant| build_pat(ctx.db(), module, variant));
8694
ast::Pat::from(make::tuple_pat(patterns))
8795
})
88-
.filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
96+
.filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat))
8997
.map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
9098
.collect()
9199
} else {
@@ -128,16 +136,19 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
128136
)
129137
}
130138

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-
};
139+
fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool {
140+
!existing_pats.iter().any(|pat| does_pat_match_variant(pat, var))
141+
}
138142

139-
!top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var))
140-
})
143+
// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check?
144+
fn 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+
}
141152
}
142153

143154
fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
@@ -467,20 +478,81 @@ fn main() {
467478

468479
#[test]
469480
fn fill_match_arms_tuple_of_enum_partial() {
470-
check_assist_not_applicable(
481+
check_assist(
471482
fill_match_arms,
472483
r#"
473-
enum A { One, Two }
474-
enum B { One, Two }
484+
enum A { One, Two }
485+
enum B { One, Two }
475486
476-
fn main() {
477-
let a = A::One;
478-
let b = B::One;
479-
match (a$0, b) {
480-
(A::Two, B::One) => {}
481-
}
482-
}
483-
"#,
487+
fn main() {
488+
let a = A::One;
489+
let b = B::One;
490+
match (a$0, b) {
491+
(A::Two, B::One) => {}
492+
}
493+
}
494+
"#,
495+
r#"
496+
enum A { One, Two }
497+
enum B { One, Two }
498+
499+
fn 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#"
516+
fn 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#"
529+
fn 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#"
546+
fn 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),
484556
);
485557
}
486558

0 commit comments

Comments
 (0)