generate default implementation for an enum from an enum variant #6860

Signed-off-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
This commit is contained in:
Benjamin Coenen 2020-12-13 22:00:44 +01:00
parent dbd0cfba53
commit 08090d81b1
3 changed files with 200 additions and 0 deletions

View File

@ -0,0 +1,171 @@
use ide_db::helpers::FamousDefs;
use ide_db::RootDatabase;
use syntax::ast::{self, AstNode, NameOwner};
use test_utils::mark;
use crate::{AssistContext, AssistId, AssistKind, Assists};
// Assist: generate_default_from_enum_variant
//
// Adds a Default impl for an enum using a variant.
//
// ```
// enum Version {
// Undefined,
// Minor<|>,
// Major,
// }
// ```
// ->
// ```
// enum Version {
// Undefined,
// Minor,
// Major,
// }
//
// impl Default for Version {
// fn default() -> Self {
// Self::Minor
// }
// }
// ```
pub(crate) fn generate_default_from_enum_variant(
acc: &mut Assists,
ctx: &AssistContext,
) -> Option<()> {
let variant = ctx.find_node_at_offset::<ast::Variant>()?;
let variant_name = variant.name()?;
let enum_name = variant.parent_enum().name()?;
if !matches!(variant.kind(), ast::StructKind::Unit) {
mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
return None;
}
if existing_default_impl(&ctx.sema, &variant).is_some() {
mark::hit!(test_gen_default_impl_already_exists);
return None;
}
let target = variant.syntax().text_range();
acc.add(
AssistId("generate_default_from_enum_variant", AssistKind::Generate),
"Generate `Default` impl from this enum variant",
target,
|edit| {
let start_offset = variant.parent_enum().syntax().text_range().end();
let buf = format!(
r#"
impl Default for {0} {{
fn default() -> Self {{
Self::{1}
}}
}}"#,
enum_name, variant_name
);
edit.insert(start_offset, buf);
},
)
}
fn existing_default_impl(
sema: &'_ hir::Semantics<'_, RootDatabase>,
variant: &ast::Variant,
) -> Option<()> {
let variant = sema.to_def(variant)?;
let enum_ = variant.parent_enum(sema.db);
let krate = enum_.module(sema.db).krate();
let default_trait = FamousDefs(sema, Some(krate)).core_default_Default()?;
let enum_type = enum_.ty(sema.db);
if enum_type.impls_trait(sema.db, default_trait, &[]) {
Some(())
} else {
None
}
}
#[cfg(test)]
mod tests {
use test_utils::mark;
use crate::tests::{check_assist, check_assist_not_applicable};
use super::*;
fn check_not_applicable(ra_fixture: &str) {
let fixture =
format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
check_assist_not_applicable(generate_default_from_enum_variant, &fixture)
}
#[test]
fn test_generate_default_from_variant() {
check_assist(
generate_default_from_enum_variant,
r#"enum Variant {
Undefined,
Minor<|>,
Major,
}"#,
r#"enum Variant {
Undefined,
Minor,
Major,
}
impl Default for Variant {
fn default() -> Self {
Self::Minor
}
}"#,
);
}
#[test]
fn test_generate_default_already_implemented() {
mark::check!(test_gen_default_impl_already_exists);
check_not_applicable(
r#"enum Variant {
Undefined,
Minor<|>,
Major,
}
impl Default for Variant {
fn default() -> Self {
Self::Minor
}
}"#,
);
}
#[test]
fn test_add_from_impl_no_element() {
mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
check_not_applicable(
r#"enum Variant {
Undefined,
Minor(u32)<|>,
Major,
}"#,
);
}
#[test]
fn test_generate_default_from_variant_with_one_variant() {
check_assist(
generate_default_from_enum_variant,
r#"enum Variant { Undefi<|>ned }"#,
r#"enum Variant { Undefined }
impl Default for Variant {
fn default() -> Self {
Self::Undefined
}
}"#,
);
}
}

View File

@ -137,6 +137,7 @@ mod handlers {
mod flip_comma;
mod flip_trait_bound;
mod generate_derive;
mod generate_default_from_enum_variant;
mod generate_from_impl_for_enum;
mod generate_function;
mod generate_impl;
@ -186,6 +187,7 @@ pub(crate) fn all() -> &'static [Handler] {
flip_comma::flip_comma,
flip_trait_bound::flip_trait_bound,
generate_derive::generate_derive,
generate_default_from_enum_variant::generate_default_from_enum_variant,
generate_from_impl_for_enum::generate_from_impl_for_enum,
generate_function::generate_function,
generate_impl::generate_impl,

View File

@ -364,6 +364,33 @@ fn foo<T: Copy + Clone>() { }
)
}
#[test]
fn doctest_generate_default_from_enum_variant() {
check_doc_test(
"generate_default_from_enum_variant",
r#####"
enum Version {
Undefined,
Minor<|>,
Major,
}
"#####,
r#####"
enum Version {
Undefined,
Minor,
Major,
}
impl Default for Version {
fn default() -> Self {
Self::Minor
}
}
"#####,
)
}
#[test]
fn doctest_generate_derive() {
check_doc_test(