mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 04:53:34 +00:00
Add support for fill match arms of boolean values
- Add support for boolean inside tuple
This commit is contained in:
parent
7ae0bc1bd4
commit
09147c3303
2 changed files with 207 additions and 23 deletions
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue