From edbb1797fb16523f3d49b048b8d8ee8fe1c48c99 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Sat, 17 Apr 2021 01:09:09 +0500 Subject: Fill partial match arms for a tuple of enums --- crates/ide_assists/src/handlers/fill_match_arms.rs | 54 +++++++++++++++------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 80bd1b7e8..e4794f17c 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; @@ -48,6 +49,16 @@ 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)), + }) + .collect(); + let module = ctx.sema.scope(expr.syntax()).module()?; let missing_arms: Vec = if let Some(enum_def) = resolve_enum_def(&ctx.sema, &expr) { @@ -56,7 +67,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::>(); if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { @@ -66,11 +77,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 +91,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,15 +134,14 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< ) } -fn is_variant_missing(existing_arms: &mut Vec, 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 = match pat { - Pat::OrPat(pats) => pats.pats().collect::>(), - _ => vec![pat], - }; - - !top_level_pats.iter().any(|pat| does_pat_match_variant(pat, var)) +fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool { + !existing_pats.iter().any(|pat| match (pat, var) { + (Pat::TuplePat(tpat), Pat::TuplePat(tvar)) => { + // `does_pat_match_variant` gives false positives for tuple patterns + // Fixme: this is still somewhat limited + tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v)) + } + _ => does_pat_match_variant(pat, var), }) } @@ -467,7 +472,7 @@ 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 } @@ -481,6 +486,21 @@ fn main() { } } "#, + 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) => {} + } + } + "#, ); } -- cgit v1.2.3 From 76285f16deabe8175f0bfa9ebd913b9edef302f8 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Sat, 17 Apr 2021 15:20:29 +0500 Subject: Test fill-match-arms assist: partial with wildcards --- crates/ide_assists/src/handlers/fill_match_arms.rs | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index e4794f17c..6408d7f0b 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs @@ -504,6 +504,40 @@ fn main() { ); } + // Fixme: This fails with extra useless match arms added. + // To fix, it needs full fledged match exhaustiveness checking from + // hir_ty::diagnostics::match_check + // see https://github.com/rust-analyzer/rust-analyzer/issues/8493 + #[ignore] + #[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_tuple_of_enum_not_applicable() { check_assist_not_applicable( -- cgit v1.2.3 From 51d65caed490122a492622f3ffba4f0f8a81a9e0 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Sun, 18 Apr 2021 16:43:12 +0500 Subject: Prevent adding useless match arms --- crates/ide_assists/src/handlers/fill_match_arms.rs | 38 +++++++++++++++------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 6408d7f0b..800ce972c 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs @@ -8,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, }; @@ -135,14 +135,18 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< } fn is_variant_missing(existing_pats: &[Pat], var: &Pat) -> bool { - !existing_pats.iter().any(|pat| match (pat, var) { + !existing_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)) => { - // `does_pat_match_variant` gives false positives for tuple patterns - // Fixme: this is still somewhat limited tpat.fields().zip(tvar.fields()).all(|(p, v)| does_pat_match_variant(&p, &v)) } - _ => does_pat_match_variant(pat, var), - }) + _ => utils::does_pat_match_variant(pat, var), + } } fn resolve_enum_def(sema: &Semantics, expr: &ast::Expr) -> Option { @@ -504,11 +508,6 @@ fn main() { ); } - // Fixme: This fails with extra useless match arms added. - // To fix, it needs full fledged match exhaustiveness checking from - // hir_ty::diagnostics::match_check - // see https://github.com/rust-analyzer/rust-analyzer/issues/8493 - #[ignore] #[test] fn fill_match_arms_tuple_of_enum_partial_with_wildcards() { let ra_fixture = r#" @@ -538,6 +537,23 @@ fn main() { ); } + #[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), + ); + } + #[test] fn fill_match_arms_tuple_of_enum_not_applicable() { check_assist_not_applicable( -- cgit v1.2.3 From 8d588efc2b75a886af642b14b0d3ee87d497a20e Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Sun, 18 Apr 2021 20:17:30 +0500 Subject: Return to the status quo in #8129 --- crates/ide_assists/src/handlers/fill_match_arms.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 800ce972c..3047f91d8 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs @@ -57,6 +57,8 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< 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()?; -- cgit v1.2.3 From 9222d3b0fb815f2fce871ee919c4f1efaf9fe177 Mon Sep 17 00:00:00 2001 From: Dawer <7803845+iDawer@users.noreply.github.com> Date: Mon, 19 Apr 2021 16:24:09 +0500 Subject: Unindent test according to the style guide. --- crates/ide_assists/src/handlers/fill_match_arms.rs | 46 +++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/crates/ide_assists/src/handlers/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 3047f91d8..a30c4d04e 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs @@ -481,32 +481,32 @@ fn main() { 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 } +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) => {} - } - } - "#, +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) => {} + } +} +"#, ); } -- cgit v1.2.3