aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs')
-rw-r--r--crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs352
1 files changed, 337 insertions, 15 deletions
diff --git a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
index 70949ca35..fc5a17f05 100644
--- a/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
+++ b/crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -1,3 +1,4 @@
1use either::Either;
1use ide_db::defs::{Definition, NameRefClass}; 2use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ 3use syntax::{
3 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner}, 4 ast::{self, AstNode, GenericParamsOwner, VisibilityOwner},
@@ -8,7 +9,7 @@ use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind,
8 9
9// Assist: convert_tuple_struct_to_named_struct 10// Assist: convert_tuple_struct_to_named_struct
10// 11//
11// Converts tuple struct to struct with named fields. 12// Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
12// 13//
13// ``` 14// ```
14// struct Point$0(f32, f32); 15// struct Point$0(f32, f32);
@@ -49,14 +50,21 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
49 acc: &mut Assists, 50 acc: &mut Assists,
50 ctx: &AssistContext, 51 ctx: &AssistContext,
51) -> Option<()> { 52) -> Option<()> {
52 let strukt = ctx.find_node_at_offset::<ast::Struct>()?; 53 let strukt = ctx
53 let tuple_fields = match strukt.field_list()? { 54 .find_node_at_offset::<ast::Struct>()
55 .map(Either::Left)
56 .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
57 let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
58 let tuple_fields = match field_list {
54 ast::FieldList::TupleFieldList(it) => it, 59 ast::FieldList::TupleFieldList(it) => it,
55 ast::FieldList::RecordFieldList(_) => return None, 60 ast::FieldList::RecordFieldList(_) => return None,
56 }; 61 };
57 let strukt_def = ctx.sema.to_def(&strukt)?; 62 let strukt_def = match &strukt {
63 Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
64 Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
65 };
66 let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
58 67
59 let target = strukt.syntax().text_range();
60 acc.add( 68 acc.add(
61 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite), 69 AssistId("convert_tuple_struct_to_named_struct", AssistKind::RefactorRewrite),
62 "Convert to named struct", 70 "Convert to named struct",
@@ -73,7 +81,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct(
73fn edit_struct_def( 81fn edit_struct_def(
74 ctx: &AssistContext, 82 ctx: &AssistContext,
75 edit: &mut AssistBuilder, 83 edit: &mut AssistBuilder,
76 strukt: &ast::Struct, 84 strukt: &Either<ast::Struct, ast::Variant>,
77 tuple_fields: ast::TupleFieldList, 85 tuple_fields: ast::TupleFieldList,
78 names: Vec<ast::Name>, 86 names: Vec<ast::Name>,
79) { 87) {
@@ -86,27 +94,40 @@ fn edit_struct_def(
86 94
87 edit.edit_file(ctx.frange.file_id); 95 edit.edit_file(ctx.frange.file_id);
88 96
89 if let Some(w) = strukt.where_clause() { 97 if let Either::Left(strukt) = strukt {
90 edit.delete(w.syntax().text_range()); 98 if let Some(w) = strukt.where_clause() {
91 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); 99 edit.delete(w.syntax().text_range());
92 edit.insert(tuple_fields_text_range.start(), w.syntax().text()); 100 edit.insert(
93 edit.insert(tuple_fields_text_range.start(), ","); 101 tuple_fields_text_range.start(),
94 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); 102 ast::make::tokens::single_newline().text(),
103 );
104 edit.insert(tuple_fields_text_range.start(), w.syntax().text());
105 edit.insert(tuple_fields_text_range.start(), ",");
106 edit.insert(
107 tuple_fields_text_range.start(),
108 ast::make::tokens::single_newline().text(),
109 );
110 } else {
111 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
112 }
113 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
95 } else { 114 } else {
96 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); 115 edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text());
97 } 116 }
98 117
99 edit.replace(tuple_fields_text_range, record_fields.to_string()); 118 edit.replace(tuple_fields_text_range, record_fields.to_string());
100 strukt.semicolon_token().map(|t| edit.delete(t.text_range()));
101} 119}
102 120
103fn edit_struct_references( 121fn edit_struct_references(
104 ctx: &AssistContext, 122 ctx: &AssistContext,
105 edit: &mut AssistBuilder, 123 edit: &mut AssistBuilder,
106 strukt: hir::Struct, 124 strukt: Either<hir::Struct, hir::Variant>,
107 names: &[ast::Name], 125 names: &[ast::Name],
108) { 126) {
109 let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); 127 let strukt_def = match strukt {
128 Either::Left(s) => Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(s))),
129 Either::Right(v) => Definition::ModuleDef(hir::ModuleDef::Variant(v)),
130 };
110 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); 131 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
111 132
112 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { 133 let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> {
@@ -513,4 +534,305 @@ where
513"#, 534"#,
514 ); 535 );
515 } 536 }
537 #[test]
538 fn not_applicable_other_than_tuple_variant() {
539 check_assist_not_applicable(
540 convert_tuple_struct_to_named_struct,
541 r#"enum Enum { Variant$0 { value: usize } };"#,
542 );
543 check_assist_not_applicable(
544 convert_tuple_struct_to_named_struct,
545 r#"enum Enum { Variant$0 }"#,
546 );
547 }
548
549 #[test]
550 fn convert_simple_variant() {
551 check_assist(
552 convert_tuple_struct_to_named_struct,
553 r#"
554enum A {
555 $0Variant(usize),
556}
557
558impl A {
559 fn new(value: usize) -> A {
560 A::Variant(value)
561 }
562
563 fn new_with_default() -> A {
564 A::new(Default::default())
565 }
566
567 fn value(self) -> usize {
568 match self {
569 A::Variant(value) => value,
570 }
571 }
572}"#,
573 r#"
574enum A {
575 Variant { field1: usize },
576}
577
578impl A {
579 fn new(value: usize) -> A {
580 A::Variant { field1: value }
581 }
582
583 fn new_with_default() -> A {
584 A::new(Default::default())
585 }
586
587 fn value(self) -> usize {
588 match self {
589 A::Variant { field1: value } => value,
590 }
591 }
592}"#,
593 );
594 }
595
596 #[test]
597 fn convert_variant_referenced_via_self_kw() {
598 check_assist(
599 convert_tuple_struct_to_named_struct,
600 r#"
601enum A {
602 $0Variant(usize),
603}
604
605impl A {
606 fn new(value: usize) -> A {
607 Self::Variant(value)
608 }
609
610 fn new_with_default() -> A {
611 Self::new(Default::default())
612 }
613
614 fn value(self) -> usize {
615 match self {
616 Self::Variant(value) => value,
617 }
618 }
619}"#,
620 r#"
621enum A {
622 Variant { field1: usize },
623}
624
625impl A {
626 fn new(value: usize) -> A {
627 Self::Variant { field1: value }
628 }
629
630 fn new_with_default() -> A {
631 Self::new(Default::default())
632 }
633
634 fn value(self) -> usize {
635 match self {
636 Self::Variant { field1: value } => value,
637 }
638 }
639}"#,
640 );
641 }
642
643 #[test]
644 fn convert_destructured_variant() {
645 check_assist(
646 convert_tuple_struct_to_named_struct,
647 r#"
648enum A {
649 $0Variant(usize),
650}
651
652impl A {
653 fn into_inner(self) -> usize {
654 let A::Variant(first) = self;
655 first
656 }
657
658 fn into_inner_via_self(self) -> usize {
659 let Self::Variant(first) = self;
660 first
661 }
662}"#,
663 r#"
664enum A {
665 Variant { field1: usize },
666}
667
668impl A {
669 fn into_inner(self) -> usize {
670 let A::Variant { field1: first } = self;
671 first
672 }
673
674 fn into_inner_via_self(self) -> usize {
675 let Self::Variant { field1: first } = self;
676 first
677 }
678}"#,
679 );
680 }
681
682 #[test]
683 fn convert_variant_with_wrapped_references() {
684 check_assist(
685 convert_tuple_struct_to_named_struct,
686 r#"
687enum Inner {
688 $0Variant(usize),
689}
690enum Outer {
691 Variant(Inner),
692}
693
694impl Outer {
695 fn new() -> Self {
696 Self::Variant(Inner::Variant(42))
697 }
698
699 fn into_inner_destructed(self) -> u32 {
700 let Outer::Variant(Inner::Variant(x)) = self;
701 x
702 }
703}"#,
704 r#"
705enum Inner {
706 Variant { field1: usize },
707}
708enum Outer {
709 Variant(Inner),
710}
711
712impl Outer {
713 fn new() -> Self {
714 Self::Variant(Inner::Variant { field1: 42 })
715 }
716
717 fn into_inner_destructed(self) -> u32 {
718 let Outer::Variant(Inner::Variant { field1: x }) = self;
719 x
720 }
721}"#,
722 );
723
724 check_assist(
725 convert_tuple_struct_to_named_struct,
726 r#"
727enum Inner {
728 Variant(usize),
729}
730enum Outer {
731 $0Variant(Inner),
732}
733
734impl Outer {
735 fn new() -> Self {
736 Self::Variant(Inner::Variant(42))
737 }
738
739 fn into_inner_destructed(self) -> u32 {
740 let Outer::Variant(Inner::Variant(x)) = self;
741 x
742 }
743}"#,
744 r#"
745enum Inner {
746 Variant(usize),
747}
748enum Outer {
749 Variant { field1: Inner },
750}
751
752impl Outer {
753 fn new() -> Self {
754 Self::Variant { field1: Inner::Variant(42) }
755 }
756
757 fn into_inner_destructed(self) -> u32 {
758 let Outer::Variant { field1: Inner::Variant(x) } = self;
759 x
760 }
761}"#,
762 );
763 }
764
765 #[test]
766 fn convert_variant_with_multi_file_references() {
767 check_assist(
768 convert_tuple_struct_to_named_struct,
769 r#"
770//- /main.rs
771struct Inner;
772enum A {
773 $0Variant(Inner),
774}
775
776mod foo;
777
778//- /foo.rs
779use crate::{A, Inner};
780fn f() {
781 let a = A::Variant(Inner);
782}
783"#,
784 r#"
785//- /main.rs
786struct Inner;
787enum A {
788 Variant { field1: Inner },
789}
790
791mod foo;
792
793//- /foo.rs
794use crate::{A, Inner};
795fn f() {
796 let a = A::Variant { field1: Inner };
797}
798"#,
799 );
800 }
801
802 #[test]
803 fn convert_directly_used_variant() {
804 check_assist(
805 convert_tuple_struct_to_named_struct,
806 r#"
807//- /main.rs
808struct Inner;
809enum A {
810 $0Variant(Inner),
811}
812
813mod foo;
814
815//- /foo.rs
816use crate::{A::Variant, Inner};
817fn f() {
818 let a = Variant(Inner);
819}
820"#,
821 r#"
822//- /main.rs
823struct Inner;
824enum A {
825 Variant { field1: Inner },
826}
827
828mod foo;
829
830//- /foo.rs
831use crate::{A::Variant, Inner};
832fn f() {
833 let a = Variant { field1: Inner };
834}
835"#,
836 );
837 }
516} 838}