diff options
| -rw-r--r-- | crates/ide_assists/src/handlers/fill_match_arms.rs | 128 |
1 files changed, 100 insertions, 28 deletions
diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 80bd1b7e8a4..a30c4d04ee5 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs @@ -1,5 +1,6 @@ use std::iter; +use either::Either; use hir::{Adt, HasSource, ModuleDef, Semantics}; use ide_db::helpers::{mod_path_to_ast, FamousDefs}; use ide_db::RootDatabase; @@ -7,7 +8,7 @@ use itertools::Itertools; use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat}; use crate::{ - utils::{does_pat_match_variant, render_snippet, Cursor}, + utils::{self, render_snippet, Cursor}, AssistContext, AssistId, AssistKind, Assists, }; @@ -48,6 +49,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< } } + let top_lvl_pats: Vec<_> = arms + .iter() + .filter_map(ast::MatchArm::pat) + .flat_map(|pat| match pat { + // Special casee OrPat as separate top-level pats + Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), + _ => Either::Right(iter::once(pat)), + }) + // Exclude top level wildcards so that they are expanded by this assist, retains status quo in #8129. + .filter(|pat| !matches!(pat, Pat::WildcardPat(_))) + .collect(); + let module = ctx.sema.scope(expr.syntax()).module()?; 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< let mut variants = variants .into_iter() .filter_map(|variant| build_pat(ctx.db(), module, variant)) - .filter(|variant_pat| is_variant_missing(&mut arms, variant_pat)) + .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) .collect::<Vec<_>>(); 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< } variants } else if let Some(enum_defs) = resolve_tuple_of_enum_def(&ctx.sema, &expr) { - // Partial fill not currently supported for tuple of enums. - if !arms.is_empty() { - 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 @@ -85,7 +93,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< 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)) + .filter(|variant_pat| is_variant_missing(&top_lvl_pats, variant_pat)) .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block())) .collect() } else { @@ -128,16 +136,19 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< ) } -fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool { - existing_arms.iter().filter_map(|arm| arm.pat()).all(|pat| { - // Special casee OrPat as separate top-level pats - let top_level_pats: Vec<Pat> = match pat { - Pat::OrPat(pats) => pats.pats().collect::<Vec<_>>(), - _ => vec![pat], - }; +fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool { + !existing_pats.iter().any(|pat| does_pat_match_variant(pat, var)) +} - !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var)) - }) +// Fixme: this is still somewhat limited, use hir_ty::diagnostics::match_check? +fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { + match (pat, var) { + (Pat::WildcardPat(_), _) => true, + (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => { + tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v)) + } + _ => utils::does_pat_match_variant(pat, var), + } } fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { @@ -467,20 +478,81 @@ fn main() { #[test] fn fill_match_arms_tuple_of_enum_partial() { - check_assist_not_applicable( + check_assist( fill_match_arms, r#" - enum A { One, Two } - enum B { One, Two } +enum A { One, Two } +enum B { One, Two } - fn main() { - let a = A::One; - let b = B::One; - match (a$0, b) { - (A::Two, B::One) => {} - } - } - "#, +fn main() { + let a = A::One; + let b = B::One; + match (a$0, b) { + (A::Two, B::One) => {} + } +} +"#, + r#" +enum A { One, Two } +enum B { One, Two } + +fn main() { + let a = A::One; + let b = B::One; + match (a, b) { + (A::Two, B::One) => {} + $0(A::One, B::One) => {} + (A::One, B::Two) => {} + (A::Two, B::Two) => {} + } +} +"#, + ); + } + + #[test] + fn fill_match_arms_tuple_of_enum_partial_with_wildcards() { + let ra_fixture = r#" +fn main() { + let a = Some(1); + let b = Some(()); + match (a$0, b) { + (Some(_), _) => {} + (None, Some(_)) => {} + } +} +"#; + check_assist( + fill_match_arms, + &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE), + r#" +fn main() { + let a = Some(1); + let b = Some(()); + match (a, b) { + (Some(_), _) => {} + (None, Some(_)) => {} + $0(None, None) => {} + } +} +"#, + ); + } + + #[test] + fn fill_match_arms_partial_with_deep_pattern() { + // Fixme: cannot handle deep patterns + let ra_fixture = r#" +fn main() { + match $0Some(true) { + Some(true) => {} + None => {} + } +} +"#; + check_assist_not_applicable( + fill_match_arms, + &format!("//- /main.rs crate:main deps:core{}{}", ra_fixture, FamousDefs::FIXTURE), ); } |
