Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs')
-rw-r--r--crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs179
1 files changed, 179 insertions, 0 deletions
diff --git a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
new file mode 100644
index 0000000000..4d47437f7c
--- /dev/null
+++ b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
@@ -0,0 +1,179 @@
+use ide_db::{famous_defs::FamousDefs, RootDatabase};
+use syntax::ast::{self, AstNode, HasName};
+
+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$0,
+// 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) {
+ cov_mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
+ return None;
+ }
+
+ if existing_default_impl(&ctx.sema, &variant).is_some() {
+ cov_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, 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 crate::tests::{check_assist, check_assist_not_applicable};
+
+ use super::*;
+
+ #[test]
+ fn test_generate_default_from_variant() {
+ check_assist(
+ generate_default_from_enum_variant,
+ r#"
+//- minicore: default
+enum Variant {
+ Undefined,
+ Minor$0,
+ Major,
+}
+"#,
+ r#"
+enum Variant {
+ Undefined,
+ Minor,
+ Major,
+}
+
+impl Default for Variant {
+ fn default() -> Self {
+ Self::Minor
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_generate_default_already_implemented() {
+ cov_mark::check!(test_gen_default_impl_already_exists);
+ check_assist_not_applicable(
+ generate_default_from_enum_variant,
+ r#"
+//- minicore: default
+enum Variant {
+ Undefined,
+ Minor$0,
+ Major,
+}
+
+impl Default for Variant {
+ fn default() -> Self {
+ Self::Minor
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_add_from_impl_no_element() {
+ cov_mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
+ check_assist_not_applicable(
+ generate_default_from_enum_variant,
+ r#"
+//- minicore: default
+enum Variant {
+ Undefined,
+ Minor(u32)$0,
+ Major,
+}
+"#,
+ );
+ }
+
+ #[test]
+ fn test_generate_default_from_variant_with_one_variant() {
+ check_assist(
+ generate_default_from_enum_variant,
+ r#"
+//- minicore: default
+enum Variant { Undefi$0ned }
+"#,
+ r#"
+enum Variant { Undefined }
+
+impl Default for Variant {
+ fn default() -> Self {
+ Self::Undefined
+ }
+}
+"#,
+ );
+ }
+}