1use hir::next_solver::{DbInterner, TypingMode};
2use ide_db::{RootDatabase, famous_defs::FamousDefs};
3use syntax::ast::{self, AstNode, HasName};
4
5use crate::{
6 AssistContext, AssistId, Assists,
7 utils::{generate_trait_impl_text_intransitive, is_selected},
8};
9
10pub(crate) fn generate_from_impl_for_enum(
28 acc: &mut Assists,
29 ctx: &AssistContext<'_>,
30) -> Option<()> {
31 let variant = ctx.find_node_at_offset::<ast::Variant>()?;
32 let adt = ast::Adt::Enum(variant.parent_enum());
33 let variants = selected_variants(ctx, &variant)?;
34
35 let target = variant.syntax().text_range();
36 acc.add(
37 AssistId::generate("generate_from_impl_for_enum"),
38 "Generate `From` impl for this enum variant(s)",
39 target,
40 |edit| {
41 let start_offset = variant.parent_enum().syntax().text_range().end();
42 let from_impl = variants
43 .into_iter()
44 .map(|variant_info| {
45 let from_trait = format!("From<{}>", variant_info.ty);
46 let impl_code = generate_impl_code(variant_info);
47 generate_trait_impl_text_intransitive(&adt, &from_trait, &impl_code)
48 })
49 .collect::<String>();
50 edit.insert(start_offset, from_impl);
51 },
52 )
53}
54
55fn generate_impl_code(VariantInfo { name, field_name, ty }: VariantInfo) -> String {
56 if let Some(field) = field_name {
57 format!(
58 r#" fn from({field}: {ty}) -> Self {{
59 Self::{name} {{ {field} }}
60 }}"#
61 )
62 } else {
63 format!(
64 r#" fn from(v: {ty}) -> Self {{
65 Self::{name}(v)
66 }}"#
67 )
68 }
69}
70
71struct VariantInfo {
72 name: ast::Name,
73 field_name: Option<ast::Name>,
74 ty: ast::Type,
75}
76
77fn selected_variants(ctx: &AssistContext<'_>, variant: &ast::Variant) -> Option<Vec<VariantInfo>> {
78 variant
79 .parent_enum()
80 .variant_list()?
81 .variants()
82 .filter(|it| is_selected(it, ctx.selection_trimmed(), true))
83 .map(|variant| {
84 let (name, ty) = extract_variant_info(&ctx.sema, &variant)?;
85 Some(VariantInfo { name: variant.name()?, field_name: name, ty })
86 })
87 .collect()
88}
89
90fn extract_variant_info(
91 sema: &'_ hir::Semantics<'_, RootDatabase>,
92 variant: &ast::Variant,
93) -> Option<(Option<ast::Name>, ast::Type)> {
94 let (field_name, field_type) = match variant.kind() {
95 ast::StructKind::Tuple(field_list) => {
96 if field_list.fields().count() != 1 {
97 return None;
98 }
99 (None, field_list.fields().next()?.ty()?)
100 }
101 ast::StructKind::Record(field_list) => {
102 if field_list.fields().count() != 1 {
103 return None;
104 }
105 let field = field_list.fields().next()?;
106 (Some(field.name()?), field.ty()?)
107 }
108 ast::StructKind::Unit => return None,
109 };
110
111 if existing_from_impl(sema, variant).is_some() {
112 cov_mark::hit!(test_add_from_impl_already_exists);
113 return None;
114 }
115 Some((field_name, field_type))
116}
117
118fn existing_from_impl(
119 sema: &'_ hir::Semantics<'_, RootDatabase>,
120 variant: &ast::Variant,
121) -> Option<()> {
122 let db = sema.db;
123 let variant = sema.to_def(variant)?;
124 let krate = variant.module(db).krate(db);
125 let from_trait = FamousDefs(sema, krate).core_convert_From()?;
126 let interner = DbInterner::new_with(db, krate.base());
127 use hir::next_solver::infer::DbInternerInferExt;
128 let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis());
129
130 let variant = variant.instantiate_infer(&infcx);
131 let enum_ = variant.parent_enum(sema.db);
132 let field_ty = variant.fields(sema.db).first()?.ty(sema.db);
133 let enum_ty = enum_.ty(sema.db);
134 tracing::debug!(?enum_, ?field_ty, ?enum_ty);
135 enum_ty.impls_trait(infcx, from_trait, &[field_ty]).then_some(())
136}
137
138#[cfg(test)]
139mod tests {
140 use crate::tests::{check_assist, check_assist_not_applicable};
141
142 use super::*;
143
144 #[test]
145 fn test_generate_from_impl_for_enum() {
146 check_assist(
147 generate_from_impl_for_enum,
148 r#"
149//- minicore: from
150enum A { $0One(u32) }
151"#,
152 r#"
153enum A { One(u32) }
154
155impl From<u32> for A {
156 fn from(v: u32) -> Self {
157 Self::One(v)
158 }
159}
160"#,
161 );
162 }
163
164 #[test]
165 fn test_generate_from_impl_for_multiple_enum_variants() {
166 check_assist(
167 generate_from_impl_for_enum,
168 r#"
169//- minicore: from
170enum A { $0Foo(u32), Bar$0(i32) }
171"#,
172 r#"
173enum A { Foo(u32), Bar(i32) }
174
175impl From<u32> for A {
176 fn from(v: u32) -> Self {
177 Self::Foo(v)
178 }
179}
180
181impl From<i32> for A {
182 fn from(v: i32) -> Self {
183 Self::Bar(v)
184 }
185}
186"#,
187 );
188 }
189
190 #[test]
193 fn test_generate_from_impl_for_enum_complicated_path() {
194 check_assist(
195 generate_from_impl_for_enum,
196 r#"
197//- minicore: from
198mod foo { pub mod bar { pub mod baz { pub struct Boo; } } }
199enum A { $0One(foo::bar::baz::Boo) }
200"#,
201 r#"
202mod foo { pub mod bar { pub mod baz { pub struct Boo; } } }
203enum A { One(foo::bar::baz::Boo) }
204
205impl From<foo::bar::baz::Boo> for A {
206 fn from(v: foo::bar::baz::Boo) -> Self {
207 Self::One(v)
208 }
209}
210"#,
211 );
212 }
213
214 #[test]
215 fn test_add_from_impl_no_element() {
216 check_assist_not_applicable(
217 generate_from_impl_for_enum,
218 r#"
219//- minicore: from
220enum A { $0One }
221"#,
222 );
223 }
224
225 #[test]
226 fn test_add_from_impl_more_than_one_element_in_tuple() {
227 check_assist_not_applicable(
228 generate_from_impl_for_enum,
229 r#"
230//- minicore: from
231enum A { $0One(u32, String) }
232"#,
233 );
234 }
235
236 #[test]
237 fn test_add_from_impl_struct_variant() {
238 check_assist(
239 generate_from_impl_for_enum,
240 r#"
241//- minicore: from
242enum A { $0One { x: u32 } }
243"#,
244 r#"
245enum A { One { x: u32 } }
246
247impl From<u32> for A {
248 fn from(x: u32) -> Self {
249 Self::One { x }
250 }
251}
252"#,
253 );
254 }
255
256 #[test]
257 fn test_add_from_impl_already_exists() {
258 cov_mark::check!(test_add_from_impl_already_exists);
259 check_assist_not_applicable(
260 generate_from_impl_for_enum,
261 r#"
262//- minicore: from
263enum A { $0One(u32), }
264
265impl From<u32> for A {
266 fn from(v: u32) -> Self {
267 Self::One(v)
268 }
269}
270"#,
271 );
272 }
273
274 #[test]
275 fn test_add_from_impl_different_variant_impl_exists() {
276 check_assist(
277 generate_from_impl_for_enum,
278 r#"
279//- minicore: from
280enum A { $0One(u32), Two(String), }
281
282impl From<String> for A {
283 fn from(v: String) -> Self {
284 A::Two(v)
285 }
286}
287
288pub trait From<T> {
289 fn from(T) -> Self;
290}
291"#,
292 r#"
293enum A { One(u32), Two(String), }
294
295impl From<u32> for A {
296 fn from(v: u32) -> Self {
297 Self::One(v)
298 }
299}
300
301impl From<String> for A {
302 fn from(v: String) -> Self {
303 A::Two(v)
304 }
305}
306
307pub trait From<T> {
308 fn from(T) -> Self;
309}
310"#,
311 );
312 }
313
314 #[test]
315 fn test_add_from_impl_static_str() {
316 check_assist(
317 generate_from_impl_for_enum,
318 r#"
319//- minicore: from
320enum A { $0One(&'static str) }
321"#,
322 r#"
323enum A { One(&'static str) }
324
325impl From<&'static str> for A {
326 fn from(v: &'static str) -> Self {
327 Self::One(v)
328 }
329}
330"#,
331 );
332 }
333
334 #[test]
335 fn test_add_from_impl_generic_enum() {
336 check_assist(
337 generate_from_impl_for_enum,
338 r#"
339//- minicore: from
340enum Generic<T, U: Clone> { $0One(T), Two(U) }
341"#,
342 r#"
343enum Generic<T, U: Clone> { One(T), Two(U) }
344
345impl<T, U: Clone> From<T> for Generic<T, U> {
346 fn from(v: T) -> Self {
347 Self::One(v)
348 }
349}
350"#,
351 );
352 }
353
354 #[test]
355 fn test_add_from_impl_with_lifetime() {
356 check_assist(
357 generate_from_impl_for_enum,
358 r#"
359//- minicore: from
360enum Generic<'a> { $0One(&'a i32) }
361"#,
362 r#"
363enum Generic<'a> { One(&'a i32) }
364
365impl<'a> From<&'a i32> for Generic<'a> {
366 fn from(v: &'a i32) -> Self {
367 Self::One(v)
368 }
369}
370"#,
371 );
372 }
373}