ide_assists/handlers/
convert_named_struct_to_tuple_struct.rs

1use either::Either;
2use ide_db::{defs::Definition, search::FileReference};
3use itertools::Itertools;
4use syntax::{
5    SyntaxKind,
6    ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
7    match_ast,
8    syntax_editor::{Position, SyntaxEditor},
9};
10
11use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder};
12
13// Assist: convert_named_struct_to_tuple_struct
14//
15// Converts struct with named fields to tuple struct, and analogously for enum variants with named
16// fields.
17//
18// ```
19// struct Point$0 { x: f32, y: f32 }
20//
21// impl Point {
22//     pub fn new(x: f32, y: f32) -> Self {
23//         Point { x, y }
24//     }
25//
26//     pub fn x(&self) -> f32 {
27//         self.x
28//     }
29//
30//     pub fn y(&self) -> f32 {
31//         self.y
32//     }
33// }
34// ```
35// ->
36// ```
37// struct Point(f32, f32);
38//
39// impl Point {
40//     pub fn new(x: f32, y: f32) -> Self {
41//         Point(x, y)
42//     }
43//
44//     pub fn x(&self) -> f32 {
45//         self.0
46//     }
47//
48//     pub fn y(&self) -> f32 {
49//         self.1
50//     }
51// }
52// ```
53pub(crate) fn convert_named_struct_to_tuple_struct(
54    acc: &mut Assists,
55    ctx: &AssistContext<'_>,
56) -> Option<()> {
57    // XXX: We don't currently provide this assist for struct definitions inside macros, but if we
58    // are to lift this limitation, don't forget to make `edit_struct_def()` consider macro files
59    // too.
60    let strukt_or_variant = ctx
61        .find_node_at_offset::<ast::Struct>()
62        .map(Either::Left)
63        .or_else(|| ctx.find_node_at_offset::<ast::Variant>().map(Either::Right))?;
64    let field_list = strukt_or_variant.as_ref().either(|s| s.field_list(), |v| v.field_list())?;
65
66    if ctx.offset() > field_list.syntax().text_range().start() {
67        // Assist could be distracting after the braces
68        return None;
69    }
70
71    let record_fields = match field_list {
72        ast::FieldList::RecordFieldList(it) => it,
73        ast::FieldList::TupleFieldList(_) => return None,
74    };
75    let strukt_def = match &strukt_or_variant {
76        Either::Left(s) => Either::Left(ctx.sema.to_def(s)?),
77        Either::Right(v) => Either::Right(ctx.sema.to_def(v)?),
78    };
79
80    acc.add(
81        AssistId::refactor_rewrite("convert_named_struct_to_tuple_struct"),
82        "Convert to tuple struct",
83        strukt_or_variant.syntax().text_range(),
84        |edit| {
85            edit_field_references(ctx, edit, record_fields.fields());
86            edit_struct_references(ctx, edit, strukt_def);
87            edit_struct_def(ctx, edit, &strukt_or_variant, record_fields);
88        },
89    )
90}
91
92fn edit_struct_def(
93    ctx: &AssistContext<'_>,
94    edit: &mut SourceChangeBuilder,
95    strukt: &Either<ast::Struct, ast::Variant>,
96    record_fields: ast::RecordFieldList,
97) {
98    // Note that we don't need to consider macro files in this function because this is
99    // currently not triggered for struct definitions inside macro calls.
100    let tuple_fields = record_fields.fields().filter_map(|f| {
101        let field = ast::make::tuple_field(f.visibility(), f.ty()?);
102        let mut editor = SyntaxEditor::new(field.syntax().clone());
103        editor.insert_all(
104            Position::first_child_of(field.syntax()),
105            f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
106        );
107        let field_syntax = editor.finish().new_root().clone();
108        let field = ast::TupleField::cast(field_syntax)?;
109        Some(field)
110    });
111    let tuple_fields = ast::make::tuple_field_list(tuple_fields);
112    let record_fields_text_range = record_fields.syntax().text_range();
113
114    edit.edit_file(ctx.vfs_file_id());
115    edit.replace(record_fields_text_range, tuple_fields.syntax().text());
116
117    if let Either::Left(strukt) = strukt {
118        if let Some(w) = strukt.where_clause() {
119            let mut where_clause = w.to_string();
120            if where_clause.ends_with(',') {
121                where_clause.pop();
122            }
123            where_clause.push(';');
124
125            edit.delete(w.syntax().text_range());
126            edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text());
127            edit.insert(record_fields_text_range.end(), where_clause);
128            edit.insert(record_fields_text_range.end(), ast::make::tokens::single_newline().text());
129
130            if let Some(tok) = strukt
131                .generic_param_list()
132                .and_then(|l| l.r_angle_token())
133                .and_then(|tok| tok.next_token())
134                .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
135            {
136                edit.delete(tok.text_range());
137            }
138        } else {
139            edit.insert(record_fields_text_range.end(), ";");
140        }
141    }
142
143    if let Some(tok) = record_fields
144        .l_curly_token()
145        .and_then(|tok| tok.prev_token())
146        .filter(|tok| tok.kind() == SyntaxKind::WHITESPACE)
147    {
148        edit.delete(tok.text_range())
149    }
150}
151
152fn edit_struct_references(
153    ctx: &AssistContext<'_>,
154    edit: &mut SourceChangeBuilder,
155    strukt: Either<hir::Struct, hir::Variant>,
156) {
157    let strukt_def = match strukt {
158        Either::Left(s) => Definition::Adt(hir::Adt::Struct(s)),
159        Either::Right(v) => Definition::Variant(v),
160    };
161    let usages = strukt_def.usages(&ctx.sema).include_self_refs().all();
162
163    for (file_id, refs) in usages {
164        edit.edit_file(file_id.file_id(ctx.db()));
165        for r in refs {
166            process_struct_name_reference(ctx, r, edit);
167        }
168    }
169}
170
171fn process_struct_name_reference(
172    ctx: &AssistContext<'_>,
173    r: FileReference,
174    edit: &mut SourceChangeBuilder,
175) -> Option<()> {
176    // First check if it's the last semgnet of a path that directly belongs to a record
177    // expression/pattern.
178    let name_ref = r.name.as_name_ref()?;
179    let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?;
180    // A `PathSegment` always belongs to a `Path`, so there's at least one `Path` at this point.
181    let full_path =
182        path_segment.syntax().parent()?.ancestors().map_while(ast::Path::cast).last()?;
183
184    if full_path.segment()?.name_ref()? != *name_ref {
185        // `name_ref` isn't the last segment of the path, so `full_path` doesn't point to the
186        // struct we want to edit.
187        return None;
188    }
189
190    // FIXME: Processing RecordPat and RecordExpr for unordered fields, and insert RestPat
191    let parent = full_path.syntax().parent()?;
192    match_ast! {
193        match parent {
194            ast::RecordPat(record_struct_pat) => {
195                // When we failed to get the original range for the whole struct expression node,
196                // we can't provide any reasonable edit. Leave it untouched.
197                let file_range = ctx.sema.original_range_opt(record_struct_pat.syntax())?;
198                edit.replace(
199                    file_range.range,
200                    ast::make::tuple_struct_pat(
201                        record_struct_pat.path()?,
202                        record_struct_pat
203                            .record_pat_field_list()?
204                            .fields()
205                            .filter_map(|pat| pat.pat())
206                            .chain(record_struct_pat.record_pat_field_list()?
207                                .rest_pat()
208                                .map(Into::into))
209                    )
210                    .to_string()
211                );
212            },
213            ast::RecordExpr(record_expr) => {
214                // When we failed to get the original range for the whole struct pattern node,
215                // we can't provide any reasonable edit. Leave it untouched.
216                let file_range = ctx.sema.original_range_opt(record_expr.syntax())?;
217                let path = record_expr.path()?;
218                let args = record_expr
219                    .record_expr_field_list()?
220                    .fields()
221                    .filter_map(|f| f.expr())
222                    .join(", ");
223
224                edit.replace(file_range.range, format!("{path}({args})"));
225            },
226            _ => {}
227        }
228    }
229
230    Some(())
231}
232
233fn edit_field_references(
234    ctx: &AssistContext<'_>,
235    edit: &mut SourceChangeBuilder,
236    fields: impl Iterator<Item = ast::RecordField>,
237) {
238    for (index, field) in fields.enumerate() {
239        let field = match ctx.sema.to_def(&field) {
240            Some(it) => it,
241            None => continue,
242        };
243        let def = Definition::Field(field);
244        let usages = def.usages(&ctx.sema).all();
245        for (file_id, refs) in usages {
246            edit.edit_file(file_id.file_id(ctx.db()));
247            for r in refs {
248                if let Some(name_ref) = r.name.as_name_ref() {
249                    // Only edit the field reference if it's part of a `.field` access
250                    if name_ref.syntax().parent().and_then(ast::FieldExpr::cast).is_some() {
251                        edit.replace(r.range, index.to_string());
252                    }
253                }
254            }
255        }
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use crate::tests::{check_assist, check_assist_not_applicable};
262
263    use super::*;
264
265    #[test]
266    fn not_applicable_other_than_record_struct() {
267        check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0(u32)"#);
268        check_assist_not_applicable(convert_named_struct_to_tuple_struct, r#"struct Foo$0;"#);
269    }
270
271    #[test]
272    fn convert_simple_struct() {
273        check_assist(
274            convert_named_struct_to_tuple_struct,
275            r#"
276struct Inner;
277struct A$0 { inner: Inner }
278
279impl A {
280    fn new(inner: Inner) -> A {
281        A { inner }
282    }
283
284    fn new_with_default() -> A {
285        A::new(Inner)
286    }
287
288    fn into_inner(self) -> Inner {
289        self.inner
290    }
291}"#,
292            r#"
293struct Inner;
294struct A(Inner);
295
296impl A {
297    fn new(inner: Inner) -> A {
298        A(inner)
299    }
300
301    fn new_with_default() -> A {
302        A::new(Inner)
303    }
304
305    fn into_inner(self) -> Inner {
306        self.0
307    }
308}"#,
309        );
310    }
311
312    #[test]
313    fn convert_simple_struct_cursor_on_struct_keyword() {
314        check_assist(
315            convert_named_struct_to_tuple_struct,
316            r#"
317struct Inner;
318struct$0 A { inner: Inner }
319
320impl A {
321    fn new(inner: Inner) -> A {
322        A { inner }
323    }
324
325    fn new_with_default() -> A {
326        A::new(Inner)
327    }
328
329    fn into_inner(self) -> Inner {
330        self.inner
331    }
332}"#,
333            r#"
334struct Inner;
335struct A(Inner);
336
337impl A {
338    fn new(inner: Inner) -> A {
339        A(inner)
340    }
341
342    fn new_with_default() -> A {
343        A::new(Inner)
344    }
345
346    fn into_inner(self) -> Inner {
347        self.0
348    }
349}"#,
350        );
351    }
352
353    #[test]
354    fn convert_struct_and_rest_pat() {
355        check_assist(
356            convert_named_struct_to_tuple_struct,
357            r#"
358struct Inner;
359struct A$0 { inner: Inner }
360fn foo(A { .. }: A) {}
361"#,
362            r#"
363struct Inner;
364struct A(Inner);
365fn foo(A(..): A) {}
366"#,
367        );
368
369        check_assist(
370            convert_named_struct_to_tuple_struct,
371            r#"
372struct Inner;
373struct A$0 { inner: Inner, extra: Inner }
374fn foo(A { inner, .. }: A) {}
375"#,
376            r#"
377struct Inner;
378struct A(Inner, Inner);
379fn foo(A(inner, ..): A) {}
380"#,
381        );
382    }
383
384    #[test]
385    fn convert_simple_struct_cursor_on_visibility_keyword() {
386        check_assist(
387            convert_named_struct_to_tuple_struct,
388            r#"
389struct Inner;
390pub$0 struct A { inner: Inner }
391
392impl A {
393    fn new(inner: Inner) -> A {
394        A { inner }
395    }
396
397    fn new_with_default() -> A {
398        A::new(Inner)
399    }
400
401    fn into_inner(self) -> Inner {
402        self.inner
403    }
404}"#,
405            r#"
406struct Inner;
407pub struct A(Inner);
408
409impl A {
410    fn new(inner: Inner) -> A {
411        A(inner)
412    }
413
414    fn new_with_default() -> A {
415        A::new(Inner)
416    }
417
418    fn into_inner(self) -> Inner {
419        self.0
420    }
421}"#,
422        );
423    }
424
425    #[test]
426    fn convert_struct_referenced_via_self_kw() {
427        check_assist(
428            convert_named_struct_to_tuple_struct,
429            r#"
430struct Inner;
431struct A$0 { inner: Inner }
432
433impl A {
434    fn new(inner: Inner) -> Self {
435        Self { inner }
436    }
437
438    fn new_with_default() -> Self {
439        Self::new(Inner)
440    }
441
442    fn into_inner(self) -> Inner {
443        self.inner
444    }
445}"#,
446            r#"
447struct Inner;
448struct A(Inner);
449
450impl A {
451    fn new(inner: Inner) -> Self {
452        Self(inner)
453    }
454
455    fn new_with_default() -> Self {
456        Self::new(Inner)
457    }
458
459    fn into_inner(self) -> Inner {
460        self.0
461    }
462}"#,
463        );
464    }
465
466    #[test]
467    fn convert_destructured_struct() {
468        check_assist(
469            convert_named_struct_to_tuple_struct,
470            r#"
471struct Inner;
472struct A$0 { inner: Inner }
473
474impl A {
475    fn into_inner(self) -> Inner {
476        let A { inner: a } = self;
477        a
478    }
479
480    fn into_inner_via_self(self) -> Inner {
481        let Self { inner } = self;
482        inner
483    }
484}"#,
485            r#"
486struct Inner;
487struct A(Inner);
488
489impl A {
490    fn into_inner(self) -> Inner {
491        let A(a) = self;
492        a
493    }
494
495    fn into_inner_via_self(self) -> Inner {
496        let Self(inner) = self;
497        inner
498    }
499}"#,
500        );
501    }
502
503    #[test]
504    fn convert_struct_with_visibility() {
505        check_assist(
506            convert_named_struct_to_tuple_struct,
507            r#"
508struct A$0 {
509    pub first: u32,
510    pub(crate) second: u64
511}
512
513impl A {
514    fn new() -> A {
515        A { first: 42, second: 42 }
516    }
517
518    fn into_first(self) -> u32 {
519        self.first
520    }
521
522    fn into_second(self) -> u64 {
523        self.second
524    }
525}"#,
526            r#"
527struct A(pub u32, pub(crate) u64);
528
529impl A {
530    fn new() -> A {
531        A(42, 42)
532    }
533
534    fn into_first(self) -> u32 {
535        self.0
536    }
537
538    fn into_second(self) -> u64 {
539        self.1
540    }
541}"#,
542        );
543    }
544
545    #[test]
546    fn convert_struct_with_wrapped_references() {
547        check_assist(
548            convert_named_struct_to_tuple_struct,
549            r#"
550struct Inner$0 { uint: u32 }
551struct Outer { inner: Inner }
552
553impl Outer {
554    fn new() -> Self {
555        Self { inner: Inner { uint: 42 } }
556    }
557
558    fn into_inner(self) -> u32 {
559        self.inner.uint
560    }
561
562    fn into_inner_destructed(self) -> u32 {
563        let Outer { inner: Inner { uint: x } } = self;
564        x
565    }
566}"#,
567            r#"
568struct Inner(u32);
569struct Outer { inner: Inner }
570
571impl Outer {
572    fn new() -> Self {
573        Self { inner: Inner(42) }
574    }
575
576    fn into_inner(self) -> u32 {
577        self.inner.0
578    }
579
580    fn into_inner_destructed(self) -> u32 {
581        let Outer { inner: Inner(x) } = self;
582        x
583    }
584}"#,
585        );
586
587        check_assist(
588            convert_named_struct_to_tuple_struct,
589            r#"
590struct Inner { uint: u32 }
591struct Outer$0 { inner: Inner }
592
593impl Outer {
594    fn new() -> Self {
595        Self { inner: Inner { uint: 42 } }
596    }
597
598    fn into_inner(self) -> u32 {
599        self.inner.uint
600    }
601
602    fn into_inner_destructed(self) -> u32 {
603        let Outer { inner: Inner { uint: x } } = self;
604        x
605    }
606}"#,
607            r#"
608struct Inner { uint: u32 }
609struct Outer(Inner);
610
611impl Outer {
612    fn new() -> Self {
613        Self(Inner { uint: 42 })
614    }
615
616    fn into_inner(self) -> u32 {
617        self.0.uint
618    }
619
620    fn into_inner_destructed(self) -> u32 {
621        let Outer(Inner { uint: x }) = self;
622        x
623    }
624}"#,
625        );
626    }
627
628    #[test]
629    fn convert_struct_with_multi_file_references() {
630        check_assist(
631            convert_named_struct_to_tuple_struct,
632            r#"
633//- /main.rs
634struct Inner;
635struct A$0 { inner: Inner }
636
637mod foo;
638
639//- /foo.rs
640use crate::{A, Inner};
641fn f() {
642    let a = A { inner: Inner };
643}
644"#,
645            r#"
646//- /main.rs
647struct Inner;
648struct A(Inner);
649
650mod foo;
651
652//- /foo.rs
653use crate::{A, Inner};
654fn f() {
655    let a = A(Inner);
656}
657"#,
658        );
659    }
660
661    #[test]
662    fn convert_struct_with_where_clause() {
663        check_assist(
664            convert_named_struct_to_tuple_struct,
665            r#"
666struct Wrap$0<T>
667where
668    T: Display,
669{ field1: T }
670"#,
671            r#"
672struct Wrap<T>(T)
673where
674    T: Display;
675
676"#,
677        );
678    }
679
680    #[test]
681    fn not_applicable_other_than_record_variant() {
682        check_assist_not_applicable(
683            convert_named_struct_to_tuple_struct,
684            r#"enum Enum { Variant$0(usize) };"#,
685        );
686        check_assist_not_applicable(
687            convert_named_struct_to_tuple_struct,
688            r#"enum Enum { Variant$0 }"#,
689        );
690    }
691
692    #[test]
693    fn convert_simple_variant() {
694        check_assist(
695            convert_named_struct_to_tuple_struct,
696            r#"
697enum A {
698    $0Variant { field1: usize },
699}
700
701impl A {
702    fn new(value: usize) -> A {
703        A::Variant { field1: value }
704    }
705
706    fn new_with_default() -> A {
707        A::new(Default::default())
708    }
709
710    fn value(self) -> usize {
711        match self {
712            A::Variant { field1: value } => value,
713        }
714    }
715}"#,
716            r#"
717enum A {
718    Variant(usize),
719}
720
721impl A {
722    fn new(value: usize) -> A {
723        A::Variant(value)
724    }
725
726    fn new_with_default() -> A {
727        A::new(Default::default())
728    }
729
730    fn value(self) -> usize {
731        match self {
732            A::Variant(value) => value,
733        }
734    }
735}"#,
736        );
737    }
738
739    #[test]
740    fn convert_variant_referenced_via_self_kw() {
741        check_assist(
742            convert_named_struct_to_tuple_struct,
743            r#"
744enum A {
745    $0Variant { field1: usize },
746}
747
748impl A {
749    fn new(value: usize) -> A {
750        Self::Variant { field1: value }
751    }
752
753    fn new_with_default() -> A {
754        Self::new(Default::default())
755    }
756
757    fn value(self) -> usize {
758        match self {
759            Self::Variant { field1: value } => value,
760        }
761    }
762}"#,
763            r#"
764enum A {
765    Variant(usize),
766}
767
768impl A {
769    fn new(value: usize) -> A {
770        Self::Variant(value)
771    }
772
773    fn new_with_default() -> A {
774        Self::new(Default::default())
775    }
776
777    fn value(self) -> usize {
778        match self {
779            Self::Variant(value) => value,
780        }
781    }
782}"#,
783        );
784    }
785
786    #[test]
787    fn convert_destructured_variant() {
788        check_assist(
789            convert_named_struct_to_tuple_struct,
790            r#"
791enum A {
792    $0Variant { field1: usize },
793}
794
795impl A {
796    fn into_inner(self) -> usize {
797        let A::Variant { field1: first } = self;
798        first
799    }
800
801    fn into_inner_via_self(self) -> usize {
802        let Self::Variant { field1: first } = self;
803        first
804    }
805}"#,
806            r#"
807enum A {
808    Variant(usize),
809}
810
811impl A {
812    fn into_inner(self) -> usize {
813        let A::Variant(first) = self;
814        first
815    }
816
817    fn into_inner_via_self(self) -> usize {
818        let Self::Variant(first) = self;
819        first
820    }
821}"#,
822        );
823    }
824
825    #[test]
826    fn convert_variant_with_wrapped_references() {
827        check_assist(
828            convert_named_struct_to_tuple_struct,
829            r#"
830enum Inner {
831    $0Variant { field1: usize },
832}
833enum Outer {
834    Variant(Inner),
835}
836
837impl Outer {
838    fn new() -> Self {
839        Self::Variant(Inner::Variant { field1: 42 })
840    }
841
842    fn into_inner_destructed(self) -> u32 {
843        let Outer::Variant(Inner::Variant { field1: x }) = self;
844        x
845    }
846}"#,
847            r#"
848enum Inner {
849    Variant(usize),
850}
851enum Outer {
852    Variant(Inner),
853}
854
855impl Outer {
856    fn new() -> Self {
857        Self::Variant(Inner::Variant(42))
858    }
859
860    fn into_inner_destructed(self) -> u32 {
861        let Outer::Variant(Inner::Variant(x)) = self;
862        x
863    }
864}"#,
865        );
866
867        check_assist(
868            convert_named_struct_to_tuple_struct,
869            r#"
870enum Inner {
871    Variant(usize),
872}
873enum Outer {
874    $0Variant { field1: Inner },
875}
876
877impl Outer {
878    fn new() -> Self {
879        Self::Variant { field1: Inner::Variant(42) }
880    }
881
882    fn into_inner_destructed(self) -> u32 {
883        let Outer::Variant { field1: Inner::Variant(x) } = self;
884        x
885    }
886}"#,
887            r#"
888enum Inner {
889    Variant(usize),
890}
891enum Outer {
892    Variant(Inner),
893}
894
895impl Outer {
896    fn new() -> Self {
897        Self::Variant(Inner::Variant(42))
898    }
899
900    fn into_inner_destructed(self) -> u32 {
901        let Outer::Variant(Inner::Variant(x)) = self;
902        x
903    }
904}"#,
905        );
906    }
907
908    #[test]
909    fn convert_variant_with_multi_file_references() {
910        check_assist(
911            convert_named_struct_to_tuple_struct,
912            r#"
913//- /main.rs
914struct Inner;
915enum A {
916    $0Variant { field1: Inner },
917}
918
919mod foo;
920
921//- /foo.rs
922use crate::{A, Inner};
923fn f() {
924    let a = A::Variant { field1: Inner };
925}
926"#,
927            r#"
928//- /main.rs
929struct Inner;
930enum A {
931    Variant(Inner),
932}
933
934mod foo;
935
936//- /foo.rs
937use crate::{A, Inner};
938fn f() {
939    let a = A::Variant(Inner);
940}
941"#,
942        );
943    }
944
945    #[test]
946    fn convert_directly_used_variant() {
947        check_assist(
948            convert_named_struct_to_tuple_struct,
949            r#"
950//- /main.rs
951struct Inner;
952enum A {
953    $0Variant { field1: Inner },
954}
955
956mod foo;
957
958//- /foo.rs
959use crate::{A::Variant, Inner};
960fn f() {
961    let a = Variant { field1: Inner };
962}
963"#,
964            r#"
965//- /main.rs
966struct Inner;
967enum A {
968    Variant(Inner),
969}
970
971mod foo;
972
973//- /foo.rs
974use crate::{A::Variant, Inner};
975fn f() {
976    let a = Variant(Inner);
977}
978"#,
979        );
980    }
981
982    #[test]
983    fn field_access_inside_macro_call() {
984        check_assist(
985            convert_named_struct_to_tuple_struct,
986            r#"
987struct $0Struct {
988    inner: i32,
989}
990
991macro_rules! id {
992    ($e:expr) => { $e }
993}
994
995fn test(c: Struct) {
996    id!(c.inner);
997}
998"#,
999            r#"
1000struct Struct(i32);
1001
1002macro_rules! id {
1003    ($e:expr) => { $e }
1004}
1005
1006fn test(c: Struct) {
1007    id!(c.0);
1008}
1009"#,
1010        )
1011    }
1012
1013    #[test]
1014    fn struct_usage_inside_macro_call() {
1015        check_assist(
1016            convert_named_struct_to_tuple_struct,
1017            r#"
1018macro_rules! id {
1019    ($($t:tt)*) => { $($t)* }
1020}
1021
1022struct $0Struct {
1023    inner: i32,
1024}
1025
1026fn test() {
1027    id! {
1028        let s = Struct {
1029            inner: 42,
1030        };
1031        let Struct { inner: value } = s;
1032        let Struct { inner } = s;
1033    }
1034}
1035"#,
1036            r#"
1037macro_rules! id {
1038    ($($t:tt)*) => { $($t)* }
1039}
1040
1041struct Struct(i32);
1042
1043fn test() {
1044    id! {
1045        let s = Struct(42);
1046        let Struct(value) = s;
1047        let Struct(inner) = s;
1048    }
1049}
1050"#,
1051        );
1052    }
1053
1054    #[test]
1055    fn struct_name_ref_may_not_be_part_of_struct_expr_or_struct_pat() {
1056        check_assist(
1057            convert_named_struct_to_tuple_struct,
1058            r#"
1059struct $0Struct {
1060    inner: i32,
1061}
1062struct Outer<T> {
1063    value: T,
1064}
1065fn foo<T>() -> T { loop {} }
1066
1067fn test() {
1068    Outer {
1069        value: foo::<Struct>();
1070    }
1071}
1072
1073trait HasAssoc {
1074    type Assoc;
1075    fn test();
1076}
1077impl HasAssoc for Struct {
1078    type Assoc = Outer<i32>;
1079    fn test() {
1080        let a = Self::Assoc {
1081            value: 42,
1082        };
1083        let Self::Assoc { value } = a;
1084    }
1085}
1086"#,
1087            r#"
1088struct Struct(i32);
1089struct Outer<T> {
1090    value: T,
1091}
1092fn foo<T>() -> T { loop {} }
1093
1094fn test() {
1095    Outer {
1096        value: foo::<Struct>();
1097    }
1098}
1099
1100trait HasAssoc {
1101    type Assoc;
1102    fn test();
1103}
1104impl HasAssoc for Struct {
1105    type Assoc = Outer<i32>;
1106    fn test() {
1107        let a = Self::Assoc {
1108            value: 42,
1109        };
1110        let Self::Assoc { value } = a;
1111    }
1112}
1113"#,
1114        );
1115    }
1116
1117    #[test]
1118    fn fields_with_attrs() {
1119        check_assist(
1120            convert_named_struct_to_tuple_struct,
1121            r#"
1122pub struct $0Foo {
1123    #[my_custom_attr]
1124    value: u32,
1125}
1126"#,
1127            r#"
1128pub struct Foo(#[my_custom_attr]u32);
1129"#,
1130        );
1131    }
1132}