]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
Rollup merge of #99954 - dingxiangfei2009:break-out-let-else-higher-up, r=oli-obk
[rust.git] / src / tools / rust-analyzer / crates / ide-assists / src / handlers / extract_struct_from_enum_variant.rs
1 use std::iter;
2
3 use either::Either;
4 use hir::{Module, ModuleDef, Name, Variant};
5 use ide_db::{
6     defs::Definition,
7     helpers::mod_path_to_ast,
8     imports::insert_use::{insert_use, ImportScope, InsertUseConfig},
9     search::FileReference,
10     FxHashSet, RootDatabase,
11 };
12 use itertools::{Itertools, Position};
13 use syntax::{
14     ast::{
15         self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
16         HasName, HasVisibility,
17     },
18     match_ast, ted, SyntaxElement,
19     SyntaxKind::*,
20     SyntaxNode, T,
21 };
22
23 use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
24
25 // Assist: extract_struct_from_enum_variant
26 //
27 // Extracts a struct from enum variant.
28 //
29 // ```
30 // enum A { $0One(u32, u32) }
31 // ```
32 // ->
33 // ```
34 // struct One(u32, u32);
35 //
36 // enum A { One(One) }
37 // ```
38 pub(crate) fn extract_struct_from_enum_variant(
39     acc: &mut Assists,
40     ctx: &AssistContext<'_>,
41 ) -> Option<()> {
42     let variant = ctx.find_node_at_offset::<ast::Variant>()?;
43     let field_list = extract_field_list_if_applicable(&variant)?;
44
45     let variant_name = variant.name()?;
46     let variant_hir = ctx.sema.to_def(&variant)?;
47     if existing_definition(ctx.db(), &variant_name, &variant_hir) {
48         cov_mark::hit!(test_extract_enum_not_applicable_if_struct_exists);
49         return None;
50     }
51
52     let enum_ast = variant.parent_enum();
53     let enum_hir = ctx.sema.to_def(&enum_ast)?;
54     let target = variant.syntax().text_range();
55     acc.add(
56         AssistId("extract_struct_from_enum_variant", AssistKind::RefactorRewrite),
57         "Extract struct from enum variant",
58         target,
59         |builder| {
60             let variant_hir_name = variant_hir.name(ctx.db());
61             let enum_module_def = ModuleDef::from(enum_hir);
62             let usages = Definition::Variant(variant_hir).usages(&ctx.sema).all();
63
64             let mut visited_modules_set = FxHashSet::default();
65             let current_module = enum_hir.module(ctx.db());
66             visited_modules_set.insert(current_module);
67             // record file references of the file the def resides in, we only want to swap to the edited file in the builder once
68             let mut def_file_references = None;
69             for (file_id, references) in usages {
70                 if file_id == ctx.file_id() {
71                     def_file_references = Some(references);
72                     continue;
73                 }
74                 builder.edit_file(file_id);
75                 let processed = process_references(
76                     ctx,
77                     builder,
78                     &mut visited_modules_set,
79                     &enum_module_def,
80                     &variant_hir_name,
81                     references,
82                 );
83                 processed.into_iter().for_each(|(path, node, import)| {
84                     apply_references(ctx.config.insert_use, path, node, import)
85                 });
86             }
87             builder.edit_file(ctx.file_id());
88
89             let variant = builder.make_mut(variant.clone());
90             if let Some(references) = def_file_references {
91                 let processed = process_references(
92                     ctx,
93                     builder,
94                     &mut visited_modules_set,
95                     &enum_module_def,
96                     &variant_hir_name,
97                     references,
98                 );
99                 processed.into_iter().for_each(|(path, node, import)| {
100                     apply_references(ctx.config.insert_use, path, node, import)
101                 });
102             }
103
104             let indent = enum_ast.indent_level();
105             let generic_params = enum_ast
106                 .generic_param_list()
107                 .and_then(|known_generics| extract_generic_params(&known_generics, &field_list));
108             let generics = generic_params.as_ref().map(|generics| generics.clone_for_update());
109             let def =
110                 create_struct_def(variant_name.clone(), &variant, &field_list, generics, &enum_ast);
111             def.reindent_to(indent);
112
113             let start_offset = &variant.parent_enum().syntax().clone();
114             ted::insert_all_raw(
115                 ted::Position::before(start_offset),
116                 vec![
117                     def.syntax().clone().into(),
118                     make::tokens::whitespace(&format!("\n\n{}", indent)).into(),
119                 ],
120             );
121
122             update_variant(&variant, generic_params.map(|g| g.clone_for_update()));
123         },
124     )
125 }
126
127 fn extract_field_list_if_applicable(
128     variant: &ast::Variant,
129 ) -> Option<Either<ast::RecordFieldList, ast::TupleFieldList>> {
130     match variant.kind() {
131         ast::StructKind::Record(field_list) if field_list.fields().next().is_some() => {
132             Some(Either::Left(field_list))
133         }
134         ast::StructKind::Tuple(field_list) if field_list.fields().count() > 1 => {
135             Some(Either::Right(field_list))
136         }
137         _ => None,
138     }
139 }
140
141 fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Variant) -> bool {
142     variant
143         .parent_enum(db)
144         .module(db)
145         .scope(db, None)
146         .into_iter()
147         .filter(|(_, def)| match def {
148             // only check type-namespace
149             hir::ScopeDef::ModuleDef(def) => matches!(
150                 def,
151                 ModuleDef::Module(_)
152                     | ModuleDef::Adt(_)
153                     | ModuleDef::Variant(_)
154                     | ModuleDef::Trait(_)
155                     | ModuleDef::TypeAlias(_)
156                     | ModuleDef::BuiltinType(_)
157             ),
158             _ => false,
159         })
160         .any(|(name, _)| name.to_string() == variant_name.to_string())
161 }
162
163 fn extract_generic_params(
164     known_generics: &ast::GenericParamList,
165     field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
166 ) -> Option<ast::GenericParamList> {
167     let mut generics = known_generics.generic_params().map(|param| (param, false)).collect_vec();
168
169     let tagged_one = match field_list {
170         Either::Left(field_list) => field_list
171             .fields()
172             .filter_map(|f| f.ty())
173             .fold(false, |tagged, ty| tag_generics_in_variant(&ty, &mut generics) || tagged),
174         Either::Right(field_list) => field_list
175             .fields()
176             .filter_map(|f| f.ty())
177             .fold(false, |tagged, ty| tag_generics_in_variant(&ty, &mut generics) || tagged),
178     };
179
180     let generics = generics.into_iter().filter_map(|(param, tag)| tag.then(|| param));
181     tagged_one.then(|| make::generic_param_list(generics))
182 }
183
184 fn tag_generics_in_variant(ty: &ast::Type, generics: &mut [(ast::GenericParam, bool)]) -> bool {
185     let mut tagged_one = false;
186
187     for token in ty.syntax().descendants_with_tokens().filter_map(SyntaxElement::into_token) {
188         for (param, tag) in generics.iter_mut().filter(|(_, tag)| !tag) {
189             match param {
190                 ast::GenericParam::LifetimeParam(lt)
191                     if matches!(token.kind(), T![lifetime_ident]) =>
192                 {
193                     if let Some(lt) = lt.lifetime() {
194                         if lt.text().as_str() == token.text() {
195                             *tag = true;
196                             tagged_one = true;
197                             break;
198                         }
199                     }
200                 }
201                 param if matches!(token.kind(), T![ident]) => {
202                     if match param {
203                         ast::GenericParam::ConstParam(konst) => konst
204                             .name()
205                             .map(|name| name.text().as_str() == token.text())
206                             .unwrap_or_default(),
207                         ast::GenericParam::TypeParam(ty) => ty
208                             .name()
209                             .map(|name| name.text().as_str() == token.text())
210                             .unwrap_or_default(),
211                         ast::GenericParam::LifetimeParam(lt) => lt
212                             .lifetime()
213                             .map(|lt| lt.text().as_str() == token.text())
214                             .unwrap_or_default(),
215                     } {
216                         *tag = true;
217                         tagged_one = true;
218                         break;
219                     }
220                 }
221                 _ => (),
222             }
223         }
224     }
225
226     tagged_one
227 }
228
229 fn create_struct_def(
230     variant_name: ast::Name,
231     variant: &ast::Variant,
232     field_list: &Either<ast::RecordFieldList, ast::TupleFieldList>,
233     generics: Option<ast::GenericParamList>,
234     enum_: &ast::Enum,
235 ) -> ast::Struct {
236     let enum_vis = enum_.visibility();
237
238     let insert_vis = |node: &'_ SyntaxNode, vis: &'_ SyntaxNode| {
239         let vis = vis.clone_for_update();
240         ted::insert(ted::Position::before(node), vis);
241     };
242
243     // for fields without any existing visibility, use visibility of enum
244     let field_list: ast::FieldList = match field_list {
245         Either::Left(field_list) => {
246             let field_list = field_list.clone_for_update();
247
248             if let Some(vis) = &enum_vis {
249                 field_list
250                     .fields()
251                     .filter(|field| field.visibility().is_none())
252                     .filter_map(|field| field.name())
253                     .for_each(|it| insert_vis(it.syntax(), vis.syntax()));
254             }
255
256             field_list.into()
257         }
258         Either::Right(field_list) => {
259             let field_list = field_list.clone_for_update();
260
261             if let Some(vis) = &enum_vis {
262                 field_list
263                     .fields()
264                     .filter(|field| field.visibility().is_none())
265                     .filter_map(|field| field.ty())
266                     .for_each(|it| insert_vis(it.syntax(), vis.syntax()));
267             }
268
269             field_list.into()
270         }
271     };
272
273     field_list.reindent_to(IndentLevel::single());
274
275     let strukt = make::struct_(enum_vis, variant_name, generics, field_list).clone_for_update();
276
277     // FIXME: Consider making this an actual function somewhere (like in `AttrsOwnerEdit`) after some deliberation
278     let attrs_and_docs = |node: &SyntaxNode| {
279         let mut select_next_ws = false;
280         node.children_with_tokens().filter(move |child| {
281             let accept = match child.kind() {
282                 ATTR | COMMENT => {
283                     select_next_ws = true;
284                     return true;
285                 }
286                 WHITESPACE if select_next_ws => true,
287                 _ => false,
288             };
289             select_next_ws = false;
290
291             accept
292         })
293     };
294
295     // copy attributes & comments from variant
296     let variant_attrs = attrs_and_docs(variant.syntax())
297         .map(|tok| match tok.kind() {
298             WHITESPACE => make::tokens::single_newline().into(),
299             _ => tok,
300         })
301         .collect();
302     ted::insert_all(ted::Position::first_child_of(strukt.syntax()), variant_attrs);
303
304     // copy attributes from enum
305     ted::insert_all(
306         ted::Position::first_child_of(strukt.syntax()),
307         enum_.attrs().map(|it| it.syntax().clone_for_update().into()).collect(),
308     );
309     strukt
310 }
311
312 fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList>) -> Option<()> {
313     let name = variant.name()?;
314     let ty = generics
315         .filter(|generics| generics.generic_params().count() > 0)
316         .map(|generics| {
317             let mut generic_str = String::with_capacity(8);
318
319             for (p, more) in generics.generic_params().with_position().map(|p| match p {
320                 Position::First(p) | Position::Middle(p) => (p, true),
321                 Position::Last(p) | Position::Only(p) => (p, false),
322             }) {
323                 match p {
324                     ast::GenericParam::ConstParam(konst) => {
325                         if let Some(name) = konst.name() {
326                             generic_str.push_str(name.text().as_str());
327                         }
328                     }
329                     ast::GenericParam::LifetimeParam(lt) => {
330                         if let Some(lt) = lt.lifetime() {
331                             generic_str.push_str(lt.text().as_str());
332                         }
333                     }
334                     ast::GenericParam::TypeParam(ty) => {
335                         if let Some(name) = ty.name() {
336                             generic_str.push_str(name.text().as_str());
337                         }
338                     }
339                 }
340                 if more {
341                     generic_str.push_str(", ");
342                 }
343             }
344
345             make::ty(&format!("{}<{}>", &name.text(), &generic_str))
346         })
347         .unwrap_or_else(|| make::ty(&name.text()));
348
349     let tuple_field = make::tuple_field(None, ty);
350     let replacement = make::variant(
351         name,
352         Some(ast::FieldList::TupleFieldList(make::tuple_field_list(iter::once(tuple_field)))),
353     )
354     .clone_for_update();
355     ted::replace(variant.syntax(), replacement.syntax());
356     Some(())
357 }
358
359 fn apply_references(
360     insert_use_cfg: InsertUseConfig,
361     segment: ast::PathSegment,
362     node: SyntaxNode,
363     import: Option<(ImportScope, hir::ModPath)>,
364 ) {
365     if let Some((scope, path)) = import {
366         insert_use(&scope, mod_path_to_ast(&path), &insert_use_cfg);
367     }
368     // deep clone to prevent cycle
369     let path = make::path_from_segments(iter::once(segment.clone_subtree()), false);
370     ted::insert_raw(ted::Position::before(segment.syntax()), path.clone_for_update().syntax());
371     ted::insert_raw(ted::Position::before(segment.syntax()), make::token(T!['(']));
372     ted::insert_raw(ted::Position::after(&node), make::token(T![')']));
373 }
374
375 fn process_references(
376     ctx: &AssistContext<'_>,
377     builder: &mut SourceChangeBuilder,
378     visited_modules: &mut FxHashSet<Module>,
379     enum_module_def: &ModuleDef,
380     variant_hir_name: &Name,
381     refs: Vec<FileReference>,
382 ) -> Vec<(ast::PathSegment, SyntaxNode, Option<(ImportScope, hir::ModPath)>)> {
383     // we have to recollect here eagerly as we are about to edit the tree we need to calculate the changes
384     // and corresponding nodes up front
385     refs.into_iter()
386         .flat_map(|reference| {
387             let (segment, scope_node, module) = reference_to_node(&ctx.sema, reference)?;
388             let segment = builder.make_mut(segment);
389             let scope_node = builder.make_syntax_mut(scope_node);
390             if !visited_modules.contains(&module) {
391                 let mod_path = module.find_use_path_prefixed(
392                     ctx.sema.db,
393                     *enum_module_def,
394                     ctx.config.insert_use.prefix_kind,
395                 );
396                 if let Some(mut mod_path) = mod_path {
397                     mod_path.pop_segment();
398                     mod_path.push_segment(variant_hir_name.clone());
399                     let scope = ImportScope::find_insert_use_container(&scope_node, &ctx.sema)?;
400                     visited_modules.insert(module);
401                     return Some((segment, scope_node, Some((scope, mod_path))));
402                 }
403             }
404             Some((segment, scope_node, None))
405         })
406         .collect()
407 }
408
409 fn reference_to_node(
410     sema: &hir::Semantics<'_, RootDatabase>,
411     reference: FileReference,
412 ) -> Option<(ast::PathSegment, SyntaxNode, hir::Module)> {
413     let segment =
414         reference.name.as_name_ref()?.syntax().parent().and_then(ast::PathSegment::cast)?;
415     let parent = segment.parent_path().syntax().parent()?;
416     let expr_or_pat = match_ast! {
417         match parent {
418             ast::PathExpr(_it) => parent.parent()?,
419             ast::RecordExpr(_it) => parent,
420             ast::TupleStructPat(_it) => parent,
421             ast::RecordPat(_it) => parent,
422             _ => return None,
423         }
424     };
425     let module = sema.scope(&expr_or_pat)?.module();
426     Some((segment, expr_or_pat, module))
427 }
428
429 #[cfg(test)]
430 mod tests {
431     use crate::tests::{check_assist, check_assist_not_applicable};
432
433     use super::*;
434
435     #[test]
436     fn test_extract_struct_several_fields_tuple() {
437         check_assist(
438             extract_struct_from_enum_variant,
439             "enum A { $0One(u32, u32) }",
440             r#"struct One(u32, u32);
441
442 enum A { One(One) }"#,
443         );
444     }
445
446     #[test]
447     fn test_extract_struct_several_fields_named() {
448         check_assist(
449             extract_struct_from_enum_variant,
450             "enum A { $0One { foo: u32, bar: u32 } }",
451             r#"struct One{ foo: u32, bar: u32 }
452
453 enum A { One(One) }"#,
454         );
455     }
456
457     #[test]
458     fn test_extract_struct_one_field_named() {
459         check_assist(
460             extract_struct_from_enum_variant,
461             "enum A { $0One { foo: u32 } }",
462             r#"struct One{ foo: u32 }
463
464 enum A { One(One) }"#,
465         );
466     }
467
468     #[test]
469     fn test_extract_struct_carries_over_generics() {
470         check_assist(
471             extract_struct_from_enum_variant,
472             r"enum En<T> { Var { a: T$0 } }",
473             r#"struct Var<T>{ a: T }
474
475 enum En<T> { Var(Var<T>) }"#,
476         );
477     }
478
479     #[test]
480     fn test_extract_struct_carries_over_attributes() {
481         check_assist(
482             extract_struct_from_enum_variant,
483             r#"#[derive(Debug)]
484 #[derive(Clone)]
485 enum Enum { Variant{ field: u32$0 } }"#,
486             r#"#[derive(Debug)]#[derive(Clone)] struct Variant{ field: u32 }
487
488 #[derive(Debug)]
489 #[derive(Clone)]
490 enum Enum { Variant(Variant) }"#,
491         );
492     }
493
494     #[test]
495     fn test_extract_struct_indent_to_parent_enum() {
496         check_assist(
497             extract_struct_from_enum_variant,
498             r#"
499 enum Enum {
500     Variant {
501         field: u32$0
502     }
503 }"#,
504             r#"
505 struct Variant{
506     field: u32
507 }
508
509 enum Enum {
510     Variant(Variant)
511 }"#,
512         );
513     }
514
515     #[test]
516     fn test_extract_struct_indent_to_parent_enum_in_mod() {
517         check_assist(
518             extract_struct_from_enum_variant,
519             r#"
520 mod indenting {
521     enum Enum {
522         Variant {
523             field: u32$0
524         }
525     }
526 }"#,
527             r#"
528 mod indenting {
529     struct Variant{
530         field: u32
531     }
532
533     enum Enum {
534         Variant(Variant)
535     }
536 }"#,
537         );
538     }
539
540     #[test]
541     fn test_extract_struct_keep_comments_and_attrs_one_field_named() {
542         check_assist(
543             extract_struct_from_enum_variant,
544             r#"
545 enum A {
546     $0One {
547         // leading comment
548         /// doc comment
549         #[an_attr]
550         foo: u32
551         // trailing comment
552     }
553 }"#,
554             r#"
555 struct One{
556     // leading comment
557     /// doc comment
558     #[an_attr]
559     foo: u32
560     // trailing comment
561 }
562
563 enum A {
564     One(One)
565 }"#,
566         );
567     }
568
569     #[test]
570     fn test_extract_struct_keep_comments_and_attrs_several_fields_named() {
571         check_assist(
572             extract_struct_from_enum_variant,
573             r#"
574 enum A {
575     $0One {
576         // comment
577         /// doc
578         #[attr]
579         foo: u32,
580         // comment
581         #[attr]
582         /// doc
583         bar: u32
584     }
585 }"#,
586             r#"
587 struct One{
588     // comment
589     /// doc
590     #[attr]
591     foo: u32,
592     // comment
593     #[attr]
594     /// doc
595     bar: u32
596 }
597
598 enum A {
599     One(One)
600 }"#,
601         );
602     }
603
604     #[test]
605     fn test_extract_struct_keep_comments_and_attrs_several_fields_tuple() {
606         check_assist(
607             extract_struct_from_enum_variant,
608             "enum A { $0One(/* comment */ #[attr] u32, /* another */ u32 /* tail */) }",
609             r#"
610 struct One(/* comment */ #[attr] u32, /* another */ u32 /* tail */);
611
612 enum A { One(One) }"#,
613         );
614     }
615
616     #[test]
617     fn test_extract_struct_keep_comments_and_attrs_on_variant_struct() {
618         check_assist(
619             extract_struct_from_enum_variant,
620             r#"
621 enum A {
622     /* comment */
623     // other
624     /// comment
625     #[attr]
626     $0One {
627         a: u32
628     }
629 }"#,
630             r#"
631 /* comment */
632 // other
633 /// comment
634 #[attr]
635 struct One{
636     a: u32
637 }
638
639 enum A {
640     One(One)
641 }"#,
642         );
643     }
644
645     #[test]
646     fn test_extract_struct_keep_comments_and_attrs_on_variant_tuple() {
647         check_assist(
648             extract_struct_from_enum_variant,
649             r#"
650 enum A {
651     /* comment */
652     // other
653     /// comment
654     #[attr]
655     $0One(u32, u32)
656 }"#,
657             r#"
658 /* comment */
659 // other
660 /// comment
661 #[attr]
662 struct One(u32, u32);
663
664 enum A {
665     One(One)
666 }"#,
667         );
668     }
669
670     #[test]
671     fn test_extract_struct_keep_existing_visibility_named() {
672         check_assist(
673             extract_struct_from_enum_variant,
674             "enum A { $0One{ a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 } }",
675             r#"
676 struct One{ a: u32, pub(crate) b: u32, pub(super) c: u32, d: u32 }
677
678 enum A { One(One) }"#,
679         );
680     }
681
682     #[test]
683     fn test_extract_struct_keep_existing_visibility_tuple() {
684         check_assist(
685             extract_struct_from_enum_variant,
686             "enum A { $0One(u32, pub(crate) u32, pub(super) u32, u32) }",
687             r#"
688 struct One(u32, pub(crate) u32, pub(super) u32, u32);
689
690 enum A { One(One) }"#,
691         );
692     }
693
694     #[test]
695     fn test_extract_enum_variant_name_value_namespace() {
696         check_assist(
697             extract_struct_from_enum_variant,
698             r#"const One: () = ();
699 enum A { $0One(u32, u32) }"#,
700             r#"const One: () = ();
701 struct One(u32, u32);
702
703 enum A { One(One) }"#,
704         );
705     }
706
707     #[test]
708     fn test_extract_struct_no_visibility() {
709         check_assist(
710             extract_struct_from_enum_variant,
711             "enum A { $0One(u32, u32) }",
712             r#"
713 struct One(u32, u32);
714
715 enum A { One(One) }"#,
716         );
717     }
718
719     #[test]
720     fn test_extract_struct_pub_visibility() {
721         check_assist(
722             extract_struct_from_enum_variant,
723             "pub enum A { $0One(u32, u32) }",
724             r#"
725 pub struct One(pub u32, pub u32);
726
727 pub enum A { One(One) }"#,
728         );
729     }
730
731     #[test]
732     fn test_extract_struct_pub_in_mod_visibility() {
733         check_assist(
734             extract_struct_from_enum_variant,
735             "pub(in something) enum A { $0One{ a: u32, b: u32 } }",
736             r#"
737 pub(in something) struct One{ pub(in something) a: u32, pub(in something) b: u32 }
738
739 pub(in something) enum A { One(One) }"#,
740         );
741     }
742
743     #[test]
744     fn test_extract_struct_pub_crate_visibility() {
745         check_assist(
746             extract_struct_from_enum_variant,
747             "pub(crate) enum A { $0One{ a: u32, b: u32, c: u32 } }",
748             r#"
749 pub(crate) struct One{ pub(crate) a: u32, pub(crate) b: u32, pub(crate) c: u32 }
750
751 pub(crate) enum A { One(One) }"#,
752         );
753     }
754
755     #[test]
756     fn test_extract_struct_with_complex_imports() {
757         check_assist(
758             extract_struct_from_enum_variant,
759             r#"mod my_mod {
760     fn another_fn() {
761         let m = my_other_mod::MyEnum::MyField(1, 1);
762     }
763
764     pub mod my_other_mod {
765         fn another_fn() {
766             let m = MyEnum::MyField(1, 1);
767         }
768
769         pub enum MyEnum {
770             $0MyField(u8, u8),
771         }
772     }
773 }
774
775 fn another_fn() {
776     let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
777 }"#,
778             r#"use my_mod::my_other_mod::MyField;
779
780 mod my_mod {
781     use self::my_other_mod::MyField;
782
783     fn another_fn() {
784         let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
785     }
786
787     pub mod my_other_mod {
788         fn another_fn() {
789             let m = MyEnum::MyField(MyField(1, 1));
790         }
791
792         pub struct MyField(pub u8, pub u8);
793
794         pub enum MyEnum {
795             MyField(MyField),
796         }
797     }
798 }
799
800 fn another_fn() {
801     let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
802 }"#,
803         );
804     }
805
806     #[test]
807     fn extract_record_fix_references() {
808         check_assist(
809             extract_struct_from_enum_variant,
810             r#"
811 enum E {
812     $0V { i: i32, j: i32 }
813 }
814
815 fn f() {
816     let E::V { i, j } = E::V { i: 9, j: 2 };
817 }
818 "#,
819             r#"
820 struct V{ i: i32, j: i32 }
821
822 enum E {
823     V(V)
824 }
825
826 fn f() {
827     let E::V(V { i, j }) = E::V(V { i: 9, j: 2 });
828 }
829 "#,
830         )
831     }
832
833     #[test]
834     fn extract_record_fix_references2() {
835         check_assist(
836             extract_struct_from_enum_variant,
837             r#"
838 enum E {
839     $0V(i32, i32)
840 }
841
842 fn f() {
843     let E::V(i, j) = E::V(9, 2);
844 }
845 "#,
846             r#"
847 struct V(i32, i32);
848
849 enum E {
850     V(V)
851 }
852
853 fn f() {
854     let E::V(V(i, j)) = E::V(V(9, 2));
855 }
856 "#,
857         )
858     }
859
860     #[test]
861     fn test_several_files() {
862         check_assist(
863             extract_struct_from_enum_variant,
864             r#"
865 //- /main.rs
866 enum E {
867     $0V(i32, i32)
868 }
869 mod foo;
870
871 //- /foo.rs
872 use crate::E;
873 fn f() {
874     let e = E::V(9, 2);
875 }
876 "#,
877             r#"
878 //- /main.rs
879 struct V(i32, i32);
880
881 enum E {
882     V(V)
883 }
884 mod foo;
885
886 //- /foo.rs
887 use crate::{E, V};
888 fn f() {
889     let e = E::V(V(9, 2));
890 }
891 "#,
892         )
893     }
894
895     #[test]
896     fn test_several_files_record() {
897         check_assist(
898             extract_struct_from_enum_variant,
899             r#"
900 //- /main.rs
901 enum E {
902     $0V { i: i32, j: i32 }
903 }
904 mod foo;
905
906 //- /foo.rs
907 use crate::E;
908 fn f() {
909     let e = E::V { i: 9, j: 2 };
910 }
911 "#,
912             r#"
913 //- /main.rs
914 struct V{ i: i32, j: i32 }
915
916 enum E {
917     V(V)
918 }
919 mod foo;
920
921 //- /foo.rs
922 use crate::{E, V};
923 fn f() {
924     let e = E::V(V { i: 9, j: 2 });
925 }
926 "#,
927         )
928     }
929
930     #[test]
931     fn test_extract_struct_record_nested_call_exp() {
932         check_assist(
933             extract_struct_from_enum_variant,
934             r#"
935 enum A { $0One { a: u32, b: u32 } }
936
937 struct B(A);
938
939 fn foo() {
940     let _ = B(A::One { a: 1, b: 2 });
941 }
942 "#,
943             r#"
944 struct One{ a: u32, b: u32 }
945
946 enum A { One(One) }
947
948 struct B(A);
949
950 fn foo() {
951     let _ = B(A::One(One { a: 1, b: 2 }));
952 }
953 "#,
954         );
955     }
956
957     #[test]
958     fn test_extract_enum_not_applicable_for_element_with_no_fields() {
959         check_assist_not_applicable(extract_struct_from_enum_variant, r#"enum A { $0One }"#);
960     }
961
962     #[test]
963     fn test_extract_enum_not_applicable_if_struct_exists() {
964         cov_mark::check!(test_extract_enum_not_applicable_if_struct_exists);
965         check_assist_not_applicable(
966             extract_struct_from_enum_variant,
967             r#"
968 struct One;
969 enum A { $0One(u8, u32) }
970 "#,
971         );
972     }
973
974     #[test]
975     fn test_extract_not_applicable_one_field() {
976         check_assist_not_applicable(extract_struct_from_enum_variant, r"enum A { $0One(u32) }");
977     }
978
979     #[test]
980     fn test_extract_not_applicable_no_field_tuple() {
981         check_assist_not_applicable(extract_struct_from_enum_variant, r"enum A { $0None() }");
982     }
983
984     #[test]
985     fn test_extract_not_applicable_no_field_named() {
986         check_assist_not_applicable(extract_struct_from_enum_variant, r"enum A { $0None {} }");
987     }
988
989     #[test]
990     fn test_extract_struct_only_copies_needed_generics() {
991         check_assist(
992             extract_struct_from_enum_variant,
993             r#"
994 enum X<'a, 'b, 'x> {
995     $0A { a: &'a &'x mut () },
996     B { b: &'b () },
997     C { c: () },
998 }
999 "#,
1000             r#"
1001 struct A<'a, 'x>{ a: &'a &'x mut () }
1002
1003 enum X<'a, 'b, 'x> {
1004     A(A<'a, 'x>),
1005     B { b: &'b () },
1006     C { c: () },
1007 }
1008 "#,
1009         );
1010     }
1011
1012     #[test]
1013     fn test_extract_struct_with_liftime_type_const() {
1014         check_assist(
1015             extract_struct_from_enum_variant,
1016             r#"
1017 enum X<'b, T, V, const C: usize> {
1018     $0A { a: T, b: X<'b>, c: [u8; C] },
1019     D { d: V },
1020 }
1021 "#,
1022             r#"
1023 struct A<'b, T, const C: usize>{ a: T, b: X<'b>, c: [u8; C] }
1024
1025 enum X<'b, T, V, const C: usize> {
1026     A(A<'b, T, C>),
1027     D { d: V },
1028 }
1029 "#,
1030         );
1031     }
1032
1033     #[test]
1034     fn test_extract_struct_without_generics() {
1035         check_assist(
1036             extract_struct_from_enum_variant,
1037             r#"
1038 enum X<'a, 'b> {
1039     A { a: &'a () },
1040     B { b: &'b () },
1041     $0C { c: () },
1042 }
1043 "#,
1044             r#"
1045 struct C{ c: () }
1046
1047 enum X<'a, 'b> {
1048     A { a: &'a () },
1049     B { b: &'b () },
1050     C(C),
1051 }
1052 "#,
1053         );
1054     }
1055
1056     #[test]
1057     fn test_extract_struct_keeps_trait_bounds() {
1058         check_assist(
1059             extract_struct_from_enum_variant,
1060             r#"
1061 enum En<T: TraitT, V: TraitV> {
1062     $0A { a: T },
1063     B { b: V },
1064 }
1065 "#,
1066             r#"
1067 struct A<T: TraitT>{ a: T }
1068
1069 enum En<T: TraitT, V: TraitV> {
1070     A(A<T>),
1071     B { b: V },
1072 }
1073 "#,
1074         );
1075     }
1076 }