2 use ide_db::defs::Definition;
3 use itertools::Itertools;
5 ast::{self, AstNode, HasGenericParams, HasVisibility},
6 match_ast, SyntaxKind, SyntaxNode,
9 use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists};
11 // Assist: convert_named_struct_to_tuple_struct
13 // Converts struct with named fields to tuple struct, and analogously for enum variants with named
17 // struct Point$0 { x: f32, y: f32 }
20 // pub fn new(x: f32, y: f32) -> Self {
24 // pub fn x(&self) -> f32 {
28 // pub fn y(&self) -> f32 {
35 // struct Point(f32, f32);
38 // pub fn new(x: f32, y: f32) -> Self {
42 // pub fn x(&self) -> f32 {
46 // pub fn y(&self) -> f32 {
51 pub(crate) fn convert_named_struct_to_tuple_struct(
53 ctx: &AssistContext<'_>,
56 .find_node_at_offset::<ast::Struct>()
58 .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
59 let field_list = strukt.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
60 let record_fields = match field_list {
61 ast::FieldList::RecordFieldList(it) => it,
62 ast::FieldList::TupleFieldList(_) => return None,
64 let strukt_def = match &strukt {
65 Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
66 Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
68 let target = strukt.as_ref().either(|s| s.syntax(), |v| v.syntax()).text_range();
71 AssistId("convert_named_struct_to_tuple_struct", AssistKind::RefactorRewrite),
72 "Convert to tuple struct",
75 edit_field_references(ctx, edit, record_fields.fields());
76 edit_struct_references(ctx, edit, strukt_def);
77 edit_struct_def(ctx, edit, &strukt, record_fields);
83 ctx: &AssistContext<'_>,
84 edit: &mut SourceChangeBuilder,
85 strukt: &Either<ast::Struct, ast::Variant>,
86 record_fields: ast::RecordFieldList,
88 let tuple_fields = record_fields
90 .filter_map(|f| Some(ast::make::tuple_field(f.visibility(), f.ty()?)));
91 let tuple_fields = ast::make::tuple_field_list(tuple_fields);
92 let record_fields_text_range = record_fields.syntax().text_range();
94 edit.edit_file(ctx.file_id());
95 edit.replace(record_fields_text_range, tuple_fields.syntax().text());
97 if let Either::Left(strukt) = strukt {
98 if let Some(w) = strukt.where_clause() {
99 let mut where_clause = w.to_string();
100 if where_clause.ends_with(',') {
103 where_clause.push(';');
105 edit.delete(w.syntax().text_range());
106 edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text());
107 edit.insert(record_fields_text_range.end(), where_clause);
108 edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text());
110 if let Some(tok) = strukt
111 .generic_param_list()
112 .and_then(|l| l.r_angle_token())
113 .and_then(|tok| tok.next_token())
114 .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
116 edit.delete(tok.text_range());
119 edit.insert(record_fields_text_range.end(), ";");
123 if let Some(tok) = record_fields
125 .and_then(|tok| tok.prev_token())
126 .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
128 edit.delete(tok.text_range())
132 fn edit_struct_references(
133 ctx: &AssistContext<'_>,
134 edit: &mut SourceChangeBuilder,
135 strukt: Either<hir::Struct, hir::Variant>,
137 let strukt_def = match strukt {
138 Either::Left(s) => Definition::Adt(hir::Adt::Struct(s)),
139 Either::Right(v) => Definition::Variant(v),
141 let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
143 let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> {
146 ast::RecordPat(record_struct_pat) => {
148 record_struct_pat.syntax().text_range(),
149 ast::make::tuple_struct_pat(
150 record_struct_pat.path()?,
152 .record_pat_field_list()?
154 .filter_map(|pat| pat.pat())
159 ast::RecordExpr(record_expr) => {
160 let path = record_expr.path()?;
161 let args = record_expr
162 .record_expr_field_list()?
164 .filter_map(|f| f.expr())
167 edit.replace(record_expr.syntax().text_range(), format!("{path}({args})"));
175 for (file_id, refs) in usages {
176 edit.edit_file(file_id);
178 for node in r.name.syntax().ancestors() {
179 if edit_node(edit, node).is_some() {
187 fn edit_field_references(
188 ctx: &AssistContext<'_>,
189 edit: &mut SourceChangeBuilder,
190 fields: impl Iterator<Item = ast::RecordField>,
192 for (index, field) in fields.enumerate() {
193 let field = match ctx.sema.to_def(&field) {
197 let def = Definition::Field(field);
198 let usages = def.usages(&ctx.sema).all();
199 for (file_id, refs) in usages {
200 edit.edit_file(file_id);
202 if let Some(name_ref) = r.name.as_name_ref() {
203 // Only edit the field reference if it's part of a `.field` access
204 if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() {
205 edit.replace(name_ref.syntax().text_range(), index.to_string());
215 use crate::tests::{check_assist, check_assist_not_applicable};
220 fn not_applicable_other_than_record_struct() {
221 check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0(u32)"#);
222 check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0;"#);
226 fn convert_simple_struct() {
228 convert_named_struct_to_tuple_struct,
231 struct A$0 { inner: Inner }
234 fn new(inner: Inner) -> A {
238 fn new_with_default() -> A {
242 fn into_inner(self) -> Inner {
251 fn new(inner: Inner) -> A {
255 fn new_with_default() -> A {
259 fn into_inner(self) -> Inner {
267 fn convert_struct_referenced_via_self_kw() {
269 convert_named_struct_to_tuple_struct,
272 struct A$0 { inner: Inner }
275 fn new(inner: Inner) -> Self {
279 fn new_with_default() -> Self {
283 fn into_inner(self) -> Inner {
292 fn new(inner: Inner) -> Self {
296 fn new_with_default() -> Self {
300 fn into_inner(self) -> Inner {
308 fn convert_destructured_struct() {
310 convert_named_struct_to_tuple_struct,
313 struct A$0 { inner: Inner }
316 fn into_inner(self) -> Inner {
317 let A { inner: a } = self;
321 fn into_inner_via_self(self) -> Inner {
322 let Self { inner } = self;
331 fn into_inner(self) -> Inner {
336 fn into_inner_via_self(self) -> Inner {
337 let Self(inner) = self;
345 fn convert_struct_with_visibility() {
347 convert_named_struct_to_tuple_struct,
351 pub(crate) second: u64
356 A { first: 42, second: 42 }
359 fn into_first(self) -> u32 {
363 fn into_second(self) -> u64 {
368 struct A(pub u32, pub(crate) u64);
375 fn into_first(self) -> u32 {
379 fn into_second(self) -> u64 {
387 fn convert_struct_with_wrapped_references() {
389 convert_named_struct_to_tuple_struct,
391 struct Inner$0 { uint: u32 }
392 struct Outer { inner: Inner }
396 Self { inner: Inner { uint: 42 } }
399 fn into_inner(self) -> u32 {
403 fn into_inner_destructed(self) -> u32 {
404 let Outer { inner: Inner { uint: x } } = self;
410 struct Outer { inner: Inner }
414 Self { inner: Inner(42) }
417 fn into_inner(self) -> u32 {
421 fn into_inner_destructed(self) -> u32 {
422 let Outer { inner: Inner(x) } = self;
429 convert_named_struct_to_tuple_struct,
431 struct Inner { uint: u32 }
432 struct Outer$0 { inner: Inner }
436 Self { inner: Inner { uint: 42 } }
439 fn into_inner(self) -> u32 {
443 fn into_inner_destructed(self) -> u32 {
444 let Outer { inner: Inner { uint: x } } = self;
449 struct Inner { uint: u32 }
454 Self(Inner { uint: 42 })
457 fn into_inner(self) -> u32 {
461 fn into_inner_destructed(self) -> u32 {
462 let Outer(Inner { uint: x }) = self;
470 fn convert_struct_with_multi_file_references() {
472 convert_named_struct_to_tuple_struct,
476 struct A$0 { inner: Inner }
481 use crate::{A, Inner};
483 let a = A { inner: Inner };
494 use crate::{A, Inner};
503 fn convert_struct_with_where_clause() {
505 convert_named_struct_to_tuple_struct,
522 fn not_applicable_other_than_record_variant() {
523 check_assist_not_applicable(
524 convert_named_struct_to_tuple_struct,
525 r#"enum Enum { Variant$0(usize) };"#,
527 check_assist_not_applicable(
528 convert_named_struct_to_tuple_struct,
529 r#"enum Enum { Variant$0 }"#,
534 fn convert_simple_variant() {
536 convert_named_struct_to_tuple_struct,
539 $0Variant { field1: usize },
543 fn new(value: usize) -> A {
544 A::Variant { field1: value }
547 fn new_with_default() -> A {
548 A::new(Default::default())
551 fn value(self) -> usize {
553 A::Variant { field1: value } => value,
563 fn new(value: usize) -> A {
567 fn new_with_default() -> A {
568 A::new(Default::default())
571 fn value(self) -> usize {
573 A::Variant(value) => value,
581 fn convert_variant_referenced_via_self_kw() {
583 convert_named_struct_to_tuple_struct,
586 $0Variant { field1: usize },
590 fn new(value: usize) -> A {
591 Self::Variant { field1: value }
594 fn new_with_default() -> A {
595 Self::new(Default::default())
598 fn value(self) -> usize {
600 Self::Variant { field1: value } => value,
610 fn new(value: usize) -> A {
614 fn new_with_default() -> A {
615 Self::new(Default::default())
618 fn value(self) -> usize {
620 Self::Variant(value) => value,
628 fn convert_destructured_variant() {
630 convert_named_struct_to_tuple_struct,
633 $0Variant { field1: usize },
637 fn into_inner(self) -> usize {
638 let A::Variant { field1: first } = self;
642 fn into_inner_via_self(self) -> usize {
643 let Self::Variant { field1: first } = self;
653 fn into_inner(self) -> usize {
654 let A::Variant(first) = self;
658 fn into_inner_via_self(self) -> usize {
659 let Self::Variant(first) = self;
667 fn convert_variant_with_wrapped_references() {
669 convert_named_struct_to_tuple_struct,
672 $0Variant { field1: usize },
680 Self::Variant(Inner::Variant { field1: 42 })
683 fn into_inner_destructed(self) -> u32 {
684 let Outer::Variant(Inner::Variant { field1: x }) = self;
698 Self::Variant(Inner::Variant(42))
701 fn into_inner_destructed(self) -> u32 {
702 let Outer::Variant(Inner::Variant(x)) = self;
709 convert_named_struct_to_tuple_struct,
715 $0Variant { field1: Inner },
720 Self::Variant { field1: Inner::Variant(42) }
723 fn into_inner_destructed(self) -> u32 {
724 let Outer::Variant { field1: Inner::Variant(x) } = self;
738 Self::Variant(Inner::Variant(42))
741 fn into_inner_destructed(self) -> u32 {
742 let Outer::Variant(Inner::Variant(x)) = self;
750 fn convert_variant_with_multi_file_references() {
752 convert_named_struct_to_tuple_struct,
757 $0Variant { field1: Inner },
763 use crate::{A, Inner};
765 let a = A::Variant { field1: Inner };
778 use crate::{A, Inner};
780 let a = A::Variant(Inner);
787 fn convert_directly_used_variant() {
789 convert_named_struct_to_tuple_struct,
794 $0Variant { field1: Inner },
800 use crate::{A::Variant, Inner};
802 let a = Variant { field1: Inner };
815 use crate::{A::Variant, Inner};
817 let a = Variant(Inner);