ide_assists/handlers/
convert_tuple_struct_to_named_struct.rs

1use either::Either;
2use hir::FileRangeWrapper;
3use ide_db::defs::{Definition, NameRefClass};
4use std::ops::RangeInclusive;
5use syntax::{
6    SyntaxElement, SyntaxKind, SyntaxNode, T, TextSize,
7    ast::{
8        self, AstNode, HasAttrs, HasGenericParams, HasVisibility, syntax_factory::SyntaxFactory,
9    },
10    match_ast,
11    syntax_editor::{Element, Position, SyntaxEditor},
12};
13
14use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder};
15
16// Assist: convert_tuple_struct_to_named_struct
17//
18// Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
19//
20// ```
21// struct Point$0(f32, f32);
22//
23// impl Point {
24//     pub fn new(x: f32, y: f32) -> Self {
25//         Point(x, y)
26//     }
27//
28//     pub fn x(&self) -> f32 {
29//         self.0
30//     }
31//
32//     pub fn y(&self) -> f32 {
33//         self.1
34//     }
35// }
36// ```
37// ->
38// ```
39// struct Point { field1: f32, field2: f32 }
40//
41// impl Point {
42//     pub fn new(x: f32, y: f32) -> Self {
43//         Point { field1: x, field2: y }
44//     }
45//
46//     pub fn x(&self) -> f32 {
47//         self.field1
48//     }
49//
50//     pub fn y(&self) -> f32 {
51//         self.field2
52//     }
53// }
54// ```
55pub(crate) fn convert_tuple_struct_to_named_struct(
56    acc: &mut Assists,
57    ctx: &AssistContext<'_>,
58) -> Option<()> {
59    let strukt_or_variant = ctx
60        .find_node_at_offset::<ast::Struct>()
61        .map(Either::Left)
62        .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
63    let field_list = strukt_or_variant.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
64
65    if ctx.offset() > field_list.syntax().text_range().start() {
66        // Assist could be distracting after the braces
67        return None;
68    }
69
70    let tuple_fields = match field_list {
71        ast::FieldList::TupleFieldList(it) => it,
72        ast::FieldList::RecordFieldList(_) => return None,
73    };
74    let strukt_def = match &strukt_or_variant {
75        Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
76        Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
77    };
78    let target = strukt_or_variant.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
79    let syntax = strukt_or_variant.as_ref().either(|s| s.syntax(), |v| v.syntax());
80    acc.add(
81        AssistId::refactor_rewrite("convert_tuple_struct_to_named_struct"),
82        "Convert to named struct",
83        target,
84        |edit| {
85            let names = generate_names(tuple_fields.fields());
86            edit_field_references(ctx, edit, tuple_fields.fields(), &names);
87            let mut editor = edit.make_editor(syntax);
88            edit_struct_references(ctx, edit, strukt_def, &names);
89            edit_struct_def(&mut editor, &strukt_or_variant, tuple_fields, names);
90            edit.add_file_edits(ctx.vfs_file_id(), editor);
91        },
92    )
93}
94
95fn edit_struct_def(
96    editor: &mut SyntaxEditor,
97    strukt: &Either<ast::Struct, ast::Variant>,
98    tuple_fields: ast::TupleFieldList,
99    names: Vec<ast::Name>,
100) {
101    let record_fields = tuple_fields.fields().zip(names).filter_map(|(f, name)| {
102        let field = ast::make::record_field(f.visibility(), name, f.ty()?);
103        let mut field_editor = SyntaxEditor::new(field.syntax().clone());
104        field_editor.insert_all(
105            Position::first_child_of(field.syntax()),
106            f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
107        );
108        ast::RecordField::cast(field_editor.finish().new_root().clone())
109    });
110    let make = SyntaxFactory::without_mappings();
111    let record_fields = make.record_field_list(record_fields);
112    let tuple_fields_before = Position::before(tuple_fields.syntax());
113
114    if let Either::Left(strukt) = strukt {
115        if let Some(w) = strukt.where_clause() {
116            editor.delete(w.syntax());
117            let mut insert_element = Vec::new();
118            insert_element.push(ast::make::tokens::single_newline().syntax_element());
119            insert_element.push(w.syntax().clone_for_update().syntax_element());
120            if w.syntax().last_token().is_none_or(|t| t.kind() != SyntaxKind::COMMA) {
121                insert_element.push(ast::make::token(T![,]).into());
122            }
123            insert_element.push(ast::make::tokens::single_newline().syntax_element());
124            editor.insert_all(tuple_fields_before, insert_element);
125        } else {
126            editor.insert(tuple_fields_before, ast::make::tokens::single_space());
127        }
128        if let Some(t) = strukt.semicolon_token() {
129            editor.delete(t);
130        }
131    } else {
132        editor.insert(tuple_fields_before, ast::make::tokens::single_space());
133    }
134
135    editor.replace(tuple_fields.syntax(), record_fields.syntax());
136}
137
138fn edit_struct_references(
139    ctx: &AssistContext<'_>,
140    edit: &mut SourceChangeBuilder,
141    strukt: Either<hir::Struct, hir::Variant>,
142    names: &[ast::Name],
143) {
144    let strukt_def = match strukt {
145        Either::Left(s) => Definition::Adt(hir::Adt::Struct(s)),
146        Either::Right(v) => Definition::Variant(v),
147    };
148    let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
149
150    let edit_node = |node: SyntaxNode| -> Option<SyntaxNode> {
151        let make = SyntaxFactory::without_mappings();
152        match_ast! {
153            match node {
154                ast::TupleStructPat(tuple_struct_pat) => {
155                    Some(make.record_pat_with_fields(
156                        tuple_struct_pat.path()?,
157                        generate_record_pat_list(&tuple_struct_pat, names),
158                    ).syntax().clone())
159                },
160                // for tuple struct creations like Foo(42)
161                ast::CallExpr(call_expr) => {
162                    let path = call_expr.syntax().descendants().find_map(ast::PathExpr::cast).and_then(|expr| expr.path())?;
163
164                    // this also includes method calls like Foo::new(42), we should skip them
165                    if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) {
166                        match NameRefClass::classify(&ctx.sema, &name_ref) {
167                            Some(NameRefClass::Definition(Definition::SelfType(_), _)) => {},
168                            Some(NameRefClass::Definition(def, _)) if def == strukt_def => {},
169                            _ => return None,
170                        };
171                    }
172
173                    let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?;
174                    Some(
175                        make.record_expr(
176                            path,
177                            ast::make::record_expr_field_list(arg_list.args().zip(names).map(
178                                |(expr, name)| {
179                                    ast::make::record_expr_field(
180                                        ast::make::name_ref(&name.to_string()),
181                                        Some(expr),
182                                    )
183                                },
184                            )),
185                        ).syntax().clone()
186                    )
187                },
188                _ => None,
189            }
190        }
191    };
192
193    for (file_id, refs) in usages {
194        let source = ctx.sema.parse(file_id);
195        let source = source.syntax();
196
197        let mut editor = edit.make_editor(source);
198        for r in refs.iter().rev() {
199            if let Some((old_node, new_node)) = r
200                .name
201                .syntax()
202                .ancestors()
203                .find_map(|node| Some((node.clone(), edit_node(node.clone())?)))
204            {
205                if let Some(old_node) = ctx.sema.original_syntax_node_rooted(&old_node) {
206                    editor.replace(old_node, new_node);
207                } else {
208                    let FileRangeWrapper { file_id: _, range } = ctx.sema.original_range(&old_node);
209                    let parent = source.covering_element(range);
210                    match parent {
211                        SyntaxElement::Token(token) => {
212                            editor.replace(token, new_node.syntax_element());
213                        }
214                        SyntaxElement::Node(parent_node) => {
215                            // replace the part of macro
216                            // ```
217                            // foo!(a, Test::A(0));
218                            //     ^^^^^^^^^^^^^^^ // parent_node
219                            //         ^^^^^^^^^^  // replace_range
220                            // ```
221                            let start = parent_node
222                                .children_with_tokens()
223                                .find(|t| t.text_range().contains(range.start()));
224                            let end = parent_node
225                                .children_with_tokens()
226                                .find(|t| t.text_range().contains(range.end() - TextSize::new(1)));
227                            if let (Some(start), Some(end)) = (start, end) {
228                                let replace_range = RangeInclusive::new(start, end);
229                                editor.replace_all(replace_range, vec![new_node.into()]);
230                            }
231                        }
232                    }
233                }
234            }
235        }
236        edit.add_file_edits(file_id.file_id(ctx.db()), editor);
237    }
238}
239
240fn edit_field_references(
241    ctx: &AssistContext<'_>,
242    edit: &mut SourceChangeBuilder,
243    fields: impl Iterator<Item = ast::TupleField>,
244    names: &[ast::Name],
245) {
246    for (field, name) in fields.zip(names) {
247        let field = match ctx.sema.to_def(&field) {
248            Some(it) => it,
249            None => continue,
250        };
251        let def = Definition::Field(field);
252        let usages = def.usages(&ctx.sema).all();
253        for (file_id, refs) in usages {
254            let source = ctx.sema.parse(file_id);
255            let source = source.syntax();
256            let mut editor = edit.make_editor(source);
257            for r in refs {
258                if let Some(name_ref) = r.name.as_name_ref()
259                    && let Some(original) = ctx.sema.original_ast_node(name_ref.clone())
260                {
261                    editor.replace(original.syntax(), name.syntax());
262                }
263            }
264            edit.add_file_edits(file_id.file_id(ctx.db()), editor);
265        }
266    }
267}
268
269fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Name> {
270    let make = SyntaxFactory::without_mappings();
271    fields
272        .enumerate()
273        .map(|(i, _)| {
274            let idx = i + 1;
275            make.name(&format!("field{idx}"))
276        })
277        .collect()
278}
279
280fn generate_record_pat_list(
281    pat: &ast::TupleStructPat,
282    names: &[ast::Name],
283) -> ast::RecordPatFieldList {
284    let pure_fields = pat.fields().filter(|p| !matches!(p, ast::Pat::RestPat(_)));
285    let rest_len = names.len().saturating_sub(pure_fields.clone().count());
286    let rest_pat = pat.fields().find_map(|p| ast::RestPat::cast(p.syntax().clone()));
287    let rest_idx =
288        pat.fields().position(|p| ast::RestPat::can_cast(p.syntax().kind())).unwrap_or(names.len());
289    let before_rest = pat.fields().zip(names).take(rest_idx);
290    let after_rest = pure_fields.zip(names.iter().skip(rest_len)).skip(rest_idx);
291
292    let fields = before_rest
293        .chain(after_rest)
294        .map(|(pat, name)| ast::make::record_pat_field(ast::make::name_ref(&name.text()), pat));
295    ast::make::record_pat_field_list(fields, rest_pat)
296}
297
298#[cfg(test)]
299mod tests {
300    use crate::tests::{check_assist, check_assist_not_applicable};
301
302    use super::*;
303
304    #[test]
305    fn not_applicable_other_than_tuple_struct() {
306        check_assist_not_applicable(
307            convert_tuple_struct_to_named_struct,
308            r#"struct Foo$0 { bar: u32 };"#,
309        );
310        check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"struct Foo$0;"#);
311    }
312    #[test]
313    fn convert_in_macro_args() {
314        check_assist(
315            convert_tuple_struct_to_named_struct,
316            r#"
317macro_rules! foo {($i:expr) => {$i} }
318struct T$0(u8);
319fn test() {
320    foo!(T(1));
321}"#,
322            r#"
323macro_rules! foo {($i:expr) => {$i} }
324struct T { field1: u8 }
325fn test() {
326    foo!(T { field1: 1 });
327}"#,
328        );
329    }
330
331    #[test]
332    fn convert_simple_struct() {
333        check_assist(
334            convert_tuple_struct_to_named_struct,
335            r#"
336struct Inner;
337struct A$0(Inner);
338
339impl A {
340    fn new(inner: Inner) -> A {
341        A(inner)
342    }
343
344    fn new_with_default() -> A {
345        A::new(Inner)
346    }
347
348    fn into_inner(self) -> Inner {
349        self.0
350    }
351}"#,
352            r#"
353struct Inner;
354struct A { field1: Inner }
355
356impl A {
357    fn new(inner: Inner) -> A {
358        A { field1: inner }
359    }
360
361    fn new_with_default() -> A {
362        A::new(Inner)
363    }
364
365    fn into_inner(self) -> Inner {
366        self.field1
367    }
368}"#,
369        );
370    }
371
372    #[test]
373    fn convert_struct_and_rest_pat() {
374        check_assist(
375            convert_tuple_struct_to_named_struct,
376            r#"
377struct Inner;
378struct A$0(Inner);
379fn foo(A(..): A) {}
380"#,
381            r#"
382struct Inner;
383struct A { field1: Inner }
384fn foo(A { .. }: A) {}
385"#,
386        );
387
388        check_assist(
389            convert_tuple_struct_to_named_struct,
390            r#"
391struct A;
392struct B;
393struct C;
394struct D;
395struct X$0(A, B, C, D);
396fn foo(X(a, .., d): X) {}
397"#,
398            r#"
399struct A;
400struct B;
401struct C;
402struct D;
403struct X { field1: A, field2: B, field3: C, field4: D }
404fn foo(X { field1: a, field4: d, .. }: X) {}
405"#,
406        );
407    }
408
409    #[test]
410    fn convert_simple_struct_cursor_on_struct_keyword() {
411        check_assist(
412            convert_tuple_struct_to_named_struct,
413            r#"
414struct Inner;
415struct$0 A(Inner);
416
417impl A {
418    fn new(inner: Inner) -> A {
419        A(inner)
420    }
421
422    fn new_with_default() -> A {
423        A::new(Inner)
424    }
425
426    fn into_inner(self) -> Inner {
427        self.0
428    }
429}"#,
430            r#"
431struct Inner;
432struct A { field1: Inner }
433
434impl A {
435    fn new(inner: Inner) -> A {
436        A { field1: inner }
437    }
438
439    fn new_with_default() -> A {
440        A::new(Inner)
441    }
442
443    fn into_inner(self) -> Inner {
444        self.field1
445    }
446}"#,
447        );
448    }
449
450    #[test]
451    fn convert_simple_struct_cursor_on_visibility_keyword() {
452        check_assist(
453            convert_tuple_struct_to_named_struct,
454            r#"
455struct Inner;
456pub$0 struct A(Inner);
457
458impl A {
459    fn new(inner: Inner) -> A {
460        A(inner)
461    }
462
463    fn new_with_default() -> A {
464        A::new(Inner)
465    }
466
467    fn into_inner(self) -> Inner {
468        self.0
469    }
470}"#,
471            r#"
472struct Inner;
473pub struct A { field1: Inner }
474
475impl A {
476    fn new(inner: Inner) -> A {
477        A { field1: inner }
478    }
479
480    fn new_with_default() -> A {
481        A::new(Inner)
482    }
483
484    fn into_inner(self) -> Inner {
485        self.field1
486    }
487}"#,
488        );
489    }
490
491    #[test]
492    fn convert_struct_referenced_via_self_kw() {
493        check_assist(
494            convert_tuple_struct_to_named_struct,
495            r#"
496struct Inner;
497struct A$0(Inner);
498
499impl A {
500    fn new(inner: Inner) -> Self {
501        Self(inner)
502    }
503
504    fn new_with_default() -> Self {
505        Self::new(Inner)
506    }
507
508    fn into_inner(self) -> Inner {
509        self.0
510    }
511}"#,
512            r#"
513struct Inner;
514struct A { field1: Inner }
515
516impl A {
517    fn new(inner: Inner) -> Self {
518        Self { field1: inner }
519    }
520
521    fn new_with_default() -> Self {
522        Self::new(Inner)
523    }
524
525    fn into_inner(self) -> Inner {
526        self.field1
527    }
528}"#,
529        );
530    }
531
532    #[test]
533    fn convert_destructured_struct() {
534        check_assist(
535            convert_tuple_struct_to_named_struct,
536            r#"
537struct Inner;
538struct A$0(Inner);
539
540impl A {
541    fn into_inner(self) -> Inner {
542        let A(first) = self;
543        first
544    }
545
546    fn into_inner_via_self(self) -> Inner {
547        let Self(first) = self;
548        first
549    }
550}"#,
551            r#"
552struct Inner;
553struct A { field1: Inner }
554
555impl A {
556    fn into_inner(self) -> Inner {
557        let A { field1: first } = self;
558        first
559    }
560
561    fn into_inner_via_self(self) -> Inner {
562        let Self { field1: first } = self;
563        first
564    }
565}"#,
566        );
567    }
568
569    #[test]
570    fn convert_struct_with_visibility() {
571        check_assist(
572            convert_tuple_struct_to_named_struct,
573            r#"
574struct A$0(pub u32, pub(crate) u64);
575
576impl A {
577    fn new() -> A {
578        A(42, 42)
579    }
580
581    fn into_first(self) -> u32 {
582        self.0
583    }
584
585    fn into_second(self) -> u64 {
586        self.1
587    }
588}"#,
589            r#"
590struct A { pub field1: u32, pub(crate) field2: u64 }
591
592impl A {
593    fn new() -> A {
594        A { field1: 42, field2: 42 }
595    }
596
597    fn into_first(self) -> u32 {
598        self.field1
599    }
600
601    fn into_second(self) -> u64 {
602        self.field2
603    }
604}"#,
605        );
606    }
607
608    #[test]
609    fn convert_struct_with_wrapped_references() {
610        check_assist(
611            convert_tuple_struct_to_named_struct,
612            r#"
613struct Inner$0(u32);
614struct Outer(Inner);
615
616impl Outer {
617    fn new() -> Self {
618        Self(Inner(42))
619    }
620
621    fn into_inner(self) -> u32 {
622        (self.0).0
623    }
624
625    fn into_inner_destructed(self) -> u32 {
626        let Outer(Inner(x)) = self;
627        x
628    }
629}"#,
630            r#"
631struct Inner { field1: u32 }
632struct Outer(Inner);
633
634impl Outer {
635    fn new() -> Self {
636        Self(Inner { field1: 42 })
637    }
638
639    fn into_inner(self) -> u32 {
640        (self.0).field1
641    }
642
643    fn into_inner_destructed(self) -> u32 {
644        let Outer(Inner { field1: x }) = self;
645        x
646    }
647}"#,
648        );
649
650        check_assist(
651            convert_tuple_struct_to_named_struct,
652            r#"
653struct Inner(u32);
654struct Outer$0(Inner);
655
656impl Outer {
657    fn new() -> Self {
658        Self(Inner(42))
659    }
660
661    fn into_inner(self) -> u32 {
662        (self.0).0
663    }
664
665    fn into_inner_destructed(self) -> u32 {
666        let Outer(Inner(x)) = self;
667        x
668    }
669}"#,
670            r#"
671struct Inner(u32);
672struct Outer { field1: Inner }
673
674impl Outer {
675    fn new() -> Self {
676        Self { field1: Inner(42) }
677    }
678
679    fn into_inner(self) -> u32 {
680        (self.field1).0
681    }
682
683    fn into_inner_destructed(self) -> u32 {
684        let Outer { field1: Inner(x) } = self;
685        x
686    }
687}"#,
688        );
689    }
690
691    #[test]
692    fn convert_struct_with_multi_file_references() {
693        check_assist(
694            convert_tuple_struct_to_named_struct,
695            r#"
696//- /main.rs
697struct Inner;
698struct A$0(Inner);
699
700mod foo;
701
702//- /foo.rs
703use crate::{A, Inner};
704fn f() {
705    let a = A(Inner);
706}
707"#,
708            r#"
709//- /main.rs
710struct Inner;
711struct A { field1: Inner }
712
713mod foo;
714
715//- /foo.rs
716use crate::{A, Inner};
717fn f() {
718    let a = A { field1: Inner };
719}
720"#,
721        );
722    }
723
724    #[test]
725    fn convert_struct_with_where_clause() {
726        check_assist(
727            convert_tuple_struct_to_named_struct,
728            r#"
729struct Wrap$0<T>(T)
730where
731    T: Display;
732"#,
733            r#"
734struct Wrap<T>
735where
736    T: Display,
737{ field1: T }
738
739"#,
740        );
741    }
742    #[test]
743    fn not_applicable_other_than_tuple_variant() {
744        check_assist_not_applicable(
745            convert_tuple_struct_to_named_struct,
746            r#"enum Enum { Variant$0 { value: usize } };"#,
747        );
748        check_assist_not_applicable(
749            convert_tuple_struct_to_named_struct,
750            r#"enum Enum { Variant$0 }"#,
751        );
752    }
753
754    #[test]
755    fn convert_variant_in_macro_args() {
756        check_assist(
757            convert_tuple_struct_to_named_struct,
758            r#"
759macro_rules! foo {($i:expr) => {$i} }
760enum T {
761  V$0(u8)
762}
763fn test() {
764    foo!(T::V(1));
765}"#,
766            r#"
767macro_rules! foo {($i:expr) => {$i} }
768enum T {
769  V { field1: u8 }
770}
771fn test() {
772    foo!(T::V { field1: 1 });
773}"#,
774        );
775    }
776
777    #[test]
778    fn convert_simple_variant() {
779        check_assist(
780            convert_tuple_struct_to_named_struct,
781            r#"
782enum A {
783    $0Variant(usize),
784}
785
786impl A {
787    fn new(value: usize) -> A {
788        A::Variant(value)
789    }
790
791    fn new_with_default() -> A {
792        A::new(Default::default())
793    }
794
795    fn value(self) -> usize {
796        match self {
797            A::Variant(value) => value,
798        }
799    }
800}"#,
801            r#"
802enum A {
803    Variant { field1: usize },
804}
805
806impl A {
807    fn new(value: usize) -> A {
808        A::Variant { field1: value }
809    }
810
811    fn new_with_default() -> A {
812        A::new(Default::default())
813    }
814
815    fn value(self) -> usize {
816        match self {
817            A::Variant { field1: value } => value,
818        }
819    }
820}"#,
821        );
822    }
823
824    #[test]
825    fn convert_variant_referenced_via_self_kw() {
826        check_assist(
827            convert_tuple_struct_to_named_struct,
828            r#"
829enum A {
830    $0Variant(usize),
831}
832
833impl A {
834    fn new(value: usize) -> A {
835        Self::Variant(value)
836    }
837
838    fn new_with_default() -> A {
839        Self::new(Default::default())
840    }
841
842    fn value(self) -> usize {
843        match self {
844            Self::Variant(value) => value,
845        }
846    }
847}"#,
848            r#"
849enum A {
850    Variant { field1: usize },
851}
852
853impl A {
854    fn new(value: usize) -> A {
855        Self::Variant { field1: value }
856    }
857
858    fn new_with_default() -> A {
859        Self::new(Default::default())
860    }
861
862    fn value(self) -> usize {
863        match self {
864            Self::Variant { field1: value } => value,
865        }
866    }
867}"#,
868        );
869    }
870
871    #[test]
872    fn convert_destructured_variant() {
873        check_assist(
874            convert_tuple_struct_to_named_struct,
875            r#"
876enum A {
877    $0Variant(usize),
878}
879
880impl A {
881    fn into_inner(self) -> usize {
882        let A::Variant(first) = self;
883        first
884    }
885
886    fn into_inner_via_self(self) -> usize {
887        let Self::Variant(first) = self;
888        first
889    }
890}"#,
891            r#"
892enum A {
893    Variant { field1: usize },
894}
895
896impl A {
897    fn into_inner(self) -> usize {
898        let A::Variant { field1: first } = self;
899        first
900    }
901
902    fn into_inner_via_self(self) -> usize {
903        let Self::Variant { field1: first } = self;
904        first
905    }
906}"#,
907        );
908    }
909
910    #[test]
911    fn convert_variant_with_wrapped_references() {
912        check_assist(
913            convert_tuple_struct_to_named_struct,
914            r#"
915enum Inner {
916    $0Variant(usize),
917}
918enum Outer {
919    Variant(Inner),
920}
921
922impl Outer {
923    fn new() -> Self {
924        Self::Variant(Inner::Variant(42))
925    }
926
927    fn into_inner_destructed(self) -> u32 {
928        let Outer::Variant(Inner::Variant(x)) = self;
929        x
930    }
931}"#,
932            r#"
933enum Inner {
934    Variant { field1: usize },
935}
936enum Outer {
937    Variant(Inner),
938}
939
940impl Outer {
941    fn new() -> Self {
942        Self::Variant(Inner::Variant { field1: 42 })
943    }
944
945    fn into_inner_destructed(self) -> u32 {
946        let Outer::Variant(Inner::Variant { field1: x }) = self;
947        x
948    }
949}"#,
950        );
951
952        check_assist(
953            convert_tuple_struct_to_named_struct,
954            r#"
955enum Inner {
956    Variant(usize),
957}
958enum Outer {
959    $0Variant(Inner),
960}
961
962impl Outer {
963    fn new() -> Self {
964        Self::Variant(Inner::Variant(42))
965    }
966
967    fn into_inner_destructed(self) -> u32 {
968        let Outer::Variant(Inner::Variant(x)) = self;
969        x
970    }
971}"#,
972            r#"
973enum Inner {
974    Variant(usize),
975}
976enum Outer {
977    Variant { field1: Inner },
978}
979
980impl Outer {
981    fn new() -> Self {
982        Self::Variant { field1: Inner::Variant(42) }
983    }
984
985    fn into_inner_destructed(self) -> u32 {
986        let Outer::Variant { field1: Inner::Variant(x) } = self;
987        x
988    }
989}"#,
990        );
991    }
992
993    #[test]
994    fn convert_variant_with_multi_file_references() {
995        check_assist(
996            convert_tuple_struct_to_named_struct,
997            r#"
998//- /main.rs
999struct Inner;
1000enum A {
1001    $0Variant(Inner),
1002}
1003
1004mod foo;
1005
1006//- /foo.rs
1007use crate::{A, Inner};
1008fn f() {
1009    let a = A::Variant(Inner);
1010}
1011"#,
1012            r#"
1013//- /main.rs
1014struct Inner;
1015enum A {
1016    Variant { field1: Inner },
1017}
1018
1019mod foo;
1020
1021//- /foo.rs
1022use crate::{A, Inner};
1023fn f() {
1024    let a = A::Variant { field1: Inner };
1025}
1026"#,
1027        );
1028    }
1029
1030    #[test]
1031    fn convert_directly_used_variant() {
1032        check_assist(
1033            convert_tuple_struct_to_named_struct,
1034            r#"
1035//- /main.rs
1036struct Inner;
1037enum A {
1038    $0Variant(Inner),
1039}
1040
1041mod foo;
1042
1043//- /foo.rs
1044use crate::{A::Variant, Inner};
1045fn f() {
1046    let a = Variant(Inner);
1047}
1048"#,
1049            r#"
1050//- /main.rs
1051struct Inner;
1052enum A {
1053    Variant { field1: Inner },
1054}
1055
1056mod foo;
1057
1058//- /foo.rs
1059use crate::{A::Variant, Inner};
1060fn f() {
1061    let a = Variant { field1: Inner };
1062}
1063"#,
1064        );
1065    }
1066
1067    #[test]
1068    fn where_clause_with_trailing_comma() {
1069        check_assist(
1070            convert_tuple_struct_to_named_struct,
1071            r#"
1072trait Foo {}
1073
1074struct Bar$0<T>(pub T)
1075where
1076    T: Foo,;
1077"#,
1078            r#"
1079trait Foo {}
1080
1081struct Bar<T>
1082where
1083    T: Foo,
1084{ pub field1: T }
1085
1086"#,
1087        );
1088    }
1089
1090    #[test]
1091    fn fields_with_attrs() {
1092        check_assist(
1093            convert_tuple_struct_to_named_struct,
1094            r#"
1095pub struct $0Foo(#[my_custom_attr] u32);
1096"#,
1097            r#"
1098pub struct Foo { #[my_custom_attr]field1: u32 }
1099"#,
1100        );
1101    }
1102
1103    #[test]
1104    fn convert_in_macro_pattern_args() {
1105        check_assist(
1106            convert_tuple_struct_to_named_struct,
1107            r#"
1108macro_rules! foo {
1109    ($expression:expr, $pattern:pat) => {
1110        match $expression {
1111            $pattern => true,
1112            _ => false
1113        }
1114    };
1115}
1116enum Expr {
1117    A$0(usize),
1118}
1119fn main() {
1120    let e = Expr::A(0);
1121    foo!(e, Expr::A(0));
1122}
1123"#,
1124            r#"
1125macro_rules! foo {
1126    ($expression:expr, $pattern:pat) => {
1127        match $expression {
1128            $pattern => true,
1129            _ => false
1130        }
1131    };
1132}
1133enum Expr {
1134    A { field1: usize },
1135}
1136fn main() {
1137    let e = Expr::A { field1: 0 };
1138    foo!(e, Expr::A { field1: 0 });
1139}
1140"#,
1141        );
1142    }
1143
1144    #[test]
1145    fn convert_in_multi_file_macro_pattern_args() {
1146        check_assist(
1147            convert_tuple_struct_to_named_struct,
1148            r#"
1149//- /main.rs
1150mod foo;
1151
1152enum Test {
1153    A$0(i32)
1154}
1155
1156//- /foo.rs
1157use crate::Test;
1158
1159macro_rules! foo {
1160    ($expression:expr, $pattern:pat) => {
1161        match $expression {
1162            $pattern => true,
1163            _ => false
1164        }
1165    };
1166}
1167
1168fn foo() {
1169    let a = Test::A(0);
1170    foo!(a, Test::A(0));
1171}
1172"#,
1173            r#"
1174//- /main.rs
1175mod foo;
1176
1177enum Test {
1178    A { field1: i32 }
1179}
1180
1181//- /foo.rs
1182use crate::Test;
1183
1184macro_rules! foo {
1185    ($expression:expr, $pattern:pat) => {
1186        match $expression {
1187            $pattern => true,
1188            _ => false
1189        }
1190    };
1191}
1192
1193fn foo() {
1194    let a = Test::A { field1: 0 };
1195    foo!(a, Test::A { field1: 0 });
1196}
1197"#,
1198        );
1199    }
1200
1201    #[test]
1202    fn regression_issue_21020() {
1203        check_assist(
1204            convert_tuple_struct_to_named_struct,
1205            r#"
1206pub struct S$0(pub ());
1207
1208trait T {
1209    fn id(&self) -> usize;
1210}
1211
1212trait T2 {
1213    fn foo(&self) -> usize;
1214}
1215
1216impl T for S {
1217    fn id(&self) -> usize {
1218        self.0.len()
1219    }
1220}
1221
1222impl T2 for S {
1223    fn foo(&self) -> usize {
1224        self.0.len()
1225    }
1226}
1227            "#,
1228            r#"
1229pub struct S { pub field1: () }
1230
1231trait T {
1232    fn id(&self) -> usize;
1233}
1234
1235trait T2 {
1236    fn foo(&self) -> usize;
1237}
1238
1239impl T for S {
1240    fn id(&self) -> usize {
1241        self.field1.len()
1242    }
1243}
1244
1245impl T2 for S {
1246    fn foo(&self) -> usize {
1247        self.field1.len()
1248    }
1249}
1250            "#,
1251        );
1252    }
1253}