diff options
3 files changed, 328 insertions, 532 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..53709e31f 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,34 @@ 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(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); |
93 | edit.insert(tuple_fields_text_range.start(), ","); | 101 | edit.insert(tuple_fields_text_range.start(), w.syntax().text()); |
94 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); | 102 | edit.insert(tuple_fields_text_range.start(), ","); |
103 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_newline().text()); | ||
104 | } else { | ||
105 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); | ||
106 | } | ||
107 | strukt.semicolon_token().map(|t| edit.delete(t.text_range())); | ||
95 | } else { | 108 | } else { |
96 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); | 109 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); |
97 | } | 110 | } |
98 | 111 | ||
99 | edit.replace(tuple_fields_text_range, record_fields.to_string()); | 112 | edit.replace(tuple_fields_text_range, record_fields.to_string()); |
100 | strukt.semicolon_token().map(|t| edit.delete(t.text_range())); | ||
101 | } | 113 | } |
102 | 114 | ||
103 | fn edit_struct_references( | 115 | fn edit_struct_references( |
104 | ctx: &AssistContext, | 116 | ctx: &AssistContext, |
105 | edit: &mut AssistBuilder, | 117 | edit: &mut AssistBuilder, |
106 | strukt: hir::Struct, | 118 | strukt: Either<hir::Struct, hir::Variant>, |
107 | names: &[ast::Name], | 119 | names: &[ast::Name], |
108 | ) { | 120 | ) { |
109 | let strukt_def = Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(strukt))); | 121 | let strukt_def = match strukt { |
122 | Either::Left(s) => Definition::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(s))), | ||
123 | Either::Right(v) => Definition::ModuleDef(hir::ModuleDef::Variant(v)), | ||
124 | }; | ||
110 | let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); | 125 | let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); |
111 | 126 | ||
112 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { | 127 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { |
@@ -513,4 +528,302 @@ where | |||
513 | "#, | 528 | "#, |
514 | ); | 529 | ); |
515 | } | 530 | } |
531 | #[test] | ||
532 | fn not_applicable_other_than_tuple_variant() { | ||
533 | check_assist_not_applicable( | ||
534 | convert_tuple_struct_to_named_struct, | ||
535 | r#"enum Enum { Variant$0 { value: usize } };"#, | ||
536 | ); | ||
537 | check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"enum Enum { Variant$0 }"#); | ||
538 | } | ||
539 | |||
540 | #[test] | ||
541 | fn convert_simple_variant() { | ||
542 | check_assist( | ||
543 | convert_tuple_struct_to_named_struct, | ||
544 | r#" | ||
545 | enum A { | ||
546 | $0Variant(usize), | ||
547 | } | ||
548 | |||
549 | impl A { | ||
550 | fn new(value: usize) -> A { | ||
551 | A::Variant(value) | ||
552 | } | ||
553 | |||
554 | fn new_with_default() -> A { | ||
555 | A::new(Default::default()) | ||
556 | } | ||
557 | |||
558 | fn value(self) -> usize { | ||
559 | match self { | ||
560 | A::Variant(value) => value, | ||
561 | } | ||
562 | } | ||
563 | }"#, | ||
564 | r#" | ||
565 | enum A { | ||
566 | Variant { field1: usize }, | ||
567 | } | ||
568 | |||
569 | impl A { | ||
570 | fn new(value: usize) -> A { | ||
571 | A::Variant { field1: value } | ||
572 | } | ||
573 | |||
574 | fn new_with_default() -> A { | ||
575 | A::new(Default::default()) | ||
576 | } | ||
577 | |||
578 | fn value(self) -> usize { | ||
579 | match self { | ||
580 | A::Variant { field1: value } => value, | ||
581 | } | ||
582 | } | ||
583 | }"#, | ||
584 | ); | ||
585 | } | ||
586 | |||
587 | #[test] | ||
588 | fn convert_variant_referenced_via_self_kw() { | ||
589 | check_assist( | ||
590 | convert_tuple_struct_to_named_struct, | ||
591 | r#" | ||
592 | enum A { | ||
593 | $0Variant(usize), | ||
594 | } | ||
595 | |||
596 | impl A { | ||
597 | fn new(value: usize) -> A { | ||
598 | Self::Variant(value) | ||
599 | } | ||
600 | |||
601 | fn new_with_default() -> A { | ||
602 | Self::new(Default::default()) | ||
603 | } | ||
604 | |||
605 | fn value(self) -> usize { | ||
606 | match self { | ||
607 | Self::Variant(value) => value, | ||
608 | } | ||
609 | } | ||
610 | }"#, | ||
611 | r#" | ||
612 | enum A { | ||
613 | Variant { field1: usize }, | ||
614 | } | ||
615 | |||
616 | impl A { | ||
617 | fn new(value: usize) -> A { | ||
618 | Self::Variant { field1: value } | ||
619 | } | ||
620 | |||
621 | fn new_with_default() -> A { | ||
622 | Self::new(Default::default()) | ||
623 | } | ||
624 | |||
625 | fn value(self) -> usize { | ||
626 | match self { | ||
627 | Self::Variant { field1: value } => value, | ||
628 | } | ||
629 | } | ||
630 | }"#, | ||
631 | ); | ||
632 | } | ||
633 | |||
634 | #[test] | ||
635 | fn convert_destructured_variant() { | ||
636 | check_assist( | ||
637 | convert_tuple_struct_to_named_struct, | ||
638 | r#" | ||
639 | enum A { | ||
640 | $0Variant(usize), | ||
641 | } | ||
642 | |||
643 | impl A { | ||
644 | fn into_inner(self) -> usize { | ||
645 | let A::Variant(first) = self; | ||
646 | first | ||
647 | } | ||
648 | |||
649 | fn into_inner_via_self(self) -> usize { | ||
650 | let Self::Variant(first) = self; | ||
651 | first | ||
652 | } | ||
653 | }"#, | ||
654 | r#" | ||
655 | enum A { | ||
656 | Variant { field1: usize }, | ||
657 | } | ||
658 | |||
659 | impl A { | ||
660 | fn into_inner(self) -> usize { | ||
661 | let A::Variant { field1: first } = self; | ||
662 | first | ||
663 | } | ||
664 | |||
665 | fn into_inner_via_self(self) -> usize { | ||
666 | let Self::Variant { field1: first } = self; | ||
667 | first | ||
668 | } | ||
669 | }"#, | ||
670 | ); | ||
671 | } | ||
672 | |||
673 | #[test] | ||
674 | fn convert_variant_with_wrapped_references() { | ||
675 | check_assist( | ||
676 | convert_tuple_struct_to_named_struct, | ||
677 | r#" | ||
678 | enum Inner { | ||
679 | $0Variant(usize), | ||
680 | } | ||
681 | enum Outer { | ||
682 | Variant(Inner), | ||
683 | } | ||
684 | |||
685 | impl Outer { | ||
686 | fn new() -> Self { | ||
687 | Self::Variant(Inner::Variant(42)) | ||
688 | } | ||
689 | |||
690 | fn into_inner_destructed(self) -> u32 { | ||
691 | let Outer::Variant(Inner::Variant(x)) = self; | ||
692 | x | ||
693 | } | ||
694 | }"#, | ||
695 | r#" | ||
696 | enum Inner { | ||
697 | Variant { field1: usize }, | ||
698 | } | ||
699 | enum Outer { | ||
700 | Variant(Inner), | ||
701 | } | ||
702 | |||
703 | impl Outer { | ||
704 | fn new() -> Self { | ||
705 | Self::Variant(Inner::Variant { field1: 42 }) | ||
706 | } | ||
707 | |||
708 | fn into_inner_destructed(self) -> u32 { | ||
709 | let Outer::Variant(Inner::Variant { field1: x }) = self; | ||
710 | x | ||
711 | } | ||
712 | }"#, | ||
713 | ); | ||
714 | |||
715 | check_assist( | ||
716 | convert_tuple_struct_to_named_struct, | ||
717 | r#" | ||
718 | enum Inner { | ||
719 | Variant(usize), | ||
720 | } | ||
721 | enum Outer { | ||
722 | $0Variant(Inner), | ||
723 | } | ||
724 | |||
725 | impl Outer { | ||
726 | fn new() -> Self { | ||
727 | Self::Variant(Inner::Variant(42)) | ||
728 | } | ||
729 | |||
730 | fn into_inner_destructed(self) -> u32 { | ||
731 | let Outer::Variant(Inner::Variant(x)) = self; | ||
732 | x | ||
733 | } | ||
734 | }"#, | ||
735 | r#" | ||
736 | enum Inner { | ||
737 | Variant(usize), | ||
738 | } | ||
739 | enum Outer { | ||
740 | Variant { field1: Inner }, | ||
741 | } | ||
742 | |||
743 | impl Outer { | ||
744 | fn new() -> Self { | ||
745 | Self::Variant { field1: Inner::Variant(42) } | ||
746 | } | ||
747 | |||
748 | fn into_inner_destructed(self) -> u32 { | ||
749 | let Outer::Variant { field1: Inner::Variant(x) } = self; | ||
750 | x | ||
751 | } | ||
752 | }"#, | ||
753 | ); | ||
754 | } | ||
755 | |||
756 | #[test] | ||
757 | fn convert_variant_with_multi_file_references() { | ||
758 | check_assist( | ||
759 | convert_tuple_struct_to_named_struct, | ||
760 | r#" | ||
761 | //- /main.rs | ||
762 | struct Inner; | ||
763 | enum A { | ||
764 | $0Variant(Inner), | ||
765 | } | ||
766 | |||
767 | mod foo; | ||
768 | |||
769 | //- /foo.rs | ||
770 | use crate::{A, Inner}; | ||
771 | fn f() { | ||
772 | let a = A::Variant(Inner); | ||
773 | } | ||
774 | "#, | ||
775 | r#" | ||
776 | //- /main.rs | ||
777 | struct Inner; | ||
778 | enum A { | ||
779 | Variant { field1: Inner }, | ||
780 | } | ||
781 | |||
782 | mod foo; | ||
783 | |||
784 | //- /foo.rs | ||
785 | use crate::{A, Inner}; | ||
786 | fn f() { | ||
787 | let a = A::Variant { field1: Inner }; | ||
788 | } | ||
789 | "#, | ||
790 | ); | ||
791 | } | ||
792 | |||
793 | #[test] | ||
794 | fn convert_directly_used_variant() { | ||
795 | check_assist( | ||
796 | convert_tuple_struct_to_named_struct, | ||
797 | r#" | ||
798 | //- /main.rs | ||
799 | struct Inner; | ||
800 | enum A { | ||
801 | $0Variant(Inner), | ||
802 | } | ||
803 | |||
804 | mod foo; | ||
805 | |||
806 | //- /foo.rs | ||
807 | use crate::{A::Variant, Inner}; | ||
808 | fn f() { | ||
809 | let a = Variant(Inner); | ||
810 | } | ||
811 | "#, | ||
812 | r#" | ||
813 | //- /main.rs | ||
814 | struct Inner; | ||
815 | enum A { | ||
816 | Variant { field1: Inner }, | ||
817 | } | ||
818 | |||
819 | mod foo; | ||
820 | |||
821 | //- /foo.rs | ||
822 | use crate::{A::Variant, Inner}; | ||
823 | fn f() { | ||
824 | let a = Variant { field1: Inner }; | ||
825 | } | ||
826 | "#, | ||
827 | ); | ||
828 | } | ||
516 | } | 829 | } |
diff --git a/crates/ide_assists/src/handlers/convert_tuple_variant_to_named_variant.rs b/crates/ide_assists/src/handlers/convert_tuple_variant_to_named_variant.rs deleted file mode 100644 index 586ad9809..000000000 --- a/crates/ide_assists/src/handlers/convert_tuple_variant_to_named_variant.rs +++ /dev/null | |||
@@ -1,515 +0,0 @@ | |||
1 | use ide_db::defs::{Definition, NameRefClass}; | ||
2 | use syntax::{ | ||
3 | ast::{self, AstNode, VisibilityOwner}, | ||
4 | match_ast, SyntaxNode, | ||
5 | }; | ||
6 | |||
7 | use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; | ||
8 | |||
9 | // Assist: convert_tuple_variant_to_named_variant | ||
10 | // | ||
11 | // Converts tuple variant to variant with named fields. | ||
12 | // | ||
13 | // ``` | ||
14 | // enum A { | ||
15 | // Variant(usize), | ||
16 | // } | ||
17 | // | ||
18 | // impl A { | ||
19 | // fn new(value: usize) -> A { | ||
20 | // A::Variant(value) | ||
21 | // } | ||
22 | // | ||
23 | // fn new_with_default() -> A { | ||
24 | // A::new(Default::default()) | ||
25 | // } | ||
26 | // | ||
27 | // fn value(self) -> usize { | ||
28 | // match self { | ||
29 | // A::Variant(value) => value, | ||
30 | // } | ||
31 | // } | ||
32 | // } | ||
33 | // ``` | ||
34 | // -> | ||
35 | // ``` | ||
36 | // enum A { | ||
37 | // Variant { | ||
38 | // field1: usize | ||
39 | // }, | ||
40 | // } | ||
41 | // | ||
42 | // impl A { | ||
43 | // fn new(value: usize) -> A { | ||
44 | // A::Variant { | ||
45 | // field1: value, | ||
46 | // } | ||
47 | // } | ||
48 | // | ||
49 | // fn new_with_default() -> A { | ||
50 | // A::new(Default::default()) | ||
51 | // } | ||
52 | // | ||
53 | // fn value(self) -> usize { | ||
54 | // match self { | ||
55 | // A::Variant { field1: value } => value, | ||
56 | // } | ||
57 | // } | ||
58 | // } | ||
59 | // ``` | ||
60 | pub(crate) fn convert_tuple_variant_to_named_variant( | ||
61 | acc: &mut Assists, | ||
62 | ctx: &AssistContext, | ||
63 | ) -> Option<()> { | ||
64 | let variant = ctx.find_node_at_offset::<ast::Variant>()?; | ||
65 | let tuple_fields = match variant.field_list()? { | ||
66 | ast::FieldList::TupleFieldList(it) => it, | ||
67 | ast::FieldList::RecordFieldList(_) => return None, | ||
68 | }; | ||
69 | let variant_def = ctx.sema.to_def(&variant)?; | ||
70 | |||
71 | let target = variant.syntax().text_range(); | ||
72 | acc.add( | ||
73 | AssistId("convert_tuple_variant_to_named_variant", AssistKind::RefactorRewrite), | ||
74 | "Convert to named struct", | ||
75 | target, | ||
76 | |edit| { | ||
77 | let names = generate_names(tuple_fields.fields()); | ||
78 | edit_field_references(ctx, edit, tuple_fields.fields(), &names); // TODO: is this needed? | ||
79 | edit_variant_references(ctx, edit, variant_def, &names); | ||
80 | edit_variant_def(ctx, edit, tuple_fields, names); | ||
81 | }, | ||
82 | ) | ||
83 | } | ||
84 | |||
85 | fn edit_variant_def( | ||
86 | ctx: &AssistContext, | ||
87 | edit: &mut AssistBuilder, | ||
88 | tuple_fields: ast::TupleFieldList, | ||
89 | names: Vec<ast::Name>, | ||
90 | ) { | ||
91 | let record_fields = tuple_fields | ||
92 | .fields() | ||
93 | .zip(names) | ||
94 | .filter_map(|(f, name)| Some(ast::make::record_field(f.visibility(), name, f.ty()?))); | ||
95 | let record_fields = ast::make::record_field_list(record_fields); | ||
96 | let tuple_fields_text_range = tuple_fields.syntax().text_range(); | ||
97 | |||
98 | edit.edit_file(ctx.frange.file_id); | ||
99 | |||
100 | edit.insert(tuple_fields_text_range.start(), ast::make::tokens::single_space().text()); | ||
101 | |||
102 | edit.replace(tuple_fields_text_range, record_fields.to_string()); | ||
103 | } | ||
104 | |||
105 | fn edit_variant_references( | ||
106 | ctx: &AssistContext, | ||
107 | edit: &mut AssistBuilder, | ||
108 | variant: hir::Variant, | ||
109 | names: &[ast::Name], | ||
110 | ) { | ||
111 | let variant_def = Definition::ModuleDef(hir::ModuleDef::Variant(variant)); | ||
112 | let usages = variant_def.usages(&ctx.sema).include_self_refs().all(); | ||
113 | |||
114 | let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { | ||
115 | match_ast! { | ||
116 | match node { | ||
117 | ast::TupleStructPat(tuple_struct_pat) => { | ||
118 | edit.replace( | ||
119 | tuple_struct_pat.syntax().text_range(), | ||
120 | ast::make::record_pat_with_fields( | ||
121 | tuple_struct_pat.path()?, | ||
122 | ast::make::record_pat_field_list(tuple_struct_pat.fields().zip(names).map( | ||
123 | |(pat, name)| { | ||
124 | ast::make::record_pat_field( | ||
125 | ast::make::name_ref(&name.to_string()), | ||
126 | pat, | ||
127 | ) | ||
128 | }, | ||
129 | )), | ||
130 | ) | ||
131 | .to_string(), | ||
132 | ); | ||
133 | }, | ||
134 | // for tuple struct creations like Foo(42) | ||
135 | ast::CallExpr(call_expr) => { | ||
136 | let path = call_expr.syntax().descendants().find_map(ast::PathExpr::cast).and_then(|expr| expr.path())?; | ||
137 | |||
138 | // this also includes method calls like Foo::new(42), we should skip them | ||
139 | if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) { | ||
140 | match NameRefClass::classify(&ctx.sema, &name_ref) { | ||
141 | Some(NameRefClass::Definition(Definition::SelfType(_))) => {}, | ||
142 | Some(NameRefClass::Definition(def)) if def == variant_def => {}, | ||
143 | _ => return None, | ||
144 | }; | ||
145 | } | ||
146 | |||
147 | let arg_list = call_expr.syntax().descendants().find_map(ast::ArgList::cast)?; | ||
148 | |||
149 | edit.replace( | ||
150 | call_expr.syntax().text_range(), | ||
151 | ast::make::record_expr( | ||
152 | path, | ||
153 | ast::make::record_expr_field_list(arg_list.args().zip(names).map( | ||
154 | |(expr, name)| { | ||
155 | ast::make::record_expr_field( | ||
156 | ast::make::name_ref(&name.to_string()), | ||
157 | Some(expr), | ||
158 | ) | ||
159 | }, | ||
160 | )), | ||
161 | ) | ||
162 | .to_string(), | ||
163 | ); | ||
164 | }, | ||
165 | _ => return None, | ||
166 | } | ||
167 | } | ||
168 | Some(()) | ||
169 | }; | ||
170 | |||
171 | for (file_id, refs) in usages { | ||
172 | edit.edit_file(file_id); | ||
173 | for r in refs { | ||
174 | for node in r.name.syntax().ancestors() { | ||
175 | if edit_node(edit, node).is_some() { | ||
176 | break; | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | } | ||
181 | } | ||
182 | |||
183 | fn edit_field_references( | ||
184 | ctx: &AssistContext, | ||
185 | edit: &mut AssistBuilder, | ||
186 | fields: impl Iterator<Item = ast::TupleField>, | ||
187 | names: &[ast::Name], | ||
188 | ) { | ||
189 | for (field, name) in fields.zip(names) { | ||
190 | let field = match ctx.sema.to_def(&field) { | ||
191 | Some(it) => it, | ||
192 | None => continue, | ||
193 | }; | ||
194 | let def = Definition::Field(field); | ||
195 | let usages = def.usages(&ctx.sema).all(); | ||
196 | for (file_id, refs) in usages { | ||
197 | edit.edit_file(file_id); | ||
198 | for r in refs { | ||
199 | if let Some(name_ref) = r.name.as_name_ref() { | ||
200 | edit.replace(name_ref.syntax().text_range(), name.text()); | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | } | ||
206 | |||
207 | fn generate_names(fields: impl Iterator<Item = ast::TupleField>) -> Vec<ast::Name> { | ||
208 | fields.enumerate().map(|(i, _)| ast::make::name(&format!("field{}", i + 1))).collect() | ||
209 | } | ||
210 | |||
211 | #[cfg(test)] | ||
212 | mod tests { | ||
213 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
214 | |||
215 | use super::*; | ||
216 | |||
217 | #[test] | ||
218 | fn not_applicable_other_than_tuple_variant() { | ||
219 | check_assist_not_applicable( | ||
220 | convert_tuple_variant_to_named_variant, | ||
221 | r#"enum Enum { Variant$0 { value: usize } };"#, | ||
222 | ); | ||
223 | check_assist_not_applicable(convert_tuple_variant_to_named_variant, r#"enum Enum { Variant$0 }"#); | ||
224 | } | ||
225 | |||
226 | #[test] | ||
227 | fn convert_simple_variant() { | ||
228 | check_assist( | ||
229 | convert_tuple_variant_to_named_variant, | ||
230 | r#" | ||
231 | enum A { | ||
232 | $0Variant(usize), | ||
233 | } | ||
234 | |||
235 | impl A { | ||
236 | fn new(value: usize) -> A { | ||
237 | A::Variant(value) | ||
238 | } | ||
239 | |||
240 | fn new_with_default() -> A { | ||
241 | A::new(Default::default()) | ||
242 | } | ||
243 | |||
244 | fn value(self) -> usize { | ||
245 | match self { | ||
246 | A::Variant(value) => value, | ||
247 | } | ||
248 | } | ||
249 | }"#, | ||
250 | r#" | ||
251 | enum A { | ||
252 | Variant { field1: usize }, | ||
253 | } | ||
254 | |||
255 | impl A { | ||
256 | fn new(value: usize) -> A { | ||
257 | A::Variant { field1: value } | ||
258 | } | ||
259 | |||
260 | fn new_with_default() -> A { | ||
261 | A::new(Default::default()) | ||
262 | } | ||
263 | |||
264 | fn value(self) -> usize { | ||
265 | match self { | ||
266 | A::Variant { field1: value } => value, | ||
267 | } | ||
268 | } | ||
269 | }"#, | ||
270 | ); | ||
271 | } | ||
272 | |||
273 | #[test] | ||
274 | fn convert_variant_referenced_via_self_kw() { | ||
275 | check_assist( | ||
276 | convert_tuple_variant_to_named_variant, | ||
277 | r#" | ||
278 | enum A { | ||
279 | $0Variant(usize), | ||
280 | } | ||
281 | |||
282 | impl A { | ||
283 | fn new(value: usize) -> A { | ||
284 | Self::Variant(value) | ||
285 | } | ||
286 | |||
287 | fn new_with_default() -> A { | ||
288 | Self::new(Default::default()) | ||
289 | } | ||
290 | |||
291 | fn value(self) -> usize { | ||
292 | match self { | ||
293 | Self::Variant(value) => value, | ||
294 | } | ||
295 | } | ||
296 | }"#, | ||
297 | r#" | ||
298 | enum A { | ||
299 | Variant { field1: usize }, | ||
300 | } | ||
301 | |||
302 | impl A { | ||
303 | fn new(value: usize) -> A { | ||
304 | Self::Variant { field1: value } | ||
305 | } | ||
306 | |||
307 | fn new_with_default() -> A { | ||
308 | Self::new(Default::default()) | ||
309 | } | ||
310 | |||
311 | fn value(self) -> usize { | ||
312 | match self { | ||
313 | Self::Variant { field1: value } => value, | ||
314 | } | ||
315 | } | ||
316 | }"#, | ||
317 | ); | ||
318 | } | ||
319 | |||
320 | #[test] | ||
321 | fn convert_destructured_variant() { | ||
322 | check_assist( | ||
323 | convert_tuple_variant_to_named_variant, | ||
324 | r#" | ||
325 | enum A { | ||
326 | $0Variant(usize), | ||
327 | } | ||
328 | |||
329 | impl A { | ||
330 | fn into_inner(self) -> usize { | ||
331 | let A::Variant(first) = self; | ||
332 | first | ||
333 | } | ||
334 | |||
335 | fn into_inner_via_self(self) -> usize { | ||
336 | let Self::Variant(first) = self; | ||
337 | first | ||
338 | } | ||
339 | }"#, | ||
340 | r#" | ||
341 | enum A { | ||
342 | Variant { field1: usize }, | ||
343 | } | ||
344 | |||
345 | impl A { | ||
346 | fn into_inner(self) -> usize { | ||
347 | let A::Variant { field1: first } = self; | ||
348 | first | ||
349 | } | ||
350 | |||
351 | fn into_inner_via_self(self) -> usize { | ||
352 | let Self::Variant { field1: first } = self; | ||
353 | first | ||
354 | } | ||
355 | }"#, | ||
356 | ); | ||
357 | } | ||
358 | |||
359 | #[test] | ||
360 | fn convert_variant_with_wrapped_references() { | ||
361 | check_assist( | ||
362 | convert_tuple_variant_to_named_variant, | ||
363 | r#" | ||
364 | enum Inner { | ||
365 | $0Variant(usize), | ||
366 | } | ||
367 | enum Outer { | ||
368 | Variant(Inner), | ||
369 | } | ||
370 | |||
371 | impl Outer { | ||
372 | fn new() -> Self { | ||
373 | Self::Variant(Inner::Variant(42)) | ||
374 | } | ||
375 | |||
376 | fn into_inner_destructed(self) -> u32 { | ||
377 | let Outer::Variant(Inner::Variant(x)) = self; | ||
378 | x | ||
379 | } | ||
380 | }"#, | ||
381 | r#" | ||
382 | enum Inner { | ||
383 | Variant { field1: usize }, | ||
384 | } | ||
385 | enum Outer { | ||
386 | Variant(Inner), | ||
387 | } | ||
388 | |||
389 | impl Outer { | ||
390 | fn new() -> Self { | ||
391 | Self::Variant(Inner::Variant { field1: 42 }) | ||
392 | } | ||
393 | |||
394 | fn into_inner_destructed(self) -> u32 { | ||
395 | let Outer::Variant(Inner::Variant { field1: x }) = self; | ||
396 | x | ||
397 | } | ||
398 | }"#, | ||
399 | ); | ||
400 | |||
401 | check_assist( | ||
402 | convert_tuple_variant_to_named_variant, | ||
403 | r#" | ||
404 | enum Inner { | ||
405 | Variant(usize), | ||
406 | } | ||
407 | enum Outer { | ||
408 | $0Variant(Inner), | ||
409 | } | ||
410 | |||
411 | impl Outer { | ||
412 | fn new() -> Self { | ||
413 | Self::Variant(Inner::Variant(42)) | ||
414 | } | ||
415 | |||
416 | fn into_inner_destructed(self) -> u32 { | ||
417 | let Outer::Variant(Inner::Variant(x)) = self; | ||
418 | x | ||
419 | } | ||
420 | }"#, | ||
421 | r#" | ||
422 | enum Inner { | ||
423 | Variant(usize), | ||
424 | } | ||
425 | enum Outer { | ||
426 | Variant { field1: Inner }, | ||
427 | } | ||
428 | |||
429 | impl Outer { | ||
430 | fn new() -> Self { | ||
431 | Self::Variant { field1: Inner::Variant(42) } | ||
432 | } | ||
433 | |||
434 | fn into_inner_destructed(self) -> u32 { | ||
435 | let Outer::Variant { field1: Inner::Variant(x) } = self; | ||
436 | x | ||
437 | } | ||
438 | }"#, | ||
439 | ); | ||
440 | } | ||
441 | |||
442 | #[test] | ||
443 | fn convert_variant_with_multi_file_references() { | ||
444 | check_assist( | ||
445 | convert_tuple_variant_to_named_variant, | ||
446 | r#" | ||
447 | //- /main.rs | ||
448 | struct Inner; | ||
449 | enum A { | ||
450 | $0Variant(Inner), | ||
451 | } | ||
452 | |||
453 | mod foo; | ||
454 | |||
455 | //- /foo.rs | ||
456 | use crate::{A, Inner}; | ||
457 | fn f() { | ||
458 | let a = A::Variant(Inner); | ||
459 | } | ||
460 | "#, | ||
461 | r#" | ||
462 | //- /main.rs | ||
463 | struct Inner; | ||
464 | enum A { | ||
465 | Variant { field1: Inner }, | ||
466 | } | ||
467 | |||
468 | mod foo; | ||
469 | |||
470 | //- /foo.rs | ||
471 | use crate::{A, Inner}; | ||
472 | fn f() { | ||
473 | let a = A::Variant { field1: Inner }; | ||
474 | } | ||
475 | "#, | ||
476 | ); | ||
477 | } | ||
478 | |||
479 | #[test] | ||
480 | fn convert_directly_used_variant() { | ||
481 | check_assist( | ||
482 | convert_tuple_variant_to_named_variant, | ||
483 | r#" | ||
484 | //- /main.rs | ||
485 | struct Inner; | ||
486 | enum A { | ||
487 | $0Variant(Inner), | ||
488 | } | ||
489 | |||
490 | mod foo; | ||
491 | |||
492 | //- /foo.rs | ||
493 | use crate::{A::Variant, Inner}; | ||
494 | fn f() { | ||
495 | let a = Variant(Inner); | ||
496 | } | ||
497 | "#, | ||
498 | r#" | ||
499 | //- /main.rs | ||
500 | struct Inner; | ||
501 | enum A { | ||
502 | Variant { field1: Inner }, | ||
503 | } | ||
504 | |||
505 | mod foo; | ||
506 | |||
507 | //- /foo.rs | ||
508 | use crate::{A::Variant, Inner}; | ||
509 | fn f() { | ||
510 | let a = Variant { field1: Inner }; | ||
511 | } | ||
512 | "#, | ||
513 | ); | ||
514 | } | ||
515 | } | ||
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index d64233315..16af72927 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -186,7 +186,6 @@ mod handlers { | |||
186 | mod convert_iter_for_each_to_for; | 186 | mod convert_iter_for_each_to_for; |
187 | mod convert_into_to_from; | 187 | mod convert_into_to_from; |
188 | mod convert_tuple_struct_to_named_struct; | 188 | mod convert_tuple_struct_to_named_struct; |
189 | mod convert_tuple_variant_to_named_variant; | ||
190 | mod early_return; | 189 | mod early_return; |
191 | mod expand_glob_import; | 190 | mod expand_glob_import; |
192 | mod extract_function; | 191 | mod extract_function; |
@@ -257,7 +256,6 @@ mod handlers { | |||
257 | convert_iter_for_each_to_for::convert_iter_for_each_to_for, | 256 | convert_iter_for_each_to_for::convert_iter_for_each_to_for, |
258 | convert_into_to_from::convert_into_to_from, | 257 | convert_into_to_from::convert_into_to_from, |
259 | convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct, | 258 | convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct, |
260 | convert_tuple_variant_to_named_variant::convert_tuple_variant_to_named_variant, | ||
261 | early_return::convert_to_guarded_return, | 259 | early_return::convert_to_guarded_return, |
262 | expand_glob_import::expand_glob_import, | 260 | expand_glob_import::expand_glob_import, |
263 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, | 261 | extract_struct_from_enum_variant::extract_struct_from_enum_variant, |