]> git.proxmox.com Git - rustc.git/blame - src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_from_impl_for_enum.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / generate_from_impl_for_enum.rs
CommitLineData
064997fb
FG
1use ide_db::{famous_defs::FamousDefs, RootDatabase};
2use syntax::ast::{self, AstNode, HasName};
3
f25598a0
FG
4use 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// ```
25pub(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
81fn 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)]
103mod 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
114enum A { $0One(u32) }
115"#,
116 r#"
117enum A { One(u32) }
118
119impl 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
134enum A { $0One(foo::bar::baz::Boo) }
135"#,
136 r#"
137enum A { One(foo::bar::baz::Boo) }
138
139impl 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
154enum 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
165enum 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
176enum A { $0One { x: u32 } }
177"#,
178 r#"
179enum A { One { x: u32 } }
180
181impl 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
197enum A { $0One(u32), }
198
199impl 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
214enum A { $0One(u32), Two(String), }
215
216impl From<String> for A {
217 fn from(v: String) -> Self {
218 A::Two(v)
219 }
220}
221
222pub trait From<T> {
223 fn from(T) -> Self;
224}
225"#,
226 r#"
227enum A { One(u32), Two(String), }
228
229impl From<u32> for A {
230 fn from(v: u32) -> Self {
231 Self::One(v)
232 }
233}
234
235impl From<String> for A {
236 fn from(v: String) -> Self {
237 A::Two(v)
238 }
239}
240
241pub 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
254enum A { $0One(&'static str) }
255"#,
256 r#"
257enum A { One(&'static str) }
258
259impl 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
274enum Generic<T, U: Clone> { $0One(T), Two(U) }
275"#,
276 r#"
277enum Generic<T, U: Clone> { One(T), Two(U) }
278
279impl<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
294enum Generic<'a> { $0One(&'a i32) }
295"#,
296 r#"
297enum Generic<'a> { One(&'a i32) }
298
299impl<'a> From<&'a i32> for Generic<'a> {
300 fn from(v: &'a i32) -> Self {
301 Self::One(v)
302 }
303}
304"#,
305 );
306 }
307}