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