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
16pub(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 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 ast::CallExpr(call_expr) => {
162 let path = call_expr.syntax().descendants().find_map(ast::PathExpr::cast).and_then(|expr| expr.path())?;
163
164 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 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}