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