about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-12-16 08:36:05 +0000
committerGitHub <noreply@github.com>2020-12-16 08:36:05 +0000
commitc1c36acb025880c742c65f436a61fd87fc627eb0 (patch)
treeffb378cf473e9a2d07bf341c3852ed1b6e0e0729
parentece626fe81579b6b38cdbd17d3e47bb422360a56 (diff)
parent2c82a1aec71e858040f24d4b39d3937c737f1ef5 (diff)
downloadrust-c1c36acb025880c742c65f436a61fd87fc627eb0.tar.gz
rust-c1c36acb025880c742c65f436a61fd87fc627eb0.zip
Merge #6861
6861: generate default implementation for an enum from an enum variant #6860 r=matklad a=bnjjj

close #6860

Co-authored-by: Benjamin Coenen <5719034+bnjjj@users.noreply.github.com>
-rw-r--r--crates/assists/src/handlers/generate_default_from_enum_variant.rs175
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs27
3 files changed, 204 insertions, 0 deletions
diff --git a/crates/assists/src/handlers/generate_default_from_enum_variant.rs b/crates/assists/src/handlers/generate_default_from_enum_variant.rs
new file mode 100644
index 00000000000..bcea4673518
--- /dev/null
+++ b/crates/assists/src/handlers/generate_default_from_enum_variant.rs
@@ -0,0 +1,175 @@
+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
+    }
+}"#,
+        );
+    }
+}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index b8ce7418d6f..6e736ccb389 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -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 @@ mod handlers {
             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,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 853bde09ca2..cc7c4a3433a 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -365,6 +365,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(
         "generate_derive",