2 use ide_db
::defs
::{Definition, NameRefClass}
;
4 ast
::{self, AstNode, HasGenericParams, HasVisibility}
,
8 use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}
;
10 // Assist: convert_tuple_struct_to_named_struct
12 // Converts tuple struct to struct with named fields, and analogously for tuple enum variants.
15 // struct Point$0(f32, f32);
18 // pub fn new(x: f32, y: f32) -> Self {
22 // pub fn x(&self) -> f32 {
26 // pub fn y(&self) -> f32 {
33 // struct Point { field1: f32, field2: f32 }
36 // pub fn new(x: f32, y: f32) -> Self {
37 // Point { field1: x, field2: y }
40 // pub fn x(&self) -> f32 {
44 // pub fn y(&self) -> f32 {
49 pub(crate) fn convert_tuple_struct_to_named_struct(
51 ctx
: &AssistContext
<'_
>,
54 .find_node_at_offset
::<ast
::Struct
>()
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
{
59 ast
::FieldList
::TupleFieldList(it
) => it
,
60 ast
::FieldList
::RecordFieldList(_
) => return None
,
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
)?
),
66 let target
= strukt
.as_ref().either(|s
| s
.syntax(), |v
| v
.syntax()).text_range();
69 AssistId("convert_tuple_struct_to_named_struct", AssistKind
::RefactorRewrite
),
70 "Convert to named struct",
73 let names
= generate_names(tuple_fields
.fields());
74 edit_field_references(ctx
, edit
, tuple_fields
.fields(), &names
);
75 edit_struct_references(ctx
, edit
, strukt_def
, &names
);
76 edit_struct_def(ctx
, edit
, &strukt
, tuple_fields
, names
);
82 ctx
: &AssistContext
<'_
>,
83 edit
: &mut SourceChangeBuilder
,
84 strukt
: &Either
<ast
::Struct
, ast
::Variant
>,
85 tuple_fields
: ast
::TupleFieldList
,
86 names
: Vec
<ast
::Name
>,
88 let record_fields
= tuple_fields
91 .filter_map(|(f
, name
)| Some(ast
::make
::record_field(f
.visibility(), name
, f
.ty()?
)));
92 let record_fields
= ast
::make
::record_field_list(record_fields
);
93 let tuple_fields_text_range
= tuple_fields
.syntax().text_range();
95 edit
.edit_file(ctx
.file_id());
97 if let Either
::Left(strukt
) = strukt
{
98 if let Some(w
) = strukt
.where_clause() {
99 edit
.delete(w
.syntax().text_range());
101 tuple_fields_text_range
.start(),
102 ast
::make
::tokens
::single_newline().text(),
104 edit
.insert(tuple_fields_text_range
.start(), w
.syntax().text());
105 edit
.insert(tuple_fields_text_range
.start(), ",");
107 tuple_fields_text_range
.start(),
108 ast
::make
::tokens
::single_newline().text(),
111 edit
.insert(tuple_fields_text_range
.start(), ast
::make
::tokens
::single_space().text());
113 if let Some(t
) = strukt
.semicolon_token() {
114 edit
.delete(t
.text_range());
117 edit
.insert(tuple_fields_text_range
.start(), ast
::make
::tokens
::single_space().text());
120 edit
.replace(tuple_fields_text_range
, record_fields
.to_string());
123 fn edit_struct_references(
124 ctx
: &AssistContext
<'_
>,
125 edit
: &mut SourceChangeBuilder
,
126 strukt
: Either
<hir
::Struct
, hir
::Variant
>,
129 let strukt_def
= match strukt
{
130 Either
::Left(s
) => Definition
::Adt(hir
::Adt
::Struct(s
)),
131 Either
::Right(v
) => Definition
::Variant(v
),
133 let usages
= strukt_def
.usages(&ctx
.sema
).include_self_refs().all();
135 let edit_node
= |edit
: &mut SourceChangeBuilder
, node
: SyntaxNode
| -> Option
<()> {
138 ast
::TupleStructPat(tuple_struct_pat
) => {
140 tuple_struct_pat
.syntax().text_range(),
141 ast
::make
::record_pat_with_fields(
142 tuple_struct_pat
.path()?
,
143 ast
::make
::record_pat_field_list(tuple_struct_pat
.fields().zip(names
).map(
145 ast
::make
::record_pat_field(
146 ast
::make
::name_ref(&name
.to_string()),
155 // for tuple struct creations like Foo(42)
156 ast
::CallExpr(call_expr
) => {
157 let path
= call_expr
.syntax().descendants().find_map(ast
::PathExpr
::cast
).and_then(|expr
| expr
.path())?
;
159 // this also includes method calls like Foo::new(42), we should skip them
160 if let Some(name_ref
) = path
.segment().and_then(|s
| s
.name_ref()) {
161 match NameRefClass
::classify(&ctx
.sema
, &name_ref
) {
162 Some(NameRefClass
::Definition(Definition
::SelfType(_
))) => {}
,
163 Some(NameRefClass
::Definition(def
)) if def
== strukt_def
=> {}
,
168 let arg_list
= call_expr
.syntax().descendants().find_map(ast
::ArgList
::cast
)?
;
171 call_expr
.syntax().text_range(),
172 ast
::make
::record_expr(
174 ast
::make
::record_expr_field_list(arg_list
.args().zip(names
).map(
176 ast
::make
::record_expr_field(
177 ast
::make
::name_ref(&name
.to_string()),
192 for (file_id
, refs
) in usages
{
193 edit
.edit_file(file_id
);
195 for node
in r
.name
.syntax().ancestors() {
196 if edit_node(edit
, node
).is_some() {
204 fn edit_field_references(
205 ctx
: &AssistContext
<'_
>,
206 edit
: &mut SourceChangeBuilder
,
207 fields
: impl Iterator
<Item
= ast
::TupleField
>,
210 for (field
, name
) in fields
.zip(names
) {
211 let field
= match ctx
.sema
.to_def(&field
) {
215 let def
= Definition
::Field(field
);
216 let usages
= def
.usages(&ctx
.sema
).all();
217 for (file_id
, refs
) in usages
{
218 edit
.edit_file(file_id
);
220 if let Some(name_ref
) = r
.name
.as_name_ref() {
221 edit
.replace(name_ref
.syntax().text_range(), name
.text());
228 fn generate_names(fields
: impl Iterator
<Item
= ast
::TupleField
>) -> Vec
<ast
::Name
> {
229 fields
.enumerate().map(|(i
, _
)| ast
::make
::name(&format
!("field{}", i
+ 1))).collect()
234 use crate::tests
::{check_assist, check_assist_not_applicable}
;
239 fn not_applicable_other_than_tuple_struct() {
240 check_assist_not_applicable(
241 convert_tuple_struct_to_named_struct
,
242 r
#"struct Foo$0 { bar: u32 };"#,
244 check_assist_not_applicable(convert_tuple_struct_to_named_struct
, r
#"struct Foo$0;"#);
248 fn convert_simple_struct() {
250 convert_tuple_struct_to_named_struct
,
256 fn new(inner: Inner) -> A {
260 fn new_with_default() -> A {
264 fn into_inner(self) -> Inner {
270 struct A { field1: Inner }
273 fn new(inner: Inner) -> A {
277 fn new_with_default() -> A {
281 fn into_inner(self) -> Inner {
289 fn convert_struct_referenced_via_self_kw() {
291 convert_tuple_struct_to_named_struct
,
297 fn new(inner: Inner) -> Self {
301 fn new_with_default() -> Self {
305 fn into_inner(self) -> Inner {
311 struct A { field1: Inner }
314 fn new(inner: Inner) -> Self {
315 Self { field1: inner }
318 fn new_with_default() -> Self {
322 fn into_inner(self) -> Inner {
330 fn convert_destructured_struct() {
332 convert_tuple_struct_to_named_struct
,
338 fn into_inner(self) -> Inner {
343 fn into_inner_via_self(self) -> Inner {
344 let Self(first) = self;
350 struct A { field1: Inner }
353 fn into_inner(self) -> Inner {
354 let A { field1: first } = self;
358 fn into_inner_via_self(self) -> Inner {
359 let Self { field1: first } = self;
367 fn convert_struct_with_visibility() {
369 convert_tuple_struct_to_named_struct
,
371 struct A$0(pub u32, pub(crate) u64);
378 fn into_first(self) -> u32 {
382 fn into_second(self) -> u64 {
387 struct A { pub field1: u32, pub(crate) field2: u64 }
391 A { field1: 42, field2: 42 }
394 fn into_first(self) -> u32 {
398 fn into_second(self) -> u64 {
406 fn convert_struct_with_wrapped_references() {
408 convert_tuple_struct_to_named_struct
,
418 fn into_inner(self) -> u32 {
422 fn into_inner_destructed(self) -> u32 {
423 let Outer(Inner(x)) = self;
428 struct Inner { field1: u32 }
433 Self(Inner { field1: 42 })
436 fn into_inner(self) -> u32 {
440 fn into_inner_destructed(self) -> u32 {
441 let Outer(Inner { field1: x }) = self;
448 convert_tuple_struct_to_named_struct
,
451 struct Outer$0(Inner);
458 fn into_inner(self) -> u32 {
462 fn into_inner_destructed(self) -> u32 {
463 let Outer(Inner(x)) = self;
469 struct Outer { field1: Inner }
473 Self { field1: Inner(42) }
476 fn into_inner(self) -> u32 {
480 fn into_inner_destructed(self) -> u32 {
481 let Outer { field1: Inner(x) } = self;
489 fn convert_struct_with_multi_file_references() {
491 convert_tuple_struct_to_named_struct
,
500 use crate::{A, Inner};
508 struct A { field1: Inner }
513 use crate::{A, Inner};
515 let a = A { field1: Inner };
522 fn convert_struct_with_where_clause() {
524 convert_tuple_struct_to_named_struct
,
540 fn not_applicable_other_than_tuple_variant() {
541 check_assist_not_applicable(
542 convert_tuple_struct_to_named_struct
,
543 r
#"enum Enum { Variant$0 { value: usize } };"#,
545 check_assist_not_applicable(
546 convert_tuple_struct_to_named_struct
,
547 r
#"enum Enum { Variant$0 }"#,
552 fn convert_simple_variant() {
554 convert_tuple_struct_to_named_struct
,
561 fn new(value: usize) -> A {
565 fn new_with_default() -> A {
566 A::new(Default::default())
569 fn value(self) -> usize {
571 A::Variant(value) => value,
577 Variant { field1: usize },
581 fn new(value: usize) -> A {
582 A::Variant { field1: value }
585 fn new_with_default() -> A {
586 A::new(Default::default())
589 fn value(self) -> usize {
591 A::Variant { field1: value } => value,
599 fn convert_variant_referenced_via_self_kw() {
601 convert_tuple_struct_to_named_struct
,
608 fn new(value: usize) -> A {
612 fn new_with_default() -> A {
613 Self::new(Default::default())
616 fn value(self) -> usize {
618 Self::Variant(value) => value,
624 Variant { field1: usize },
628 fn new(value: usize) -> A {
629 Self::Variant { field1: value }
632 fn new_with_default() -> A {
633 Self::new(Default::default())
636 fn value(self) -> usize {
638 Self::Variant { field1: value } => value,
646 fn convert_destructured_variant() {
648 convert_tuple_struct_to_named_struct
,
655 fn into_inner(self) -> usize {
656 let A::Variant(first) = self;
660 fn into_inner_via_self(self) -> usize {
661 let Self::Variant(first) = self;
667 Variant { field1: usize },
671 fn into_inner(self) -> usize {
672 let A::Variant { field1: first } = self;
676 fn into_inner_via_self(self) -> usize {
677 let Self::Variant { field1: first } = self;
685 fn convert_variant_with_wrapped_references() {
687 convert_tuple_struct_to_named_struct
,
698 Self::Variant(Inner::Variant(42))
701 fn into_inner_destructed(self) -> u32 {
702 let Outer::Variant(Inner::Variant(x)) = self;
708 Variant { field1: usize },
716 Self::Variant(Inner::Variant { field1: 42 })
719 fn into_inner_destructed(self) -> u32 {
720 let Outer::Variant(Inner::Variant { field1: x }) = self;
727 convert_tuple_struct_to_named_struct
,
738 Self::Variant(Inner::Variant(42))
741 fn into_inner_destructed(self) -> u32 {
742 let Outer::Variant(Inner::Variant(x)) = self;
751 Variant { field1: Inner },
756 Self::Variant { field1: Inner::Variant(42) }
759 fn into_inner_destructed(self) -> u32 {
760 let Outer::Variant { field1: Inner::Variant(x) } = self;
768 fn convert_variant_with_multi_file_references() {
770 convert_tuple_struct_to_named_struct
,
781 use crate::{A, Inner};
783 let a = A::Variant(Inner);
790 Variant { field1: Inner },
796 use crate::{A, Inner};
798 let a = A::Variant { field1: Inner };
805 fn convert_directly_used_variant() {
807 convert_tuple_struct_to_named_struct
,
818 use crate::{A::Variant, Inner};
820 let a = Variant(Inner);
827 Variant { field1: Inner },
833 use crate::{A::Variant, Inner};
835 let a = Variant { field1: Inner };