]> git.lizzy.rs Git - rust.git/blob - src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
Rollup merge of #99954 - dingxiangfei2009:break-out-let-else-higher-up, r=oli-obk
[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.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 }