diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2021-02-13 15:56:17 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2021-02-13 15:56:17 +0000 |
commit | 2967e783ac53e89f06a8f8bd1afc12433311fded (patch) | |
tree | d46451d2029fe7842a654feec4c1bbf8246d1829 | |
parent | 11ebbac3a4a962701639bb14a4a5a7309399d7e8 (diff) | |
parent | b07f530e3ae025c57b2b64e8ebb2852e60bced7b (diff) |
Merge #7659
7659: Improve "Generate From impl" r=Veykril a=jDomantas
* Allows any field type. Previously it was restricted to path types, but I don't see why it couldn't apply to all other types too. (the main reason for is PR is that I'm too lazy to write out `From<&'static str>` by hand 😄)
* More correct handling for generic enums - previously it wouldn't emit generic params on the impl.
* Also accepts variants with named field.
The impl generation code got mostly copy-pasted from generate_impl assist - please tell if there's an easy way to avoid this duplication.
Co-authored-by: Domantas Jadenkus <[email protected]>
-rw-r--r-- | crates/assists/src/handlers/generate_from_impl_for_enum.rs | 150 | ||||
-rw-r--r-- | crates/assists/src/tests/generated.rs | 2 |
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 @@ | |||
1 | use ast::GenericParamsOwner; | ||
1 | use ide_db::helpers::FamousDefs; | 2 | use ide_db::helpers::FamousDefs; |
2 | use ide_db::RootDatabase; | 3 | use ide_db::RootDatabase; |
3 | use syntax::ast::{self, AstNode, NameOwner}; | 4 | use itertools::Itertools; |
5 | use stdx::format_to; | ||
6 | use syntax::{ | ||
7 | ast::{self, AstNode, NameOwner}, | ||
8 | SmolStr, | ||
9 | }; | ||
4 | use test_utils::mark; | 10 | use test_utils::mark; |
5 | 11 | ||
6 | use crate::{AssistContext, AssistId, AssistKind, Assists}; | 12 | use 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 | ||
57 | impl 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 | ||
107 | impl From<u32> for A { | 148 | impl 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 | ||
122 | impl From<foo::bar::baz::Boo> for A { | 163 | impl 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 | |||
194 | impl 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 | ||
158 | impl From<u32> for A { | 209 | impl 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 | ||
184 | impl From<u32> for A { | 235 | impl 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 | |||
260 | impl 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 | |||
275 | impl<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 | |||
290 | impl<'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 | ||
500 | impl From<u32> for A { | 500 | impl 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 | "#####, |