Add support for fill match arms of boolean values

- Add support for boolean inside tuple
This commit is contained in:
Comonad 2021-04-21 19:33:45 +08:00
parent 7ae0bc1bd4
commit 09147c3303
2 changed files with 207 additions and 23 deletions

View file

@ -53,7 +53,7 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
.iter() .iter()
.filter_map(ast::MatchArm::pat) .filter_map(ast::MatchArm::pat)
.flat_map(|pat| match pat { .flat_map(|pat| match pat {
// Special casee OrPat as separate top-level pats // Special case OrPat as separate top-level pats
Pat::OrPat(or_pat) => Either::Left(or_pat.pats()), Pat::OrPat(or_pat) => Either::Left(or_pat.pats()),
_ => Either::Right(iter::once(pat)), _ => Either::Right(iter::once(pat)),
}) })
@ -72,7 +72,11 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option<
.filter(|variant_pat| is_variant_missing(&top_lvl_pats, 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())) .map(|pat| make::match_arm(iter::once(pat), make::expr_empty_block()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if Some(enum_def) == FamousDefs(&ctx.sema, Some(module.krate())).core_option_Option() { if Some(enum_def)
== FamousDefs(&ctx.sema, Some(module.krate()))
.core_option_Option()
.map(|x| lift_enum(x))
{
// Match `Some` variant first. // Match `Some` variant first.
cov_mark::hit!(option_order); cov_mark::hit!(option_order);
variants.reverse() variants.reverse()
@ -151,49 +155,99 @@ fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
} }
} }
fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> { #[derive(Eq, PartialEq, Clone)]
enum ExtendedEnum {
Bool,
Enum(hir::Enum),
}
#[derive(Eq, PartialEq, Clone)]
enum ExtendedVariant {
True,
False,
Variant(hir::Variant),
}
fn lift_enum(e: hir::Enum) -> ExtendedEnum {
ExtendedEnum::Enum(e)
}
impl ExtendedEnum {
fn variants(&self, db: &RootDatabase) -> Vec<ExtendedVariant> {
match self {
ExtendedEnum::Enum(e) => {
e.variants(db).into_iter().map(|x| ExtendedVariant::Variant(x)).collect::<Vec<_>>()
}
ExtendedEnum::Bool => {
Vec::<ExtendedVariant>::from([ExtendedVariant::True, ExtendedVariant::False])
}
}
}
}
fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ExtendedEnum> {
sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() { sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
Some(Adt::Enum(e)) => Some(e), Some(Adt::Enum(e)) => Some(ExtendedEnum::Enum(e)),
_ => None, _ => {
if ty.is_bool() {
Some(ExtendedEnum::Bool)
} else {
None
}
}
}) })
} }
fn resolve_tuple_of_enum_def( fn resolve_tuple_of_enum_def(
sema: &Semantics<RootDatabase>, sema: &Semantics<RootDatabase>,
expr: &ast::Expr, expr: &ast::Expr,
) -> Option<Vec<hir::Enum>> { ) -> Option<Vec<ExtendedEnum>> {
sema.type_of_expr(&expr)? sema.type_of_expr(&expr)?
.tuple_fields(sema.db) .tuple_fields(sema.db)
.iter() .iter()
.map(|ty| { .map(|ty| {
ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() { ty.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
Some(Adt::Enum(e)) => Some(e), Some(Adt::Enum(e)) => Some(lift_enum(e)),
// For now we only handle expansion for a tuple of enums. Here // For now we only handle expansion for a tuple of enums. Here
// we map non-enum items to None and rely on `collect` to // we map non-enum items to None and rely on `collect` to
// convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>. // convert Vec<Option<hir::Enum>> into Option<Vec<hir::Enum>>.
_ => None, _ => {
if ty.is_bool() {
Some(ExtendedEnum::Bool)
} else {
None
}
}
}) })
}) })
.collect() .collect()
} }
fn build_pat(db: &RootDatabase, module: hir::Module, var: hir::Variant) -> Option<ast::Pat> { fn build_pat(db: &RootDatabase, module: hir::Module, var: ExtendedVariant) -> Option<ast::Pat> {
let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?); match var {
ExtendedVariant::Variant(var) => {
let path = mod_path_to_ast(&module.find_use_path(db, ModuleDef::from(var))?);
// FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though
let pat: ast::Pat = match var.source(db)?.value.kind() { let pat: ast::Pat = match var.source(db)?.value.kind() {
ast::StructKind::Tuple(field_list) => { ast::StructKind::Tuple(field_list) => {
let pats = iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); let pats =
make::tuple_struct_pat(path, pats).into() iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count());
} make::tuple_struct_pat(path, pats).into()
ast::StructKind::Record(field_list) => { }
let pats = field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into()); ast::StructKind::Record(field_list) => {
make::record_pat(path, pats).into() let pats =
} field_list.fields().map(|f| make::ident_pat(f.name().unwrap()).into());
ast::StructKind::Unit => make::path_pat(path), make::record_pat(path, pats).into()
}; }
ast::StructKind::Unit => make::path_pat(path),
};
Some(pat) Some(pat)
}
ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))),
ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))),
}
} }
#[cfg(test)] #[cfg(test)]
@ -225,6 +279,21 @@ mod tests {
); );
} }
#[test]
fn all_boolean_match_arms_provided() {
check_assist_not_applicable(
fill_match_arms,
r#"
fn foo(a: bool) {
match a$0 {
true => {}
false => {}
}
}
"#,
)
}
#[test] #[test]
fn tuple_of_non_enum() { fn tuple_of_non_enum() {
// for now this case is not handled, although it potentially could be // for now this case is not handled, although it potentially could be
@ -240,6 +309,113 @@ mod tests {
); );
} }
#[test]
fn fill_match_arms_boolean() {
check_assist(
fill_match_arms,
r#"
fn foo(a: bool) {
match a$0 {
}
}
"#,
r#"
fn foo(a: bool) {
match a {
$0true => {}
false => {}
}
}
"#,
)
}
#[test]
fn partial_fill_boolean() {
check_assist(
fill_match_arms,
r#"
fn foo(a: bool) {
match a$0 {
true => {}
}
}
"#,
r#"
fn foo(a: bool) {
match a {
true => {}
$0false => {}
}
}
"#,
)
}
#[test]
fn all_boolean_tuple_arms_provided() {
check_assist_not_applicable(
fill_match_arms,
r#"
fn foo(a: bool) {
match (a, a)$0 {
(true, true) => {}
(true, false) => {}
(false, true) => {}
(false, false) => {}
}
}
"#,
)
}
#[test]
fn fill_boolean_tuple() {
check_assist(
fill_match_arms,
r#"
fn foo(a: bool) {
match (a, a)$0 {
}
}
"#,
r#"
fn foo(a: bool) {
match (a, a) {
$0(true, true) => {}
(true, false) => {}
(false, true) => {}
(false, false) => {}
}
}
"#,
)
}
#[test]
fn partial_fill_boolean_tuple() {
check_assist(
fill_match_arms,
r#"
fn foo(a: bool) {
match (a, a)$0 {
(false, true) => {}
}
}
"#,
r#"
fn foo(a: bool) {
match (a, a) {
(false, true) => {}
$0(true, true) => {}
(true, false) => {}
(false, false) => {}
}
}
"#,
)
}
#[test] #[test]
fn partial_fill_record_tuple() { fn partial_fill_record_tuple() {
check_assist( check_assist(

View file

@ -294,6 +294,14 @@ pub fn wildcard_pat() -> ast::WildcardPat {
} }
} }
pub fn literal_pat(lit: &str) -> ast::LiteralPat {
return from_text(lit);
fn from_text(text: &str) -> ast::LiteralPat {
ast_from_text(&format!("fn f() {{ match x {{ {} => {{}} }} }}", text))
}
}
/// Creates a tuple of patterns from an iterator of patterns. /// Creates a tuple of patterns from an iterator of patterns.
/// ///
/// Invariant: `pats` must be length > 0 /// Invariant: `pats` must be length > 0