aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/assists/src/handlers/generate_from_impl_for_enum.rs150
-rw-r--r--crates/assists/src/tests/generated.rs2
2 files changed, 124 insertions, 28 deletions
diff --git a/crates/assists/src/handlers/generate_from_impl_for_enum.rs b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
index d9af6ab11..f6febd3aa 100644
--- a/crates/assists/src/handlers/generate_from_impl_for_enum.rs
+++ b/crates/assists/src/handlers/generate_from_impl_for_enum.rs
@@ -1,6 +1,12 @@
1use ast::GenericParamsOwner;
1use ide_db::helpers::FamousDefs; 2use ide_db::helpers::FamousDefs;
2use ide_db::RootDatabase; 3use ide_db::RootDatabase;
3use syntax::ast::{self, AstNode, NameOwner}; 4use itertools::Itertools;
5use stdx::format_to;
6use syntax::{
7 ast::{self, AstNode, NameOwner},
8 SmolStr,
9};
4use test_utils::mark; 10use test_utils::mark;
5 11
6use crate::{AssistContext, AssistId, AssistKind, Assists}; 12use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -18,7 +24,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
18// 24//
19// impl From<u32> for A { 25// impl From<u32> for A {
20// fn from(v: u32) -> Self { 26// fn from(v: u32) -> Self {
21// A::One(v) 27// Self::One(v)
22// } 28// }
23// } 29// }
24// ``` 30// ```
@@ -26,17 +32,22 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
26 let variant = ctx.find_node_at_offset::<ast::Variant>()?; 32 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
27 let variant_name = variant.name()?; 33 let variant_name = variant.name()?;
28 let enum_name = variant.parent_enum().name()?; 34 let enum_name = variant.parent_enum().name()?;
29 let field_list = match variant.kind() { 35 let enum_type_params = variant.parent_enum().generic_param_list();
30 ast::StructKind::Tuple(field_list) => field_list, 36 let (field_name, field_type) = match variant.kind() {
31 _ => return None, 37 ast::StructKind::Tuple(field_list) => {
32 }; 38 if field_list.fields().count() != 1 {
33 if field_list.fields().count() != 1 { 39 return None;
34 return None; 40 }
35 } 41 (None, field_list.fields().next()?.ty()?)
36 let field_type = field_list.fields().next()?.ty()?; 42 }
37 let path = match field_type { 43 ast::StructKind::Record(field_list) => {
38 ast::Type::PathType(it) => it, 44 if field_list.fields().count() != 1 {
39 _ => return None, 45 return None;
46 }
47 let field = field_list.fields().next()?;
48 (Some(field.name()?), field.ty()?)
49 }
50 ast::StructKind::Unit => return None,
40 }; 51 };
41 52
42 if existing_from_impl(&ctx.sema, &variant).is_some() { 53 if existing_from_impl(&ctx.sema, &variant).is_some() {
@@ -51,18 +62,48 @@ pub(crate) fn generate_from_impl_for_enum(acc: &mut Assists, ctx: &AssistContext
51 target, 62 target,
52 |edit| { 63 |edit| {
53 let start_offset = variant.parent_enum().syntax().text_range().end(); 64 let start_offset = variant.parent_enum().syntax().text_range().end();
54 let buf = format!( 65 let mut buf = String::from("\n\nimpl");
55 r#" 66 if let Some(type_params) = &enum_type_params {
67 format_to!(buf, "{}", type_params.syntax());
68 }
69 format_to!(buf, " From<{}> for {}", field_type.syntax(), enum_name);
70 if let Some(type_params) = enum_type_params {
71 let lifetime_params = type_params
72 .lifetime_params()
73 .filter_map(|it| it.lifetime())
74 .map(|it| SmolStr::from(it.text()));
75 let type_params = type_params
76 .type_params()
77 .filter_map(|it| it.name())
78 .map(|it| SmolStr::from(it.text()));
56 79
57impl From<{0}> for {1} {{ 80 let generic_params = lifetime_params.chain(type_params).format(", ");
58 fn from(v: {0}) -> Self {{ 81 format_to!(buf, "<{}>", generic_params)
59 {1}::{2}(v) 82 }
83 if let Some(name) = field_name {
84 format_to!(
85 buf,
86 r#" {{
87 fn from({0}: {1}) -> Self {{
88 Self::{2} {{ {0} }}
89 }}
90}}"#,
91 name.text(),
92 field_type.syntax(),
93 variant_name,
94 );
95 } else {
96 format_to!(
97 buf,
98 r#" {{
99 fn from(v: {}) -> Self {{
100 Self::{}(v)
60 }} 101 }}
61}}"#, 102}}"#,
62 path.syntax(), 103 field_type.syntax(),
63 enum_name, 104 variant_name,
64 variant_name 105 );
65 ); 106 }
66 edit.insert(start_offset, buf); 107 edit.insert(start_offset, buf);
67 }, 108 },
68 ) 109 )
@@ -106,7 +147,7 @@ mod tests {
106 147
107impl From<u32> for A { 148impl From<u32> for A {
108 fn from(v: u32) -> Self { 149 fn from(v: u32) -> Self {
109 A::One(v) 150 Self::One(v)
110 } 151 }
111}"#, 152}"#,
112 ); 153 );
@@ -121,7 +162,7 @@ impl From<u32> for A {
121 162
122impl From<foo::bar::baz::Boo> for A { 163impl From<foo::bar::baz::Boo> for A {
123 fn from(v: foo::bar::baz::Boo) -> Self { 164 fn from(v: foo::bar::baz::Boo) -> Self {
124 A::One(v) 165 Self::One(v)
125 } 166 }
126}"#, 167}"#,
127 ); 168 );
@@ -145,7 +186,17 @@ impl From<foo::bar::baz::Boo> for A {
145 186
146 #[test] 187 #[test]
147 fn test_add_from_impl_struct_variant() { 188 fn test_add_from_impl_struct_variant() {
148 check_not_applicable("enum A { $0One { x: u32 } }"); 189 check_assist(
190 generate_from_impl_for_enum,
191 "enum A { $0One { x: u32 } }",
192 r#"enum A { One { x: u32 } }
193
194impl From<u32> for A {
195 fn from(x: u32) -> Self {
196 Self::One { x }
197 }
198}"#,
199 );
149 } 200 }
150 201
151 #[test] 202 #[test]
@@ -157,7 +208,7 @@ enum A { $0One(u32), }
157 208
158impl From<u32> for A { 209impl From<u32> for A {
159 fn from(v: u32) -> Self { 210 fn from(v: u32) -> Self {
160 A::One(v) 211 Self::One(v)
161 } 212 }
162} 213}
163"#, 214"#,
@@ -183,7 +234,7 @@ pub trait From<T> {
183 234
184impl From<u32> for A { 235impl From<u32> for A {
185 fn from(v: u32) -> Self { 236 fn from(v: u32) -> Self {
186 A::One(v) 237 Self::One(v)
187 } 238 }
188} 239}
189 240
@@ -198,4 +249,49 @@ pub trait From<T> {
198}"#, 249}"#,
199 ); 250 );
200 } 251 }
252
253 #[test]
254 fn test_add_from_impl_static_str() {
255 check_assist(
256 generate_from_impl_for_enum,
257 "enum A { $0One(&'static str) }",
258 r#"enum A { One(&'static str) }
259
260impl From<&'static str> for A {
261 fn from(v: &'static str) -> Self {
262 Self::One(v)
263 }
264}"#,
265 );
266 }
267
268 #[test]
269 fn test_add_from_impl_generic_enum() {
270 check_assist(
271 generate_from_impl_for_enum,
272 "enum Generic<T, U: Clone> { $0One(T), Two(U) }",
273 r#"enum Generic<T, U: Clone> { One(T), Two(U) }
274
275impl<T, U: Clone> From<T> for Generic<T, U> {
276 fn from(v: T) -> Self {
277 Self::One(v)
278 }
279}"#,
280 );
281 }
282
283 #[test]
284 fn test_add_from_impl_with_lifetime() {
285 check_assist(
286 generate_from_impl_for_enum,
287 "enum Generic<'a> { $0One(&'a i32) }",
288 r#"enum Generic<'a> { One(&'a i32) }
289
290impl<'a> From<&'a i32> for Generic<'a> {
291 fn from(v: &'a i32) -> Self {
292 Self::One(v)
293 }
294}"#,
295 );
296 }
201} 297}
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 6f2b22bc2..0516deaff 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -499,7 +499,7 @@ enum A { One(u32) }
499 499
500impl From<u32> for A { 500impl From<u32> for A {
501 fn from(v: u32) -> Self { 501 fn from(v: u32) -> Self {
502 A::One(v) 502 Self::One(v)
503 } 503 }
504} 504}
505"#####, 505"#####,