1 module boilerplate.constructor;
2 
3 import std.algorithm : canFind, map;
4 import std.meta : AliasSeq, allSatisfy, ApplyLeft;
5 import std.range : array;
6 import std.traits : hasElaborateDestructor, isInstanceOf, isNested;
7 import std.typecons : Tuple;
8 
9 version(unittest)
10 {
11     import unit_threaded.should;
12 }
13 
14 /++
15 GenerateThis is a mixin string that automatically generates a this() function, customizable with UDA.
16 +/
17 public enum string GenerateThis = `
18     import boilerplate.constructor :
19         ConstructorField, GenerateThisTemplate, getUDADefaultOrNothing, saveConstructorInfo;
20     import std.string : replace;
21     mixin GenerateThisTemplate;
22     mixin(typeof(this).generateThisImpl());
23 `;
24 
25 ///
26 @("creates a constructor")
27 unittest
28 {
29     class Class
30     {
31         int field;
32 
33         mixin(GenerateThis);
34     }
35 
36     auto obj = new Class(5);
37 
38     obj.field.shouldEqual(5);
39 }
40 
41 ///
42 @("calls the super constructor if it exists")
43 unittest
44 {
45     class Class
46     {
47         int field;
48 
49         mixin(GenerateThis);
50     }
51 
52     class Child : Class
53     {
54         int field2;
55 
56         mixin(GenerateThis);
57     }
58 
59     auto obj = new Child(5, 8);
60 
61     obj.field.shouldEqual(5);
62     obj.field2.shouldEqual(8);
63 }
64 
65 ///
66 @("separates fields from methods")
67 unittest
68 {
69     class Class
70     {
71         int field;
72 
73         void method() { }
74 
75         mixin(GenerateThis);
76     }
77 
78     auto obj = new Class(5);
79 
80     obj.field.shouldEqual(5);
81 }
82 
83 ///
84 @("dups arrays")
85 unittest
86 {
87     class Class
88     {
89         int[] array;
90 
91         mixin(GenerateThis);
92     }
93 
94     auto array = [2, 3, 4];
95     auto obj = new Class(array);
96 
97     array[0] = 1;
98     obj.array[0].shouldEqual(2);
99 }
100 
101 ///
102 @("dups arrays hidden behind Nullable")
103 unittest
104 {
105     import std.typecons : Nullable, nullable;
106 
107     class Class
108     {
109         Nullable!(int[]) array;
110 
111         mixin(GenerateThis);
112     }
113 
114     auto array = [2, 3, 4];
115     auto obj = new Class(array.nullable);
116 
117     array[0] = 1;
118     obj.array.get[0].shouldEqual(2);
119 
120     obj = new Class(Nullable!(int[]).init);
121     obj.array.isNull.shouldBeTrue;
122 }
123 
124 ///
125 @("uses default value for default constructor parameter")
126 unittest
127 {
128     class Class
129     {
130         @(This.Default!5)
131         int value = 5;
132 
133         mixin(GenerateThis);
134     }
135 
136     auto obj1 = new Class();
137 
138     obj1.value.shouldEqual(5);
139 
140     auto obj2 = new Class(6);
141 
142     obj2.value.shouldEqual(6);
143 }
144 
145 ///
146 @("creates no constructor for an empty struct")
147 unittest
148 {
149     struct Struct
150     {
151         mixin(GenerateThis);
152     }
153 
154     auto strct = Struct();
155 }
156 
157 ///
158 @("properly generates new default values on each call")
159 unittest
160 {
161     import std.conv : to;
162 
163     class Class
164     {
165         @(This.Default!(() => new Object))
166         Object obj;
167 
168         mixin(GenerateThis);
169     }
170 
171     auto obj1 = new Class();
172     auto obj2 = new Class();
173 
174     (cast(void*) obj1.obj).shouldNotEqual(cast(void*) obj2.obj);
175 }
176 
177 ///
178 @("establishes the parent-child parameter order: parent explicit, child explicit, child implicit, parent implicit.")
179 unittest
180 {
181     class Parent
182     {
183         int field1;
184 
185         @(This.Default!2)
186         int field2 = 2;
187 
188         mixin(GenerateThis);
189     }
190 
191     class Child : Parent
192     {
193         int field3;
194 
195         @(This.Default!4)
196         int field4 = 4;
197 
198         mixin(GenerateThis);
199     }
200 
201     auto obj = new Child(1, 2, 3, 4);
202 
203     obj.field1.shouldEqual(1);
204     obj.field3.shouldEqual(2);
205     obj.field4.shouldEqual(3);
206     obj.field2.shouldEqual(4);
207 }
208 
209 ///
210 @("disregards static fields")
211 unittest
212 {
213     class Class
214     {
215         static int field1;
216         int field2;
217 
218         mixin(GenerateThis);
219     }
220 
221     auto obj = new Class(5);
222 
223     obj.field1.shouldEqual(0);
224     obj.field2.shouldEqual(5);
225 }
226 
227 ///
228 @("can initialize with immutable arrays")
229 unittest
230 {
231     class Class
232     {
233         immutable(Object)[] array;
234 
235         mixin(GenerateThis);
236     }
237 }
238 
239 ///
240 @("can define scope for constructor")
241 unittest
242 {
243     @(This.Private)
244     class PrivateClass
245     {
246         mixin(GenerateThis);
247     }
248 
249     @(This.Protected)
250     class ProtectedClass
251     {
252         mixin(GenerateThis);
253     }
254 
255     @(This.Package)
256     class PackageClass
257     {
258         mixin(GenerateThis);
259     }
260 
261     @(This.Package("boilerplate"))
262     class SubPackageClass
263     {
264         mixin(GenerateThis);
265     }
266 
267     class PublicClass
268     {
269         mixin(GenerateThis);
270     }
271 
272     static assert(__traits(getProtection, PrivateClass.__ctor) == "private");
273     static assert(__traits(getProtection, ProtectedClass.__ctor) == "protected");
274     static assert(__traits(getProtection, PackageClass.__ctor) == "package");
275     // getProtection does not return the package name of a package() attribute
276     // static assert(__traits(getProtection, SubPackageClass.__ctor) == `package(boilerplate)`);
277     static assert(__traits(getProtection, PublicClass.__ctor) == "public");
278 }
279 
280 ///
281 @("will assign the same scope to Builder")
282 unittest
283 {
284     @(This.Private)
285     class PrivateClass
286     {
287         mixin(GenerateThis);
288     }
289 
290     @(This.Protected)
291     class ProtectedClass
292     {
293         mixin(GenerateThis);
294     }
295 
296     @(This.Package)
297     class PackageClass
298     {
299         mixin(GenerateThis);
300     }
301 
302     @(This.Package("boilerplate"))
303     class SubPackageClass
304     {
305         mixin(GenerateThis);
306     }
307 
308     class PublicClass
309     {
310         mixin(GenerateThis);
311     }
312 
313     static assert(__traits(getProtection, PrivateClass.Builder) == "private");
314     static assert(__traits(getProtection, ProtectedClass.Builder) == "protected");
315     static assert(__traits(getProtection, PackageClass.Builder) == "package");
316     static assert(__traits(getProtection, PublicClass.Builder) == "public");
317 }
318 
319 ///
320 @("can use default tag with new")
321 unittest
322 {
323     class Class
324     {
325         @(This.Default!(() => new Object))
326         Object foo;
327 
328         mixin(GenerateThis);
329     }
330 
331     ((new Class).foo !is null).shouldBeTrue;
332 }
333 
334 ///
335 @("empty default tag means T()")
336 unittest
337 {
338     class Class
339     {
340         @(This.Default)
341         string s;
342 
343         @(This.Default)
344         int i;
345 
346         mixin(GenerateThis);
347     }
348 
349     (new Class()).i.shouldEqual(0);
350     (new Class()).s.shouldEqual(null);
351 }
352 
353 ///
354 @("can exclude fields from constructor")
355 unittest
356 {
357     class Class
358     {
359         @(This.Exclude)
360         int i = 5;
361 
362         mixin(GenerateThis);
363     }
364 
365     (new Class).i.shouldEqual(5);
366 }
367 
368 ///
369 @("marks duppy parameters as const when this does not prevent dupping")
370 unittest
371 {
372 
373     struct Struct
374     {
375     }
376 
377     class Class
378     {
379         Struct[] values_;
380 
381         mixin(GenerateThis);
382     }
383 
384     const Struct[] constValues;
385     auto obj = new Class(constValues);
386 }
387 
388 ///
389 @("does not include property functions in constructor list")
390 unittest
391 {
392     class Class
393     {
394         int a;
395 
396         @property int foo() const
397         {
398             return 0;
399         }
400 
401         mixin(GenerateThis);
402     }
403 
404     static assert(__traits(compiles, new Class(0)));
405     static assert(!__traits(compiles, new Class(0, 0)));
406 }
407 
408 @("declares @nogc on non-dupping constructors")
409 @nogc unittest
410 {
411     struct Struct
412     {
413         int a;
414 
415         mixin(GenerateThis);
416     }
417 
418     auto str = Struct(5);
419 }
420 
421 ///
422 @("can initialize fields using init value")
423 unittest
424 {
425     class Class
426     {
427         @(This.Init!5)
428         int field1;
429 
430         @(This.Init!(() => 8))
431         int field2;
432 
433         mixin(GenerateThis);
434     }
435 
436     auto obj = new Class;
437 
438     obj.field1.shouldEqual(5);
439     obj.field2.shouldEqual(8);
440 }
441 
442 ///
443 @("can initialize fields using init value, with lambda that accesses previous value")
444 unittest
445 {
446     class Class
447     {
448         int field1;
449 
450         @(This.Init!(self => self.field1 + 5))
451         int field2;
452 
453         mixin(GenerateThis);
454     }
455 
456     auto obj = new Class(5);
457 
458     obj.field1.shouldEqual(5);
459     obj.field2.shouldEqual(10);
460 }
461 
462 ///
463 @("can initialize fields with allocated types")
464 unittest
465 {
466     class Class1
467     {
468         @(This.Init!(self => new Object))
469         Object object;
470 
471         mixin(GenerateThis);
472     }
473 
474     class Class2
475     {
476         @(This.Init!(() => new Object))
477         Object object;
478 
479         mixin(GenerateThis);
480     }
481 
482     class Class3 : Class2
483     {
484         mixin(GenerateThis);
485     }
486 }
487 
488 ///
489 @("generates Builder class that gathers constructor parameters, then calls constructor with them")
490 unittest
491 {
492     static class Class
493     {
494         int field1;
495         int field2;
496         int field3;
497 
498         mixin(GenerateThis);
499     }
500 
501     auto obj = {
502         with (Class.Builder())
503         {
504             field1 = 1;
505             field2 = 2;
506             field3 = 3;
507             return value;
508         }
509     }();
510 
511     with (obj)
512     {
513         field1.shouldEqual(1);
514         field2.shouldEqual(2);
515         field3.shouldEqual(3);
516     }
517 }
518 
519 ///
520 @("builder field order doesn't matter")
521 unittest
522 {
523     static class Class
524     {
525         int field1;
526         int field2;
527         int field3;
528 
529         mixin(GenerateThis);
530     }
531 
532     auto obj = {
533         with (Class.Builder())
534         {
535             field3 = 1;
536             field1 = 2;
537             field2 = 3;
538             return value;
539         }
540     }();
541 
542     with (obj)
543     {
544         field1.shouldEqual(2);
545         field2.shouldEqual(3);
546         field3.shouldEqual(1);
547     }
548 }
549 
550 ///
551 @("default fields can be left out when assigning builder")
552 unittest
553 {
554     static class Class
555     {
556         int field1;
557         @(This.Default!5)
558         int field2;
559         int field3;
560 
561         mixin(GenerateThis);
562     }
563 
564     // constructor is this(field1, field3, field2 = 5)
565     auto obj = {
566         with (Class.Builder())
567         {
568             field1 = 1;
569             field3 = 3;
570             return value;
571         }
572     }();
573 
574     with (obj)
575     {
576         field1.shouldEqual(1);
577         field2.shouldEqual(5);
578         field3.shouldEqual(3);
579     }
580 }
581 
582 ///
583 @("supports Builder in structs")
584 unittest
585 {
586     struct Struct
587     {
588         int field1;
589         int field2;
590         int field3;
591 
592         mixin(GenerateThis);
593     }
594 
595     auto value = {
596         with (Struct.Builder())
597         {
598             field1 = 1;
599             field3 = 3;
600             field2 = 5;
601             return value;
602         }
603     }();
604 
605     static assert(is(typeof(value) == Struct));
606 
607     with (value)
608     {
609         field1.shouldEqual(1);
610         field2.shouldEqual(5);
611         field3.shouldEqual(3);
612     }
613 }
614 
615 ///
616 @("builder strips trailing underlines")
617 unittest
618 {
619     struct Struct
620     {
621         private int a_;
622 
623         mixin(GenerateThis);
624     }
625 
626     auto builder = Struct.Builder();
627 
628     builder.a = 1;
629 
630     auto value = builder.value;
631 
632     value.shouldEqual(Struct(1));
633 }
634 
635 ///
636 @("builder supports nested initialization")
637 unittest
638 {
639     struct Struct1
640     {
641         int a;
642         int b;
643 
644         mixin(GenerateThis);
645     }
646 
647     struct Struct2
648     {
649         int c;
650         Struct1 struct1;
651         int d;
652 
653         mixin(GenerateThis);
654     }
655 
656     auto builder = Struct2.Builder();
657 
658     builder.struct1.a = 1;
659     builder.struct1.b = 2;
660     builder.c = 3;
661     builder.d = 4;
662 
663     auto value = builder.value;
664 
665     static assert(is(typeof(value) == Struct2));
666 
667     with (value)
668     {
669         struct1.a.shouldEqual(1);
670         struct1.b.shouldEqual(2);
671         c.shouldEqual(3);
672         d.shouldEqual(4);
673     }
674 }
675 
676 ///
677 @("builder supports defaults for nested values")
678 unittest
679 {
680     struct Struct1
681     {
682         int a;
683         int b;
684 
685         mixin(GenerateThis);
686     }
687 
688     struct Struct2
689     {
690         int c;
691         @(This.Default!(Struct1(3, 4)))
692         Struct1 struct1;
693         int d;
694 
695         mixin(GenerateThis);
696     }
697 
698     auto builder = Struct2.Builder();
699 
700     builder.c = 1;
701     builder.d = 2;
702 
703     builder.value.shouldEqual(Struct2(1, 2, Struct1(3, 4)));
704 }
705 
706 ///
707 @("builder supports direct value assignment for nested values")
708 unittest
709 {
710     struct Struct1
711     {
712         int a;
713         int b;
714 
715         mixin(GenerateThis);
716     }
717 
718     struct Struct2
719     {
720         int c;
721         Struct1 struct1;
722         int d;
723 
724         mixin(GenerateThis);
725     }
726 
727     auto builder = Struct2.Builder();
728 
729     builder.struct1 = Struct1(2, 3);
730     builder.c = 1;
731     builder.d = 4;
732 
733     builder.value.shouldEqual(Struct2(1, Struct1(2, 3), 4));
734 }
735 
736 ///
737 @("builder supports overriding value assignment with field assignment later")
738 unittest
739 {
740     struct Struct1
741     {
742         int a;
743         int b;
744 
745         mixin(GenerateThis);
746     }
747 
748     struct Struct2
749     {
750         Struct1 struct1;
751 
752         mixin(GenerateThis);
753     }
754 
755     auto builder = Struct2.Builder();
756 
757     builder.struct1 = Struct1(2, 3);
758     builder.struct1.b = 4;
759 
760     builder.value.shouldEqual(Struct2(Struct1(2, 4)));
761 }
762 
763 ///
764 @("builder doesn't try to use BuilderFrom for types where nonconst references would have to be taken")
765 unittest
766 {
767     import core.exception : AssertError;
768 
769     struct Struct1
770     {
771         int a;
772 
773         private Object[] b_;
774 
775         mixin(GenerateThis);
776     }
777 
778     struct Struct2
779     {
780         Struct1 struct1;
781 
782         mixin(GenerateThis);
783     }
784 
785     // this should at least compile, despite the BuilderFrom hack not working with Struct1
786     auto builder = Struct2.Builder();
787 
788     builder.struct1 = Struct1(2, null);
789 
790     void set()
791     {
792         builder.struct1.b = null;
793     }
794 
795     set().shouldThrow!AssertError(
796         "Builder: cannot set sub-field directly since field is already " ~
797         "being initialized by value (and BuilderFrom is unavailable in Struct1)");
798 }
799 
800 ///
801 @("builder refuses overriding field assignment with value assignment")
802 unittest
803 {
804     import core.exception : AssertError;
805 
806     struct Struct1
807     {
808         int a;
809         int b;
810 
811         mixin(GenerateThis);
812     }
813 
814     struct Struct2
815     {
816         Struct1 struct1;
817 
818         mixin(GenerateThis);
819     }
820 
821     auto builder = Struct2.Builder();
822 
823     builder.struct1.b = 4;
824 
825     void set()
826     {
827         builder.struct1 = Struct1(2, 3);
828     }
829     set().shouldThrow!AssertError("Builder: cannot set field by value since a subfield has already been set.");
830 }
831 
832 ///
833 @("builder supports const args")
834 unittest
835 {
836     struct Struct
837     {
838         const int a;
839 
840         mixin(GenerateThis);
841     }
842 
843     with (Struct.Builder())
844     {
845         a = 5;
846 
847         value.shouldEqual(Struct(5));
848     }
849 }
850 
851 ///
852 @("builder supports fields with destructor")
853 unittest
854 {
855     static struct Struct1
856     {
857         ~this() pure @safe @nogc nothrow { }
858     }
859 
860     struct Struct2
861     {
862         Struct1 struct1;
863 
864         mixin(GenerateThis);
865     }
866 
867     with (Struct2.Builder())
868     {
869         struct1 = Struct1();
870 
871         value.shouldEqual(Struct2(Struct1()));
872     }
873 }
874 
875 ///
876 @("builder supports direct assignment to Nullables")
877 unittest
878 {
879     import std.typecons : Nullable, nullable;
880 
881     struct Struct
882     {
883         const Nullable!int a;
884 
885         mixin(GenerateThis);
886     }
887 
888     with (Struct.Builder())
889     {
890         a = 5;
891 
892         value.shouldEqual(Struct(5.nullable));
893     }
894 }
895 
896 ///
897 @("builder with passthrough assignment to Nullable structs")
898 unittest
899 {
900     import std.typecons : Nullable, nullable;
901 
902     struct Struct1
903     {
904         int a;
905 
906         mixin(GenerateThis);
907     }
908 
909     struct Struct2
910     {
911         @(This.Default)
912         Nullable!Struct1 b;
913 
914         mixin(GenerateThis);
915     }
916 
917     with (Struct2.Builder())
918     {
919         value.shouldEqual(Struct2(Nullable!Struct1()));
920 
921         b.a = 5;
922 
923         value.shouldEqual(Struct2(Nullable!Struct1(Struct1(5))));
924     }
925 }
926 
927 ///
928 @("builder with value assignment to Nullable struct field")
929 unittest
930 {
931     import std.typecons : Nullable, nullable;
932 
933     struct Struct1
934     {
935         mixin(GenerateThis);
936     }
937 
938     struct Struct2
939     {
940         @(This.Default)
941         Nullable!Struct1 value;
942 
943         mixin(GenerateThis);
944     }
945 
946     with (Struct2.Builder())
947     {
948         builderValue.shouldEqual(Struct2());
949 
950         value = Struct1();
951 
952         builderValue.shouldEqual(Struct2(Struct1().nullable));
953     }
954 
955     with (Struct2.Builder())
956     {
957         value = Nullable!Struct1();
958 
959         builderValue.shouldEqual(Struct2());
960     }
961 }
962 
963 ///
964 @("builder supports reconstruction from value")
965 unittest
966 {
967     import std.typecons : Nullable, nullable;
968 
969     struct Struct
970     {
971         private int a_;
972 
973         int[] b;
974 
975         mixin(GenerateThis);
976     }
977 
978     const originalValue = Struct(2, [3]);
979 
980     with (originalValue.BuilderFrom())
981     {
982         a = 5;
983 
984         value.shouldEqual(Struct(5, [3]));
985     }
986 }
987 
988 ///
989 @("builder supports struct that already contains a value field")
990 unittest
991 {
992     import std.typecons : Nullable, nullable;
993 
994     struct Struct
995     {
996         private int value_;
997 
998         mixin(GenerateThis);
999     }
1000 
1001     with (Struct.Builder())
1002     {
1003         value = 5;
1004 
1005         builderValue.shouldEqual(Struct(5));
1006     }
1007 }
1008 
1009 ///
1010 @("builder supports struct that contains struct that has @disable(this)")
1011 unittest
1012 {
1013     import std.typecons : Nullable, nullable;
1014 
1015     static struct Inner
1016     {
1017         private int i_;
1018 
1019         @disable this();
1020 
1021         mixin(GenerateThis);
1022     }
1023 
1024     static struct Struct
1025     {
1026         private Inner inner_;
1027 
1028         mixin(GenerateThis);
1029     }
1030 
1031     with (Struct.Builder())
1032     {
1033         inner.i = 3;
1034 
1035         value.shouldEqual(Struct(Inner(3)));
1036     }
1037 }
1038 
1039 @("destructors with code that is unsafe, system or throws exceptions")
1040 {
1041     struct S
1042     {
1043         ~this() { throw new Exception("test"); }
1044     }
1045 
1046     struct T
1047     {
1048         S s;
1049 
1050         mixin(GenerateThis);
1051     }
1052 }
1053 
1054 import std.string : format;
1055 
1056 enum GetSuperTypeAsString_(string member) = format!`typeof(super).ConstructorInfo.FieldInfo.%s.Type`(member);
1057 
1058 enum GetMemberTypeAsString_(string member) = format!`typeof(this.%s)`(member);
1059 
1060 enum SuperDefault_(string member) = format!`typeof(super).ConstructorInfo.FieldInfo.%s.fieldDefault`(member);
1061 
1062 enum MemberDefault_(string member) =
1063     format!`getUDADefaultOrNothing!(typeof(this.%s), __traits(getAttributes, this.%s))`(member, member);
1064 
1065 enum SuperUseDefault_(string member)
1066     = format!(`typeof(super).ConstructorInfo.FieldInfo.%s.useDefault`)(member);
1067 
1068 enum MemberUseDefault_(string member)
1069     = format!(`udaIndex!(This.Default, __traits(getAttributes, this.%s)) != -1`)(member);
1070 
1071 enum SuperAttributes_(string member)
1072     = format!(`typeof(super).ConstructorInfo.FieldInfo.%s.attributes`)(member);
1073 
1074 enum MemberAttributes_(string member)
1075     = format!(`__traits(getAttributes, this.%s)`)(member);
1076 
1077 mixin template GenerateThisTemplate()
1078 {
1079     private static generateThisImpl()
1080     {
1081         if (!__ctfe)
1082         {
1083             return null;
1084         }
1085 
1086         import boilerplate.constructor :
1087             GetMemberTypeAsString_, GetSuperTypeAsString_,
1088             MemberAttributes_, MemberDefault_, MemberUseDefault_,
1089             SuperAttributes_, SuperDefault_, SuperUseDefault_,
1090             This;
1091         import boilerplate.util :
1092             bucketSort, GenNormalMemberTuple, needToDup,
1093             optionallyRemoveTrailingUnderline,
1094             removeTrailingUnderline, reorder, udaIndex;
1095         import std.algorithm : all, canFind, filter, map;
1096         import std.meta : Alias, aliasSeqOf, staticMap;
1097         import std.range : array, drop, empty, iota, zip;
1098         import std.string : endsWith, format, join;
1099         import std.typecons : Nullable;
1100 
1101         mixin GenNormalMemberTuple;
1102 
1103         string result = null;
1104 
1105         string visibility = "public";
1106 
1107         foreach (uda; __traits(getAttributes, typeof(this)))
1108         {
1109             static if (is(typeof(uda) == ThisEnum))
1110             {
1111                 static if (uda == This.Protected)
1112                 {
1113                     visibility = "protected";
1114                 }
1115                 static if (uda == This.Private)
1116                 {
1117                     visibility = "private";
1118                 }
1119             }
1120             else static if (is(uda == This.Package))
1121             {
1122                 visibility = "package";
1123             }
1124             else static if (is(typeof(uda) == This.Package))
1125             {
1126                 visibility = "package(" ~ uda.packageMask ~ ")";
1127             }
1128         }
1129 
1130         string[] constructorAttributes = ["pure", "nothrow", "@safe", "@nogc"];
1131 
1132         static if (is(typeof(typeof(super).ConstructorInfo)))
1133         {
1134             enum argsPassedToSuper = typeof(super).ConstructorInfo.fields.length;
1135             enum members = typeof(super).ConstructorInfo.fields ~ [NormalMemberTuple];
1136             enum string[] CombinedArray(alias SuperPred, alias MemberPred) = ([
1137                 staticMap!(SuperPred, aliasSeqOf!(typeof(super).ConstructorInfo.fields)),
1138                 staticMap!(MemberPred, NormalMemberTuple)
1139             ]);
1140             constructorAttributes = typeof(super).GeneratedConstructorAttributes_;
1141         }
1142         else
1143         {
1144             enum argsPassedToSuper = 0;
1145             static if (NormalMemberTuple.length > 0)
1146             {
1147                 enum members = [NormalMemberTuple];
1148                 enum string[] CombinedArray(alias SuperPred, alias MemberPred) = ([
1149                     staticMap!(MemberPred, NormalMemberTuple)
1150                 ]);
1151             }
1152             else
1153             {
1154                 enum string[] members = null;
1155                 enum string[] CombinedArray(alias SuperPred, alias MemberPred) = null;
1156             }
1157         }
1158 
1159         enum string[] useDefaults = CombinedArray!(SuperUseDefault_, MemberUseDefault_);
1160         enum string[] memberTypes = CombinedArray!(GetSuperTypeAsString_, GetMemberTypeAsString_);
1161         enum string[] defaults = CombinedArray!(SuperDefault_, MemberDefault_);
1162         enum string[] attributes = CombinedArray!(SuperAttributes_, MemberAttributes_);
1163 
1164         string[] fields;
1165         string[] args;
1166         string[] argexprs;
1167         string[] defaultAssignments;
1168         bool[] fieldUseDefault;
1169         string[] fieldDefault;
1170         string[] fieldAttributes;
1171         string[] types;
1172         string[] directInitFields;
1173         int[] directInitIndex;
1174         bool[] directInitUseSelf;
1175 
1176         foreach (i; aliasSeqOf!(members.length.iota))
1177         {
1178             enum member = members[i];
1179 
1180             mixin(`alias Type = ` ~ memberTypes[i] ~ `;`);
1181             mixin(`enum bool useDefault = ` ~ useDefaults[i] ~ `;`);
1182 
1183             bool includeMember = false;
1184 
1185             enum isNullable = is(Type: Nullable!Arg, Arg);
1186 
1187             static if (!isNullable)
1188             {
1189                 bool dupExpr = needToDup!Type;
1190                 bool passExprAsConst = dupExpr && __traits(compiles, const(Type).init.dup);
1191             }
1192             else
1193             {
1194                 // unpack nullable for dup
1195                 bool dupExpr = needToDup!(typeof(Type.init.get));
1196                 bool passExprAsConst = dupExpr && __traits(compiles, Type(const(Type).init.get.dup));
1197             }
1198 
1199             // account for unsafe implicit destructor calls
1200             enum scopeAttributes = [__traits(getFunctionAttributes, { Type value = Type.init; })];
1201             constructorAttributes = constructorAttributes.filter!(a => scopeAttributes.canFind(a)).array;
1202 
1203             bool forSuper = false;
1204 
1205             static if (i < argsPassedToSuper)
1206             {
1207                 includeMember = true;
1208                 forSuper = true;
1209             }
1210             else
1211             {
1212                 mixin("alias symbol = typeof(this)." ~ member ~ ";");
1213 
1214                 static assert (is(typeof(symbol)) && !__traits(isTemplate, symbol)); /* must have a resolvable type */
1215 
1216                 import boilerplate.util: isStatic;
1217 
1218                 includeMember = !mixin(isStatic(member));
1219 
1220                 static if (udaIndex!(This.Init, __traits(getAttributes, symbol)) != -1)
1221                 {
1222                     enum udaFieldIndex = udaIndex!(This.Init, __traits(getAttributes, symbol));
1223                     alias initArg = Alias!(__traits(getAttributes, symbol)[udaFieldIndex].value);
1224                     enum lambdaWithSelf = __traits(compiles, initArg(typeof(this).init));
1225                     enum nakedLambda = __traits(compiles, initArg());
1226 
1227                     directInitFields ~= member;
1228                     directInitIndex ~= udaFieldIndex;
1229                     directInitUseSelf ~= __traits(compiles,
1230                         __traits(getAttributes, symbol)[udaFieldIndex].value(typeof(this).init));
1231                     includeMember = false;
1232 
1233                     static if (lambdaWithSelf)
1234                     {
1235                         static if (__traits(compiles, initArg!(typeof(this))))
1236                         {
1237                             enum lambdaAttributes = [__traits(getFunctionAttributes, initArg!(typeof(this)))];
1238                         }
1239                         else
1240                         {
1241                             enum lambdaAttributes = [__traits(getFunctionAttributes, initArg)];
1242                         }
1243 
1244                         constructorAttributes = constructorAttributes.filter!(a => lambdaAttributes.canFind(a)).array;
1245                     }
1246                     else static if (nakedLambda)
1247                     {
1248                         enum lambdaAttributes = [__traits(getFunctionAttributes, initArg)];
1249 
1250                         constructorAttributes = constructorAttributes.filter!(a => lambdaAttributes.canFind(a)).array;
1251                     }
1252                 }
1253 
1254                 static if (udaIndex!(This.Exclude, __traits(getAttributes, symbol)) != -1)
1255                 {
1256                     includeMember = false;
1257                 }
1258             }
1259 
1260             if (!includeMember) continue;
1261 
1262             enum paramName = optionallyRemoveTrailingUnderline!member;
1263 
1264             string argexpr = paramName;
1265 
1266             if (dupExpr)
1267             {
1268                 constructorAttributes = constructorAttributes.filter!(a => a != "@nogc").array;
1269 
1270                 static if (isNullable)
1271                 {
1272                     argexpr = format!`%s.isNull ? %s.init : %s(%s.get.dup)`
1273                         (argexpr, memberTypes[i], memberTypes[i], argexpr);
1274                 }
1275                 else
1276                 {
1277                     argexpr = format!`%s.dup`(argexpr);
1278                 }
1279             }
1280 
1281             fields ~= member;
1282             args ~= paramName;
1283             argexprs ~= argexpr;
1284             fieldUseDefault ~= useDefault;
1285             fieldDefault ~= defaults[i];
1286             fieldAttributes ~= attributes[i];
1287             defaultAssignments ~= useDefault ? (` = ` ~ defaults[i]) : ``;
1288             types ~= passExprAsConst ? (`const ` ~ memberTypes[i]) : memberTypes[i];
1289         }
1290 
1291         size_t establishParameterRank(size_t i)
1292         {
1293             // parent explicit, our explicit, our implicit, parent implicit
1294             const fieldOfParent = i < argsPassedToSuper;
1295             return fieldUseDefault[i] * 2 + (fieldUseDefault[i] == fieldOfParent);
1296         }
1297 
1298         auto constructorFieldOrder = fields.length.iota.array.bucketSort(&establishParameterRank);
1299 
1300         assert(fields.length == types.length);
1301         assert(fields.length == fieldUseDefault.length);
1302         assert(fields.length == fieldDefault.length);
1303 
1304         result ~= format!`
1305             public static alias ConstructorInfo =
1306                 saveConstructorInfo!(%s, %-(%s, %));`
1307         (
1308             fields.reorder(constructorFieldOrder),
1309             zip(
1310                 types.reorder(constructorFieldOrder),
1311                 fieldUseDefault.reorder(constructorFieldOrder),
1312                 fieldDefault.reorder(constructorFieldOrder),
1313                 fieldAttributes.reorder(constructorFieldOrder),
1314             )
1315             .map!(args => format!`ConstructorField!(%s, %s, %s, %s)`(args[0], args[1], args[2], args[3]))
1316             .array
1317         );
1318 
1319         // don't emit this(a = b, c = d) for structs -
1320         // the compiler complains that it collides with this(), which is reserved.
1321         if (is(typeof(this) == struct) && fieldUseDefault.all)
1322         {
1323             // If there are fields, their direct-construction types may diverge from ours
1324             // specifically, see the "struct with only default fields" test below
1325             if (!fields.empty)
1326             {
1327                 result ~= `static assert(
1328                     is(typeof(this.tupleof) == ConstructorInfo.Types),
1329                     "Structs with fields, that are all default, cannot use GenerateThis when their " ~
1330                     "constructor types would diverge from their native types: " ~
1331                     typeof(this).stringof ~ ".this" ~ typeof(this.tupleof).stringof ~ ", " ~
1332                     "but generated constructor would have been " ~ typeof(this).stringof ~ ".this"
1333                     ~ ConstructorInfo.Types.stringof
1334                 );`;
1335             }
1336         }
1337         else
1338         {
1339             result ~= visibility ~ ` this(`
1340                 ~ constructorFieldOrder
1341                     .map!(i => format!`%s %s%s`(types[i], args[i], defaultAssignments[i]))
1342                     .join(`, `)
1343                 ~ format!`) %-(%s %)`(constructorAttributes);
1344 
1345             result ~= `{`;
1346 
1347             static if (is(typeof(typeof(super).ConstructorInfo)))
1348             {
1349                 result ~= `super(` ~ args[0 .. argsPassedToSuper].join(", ") ~ `);`;
1350             }
1351 
1352             result ~= fields.length.iota.drop(argsPassedToSuper)
1353                 .map!(i => format!`this.%s = %s;`(fields[i], argexprs[i]))
1354                 .join;
1355 
1356             foreach (i, field; directInitFields)
1357             {
1358                 if (directInitUseSelf[i])
1359                 {
1360                     result ~= format!`this.%s = __traits(getAttributes, this.%s)[%s].value(this);`
1361                         (field, field, directInitIndex[i]);
1362                 }
1363                 else
1364                 {
1365                     result ~= format!`this.%s = __traits(getAttributes, this.%s)[%s].value;`
1366                         (field, field, directInitIndex[i]);
1367                 }
1368             }
1369 
1370             result ~= `}`;
1371 
1372             result ~= `protected static enum string[] GeneratedConstructorAttributes_ = [`
1373                 ~ constructorAttributes.map!(a => `"` ~ a ~ `"`).join(`, `)
1374                 ~ `];`;
1375         }
1376 
1377         result ~= visibility ~ ` static struct BuilderType(alias T = typeof(this))
1378         {
1379             import boilerplate.builder : BuilderImpl;
1380 
1381             mixin BuilderImpl!T;
1382         }`;
1383 
1384         result ~= visibility ~ ` static auto Builder()()
1385         {
1386             return BuilderType!()();
1387         }`;
1388 
1389         result ~= visibility ~ ` auto BuilderFrom()() const
1390         {
1391             import boilerplate.util : removeTrailingUnderline;
1392 
1393             auto builder = BuilderType!()();
1394 
1395             static foreach (field; ConstructorInfo.fields)
1396             {
1397                 mixin("builder." ~ field.removeTrailingUnderline ~ " = this." ~ field ~ ";");
1398             }
1399             return builder;
1400         }`;
1401 
1402         return result;
1403     }
1404 }
1405 
1406 public template ConstructorField(Type_, bool useDefault_, alias fieldDefault_, attributes_...)
1407 {
1408     public alias Type = Type_;
1409     public enum useDefault = useDefault_;
1410     public alias fieldDefault = fieldDefault_;
1411     public alias attributes = attributes_;
1412 }
1413 
1414 public template saveConstructorInfo(string[] fields_, Fields...)
1415 if (fields_.length == Fields.length
1416     && allSatisfy!(ApplyLeft!(isInstanceOf, ConstructorField), Fields))
1417 {
1418     import std.format : format;
1419 
1420     public enum fields = fields_;
1421 
1422     private template FieldInfo_() {
1423         static foreach (i, field; fields)
1424         {
1425             mixin(format!q{public alias %s = Fields[%s];}(field, i));
1426         }
1427     }
1428 
1429     public alias FieldInfo = FieldInfo_!();
1430 
1431     mixin(
1432         format!q{public alias Types = AliasSeq!(%-(%s, %)); }
1433         (fields.map!(field => format!"FieldInfo.%s.Type"(field)).array));
1434 }
1435 
1436 enum ThisEnum
1437 {
1438     Private,
1439     Protected,
1440     Exclude
1441 }
1442 
1443 struct This
1444 {
1445     enum Private = ThisEnum.Private;
1446     enum Protected = ThisEnum.Protected;
1447     struct Package
1448     {
1449         string packageMask = null;
1450     }
1451     enum Exclude = ThisEnum.Exclude;
1452 
1453     // construct with value
1454     static struct Init(alias Alias)
1455     {
1456         static if (__traits(compiles, Alias()))
1457         {
1458             @property static auto value() { return Alias(); }
1459         }
1460         else
1461         {
1462             alias value = Alias;
1463         }
1464     }
1465 
1466     static struct Default(alias Alias)
1467     {
1468         static if (__traits(compiles, Alias()))
1469         {
1470             @property static auto value() { return Alias(); }
1471         }
1472         else
1473         {
1474             alias value = Alias;
1475         }
1476     }
1477 }
1478 
1479 public template getUDADefaultOrNothing(T, attributes...)
1480 {
1481     import boilerplate.util : udaIndex;
1482 
1483     template EnumTest()
1484     {
1485         enum EnumTest = attributes[udaIndex!(This.Default, attributes)].value;
1486     }
1487 
1488     static if (udaIndex!(This.Default, attributes) == -1)
1489     {
1490         enum getUDADefaultOrNothing = 0;
1491     }
1492     // @(This.Default)
1493     else static if (__traits(isSame, attributes[udaIndex!(This.Default, attributes)], This.Default))
1494     {
1495         enum getUDADefaultOrNothing = T.init;
1496     }
1497     else static if (__traits(compiles, EnumTest!()))
1498     {
1499         enum getUDADefaultOrNothing = attributes[udaIndex!(This.Default, attributes)].value;
1500     }
1501     else
1502     {
1503         @property static auto getUDADefaultOrNothing()
1504         {
1505             return attributes[udaIndex!(This.Default, attributes)].value;
1506         }
1507     }
1508 }
1509 
1510 @("struct with only default fields cannot use GenerateThis unless the default this() type matches the generated one")
1511 unittest
1512 {
1513     static assert(!__traits(compiles, {
1514         struct Foo
1515         {
1516             @(This.Default)
1517             int[] array;
1518 
1519             mixin(GenerateThis);
1520         }
1521 
1522         // because you would be able to do
1523         // const array = [2];
1524         // auto foo = Foo(array);
1525         // which would be an error, but work with a generated constructor
1526         // however, no constructor could be generated, as it would collide with this()
1527     }));
1528 
1529     // This works though.
1530     struct Bar
1531     {
1532         @(This.Default)
1533         const int[] array;
1534 
1535         mixin(GenerateThis);
1536     }
1537 
1538     const array = [2];
1539     auto bar = Bar(array);
1540 }
1541 
1542 @("very large types can be used")
1543 unittest
1544 {
1545     import std.format : format;
1546     import std.range : iota;
1547 
1548     struct VeryLargeType
1549     {
1550         static foreach (i; 500.iota)
1551         {
1552             mixin(format!"int v%s;"(i));
1553         }
1554 
1555         mixin(GenerateThis);
1556     }
1557 
1558     struct Wrapper
1559     {
1560         VeryLargeType field;
1561 
1562         mixin(GenerateThis);
1563     }
1564 
1565     auto builder = Wrapper.Builder();
1566 }