Skip to content

Commit 2afccbe

Browse files
committed
implement fill match arm assist for tuple of enums
1 parent baa11d5 commit 2afccbe

File tree

4 files changed

+160
-14
lines changed

4 files changed

+160
-14
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_assists/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ doctest = false
1111
format-buf = "1.0.0"
1212
join_to_string = "0.1.3"
1313
rustc-hash = "1.1.0"
14+
itertools = "0.8.2"
1415

1516
ra_syntax = { path = "../ra_syntax" }
1617
ra_text_edit = { path = "../ra_text_edit" }

crates/ra_assists/src/handlers/fill_match_arms.rs

Lines changed: 149 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
33
use std::iter;
44

5+
use itertools::Itertools;
6+
57
use hir::{Adt, HasSource, Semantics};
68
use ra_ide_db::RootDatabase;
79

@@ -39,13 +41,6 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
3941
let match_arm_list = match_expr.match_arm_list()?;
4042

4143
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-
}
4944

5045
let mut arms: Vec<MatchArm> = match_arm_list.arms().collect();
5146
if arms.len() == 1 {
@@ -54,13 +49,40 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
5449
}
5550
}
5651

57-
let db = ctx.db;
58-
let missing_arms: Vec<MatchArm> = variants
59-
.into_iter()
60-
.filter_map(|variant| build_pat(db, module, variant))
61-
.filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
62-
.map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
63-
.collect();
52+
let module = ctx.sema.scope(expr.syntax()).module()?;
53+
54+
let missing_arms: Vec<MatchArm> = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) {
55+
let variants = enum_def.variants(ctx.db);
56+
57+
variants
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+
};
6486

6587
if missing_arms.is_empty() {
6688
return None;
@@ -104,6 +126,22 @@ fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
104126
})
105127
}
106128

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+
107145
fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
108146
let path = crate::ast_transform::path_to_ast(module.find_use_path(db, var.into())?);
109147

@@ -307,6 +345,103 @@ mod tests {
307345
);
308346
}
309347

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+
310445
#[test]
311446
fn test_fill_match_arm_refs() {
312447
check_assist(

crates/ra_syntax/src/ast/make.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,15 @@ pub fn placeholder_pat() -> ast::PlaceholderPat {
136136
}
137137
}
138138

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+
139148
pub fn tuple_struct_pat(
140149
path: ast::Path,
141150
pats: impl IntoIterator<Item = ast::Pat>,

0 commit comments

Comments
 (0)