From bc48c9d5116f08efea26da94c82a3eaa1622fc5d Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Mon, 23 Mar 2020 05:19:09 -0700 Subject: review comments --- crates/ra_assists/src/handlers/fill_match_arms.rs | 124 +++++++++++++++++++--- crates/ra_syntax/src/ast/make.rs | 7 +- 2 files changed, 114 insertions(+), 17 deletions(-) diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs index d207c3307..7463b2af7 100644 --- a/crates/ra_assists/src/handlers/fill_match_arms.rs +++ b/crates/ra_assists/src/handlers/fill_match_arms.rs @@ -2,9 +2,8 @@ use std::iter; -use itertools::Itertools; - use hir::{Adt, HasSource, Semantics}; +use itertools::Itertools; use ra_ide_db::RootDatabase; use crate::{Assist, AssistCtx, AssistId}; @@ -61,20 +60,29 @@ pub(crate) fn fill_match_arms(ctx: AssistCtx) -> Option { .map(|pat| make::match_arm(iter::once(pat), make::expr_unit())) .collect() } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { - // partial fill not currently supported for tuple of enums + // Partial fill not currently supported for tuple of enums. if !arms.is_empty() { return None; } + // We do not currently support filling match arms for a tuple + // containing a single enum. + if enum_defs.len() < 2 { + return None; + } + + // When calculating the match arms for a tuple of enums, we want + // to create a match arm for each possible combination of enum + // values. The `multi_cartesian_product` method transforms + // Vec> into Vec<(EnumVariant, .., EnumVariant)> + // where each tuple represents a proposed match arm. enum_defs .into_iter() .map(|enum_def| enum_def.variants(ctx.db)) .multi_cartesian_product() .map(|variants| { - let patterns = variants - .into_iter() - .filter_map(|variant| build_pat(ctx.db, module, variant)) - .collect::>(); + let patterns = + variants.into_iter().filter_map(|variant| build_pat(ctx.db, module, variant)); ast::Pat::from(make::tuple_pat(patterns)) }) .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) @@ -130,16 +138,19 @@ fn resolve_tuple_of_enum_def( sema: &Semantics, expr: &ast::Expr, ) -> Option> { - Some( - sema.type_of_expr(&expr)? - .tuple_fields(sema.db) - .iter() - .map(|ty| match ty.as_adt() { - Some(Adt::Enum(e)) => e, - _ => panic!("handle the case of tuple containing non-enum"), + sema.type_of_expr(&expr)? + .tuple_fields(sema.db) + .iter() + .map(|ty| { + ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { + Some(Adt::Enum(e)) => Some(e), + // For now we only handle expansion for a tuple of enums. Here + // we map non-enum items to None and rely on `collect` to + // convert Vec> into Option>. + _ => None, }) - .collect(), - ) + }) + .collect() } fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::EnumVariant) -> Option { @@ -189,6 +200,21 @@ mod tests { ); } + #[test] + fn tuple_of_non_enum() { + // for now this case is not handled, although it potentially could be + // in the future + check_assist_not_applicable( + fill_match_arms, + r#" + fn main() { + match (0, false)<|> { + } + } + "#, + ); + } + #[test] fn partial_fill_record_tuple() { check_assist( @@ -389,6 +415,50 @@ mod tests { ); } + #[test] + fn fill_match_arms_tuple_of_enum_ref() { + check_assist( + fill_match_arms, + r#" + enum A { + One, + Two, + } + enum B { + One, + Two, + } + + fn main() { + let a = A::One; + let b = B::One; + match (&a<|>, &b) {} + } + "#, + r#" + enum A { + One, + Two, + } + enum B { + One, + Two, + } + + fn main() { + let a = A::One; + let b = B::One; + match <|>(&a, &b) { + (A::One, B::One) => (), + (A::One, B::Two) => (), + (A::Two, B::One) => (), + (A::Two, B::Two) => (), + } + } + "#, + ); + } + #[test] fn fill_match_arms_tuple_of_enum_partial() { check_assist_not_applicable( @@ -442,6 +512,28 @@ mod tests { ); } + #[test] + fn fill_match_arms_single_element_tuple_of_enum() { + // For now we don't hande the case of a single element tuple, but + // we could handle this in the future if `make::tuple_pat` allowed + // creating a tuple with a single pattern. + check_assist_not_applicable( + fill_match_arms, + r#" + enum A { + One, + Two, + } + + fn main() { + let a = A::One; + match (a<|>, ) { + } + } + "#, + ); + } + #[test] fn test_fill_match_arm_refs() { check_assist( diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 9d8ed6238..9257ccd1a 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs @@ -136,8 +136,13 @@ pub fn placeholder_pat() -> ast::PlaceholderPat { } } +/// Creates a tuple of patterns from an interator of patterns. +/// +/// Invariant: `pats` must be length > 1 +/// +/// FIXME handle `pats` length == 1 pub fn tuple_pat(pats: impl IntoIterator) -> ast::TuplePat { - let pats_str = pats.into_iter().map(|p| p.syntax().to_string()).join(", "); + let pats_str = pats.into_iter().map(|p| p.to_string()).join(", "); return from_text(&format!("({})", pats_str)); fn from_text(text: &str) -> ast::TuplePat { -- cgit v1.2.3