]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
Auto merge of #103913 - Neutron3529:patch-1, r=thomcc
[rust.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
230         .enumerate()
231         .map(|(i, _)| {
232             let idx = i + 1;
233             ast::make::name(&format!("field{idx}"))
234         })
235         .collect()
236 }
237
238 #[cfg(test)]
239 mod tests {
240     use crate::tests::{check_assist, check_assist_not_applicable};
241
242     use super::*;
243
244     #[test]
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 };"#,
249         );
250         check_assist_not_applicable(convert_tuple_struct_to_named_struct, r#"struct Foo$0;"#);
251     }
252
253     #[test]
254     fn convert_simple_struct() {
255         check_assist(
256             convert_tuple_struct_to_named_struct,
257             r#"
258 struct Inner;
259 struct A$0(Inner);
260
261 impl A {
262     fn new(inner: Inner) -> A {
263         A(inner)
264     }
265
266     fn new_with_default() -> A {
267         A::new(Inner)
268     }
269
270     fn into_inner(self) -> Inner {
271         self.0
272     }
273 }"#,
274             r#"
275 struct Inner;
276 struct A { field1: Inner }
277
278 impl A {
279     fn new(inner: Inner) -> A {
280         A { field1: inner }
281     }
282
283     fn new_with_default() -> A {
284         A::new(Inner)
285     }
286
287     fn into_inner(self) -> Inner {
288         self.field1
289     }
290 }"#,
291         );
292     }
293
294     #[test]
295     fn convert_struct_referenced_via_self_kw() {
296         check_assist(
297             convert_tuple_struct_to_named_struct,
298             r#"
299 struct Inner;
300 struct A$0(Inner);
301
302 impl A {
303     fn new(inner: Inner) -> Self {
304         Self(inner)
305     }
306
307     fn new_with_default() -> Self {
308         Self::new(Inner)
309     }
310
311     fn into_inner(self) -> Inner {
312         self.0
313     }
314 }"#,
315             r#"
316 struct Inner;
317 struct A { field1: Inner }
318
319 impl A {
320     fn new(inner: Inner) -> Self {
321         Self { field1: inner }
322     }
323
324     fn new_with_default() -> Self {
325         Self::new(Inner)
326     }
327
328     fn into_inner(self) -> Inner {
329         self.field1
330     }
331 }"#,
332         );
333     }
334
335     #[test]
336     fn convert_destructured_struct() {
337         check_assist(
338             convert_tuple_struct_to_named_struct,
339             r#"
340 struct Inner;
341 struct A$0(Inner);
342
343 impl A {
344     fn into_inner(self) -> Inner {
345         let A(first) = self;
346         first
347     }
348
349     fn into_inner_via_self(self) -> Inner {
350         let Self(first) = self;
351         first
352     }
353 }"#,
354             r#"
355 struct Inner;
356 struct A { field1: Inner }
357
358 impl A {
359     fn into_inner(self) -> Inner {
360         let A { field1: first } = self;
361         first
362     }
363
364     fn into_inner_via_self(self) -> Inner {
365         let Self { field1: first } = self;
366         first
367     }
368 }"#,
369         );
370     }
371
372     #[test]
373     fn convert_struct_with_visibility() {
374         check_assist(
375             convert_tuple_struct_to_named_struct,
376             r#"
377 struct A$0(pub u32, pub(crate) u64);
378
379 impl A {
380     fn new() -> A {
381         A(42, 42)
382     }
383
384     fn into_first(self) -> u32 {
385         self.0
386     }
387
388     fn into_second(self) -> u64 {
389         self.1
390     }
391 }"#,
392             r#"
393 struct A { pub field1: u32, pub(crate) field2: u64 }
394
395 impl A {
396     fn new() -> A {
397         A { field1: 42, field2: 42 }
398     }
399
400     fn into_first(self) -> u32 {
401         self.field1
402     }
403
404     fn into_second(self) -> u64 {
405         self.field2
406     }
407 }"#,
408         );
409     }
410
411     #[test]
412     fn convert_struct_with_wrapped_references() {
413         check_assist(
414             convert_tuple_struct_to_named_struct,
415             r#"
416 struct Inner$0(u32);
417 struct Outer(Inner);
418
419 impl Outer {
420     fn new() -> Self {
421         Self(Inner(42))
422     }
423
424     fn into_inner(self) -> u32 {
425         (self.0).0
426     }
427
428     fn into_inner_destructed(self) -> u32 {
429         let Outer(Inner(x)) = self;
430         x
431     }
432 }"#,
433             r#"
434 struct Inner { field1: u32 }
435 struct Outer(Inner);
436
437 impl Outer {
438     fn new() -> Self {
439         Self(Inner { field1: 42 })
440     }
441
442     fn into_inner(self) -> u32 {
443         (self.0).field1
444     }
445
446     fn into_inner_destructed(self) -> u32 {
447         let Outer(Inner { field1: x }) = self;
448         x
449     }
450 }"#,
451         );
452
453         check_assist(
454             convert_tuple_struct_to_named_struct,
455             r#"
456 struct Inner(u32);
457 struct Outer$0(Inner);
458
459 impl Outer {
460     fn new() -> Self {
461         Self(Inner(42))
462     }
463
464     fn into_inner(self) -> u32 {
465         (self.0).0
466     }
467
468     fn into_inner_destructed(self) -> u32 {
469         let Outer(Inner(x)) = self;
470         x
471     }
472 }"#,
473             r#"
474 struct Inner(u32);
475 struct Outer { field1: Inner }
476
477 impl Outer {
478     fn new() -> Self {
479         Self { field1: Inner(42) }
480     }
481
482     fn into_inner(self) -> u32 {
483         (self.field1).0
484     }
485
486     fn into_inner_destructed(self) -> u32 {
487         let Outer { field1: Inner(x) } = self;
488         x
489     }
490 }"#,
491         );
492     }
493
494     #[test]
495     fn convert_struct_with_multi_file_references() {
496         check_assist(
497             convert_tuple_struct_to_named_struct,
498             r#"
499 //- /main.rs
500 struct Inner;
501 struct A$0(Inner);
502
503 mod foo;
504
505 //- /foo.rs
506 use crate::{A, Inner};
507 fn f() {
508     let a = A(Inner);
509 }
510 "#,
511             r#"
512 //- /main.rs
513 struct Inner;
514 struct A { field1: Inner }
515
516 mod foo;
517
518 //- /foo.rs
519 use crate::{A, Inner};
520 fn f() {
521     let a = A { field1: Inner };
522 }
523 "#,
524         );
525     }
526
527     #[test]
528     fn convert_struct_with_where_clause() {
529         check_assist(
530             convert_tuple_struct_to_named_struct,
531             r#"
532 struct Wrap$0<T>(T)
533 where
534     T: Display;
535 "#,
536             r#"
537 struct Wrap<T>
538 where
539     T: Display,
540 { field1: T }
541
542 "#,
543         );
544     }
545     #[test]
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 } };"#,
550         );
551         check_assist_not_applicable(
552             convert_tuple_struct_to_named_struct,
553             r#"enum Enum { Variant$0 }"#,
554         );
555     }
556
557     #[test]
558     fn convert_simple_variant() {
559         check_assist(
560             convert_tuple_struct_to_named_struct,
561             r#"
562 enum A {
563     $0Variant(usize),
564 }
565
566 impl A {
567     fn new(value: usize) -> A {
568         A::Variant(value)
569     }
570
571     fn new_with_default() -> A {
572         A::new(Default::default())
573     }
574
575     fn value(self) -> usize {
576         match self {
577             A::Variant(value) => value,
578         }
579     }
580 }"#,
581             r#"
582 enum A {
583     Variant { field1: usize },
584 }
585
586 impl A {
587     fn new(value: usize) -> A {
588         A::Variant { field1: value }
589     }
590
591     fn new_with_default() -> A {
592         A::new(Default::default())
593     }
594
595     fn value(self) -> usize {
596         match self {
597             A::Variant { field1: value } => value,
598         }
599     }
600 }"#,
601         );
602     }
603
604     #[test]
605     fn convert_variant_referenced_via_self_kw() {
606         check_assist(
607             convert_tuple_struct_to_named_struct,
608             r#"
609 enum A {
610     $0Variant(usize),
611 }
612
613 impl A {
614     fn new(value: usize) -> A {
615         Self::Variant(value)
616     }
617
618     fn new_with_default() -> A {
619         Self::new(Default::default())
620     }
621
622     fn value(self) -> usize {
623         match self {
624             Self::Variant(value) => value,
625         }
626     }
627 }"#,
628             r#"
629 enum A {
630     Variant { field1: usize },
631 }
632
633 impl A {
634     fn new(value: usize) -> A {
635         Self::Variant { field1: value }
636     }
637
638     fn new_with_default() -> A {
639         Self::new(Default::default())
640     }
641
642     fn value(self) -> usize {
643         match self {
644             Self::Variant { field1: value } => value,
645         }
646     }
647 }"#,
648         );
649     }
650
651     #[test]
652     fn convert_destructured_variant() {
653         check_assist(
654             convert_tuple_struct_to_named_struct,
655             r#"
656 enum A {
657     $0Variant(usize),
658 }
659
660 impl A {
661     fn into_inner(self) -> usize {
662         let A::Variant(first) = self;
663         first
664     }
665
666     fn into_inner_via_self(self) -> usize {
667         let Self::Variant(first) = self;
668         first
669     }
670 }"#,
671             r#"
672 enum A {
673     Variant { field1: usize },
674 }
675
676 impl A {
677     fn into_inner(self) -> usize {
678         let A::Variant { field1: first } = self;
679         first
680     }
681
682     fn into_inner_via_self(self) -> usize {
683         let Self::Variant { field1: first } = self;
684         first
685     }
686 }"#,
687         );
688     }
689
690     #[test]
691     fn convert_variant_with_wrapped_references() {
692         check_assist(
693             convert_tuple_struct_to_named_struct,
694             r#"
695 enum Inner {
696     $0Variant(usize),
697 }
698 enum Outer {
699     Variant(Inner),
700 }
701
702 impl Outer {
703     fn new() -> Self {
704         Self::Variant(Inner::Variant(42))
705     }
706
707     fn into_inner_destructed(self) -> u32 {
708         let Outer::Variant(Inner::Variant(x)) = self;
709         x
710     }
711 }"#,
712             r#"
713 enum Inner {
714     Variant { field1: usize },
715 }
716 enum Outer {
717     Variant(Inner),
718 }
719
720 impl Outer {
721     fn new() -> Self {
722         Self::Variant(Inner::Variant { field1: 42 })
723     }
724
725     fn into_inner_destructed(self) -> u32 {
726         let Outer::Variant(Inner::Variant { field1: x }) = self;
727         x
728     }
729 }"#,
730         );
731
732         check_assist(
733             convert_tuple_struct_to_named_struct,
734             r#"
735 enum Inner {
736     Variant(usize),
737 }
738 enum Outer {
739     $0Variant(Inner),
740 }
741
742 impl Outer {
743     fn new() -> Self {
744         Self::Variant(Inner::Variant(42))
745     }
746
747     fn into_inner_destructed(self) -> u32 {
748         let Outer::Variant(Inner::Variant(x)) = self;
749         x
750     }
751 }"#,
752             r#"
753 enum Inner {
754     Variant(usize),
755 }
756 enum Outer {
757     Variant { field1: Inner },
758 }
759
760 impl Outer {
761     fn new() -> Self {
762         Self::Variant { field1: Inner::Variant(42) }
763     }
764
765     fn into_inner_destructed(self) -> u32 {
766         let Outer::Variant { field1: Inner::Variant(x) } = self;
767         x
768     }
769 }"#,
770         );
771     }
772
773     #[test]
774     fn convert_variant_with_multi_file_references() {
775         check_assist(
776             convert_tuple_struct_to_named_struct,
777             r#"
778 //- /main.rs
779 struct Inner;
780 enum A {
781     $0Variant(Inner),
782 }
783
784 mod foo;
785
786 //- /foo.rs
787 use crate::{A, Inner};
788 fn f() {
789     let a = A::Variant(Inner);
790 }
791 "#,
792             r#"
793 //- /main.rs
794 struct Inner;
795 enum A {
796     Variant { field1: Inner },
797 }
798
799 mod foo;
800
801 //- /foo.rs
802 use crate::{A, Inner};
803 fn f() {
804     let a = A::Variant { field1: Inner };
805 }
806 "#,
807         );
808     }
809
810     #[test]
811     fn convert_directly_used_variant() {
812         check_assist(
813             convert_tuple_struct_to_named_struct,
814             r#"
815 //- /main.rs
816 struct Inner;
817 enum A {
818     $0Variant(Inner),
819 }
820
821 mod foo;
822
823 //- /foo.rs
824 use crate::{A::Variant, Inner};
825 fn f() {
826     let a = Variant(Inner);
827 }
828 "#,
829             r#"
830 //- /main.rs
831 struct Inner;
832 enum A {
833     Variant { field1: Inner },
834 }
835
836 mod foo;
837
838 //- /foo.rs
839 use crate::{A::Variant, Inner};
840 fn f() {
841     let a = Variant { field1: Inner };
842 }
843 "#,
844         );
845     }
846 }