diff options
-rw-r--r-- | crates/ide_assists/src/handlers/convert_tuple_struct_to_named_struct.rs | 352 |
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 @@ | |||
1 | use either::Either; | ||
1 | use ide_db::defs::{Definition, NameRefClass}; | 2 | use ide_db::defs::{Definition, NameRefClass}; |
2 | use syntax::{ | 3 | use 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( | |||
73 | fn edit_struct_def( | 81 | fn 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 | ||
103 | fn edit_struct_references( | 121 | fn 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#" | ||
554 | enum A { | ||
555 | $0Variant(usize), | ||
556 | } | ||
557 | |||
558 | impl 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#" | ||
574 | enum A { | ||
575 | Variant { field1: usize }, | ||
576 | } | ||
577 | |||
578 | impl 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#" | ||
601 | enum A { | ||
602 | $0Variant(usize), | ||
603 | } | ||
604 | |||
605 | impl 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#" | ||
621 | enum A { | ||
622 | Variant { field1: usize }, | ||
623 | } | ||
624 | |||
625 | impl 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#" | ||
648 | enum A { | ||
649 | $0Variant(usize), | ||
650 | } | ||
651 | |||
652 | impl 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#" | ||
664 | enum A { | ||
665 | Variant { field1: usize }, | ||
666 | } | ||
667 | |||
668 | impl 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#" | ||
687 | enum Inner { | ||
688 | $0Variant(usize), | ||
689 | } | ||
690 | enum Outer { | ||
691 | Variant(Inner), | ||
692 | } | ||
693 | |||
694 | impl 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#" | ||
705 | enum Inner { | ||
706 | Variant { field1: usize }, | ||
707 | } | ||
708 | enum Outer { | ||
709 | Variant(Inner), | ||
710 | } | ||
711 | |||
712 | impl 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#" | ||
727 | enum Inner { | ||
728 | Variant(usize), | ||
729 | } | ||
730 | enum Outer { | ||
731 | $0Variant(Inner), | ||
732 | } | ||
733 | |||
734 | impl 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#" | ||
745 | enum Inner { | ||
746 | Variant(usize), | ||
747 | } | ||
748 | enum Outer { | ||
749 | Variant { field1: Inner }, | ||
750 | } | ||
751 | |||
752 | impl 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 | ||
771 | struct Inner; | ||
772 | enum A { | ||
773 | $0Variant(Inner), | ||
774 | } | ||
775 | |||
776 | mod foo; | ||
777 | |||
778 | //- /foo.rs | ||
779 | use crate::{A, Inner}; | ||
780 | fn f() { | ||
781 | let a = A::Variant(Inner); | ||
782 | } | ||
783 | "#, | ||
784 | r#" | ||
785 | //- /main.rs | ||
786 | struct Inner; | ||
787 | enum A { | ||
788 | Variant { field1: Inner }, | ||
789 | } | ||
790 | |||
791 | mod foo; | ||
792 | |||
793 | //- /foo.rs | ||
794 | use crate::{A, Inner}; | ||
795 | fn 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 | ||
808 | struct Inner; | ||
809 | enum A { | ||
810 | $0Variant(Inner), | ||
811 | } | ||
812 | |||
813 | mod foo; | ||
814 | |||
815 | //- /foo.rs | ||
816 | use crate::{A::Variant, Inner}; | ||
817 | fn f() { | ||
818 | let a = Variant(Inner); | ||
819 | } | ||
820 | "#, | ||
821 | r#" | ||
822 | //- /main.rs | ||
823 | struct Inner; | ||
824 | enum A { | ||
825 | Variant { field1: Inner }, | ||
826 | } | ||
827 | |||
828 | mod foo; | ||
829 | |||
830 | //- /foo.rs | ||
831 | use crate::{A::Variant, Inner}; | ||
832 | fn f() { | ||
833 | let a = Variant { field1: Inner }; | ||
834 | } | ||
835 | "#, | ||
836 | ); | ||
837 | } | ||
516 | } | 838 | } |