aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Coenen <[email protected]>2020-12-13 21:00:44 +0000
committerBenjamin Coenen <[email protected]>2020-12-14 08:44:24 +0000
commit08090d81b130d349524ec3c5523d62e15dbbf27a (patch)
treed729256f4ca5f8d89a414a7897263cd911a63873
parentdbd0cfba531c21de01af7b1a12ce9eb6b1271a5d (diff)
generate default implementation for an enum from an enum variant #6860
Signed-off-by: Benjamin Coenen <[email protected]>
-rw-r--r--crates/assists/src/handlers/generate_default_from_enum_variant.rs171
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs27
3 files changed, 200 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 000000000..6521cb543
--- /dev/null
+++ b/crates/assists/src/handlers/generate_default_from_enum_variant.rs
@@ -0,0 +1,171 @@
1use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner};
4use test_utils::mark;
5
6use crate::{AssistContext, AssistId, AssistKind, Assists};
7
8// Assist: generate_default_from_enum_variant
9//
10// Adds a Default impl for an enum using a variant.
11//
12// ```
13// enum Version {
14// Undefined,
15// Minor<|>,
16// Major,
17// }
18// ```
19// ->
20// ```
21// enum Version {
22// Undefined,
23// Minor,
24// Major,
25// }
26//
27// impl Default for Version {
28// fn default() -> Self {
29// Self::Minor
30// }
31// }
32// ```
33pub(crate) fn generate_default_from_enum_variant(
34 acc: &mut Assists,
35 ctx: &AssistContext,
36) -> Option<()> {
37 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
38 let variant_name = variant.name()?;
39 let enum_name = variant.parent_enum().name()?;
40 if !matches!(variant.kind(), ast::StructKind::Unit) {
41 mark::hit!(test_gen_default_on_non_unit_variant_not_implemented);
42 return None;
43 }
44
45 if existing_default_impl(&ctx.sema, &variant).is_some() {
46 mark::hit!(test_gen_default_impl_already_exists);
47 return None;
48 }
49
50 let target = variant.syntax().text_range();
51 acc.add(
52 AssistId("generate_default_from_enum_variant", AssistKind::Generate),
53 "Generate `Default` impl from this enum variant",
54 target,
55 |edit| {
56 let start_offset = variant.parent_enum().syntax().text_range().end();
57 let buf = format!(
58 r#"
59
60impl Default for {0} {{
61 fn default() -> Self {{
62 Self::{1}
63 }}
64}}"#,
65 enum_name, variant_name
66 );
67 edit.insert(start_offset, buf);
68 },
69 )
70}
71
72fn existing_default_impl(
73 sema: &'_ hir::Semantics<'_, RootDatabase>,
74 variant: &ast::Variant,
75) -> Option<()> {
76 let variant = sema.to_def(variant)?;
77 let enum_ = variant.parent_enum(sema.db);
78 let krate = enum_.module(sema.db).krate();
79
80 let default_trait = FamousDefs(sema, Some(krate)).core_default_Default()?;
81 let enum_type = enum_.ty(sema.db);
82
83 if enum_type.impls_trait(sema.db, default_trait, &[]) {
84 Some(())
85 } else {
86 None
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use test_utils::mark;
93
94 use crate::tests::{check_assist, check_assist_not_applicable};
95
96 use super::*;
97
98 fn check_not_applicable(ra_fixture: &str) {
99 let fixture =
100 format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
101 check_assist_not_applicable(generate_default_from_enum_variant, &fixture)
102 }
103
104 #[test]
105 fn test_generate_default_from_variant() {
106 check_assist(
107 generate_default_from_enum_variant,
108 r#"enum Variant {
109 Undefined,
110 Minor<|>,
111 Major,
112 }"#,
113 r#"enum Variant {
114 Undefined,
115 Minor,
116 Major,
117 }
118
119impl Default for Variant {
120 fn default() -> Self {
121 Self::Minor
122 }
123}"#,
124 );
125 }
126
127 #[test]
128 fn test_generate_default_already_implemented() {
129 mark::check!(test_gen_default_impl_already_exists);
130 check_not_applicable(
131 r#"enum Variant {
132 Undefined,
133 Minor<|>,
134 Major,
135 }
136
137 impl Default for Variant {
138 fn default() -> Self {
139 Self::Minor
140 }
141 }"#,
142 );
143 }
144
145 #[test]
146 fn test_add_from_impl_no_element() {
147 mark::check!(test_gen_default_on_non_unit_variant_not_implemented);
148 check_not_applicable(
149 r#"enum Variant {
150 Undefined,
151 Minor(u32)<|>,
152 Major,
153 }"#,
154 );
155 }
156
157 #[test]
158 fn test_generate_default_from_variant_with_one_variant() {
159 check_assist(
160 generate_default_from_enum_variant,
161 r#"enum Variant { Undefi<|>ned }"#,
162 r#"enum Variant { Undefined }
163
164impl Default for Variant {
165 fn default() -> Self {
166 Self::Undefined
167 }
168}"#,
169 );
170 }
171}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index b8ce7418d..6e736ccb3 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -137,6 +137,7 @@ mod handlers {
137 mod flip_comma; 137 mod flip_comma;
138 mod flip_trait_bound; 138 mod flip_trait_bound;
139 mod generate_derive; 139 mod generate_derive;
140 mod generate_default_from_enum_variant;
140 mod generate_from_impl_for_enum; 141 mod generate_from_impl_for_enum;
141 mod generate_function; 142 mod generate_function;
142 mod generate_impl; 143 mod generate_impl;
@@ -186,6 +187,7 @@ mod handlers {
186 flip_comma::flip_comma, 187 flip_comma::flip_comma,
187 flip_trait_bound::flip_trait_bound, 188 flip_trait_bound::flip_trait_bound,
188 generate_derive::generate_derive, 189 generate_derive::generate_derive,
190 generate_default_from_enum_variant::generate_default_from_enum_variant,
189 generate_from_impl_for_enum::generate_from_impl_for_enum, 191 generate_from_impl_for_enum::generate_from_impl_for_enum,
190 generate_function::generate_function, 192 generate_function::generate_function,
191 generate_impl::generate_impl, 193 generate_impl::generate_impl,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 853bde09c..cc7c4a343 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -365,6 +365,33 @@ fn foo<T: Copy + Clone>() { }
365} 365}
366 366
367#[test] 367#[test]
368fn doctest_generate_default_from_enum_variant() {
369 check_doc_test(
370 "generate_default_from_enum_variant",
371 r#####"
372enum Version {
373 Undefined,
374 Minor<|>,
375 Major,
376}
377"#####,
378 r#####"
379enum Version {
380 Undefined,
381 Minor,
382 Major,
383}
384
385impl Default for Version {
386 fn default() -> Self {
387 Self::Minor
388 }
389}
390"#####,
391 )
392}
393
394#[test]
368fn doctest_generate_derive() { 395fn doctest_generate_derive() {
369 check_doc_test( 396 check_doc_test(
370 "generate_derive", 397 "generate_derive",