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
13pub(crate) fn convert_named_struct_to_tuple_struct(
54 acc: &mut Assists,
55 ctx: &AssistContext<'_>,
56) -> Option<()> {
57 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 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 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 let name_ref = r.name.as_name_ref()?;
179 let path_segment = name_ref.syntax().parent().and_then(ast::PathSegment::cast)?;
180 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 return None;
188 }
189
190 let parent = full_path.syntax().parent()?;
192 match_ast! {
193 match parent {
194 ast::RecordPat(record_struct_pat) => {
195 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 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 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}