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> {
233 ast::make::name(&format!("field{idx}"))
240 use crate::tests::{check_assist, check_assist_not_applicable};
245 fn not_applicable_other_than_tuple_struct() {
246 check_assist_not_applicable(
247 convert_tuple_struct_to_named_struct,
248 r#"struct Foo$0 { bar: u32 };"#,
250 check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"struct Foo$0;"#);
254 fn convert_simple_struct() {
256 convert_tuple_struct_to_named_struct,
262 fn new(inner: Inner) -> A {
266 fn new_with_default() -> A {
270 fn into_inner(self) -> Inner {
276 struct A { field1: Inner }
279 fn new(inner: Inner) -> A {
283 fn new_with_default() -> A {
287 fn into_inner(self) -> Inner {
295 fn convert_struct_referenced_via_self_kw() {
297 convert_tuple_struct_to_named_struct,
303 fn new(inner: Inner) -> Self {
307 fn new_with_default() -> Self {
311 fn into_inner(self) -> Inner {
317 struct A { field1: Inner }
320 fn new(inner: Inner) -> Self {
321 Self { field1: inner }
324 fn new_with_default() -> Self {
328 fn into_inner(self) -> Inner {
336 fn convert_destructured_struct() {
338 convert_tuple_struct_to_named_struct,
344 fn into_inner(self) -> Inner {
349 fn into_inner_via_self(self) -> Inner {
350 let Self(first) = self;
356 struct A { field1: Inner }
359 fn into_inner(self) -> Inner {
360 let A { field1: first } = self;
364 fn into_inner_via_self(self) -> Inner {
365 let Self { field1: first } = self;
373 fn convert_struct_with_visibility() {
375 convert_tuple_struct_to_named_struct,
377 struct A$0(pub u32, pub(crate) u64);
384 fn into_first(self) -> u32 {
388 fn into_second(self) -> u64 {
393 struct A { pub field1: u32, pub(crate) field2: u64 }
397 A { field1: 42, field2: 42 }
400 fn into_first(self) -> u32 {
404 fn into_second(self) -> u64 {
412 fn convert_struct_with_wrapped_references() {
414 convert_tuple_struct_to_named_struct,
424 fn into_inner(self) -> u32 {
428 fn into_inner_destructed(self) -> u32 {
429 let Outer(Inner(x)) = self;
434 struct Inner { field1: u32 }
439 Self(Inner { field1: 42 })
442 fn into_inner(self) -> u32 {
446 fn into_inner_destructed(self) -> u32 {
447 let Outer(Inner { field1: x }) = self;
454 convert_tuple_struct_to_named_struct,
457 struct Outer$0(Inner);
464 fn into_inner(self) -> u32 {
468 fn into_inner_destructed(self) -> u32 {
469 let Outer(Inner(x)) = self;
475 struct Outer { field1: Inner }
479 Self { field1: Inner(42) }
482 fn into_inner(self) -> u32 {
486 fn into_inner_destructed(self) -> u32 {
487 let Outer { field1: Inner(x) } = self;
495 fn convert_struct_with_multi_file_references() {
497 convert_tuple_struct_to_named_struct,
506 use crate::{A, Inner};
514 struct A { field1: Inner }
519 use crate::{A, Inner};
521 let a = A { field1: Inner };
528 fn convert_struct_with_where_clause() {
530 convert_tuple_struct_to_named_struct,
546 fn not_applicable_other_than_tuple_variant() {
547 check_assist_not_applicable(
548 convert_tuple_struct_to_named_struct,
549 r#"enum Enum { Variant$0 { value: usize } };"#,
551 check_assist_not_applicable(
552 convert_tuple_struct_to_named_struct,
553 r#"enum Enum { Variant$0 }"#,
558 fn convert_simple_variant() {
560 convert_tuple_struct_to_named_struct,
567 fn new(value: usize) -> A {
571 fn new_with_default() -> A {
572 A::new(Default::default())
575 fn value(self) -> usize {
577 A::Variant(value) => value,
583 Variant { field1: usize },
587 fn new(value: usize) -> A {
588 A::Variant { field1: value }
591 fn new_with_default() -> A {
592 A::new(Default::default())
595 fn value(self) -> usize {
597 A::Variant { field1: value } => value,
605 fn convert_variant_referenced_via_self_kw() {
607 convert_tuple_struct_to_named_struct,
614 fn new(value: usize) -> A {
618 fn new_with_default() -> A {
619 Self::new(Default::default())
622 fn value(self) -> usize {
624 Self::Variant(value) => value,
630 Variant { field1: usize },
634 fn new(value: usize) -> A {
635 Self::Variant { field1: value }
638 fn new_with_default() -> A {
639 Self::new(Default::default())
642 fn value(self) -> usize {
644 Self::Variant { field1: value } => value,
652 fn convert_destructured_variant() {
654 convert_tuple_struct_to_named_struct,
661 fn into_inner(self) -> usize {
662 let A::Variant(first) = self;
666 fn into_inner_via_self(self) -> usize {
667 let Self::Variant(first) = self;
673 Variant { field1: usize },
677 fn into_inner(self) -> usize {
678 let A::Variant { field1: first } = self;
682 fn into_inner_via_self(self) -> usize {
683 let Self::Variant { field1: first } = self;
691 fn convert_variant_with_wrapped_references() {
693 convert_tuple_struct_to_named_struct,
704 Self::Variant(Inner::Variant(42))
707 fn into_inner_destructed(self) -> u32 {
708 let Outer::Variant(Inner::Variant(x)) = self;
714 Variant { field1: usize },
722 Self::Variant(Inner::Variant { field1: 42 })
725 fn into_inner_destructed(self) -> u32 {
726 let Outer::Variant(Inner::Variant { field1: x }) = self;
733 convert_tuple_struct_to_named_struct,
744 Self::Variant(Inner::Variant(42))
747 fn into_inner_destructed(self) -> u32 {
748 let Outer::Variant(Inner::Variant(x)) = self;
757 Variant { field1: Inner },
762 Self::Variant { field1: Inner::Variant(42) }
765 fn into_inner_destructed(self) -> u32 {
766 let Outer::Variant { field1: Inner::Variant(x) } = self;
774 fn convert_variant_with_multi_file_references() {
776 convert_tuple_struct_to_named_struct,
787 use crate::{A, Inner};
789 let a = A::Variant(Inner);
796 Variant { field1: Inner },
802 use crate::{A, Inner};
804 let a = A::Variant { field1: Inner };
811 fn convert_directly_used_variant() {
813 convert_tuple_struct_to_named_struct,
824 use crate::{A::Variant, Inner};
826 let a = Variant(Inner);
833 Variant { field1: Inner },
839 use crate::{A::Variant, Inner};
841 let a = Variant { field1: Inner };