Skip to content

Commit bc48c9d

Browse files
committed
review comments
1 parent 2afccbe commit bc48c9d

File tree

2 files changed

+114
-17
lines changed

2 files changed

+114
-17
lines changed

crates/ra_assists/src/handlers/fill_match_arms.rs

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

5-
use itertools::Itertools;
6-
75
use hir::{Adt, HasSource, Semantics};
6+
use itertools::Itertools;
87
use ra_ide_db::RootDatabase;
98

109
use crate::{Assist, AssistCtx, AssistId};
@@ -61,20 +60,29 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option<Assist> {
6160
.map(|pat| make::match_arm(iter::once(pat), make::expr_unit()))
6261
.collect()
6362
} else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) {
64-
// partial fill not currently supported for tuple of enums
63+
// Partial fill not currently supported for tuple of enums.
6564
if !arms.is_empty() {
6665
return None;
6766
}
6867

68+
// We do not currently support filling match arms for a tuple
69+
// containing a single enum.
70+
if enum_defs.len() < 2 {
71+
return None;
72+
}
73+
74+
// When calculating the match arms for a tuple of enums, we want
75+
// to create a match arm for each possible combination of enum
76+
// values. The `multi_cartesian_product` method transforms
77+
// Vec<Vec<EnumVariant>> into Vec<(EnumVariant, .., EnumVariant)>
78+
// where each tuple represents a proposed match arm.
6979
enum_defs
7080
.into_iter()
7181
.map(|enum_def| enum_def.variants(ctx.db))
7282
.multi_cartesian_product()
7383
.map(|variants| {
74-
let patterns = variants
75-
.into_iter()
76-
.filter_map(|variant| build_pat(ctx.db, module, variant))
77-
.collect::<Vec<_>>();
84+
let patterns =
85+
variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant));
7886
ast::Pat::from(make::tuple_pat(patterns))
7987
})
8088
.filter(|variant_pat| is_variant_missing(&mut arms, variant_pat))
@@ -130,16 +138,19 @@ fn resolve_tuple_of_enum_def(
130138
sema: &Semantics<RootDatabase>,
131139
expr: &ast::Expr,
132140
) -> 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"),
141+
sema.type_of_expr(&expr)?
142+
.tuple_fields(sema.db)
143+
.iter()
144+
.map(|ty| {
145+
ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
146+
Some(Adt::Enum(e)) => Some(e),
147+
// For now we only handle expansion for a tuple of enums. Here
148+
// we map non-enum items to None and rely on `collect` to
149+
// convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
150+
_ => None,
140151
})
141-
.collect(),
142-
)
152+
})
153+
.collect()
143154
}
144155

145156
fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option<ast::Pat> {
@@ -189,6 +200,21 @@ mod tests {
189200
);
190201
}
191202

203+
#[test]
204+
fn tuple_of_non_enum() {
205+
// for now this case is not handled, although it potentially could be
206+
// in the future
207+
check_assist_not_applicable(
208+
fill_match_arms,
209+
r#"
210+
fn main() {
211+
match (0, false)<|> {
212+
}
213+
}
214+
"#,
215+
);
216+
}
217+
192218
#[test]
193219
fn partial_fill_record_tuple() {
194220
check_assist(
@@ -389,6 +415,50 @@ mod tests {
389415
);
390416
}
391417

418+
#[test]
419+
fn fill_match_arms_tuple_of_enum_ref() {
420+
check_assist(
421+
fill_match_arms,
422+
r#"
423+
enum A {
424+
One,
425+
Two,
426+
}
427+
enum B {
428+
One,
429+
Two,
430+
}
431+
432+
fn main() {
433+
let a = A::One;
434+
let b = B::One;
435+
match (&a<|>, &b) {}
436+
}
437+
"#,
438+
r#"
439+
enum A {
440+
One,
441+
Two,
442+
}
443+
enum B {
444+
One,
445+
Two,
446+
}
447+
448+
fn main() {
449+
let a = A::One;
450+
let b = B::One;
451+
match <|>(&a, &b) {
452+
(A::One, B::One) => (),
453+
(A::One, B::Two) => (),
454+
(A::Two, B::One) => (),
455+
(A::Two, B::Two) => (),
456+
}
457+
}
458+
"#,
459+
);
460+
}
461+
392462
#[test]
393463
fn fill_match_arms_tuple_of_enum_partial() {
394464
check_assist_not_applicable(
@@ -442,6 +512,28 @@ mod tests {
442512
);
443513
}
444514

515+
#[test]
516+
fn fill_match_arms_single_element_tuple_of_enum() {
517+
// For now we don't hande the case of a single element tuple, but
518+
// we could handle this in the future if `make::tuple_pat` allowed
519+
// creating a tuple with a single pattern.
520+
check_assist_not_applicable(
521+
fill_match_arms,
522+
r#"
523+
enum A {
524+
One,
525+
Two,
526+
}
527+
528+
fn main() {
529+
let a = A::One;
530+
match (a<|>, ) {
531+
}
532+
}
533+
"#,
534+
);
535+
}
536+
445537
#[test]
446538
fn test_fill_match_arm_refs() {
447539
check_assist(

crates/ra_syntax/src/ast/make.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,13 @@ pub fn placeholder_pat() -> ast::PlaceholderPat {
136136
}
137137
}
138138

139+
/// Creates a tuple of patterns from an interator of patterns.
140+
///
141+
/// Invariant: `pats` must be length > 1
142+
///
143+
/// FIXME handle `pats` length == 1
139144
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(", ");
145+
let pats_str = pats.into_iter().map(|p| p.to_string()).join(", ");
141146
return from_text(&format!("({})", pats_str));
142147

143148
fn from_text(text: &str) -> ast::TuplePat {

0 commit comments

Comments
 (0)