]>
Commit | Line | Data |
---|---|---|
064997fb FG |
1 | use ide_db::{famous_defs::FamousDefs, RootDatabase}; |
2 | use syntax::ast::{self, AstNode, HasName}; | |
3 | ||
f25598a0 FG |
4 | use crate::{ |
5 | utils::generate_trait_impl_text_intransitive, AssistContext, AssistId, AssistKind, Assists, | |
6 | }; | |
064997fb FG |
7 | |
8 | // Assist: generate_from_impl_for_enum | |
9 | // | |
10 | // Adds a From impl for this enum variant with one tuple field. | |
11 | // | |
12 | // ``` | |
13 | // enum A { $0One(u32) } | |
14 | // ``` | |
15 | // -> | |
16 | // ``` | |
17 | // enum A { One(u32) } | |
18 | // | |
19 | // impl From<u32> for A { | |
20 | // fn from(v: u32) -> Self { | |
21 | // Self::One(v) | |
22 | // } | |
23 | // } | |
24 | // ``` | |
25 | pub(crate) fn generate_from_impl_for_enum( | |
26 | acc: &mut Assists, | |
27 | ctx: &AssistContext<'_>, | |
28 | ) -> Option<()> { | |
29 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | |
30 | let variant_name = variant.name()?; | |
31 | let enum_ = ast::Adt::Enum(variant.parent_enum()); | |
32 | let (field_name, field_type) = match variant.kind() { | |
33 | ast::StructKind::Tuple(field_list) => { | |
34 | if field_list.fields().count() != 1 { | |
35 | return None; | |
36 | } | |
37 | (None, field_list.fields().next()?.ty()?) | |
38 | } | |
39 | ast::StructKind::Record(field_list) => { | |
40 | if field_list.fields().count() != 1 { | |
41 | return None; | |
42 | } | |
43 | let field = field_list.fields().next()?; | |
44 | (Some(field.name()?), field.ty()?) | |
45 | } | |
46 | ast::StructKind::Unit => return None, | |
47 | }; | |
48 | ||
49 | if existing_from_impl(&ctx.sema, &variant).is_some() { | |
50 | cov_mark::hit!(test_add_from_impl_already_exists); | |
51 | return None; | |
52 | } | |
53 | ||
54 | let target = variant.syntax().text_range(); | |
55 | acc.add( | |
56 | AssistId("generate_from_impl_for_enum", AssistKind::Generate), | |
57 | "Generate `From` impl for this enum variant", | |
58 | target, | |
59 | |edit| { | |
60 | let start_offset = variant.parent_enum().syntax().text_range().end(); | |
2b03887a | 61 | let from_trait = format!("From<{field_type}>"); |
064997fb FG |
62 | let impl_code = if let Some(name) = field_name { |
63 | format!( | |
2b03887a FG |
64 | r#" fn from({name}: {field_type}) -> Self {{ |
65 | Self::{variant_name} {{ {name} }} | |
66 | }}"# | |
064997fb FG |
67 | ) |
68 | } else { | |
69 | format!( | |
2b03887a FG |
70 | r#" fn from(v: {field_type}) -> Self {{ |
71 | Self::{variant_name}(v) | |
72 | }}"# | |
064997fb FG |
73 | ) |
74 | }; | |
f25598a0 | 75 | let from_impl = generate_trait_impl_text_intransitive(&enum_, &from_trait, &impl_code); |
064997fb FG |
76 | edit.insert(start_offset, from_impl); |
77 | }, | |
78 | ) | |
79 | } | |
80 | ||
81 | fn existing_from_impl( | |
82 | sema: &'_ hir::Semantics<'_, RootDatabase>, | |
83 | variant: &ast::Variant, | |
84 | ) -> Option<()> { | |
85 | let variant = sema.to_def(variant)?; | |
86 | let enum_ = variant.parent_enum(sema.db); | |
87 | let krate = enum_.module(sema.db).krate(); | |
88 | ||
89 | let from_trait = FamousDefs(sema, krate).core_convert_From()?; | |
90 | ||
91 | let enum_type = enum_.ty(sema.db); | |
92 | ||
93 | let wrapped_type = variant.fields(sema.db).get(0)?.ty(sema.db); | |
94 | ||
95 | if enum_type.impls_trait(sema.db, from_trait, &[wrapped_type]) { | |
96 | Some(()) | |
97 | } else { | |
98 | None | |
99 | } | |
100 | } | |
101 | ||
102 | #[cfg(test)] | |
103 | mod tests { | |
104 | use crate::tests::{check_assist, check_assist_not_applicable}; | |
105 | ||
106 | use super::*; | |
107 | ||
108 | #[test] | |
109 | fn test_generate_from_impl_for_enum() { | |
110 | check_assist( | |
111 | generate_from_impl_for_enum, | |
112 | r#" | |
113 | //- minicore: from | |
114 | enum A { $0One(u32) } | |
115 | "#, | |
116 | r#" | |
117 | enum A { One(u32) } | |
118 | ||
119 | impl From<u32> for A { | |
120 | fn from(v: u32) -> Self { | |
121 | Self::One(v) | |
122 | } | |
123 | } | |
124 | "#, | |
125 | ); | |
126 | } | |
127 | ||
128 | #[test] | |
129 | fn test_generate_from_impl_for_enum_complicated_path() { | |
130 | check_assist( | |
131 | generate_from_impl_for_enum, | |
132 | r#" | |
133 | //- minicore: from | |
134 | enum A { $0One(foo::bar::baz::Boo) } | |
135 | "#, | |
136 | r#" | |
137 | enum A { One(foo::bar::baz::Boo) } | |
138 | ||
139 | impl From<foo::bar::baz::Boo> for A { | |
140 | fn from(v: foo::bar::baz::Boo) -> Self { | |
141 | Self::One(v) | |
142 | } | |
143 | } | |
144 | "#, | |
145 | ); | |
146 | } | |
147 | ||
148 | #[test] | |
149 | fn test_add_from_impl_no_element() { | |
150 | check_assist_not_applicable( | |
151 | generate_from_impl_for_enum, | |
152 | r#" | |
153 | //- minicore: from | |
154 | enum A { $0One } | |
155 | "#, | |
156 | ); | |
157 | } | |
158 | ||
159 | #[test] | |
160 | fn test_add_from_impl_more_than_one_element_in_tuple() { | |
161 | check_assist_not_applicable( | |
162 | generate_from_impl_for_enum, | |
163 | r#" | |
164 | //- minicore: from | |
165 | enum A { $0One(u32, String) } | |
166 | "#, | |
167 | ); | |
168 | } | |
169 | ||
170 | #[test] | |
171 | fn test_add_from_impl_struct_variant() { | |
172 | check_assist( | |
173 | generate_from_impl_for_enum, | |
174 | r#" | |
175 | //- minicore: from | |
176 | enum A { $0One { x: u32 } } | |
177 | "#, | |
178 | r#" | |
179 | enum A { One { x: u32 } } | |
180 | ||
181 | impl From<u32> for A { | |
182 | fn from(x: u32) -> Self { | |
183 | Self::One { x } | |
184 | } | |
185 | } | |
186 | "#, | |
187 | ); | |
188 | } | |
189 | ||
190 | #[test] | |
191 | fn test_add_from_impl_already_exists() { | |
192 | cov_mark::check!(test_add_from_impl_already_exists); | |
193 | check_assist_not_applicable( | |
194 | generate_from_impl_for_enum, | |
195 | r#" | |
196 | //- minicore: from | |
197 | enum A { $0One(u32), } | |
198 | ||
199 | impl From<u32> for A { | |
200 | fn from(v: u32) -> Self { | |
201 | Self::One(v) | |
202 | } | |
203 | } | |
204 | "#, | |
205 | ); | |
206 | } | |
207 | ||
208 | #[test] | |
209 | fn test_add_from_impl_different_variant_impl_exists() { | |
210 | check_assist( | |
211 | generate_from_impl_for_enum, | |
212 | r#" | |
213 | //- minicore: from | |
214 | enum A { $0One(u32), Two(String), } | |
215 | ||
216 | impl From<String> for A { | |
217 | fn from(v: String) -> Self { | |
218 | A::Two(v) | |
219 | } | |
220 | } | |
221 | ||
222 | pub trait From<T> { | |
223 | fn from(T) -> Self; | |
224 | } | |
225 | "#, | |
226 | r#" | |
227 | enum A { One(u32), Two(String), } | |
228 | ||
229 | impl From<u32> for A { | |
230 | fn from(v: u32) -> Self { | |
231 | Self::One(v) | |
232 | } | |
233 | } | |
234 | ||
235 | impl From<String> for A { | |
236 | fn from(v: String) -> Self { | |
237 | A::Two(v) | |
238 | } | |
239 | } | |
240 | ||
241 | pub trait From<T> { | |
242 | fn from(T) -> Self; | |
243 | } | |
244 | "#, | |
245 | ); | |
246 | } | |
247 | ||
248 | #[test] | |
249 | fn test_add_from_impl_static_str() { | |
250 | check_assist( | |
251 | generate_from_impl_for_enum, | |
252 | r#" | |
253 | //- minicore: from | |
254 | enum A { $0One(&'static str) } | |
255 | "#, | |
256 | r#" | |
257 | enum A { One(&'static str) } | |
258 | ||
259 | impl From<&'static str> for A { | |
260 | fn from(v: &'static str) -> Self { | |
261 | Self::One(v) | |
262 | } | |
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 | r#" | |
273 | //- minicore: from | |
274 | enum Generic<T, U: Clone> { $0One(T), Two(U) } | |
275 | "#, | |
276 | r#" | |
277 | enum Generic<T, U: Clone> { One(T), Two(U) } | |
278 | ||
279 | impl<T, U: Clone> From<T> for Generic<T, U> { | |
280 | fn from(v: T) -> Self { | |
281 | Self::One(v) | |
282 | } | |
283 | } | |
284 | "#, | |
285 | ); | |
286 | } | |
287 | ||
288 | #[test] | |
289 | fn test_add_from_impl_with_lifetime() { | |
290 | check_assist( | |
291 | generate_from_impl_for_enum, | |
292 | r#" | |
293 | //- minicore: from | |
294 | enum Generic<'a> { $0One(&'a i32) } | |
295 | "#, | |
296 | r#" | |
297 | enum Generic<'a> { One(&'a i32) } | |
298 | ||
299 | impl<'a> From<&'a i32> for Generic<'a> { | |
300 | fn from(v: &'a i32) -> Self { | |
301 | Self::One(v) | |
302 | } | |
303 | } | |
304 | "#, | |
305 | ); | |
306 | } | |
307 | } |