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