diff options
-rw-r--r-- | crates/assists/src/handlers/generate_default_from_enum_variant.rs | 171 | ||||
-rw-r--r-- | crates/assists/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/assists/src/tests/generated.rs | 27 |
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 @@ | |||
1 | use ide_db::helpers::FamousDefs; | ||
2 | use ide_db::RootDatabase; | ||
3 | use syntax::ast::{self, AstNode, NameOwner}; | ||
4 | use test_utils::mark; | ||
5 | |||
6 | use 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 | // ``` | ||
33 | pub(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 | |||
60 | impl 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 | |||
72 | fn 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)] | ||
91 | mod 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 | |||
119 | impl 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 | |||
164 | impl 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] |
368 | fn doctest_generate_default_from_enum_variant() { | ||
369 | check_doc_test( | ||
370 | "generate_default_from_enum_variant", | ||
371 | r#####" | ||
372 | enum Version { | ||
373 | Undefined, | ||
374 | Minor<|>, | ||
375 | Major, | ||
376 | } | ||
377 | "#####, | ||
378 | r#####" | ||
379 | enum Version { | ||
380 | Undefined, | ||
381 | Minor, | ||
382 | Major, | ||
383 | } | ||
384 | |||
385 | impl Default for Version { | ||
386 | fn default() -> Self { | ||
387 | Self::Minor | ||
388 | } | ||
389 | } | ||
390 | "#####, | ||
391 | ) | ||
392 | } | ||
393 | |||
394 | #[test] | ||
368 | fn doctest_generate_derive() { | 395 | fn doctest_generate_derive() { |
369 | check_doc_test( | 396 | check_doc_test( |
370 | "generate_derive", | 397 | "generate_derive", |