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 supports reconstruction from value")
898 unittest
899 {
900     import std.typecons : Nullable, nullable;
901 
902     struct Struct
903     {
904         private int a_;
905 
906         int[] b;
907 
908         mixin(GenerateThis);
909     }
910 
911     const originalValue = Struct(2, [3]);
912 
913     with (originalValue.BuilderFrom())
914     {
915         a = 5;
916 
917         value.shouldEqual(Struct(5, [3]));
918     }
919 }
920 
921 ///
922 @("builder supports struct that already contains a value field")
923 unittest
924 {
925     import std.typecons : Nullable, nullable;
926 
927     struct Struct
928     {
929         private int value_;
930 
931         mixin(GenerateThis);
932     }
933 
934     with (Struct.Builder())
935     {
936         value = 5;
937 
938         builderValue.shouldEqual(Struct(5));
939     }
940 }
941 
942 ///
943 @("builder supports struct that contains struct that has @disable(this)")
944 unittest
945 {
946     import std.typecons : Nullable, nullable;
947 
948     static struct Inner
949     {
950         private int i_;
951 
952         @disable this();
953 
954         mixin(GenerateThis);
955     }
956 
957     static struct Struct
958     {
959         private Inner inner_;
960 
961         mixin(GenerateThis);
962     }
963 
964     with (Struct.Builder())
965     {
966         inner.i = 3;
967 
968         value.shouldEqual(Struct(Inner(3)));
969     }
970 }
971 
972 import std.string : format;
973 
974 enum GetSuperTypeAsString_(string member) = format!`typeof(super).ConstructorInfo.FieldInfo.%s.Type`(member);
975 
976 enum GetMemberTypeAsString_(string member) = format!`typeof(this.%s)`(member);
977 
978 enum SuperDefault_(string member) = format!`typeof(super).ConstructorInfo.FieldInfo.%s.fieldDefault`(member);
979 
980 enum MemberDefault_(string member) =
981     format!`getUDADefaultOrNothing!(typeof(this.%s), __traits(getAttributes, this.%s))`(member, member);
982 
983 enum SuperUseDefault_(string member)
984     = format!(`typeof(super).ConstructorInfo.FieldInfo.%s.useDefault`)(member);
985 
986 enum MemberUseDefault_(string member)
987     = format!(`udaIndex!(This.Default, __traits(getAttributes, this.%s)) != -1`)(member);
988 
989 enum SuperAttributes_(string member)
990     = format!(`typeof(super).ConstructorInfo.FieldInfo.%s.attributes`)(member);
991 
992 enum MemberAttributes_(string member)
993     = format!(`__traits(getAttributes, this.%s)`)(member);
994 
995 mixin template GenerateThisTemplate()
996 {
997     private static generateThisImpl()
998     {
999         if (!__ctfe)
1000         {
1001             return null;
1002         }
1003 
1004         import boilerplate.constructor :
1005             GetMemberTypeAsString_, GetSuperTypeAsString_,
1006             MemberAttributes_, MemberDefault_, MemberUseDefault_,
1007             SuperAttributes_, SuperDefault_, SuperUseDefault_,
1008             This;
1009         import boilerplate.util :
1010             bucketSort, GenNormalMemberTuple, needToDup,
1011             removeTrailingUnderline, reorder, udaIndex;
1012         import std.algorithm : all, canFind, filter, map;
1013         import std.meta : Alias, aliasSeqOf, staticMap;
1014         import std.range : array, drop, empty, iota, zip;
1015         import std.string : endsWith, format, join;
1016         import std.typecons : Nullable;
1017 
1018         mixin GenNormalMemberTuple;
1019 
1020         string result = null;
1021 
1022         string visibility = "public";
1023 
1024         foreach (uda; __traits(getAttributes, typeof(this)))
1025         {
1026             static if (is(typeof(uda) == ThisEnum))
1027             {
1028                 static if (uda == This.Protected)
1029                 {
1030                     visibility = "protected";
1031                 }
1032                 static if (uda == This.Private)
1033                 {
1034                     visibility = "private";
1035                 }
1036             }
1037             else static if (is(uda == This.Package))
1038             {
1039                 visibility = "package";
1040             }
1041             else static if (is(typeof(uda) == This.Package))
1042             {
1043                 visibility = "package(" ~ uda.packageMask ~ ")";
1044             }
1045         }
1046 
1047         string[] constructorAttributes = ["pure", "nothrow", "@safe", "@nogc"];
1048 
1049         static if (is(typeof(typeof(super).ConstructorInfo)))
1050         {
1051             enum argsPassedToSuper = typeof(super).ConstructorInfo.fields.length;
1052             enum members = typeof(super).ConstructorInfo.fields ~ [NormalMemberTuple];
1053             enum string[] CombinedArray(alias SuperPred, alias MemberPred) = ([
1054                 staticMap!(SuperPred, aliasSeqOf!(typeof(super).ConstructorInfo.fields)),
1055                 staticMap!(MemberPred, NormalMemberTuple)
1056             ]);
1057             constructorAttributes = typeof(super).GeneratedConstructorAttributes_;
1058         }
1059         else
1060         {
1061             enum argsPassedToSuper = 0;
1062             static if (NormalMemberTuple.length > 0)
1063             {
1064                 enum members = [NormalMemberTuple];
1065                 enum string[] CombinedArray(alias SuperPred, alias MemberPred) = ([
1066                     staticMap!(MemberPred, NormalMemberTuple)
1067                 ]);
1068             }
1069             else
1070             {
1071                 enum string[] members = null;
1072                 enum string[] CombinedArray(alias SuperPred, alias MemberPred) = null;
1073             }
1074         }
1075 
1076         enum string[] useDefaults = CombinedArray!(SuperUseDefault_, MemberUseDefault_);
1077         enum string[] memberTypes = CombinedArray!(GetSuperTypeAsString_, GetMemberTypeAsString_);
1078         enum string[] defaults = CombinedArray!(SuperDefault_, MemberDefault_);
1079         enum string[] attributes = CombinedArray!(SuperAttributes_, MemberAttributes_);
1080 
1081         string[] fields;
1082         string[] args;
1083         string[] argexprs;
1084         string[] defaultAssignments;
1085         bool[] fieldUseDefault;
1086         string[] fieldDefault;
1087         string[] fieldAttributes;
1088         string[] types;
1089         string[] directInitFields;
1090         int[] directInitIndex;
1091         bool[] directInitUseSelf;
1092 
1093         foreach (i; aliasSeqOf!(members.length.iota))
1094         {
1095             enum member = members[i];
1096 
1097             mixin(`alias Type = ` ~ memberTypes[i] ~ `;`);
1098             mixin(`enum bool useDefault = ` ~ useDefaults[i] ~ `;`);
1099 
1100             bool includeMember = false;
1101 
1102             enum isNullable = is(Type: Nullable!Arg, Arg);
1103 
1104             static if (!isNullable)
1105             {
1106                 bool dupExpr = needToDup!Type;
1107                 bool passExprAsConst = dupExpr && __traits(compiles, const(Type).init.dup);
1108             }
1109             else
1110             {
1111                 // unpack nullable for dup
1112                 bool dupExpr = needToDup!(typeof(Type.init.get));
1113                 bool passExprAsConst = dupExpr && __traits(compiles, Type(const(Type).init.get.dup));
1114             }
1115 
1116             bool forSuper = false;
1117 
1118             static if (i < argsPassedToSuper)
1119             {
1120                 includeMember = true;
1121                 forSuper = true;
1122             }
1123             else
1124             {
1125                 mixin("alias symbol = typeof(this)." ~ member ~ ";");
1126 
1127                 static assert (is(typeof(symbol)) && !__traits(isTemplate, symbol)); /* must have a resolvable type */
1128 
1129                 import boilerplate.util: isStatic;
1130 
1131                 includeMember = !mixin(isStatic(member));
1132 
1133                 static if (udaIndex!(This.Init, __traits(getAttributes, symbol)) != -1)
1134                 {
1135                     enum udaFieldIndex = udaIndex!(This.Init, __traits(getAttributes, symbol));
1136                     alias initArg = Alias!(__traits(getAttributes, symbol)[udaFieldIndex].value);
1137                     enum lambdaWithSelf = __traits(compiles, initArg(typeof(this).init));
1138                     enum nakedLambda = __traits(compiles, initArg());
1139 
1140                     directInitFields ~= member;
1141                     directInitIndex ~= udaFieldIndex;
1142                     directInitUseSelf ~= __traits(compiles,
1143                         __traits(getAttributes, symbol)[udaFieldIndex].value(typeof(this).init));
1144                     includeMember = false;
1145 
1146                     static if (lambdaWithSelf)
1147                     {
1148                         static if (__traits(compiles, initArg!(typeof(this))))
1149                         {
1150                             enum lambdaAttributes = [__traits(getFunctionAttributes, initArg!(typeof(this)))];
1151                         }
1152                         else
1153                         {
1154                             enum lambdaAttributes = [__traits(getFunctionAttributes, initArg)];
1155                         }
1156 
1157                         constructorAttributes = constructorAttributes.filter!(a => lambdaAttributes.canFind(a)).array;
1158                     }
1159                     else static if (nakedLambda)
1160                     {
1161                         enum lambdaAttributes = [__traits(getFunctionAttributes, initArg)];
1162 
1163                         constructorAttributes = constructorAttributes.filter!(a => lambdaAttributes.canFind(a)).array;
1164                     }
1165                 }
1166 
1167                 static if (udaIndex!(This.Exclude, __traits(getAttributes, symbol)) != -1)
1168                 {
1169                     includeMember = false;
1170                 }
1171             }
1172 
1173             if (!includeMember) continue;
1174 
1175             enum paramName = member.removeTrailingUnderline;
1176 
1177             string argexpr = paramName;
1178 
1179             if (dupExpr)
1180             {
1181                 constructorAttributes = constructorAttributes.filter!(a => a != "@nogc").array;
1182 
1183                 static if (isNullable)
1184                 {
1185                     argexpr = format!`%s.isNull ? %s.init : %s(%s.get.dup)`
1186                         (argexpr, memberTypes[i], memberTypes[i], argexpr);
1187                 }
1188                 else
1189                 {
1190                     argexpr = format!`%s.dup`(argexpr);
1191                 }
1192             }
1193 
1194             fields ~= member;
1195             args ~= paramName;
1196             argexprs ~= argexpr;
1197             fieldUseDefault ~= useDefault;
1198             fieldDefault ~= defaults[i];
1199             fieldAttributes ~= attributes[i];
1200             defaultAssignments ~= useDefault ? (` = ` ~ defaults[i]) : ``;
1201             types ~= passExprAsConst ? (`const ` ~ memberTypes[i]) : memberTypes[i];
1202         }
1203 
1204         size_t establishParameterRank(size_t i)
1205         {
1206             // parent explicit, our explicit, our implicit, parent implicit
1207             const fieldOfParent = i < argsPassedToSuper;
1208             return fieldUseDefault[i] * 2 + (fieldUseDefault[i] == fieldOfParent);
1209         }
1210 
1211         auto constructorFieldOrder = fields.length.iota.array.bucketSort(&establishParameterRank);
1212 
1213         assert(fields.length == types.length);
1214         assert(fields.length == fieldUseDefault.length);
1215         assert(fields.length == fieldDefault.length);
1216 
1217         result ~= format!`
1218             public static alias ConstructorInfo =
1219                 saveConstructorInfo!(%s, %-(%s, %));`
1220         (
1221             fields.reorder(constructorFieldOrder),
1222             zip(
1223                 types.reorder(constructorFieldOrder),
1224                 fieldUseDefault.reorder(constructorFieldOrder),
1225                 fieldDefault.reorder(constructorFieldOrder),
1226                 fieldAttributes.reorder(constructorFieldOrder),
1227             )
1228             .map!(args => format!`ConstructorField!(%s, %s, %s, %s)`(args[0], args[1], args[2], args[3]))
1229             .array
1230         );
1231 
1232         // don't emit this(a = b, c = d) for structs -
1233         // the compiler complains that it collides with this(), which is reserved.
1234         if (is(typeof(this) == struct) && fieldUseDefault.all)
1235         {
1236             // If there are fields, their direct-construction types may diverge from ours
1237             // specifically, see the "struct with only default fields" test below
1238             if (!fields.empty)
1239             {
1240                 result ~= `static assert(
1241                     is(typeof(this.tupleof) == ConstructorInfo.Types),
1242                     "Structs with fields, that are all default, cannot use GenerateThis when their " ~
1243                     "constructor types would diverge from their native types: " ~
1244                     typeof(this).stringof ~ ".this" ~ typeof(this.tupleof).stringof ~ ", " ~
1245                     "but generated constructor would have been " ~ typeof(this).stringof ~ ".this"
1246                     ~ ConstructorInfo.Types.stringof
1247                 );`;
1248             }
1249         }
1250         else
1251         {
1252             result ~= visibility ~ ` this(`
1253                 ~ constructorFieldOrder
1254                     .map!(i => format!`%s %s%s`(types[i], args[i], defaultAssignments[i]))
1255                     .join(`, `)
1256                 ~ format!`) %-(%s %)`(constructorAttributes);
1257 
1258             result ~= `{`;
1259 
1260             static if (is(typeof(typeof(super).ConstructorInfo)))
1261             {
1262                 result ~= `super(` ~ args[0 .. argsPassedToSuper].join(", ") ~ `);`;
1263             }
1264 
1265             result ~= fields.length.iota.drop(argsPassedToSuper)
1266                 .map!(i => format!`this.%s = %s;`(fields[i], argexprs[i]))
1267                 .join;
1268 
1269             foreach (i, field; directInitFields)
1270             {
1271                 if (directInitUseSelf[i])
1272                 {
1273                     result ~= format!`this.%s = __traits(getAttributes, this.%s)[%s].value(this);`
1274                         (field, field, directInitIndex[i]);
1275                 }
1276                 else
1277                 {
1278                     result ~= format!`this.%s = __traits(getAttributes, this.%s)[%s].value;`
1279                         (field, field, directInitIndex[i]);
1280                 }
1281             }
1282 
1283             result ~= `}`;
1284 
1285             result ~= `protected static enum string[] GeneratedConstructorAttributes_ = [`
1286                 ~ constructorAttributes.map!(a => `"` ~ a ~ `"`).join(`, `)
1287                 ~ `];`;
1288         }
1289 
1290         result ~= visibility ~ ` static struct BuilderType(alias T = typeof(this))
1291         {
1292             import boilerplate.builder : BuilderImpl;
1293 
1294             mixin BuilderImpl!T;
1295         }`;
1296 
1297         result ~= visibility ~ ` static auto Builder()()
1298         {
1299             return BuilderType!()();
1300         }`;
1301 
1302         result ~= visibility ~ ` auto BuilderFrom()() const
1303         {
1304             import boilerplate.util : removeTrailingUnderline;
1305 
1306             auto builder = BuilderType!()();
1307 
1308             static foreach (field; ConstructorInfo.fields)
1309             {
1310                 mixin("builder." ~ field.removeTrailingUnderline ~ " = this." ~ field ~ ";");
1311             }
1312             return builder;
1313         }`;
1314 
1315         return result;
1316     }
1317 }
1318 
1319 public template ConstructorField(Type_, bool useDefault_, alias fieldDefault_, attributes_...)
1320 {
1321     public alias Type = Type_;
1322     public enum useDefault = useDefault_;
1323     public alias fieldDefault = fieldDefault_;
1324     public alias attributes = attributes_;
1325 }
1326 
1327 public template saveConstructorInfo(string[] fields_, Fields...)
1328 if (fields_.length == Fields.length
1329     && allSatisfy!(ApplyLeft!(isInstanceOf, ConstructorField), Fields))
1330 {
1331     import std.format : format;
1332 
1333     public enum fields = fields_;
1334 
1335     private template FieldInfo_() {
1336         static foreach (i, field; fields)
1337         {
1338             mixin(format!q{public alias %s = Fields[%s];}(field, i));
1339         }
1340     }
1341 
1342     public alias FieldInfo = FieldInfo_!();
1343 
1344     mixin(
1345         format!q{public alias Types = AliasSeq!(%-(%s, %)); }
1346         (fields.map!(field => format!"FieldInfo.%s.Type"(field)).array));
1347 }
1348 
1349 enum ThisEnum
1350 {
1351     Private,
1352     Protected,
1353     Exclude
1354 }
1355 
1356 struct This
1357 {
1358     enum Private = ThisEnum.Private;
1359     enum Protected = ThisEnum.Protected;
1360     struct Package
1361     {
1362         string packageMask = null;
1363     }
1364     enum Exclude = ThisEnum.Exclude;
1365 
1366     // construct with value
1367     static struct Init(alias Alias)
1368     {
1369         static if (__traits(compiles, Alias()))
1370         {
1371             @property static auto value() { return Alias(); }
1372         }
1373         else
1374         {
1375             alias value = Alias;
1376         }
1377     }
1378 
1379     static struct Default(alias Alias)
1380     {
1381         static if (__traits(compiles, Alias()))
1382         {
1383             @property static auto value() { return Alias(); }
1384         }
1385         else
1386         {
1387             alias value = Alias;
1388         }
1389     }
1390 }
1391 
1392 public template getUDADefaultOrNothing(T, attributes...)
1393 {
1394     import boilerplate.util : udaIndex;
1395 
1396     template EnumTest()
1397     {
1398         enum EnumTest = attributes[udaIndex!(This.Default, attributes)].value;
1399     }
1400 
1401     static if (udaIndex!(This.Default, attributes) == -1)
1402     {
1403         enum getUDADefaultOrNothing = 0;
1404     }
1405     // @(This.Default)
1406     else static if (__traits(isSame, attributes[udaIndex!(This.Default, attributes)], This.Default))
1407     {
1408         enum getUDADefaultOrNothing = T.init;
1409     }
1410     else static if (__traits(compiles, EnumTest!()))
1411     {
1412         enum getUDADefaultOrNothing = attributes[udaIndex!(This.Default, attributes)].value;
1413     }
1414     else
1415     {
1416         @property static auto getUDADefaultOrNothing()
1417         {
1418             return attributes[udaIndex!(This.Default, attributes)].value;
1419         }
1420     }
1421 }
1422 
1423 @("struct with only default fields cannot use GenerateThis unless the default this() type matches the generated one")
1424 unittest
1425 {
1426     static assert(!__traits(compiles, {
1427         struct Foo
1428         {
1429             @(This.Default)
1430             int[] array;
1431 
1432             mixin(GenerateThis);
1433         }
1434 
1435         // because you would be able to do
1436         // const array = [2];
1437         // auto foo = Foo(array);
1438         // which would be an error, but work with a generated constructor
1439         // however, no constructor could be generated, as it would collide with this()
1440     }));
1441 
1442     // This works though.
1443     struct Bar
1444     {
1445         @(This.Default)
1446         const int[] array;
1447 
1448         mixin(GenerateThis);
1449     }
1450 
1451     const array = [2];
1452     auto bar = Bar(array);
1453 }
1454 
1455 @("very large types can be used")
1456 unittest
1457 {
1458     import std.format : format;
1459     import std.range : iota;
1460 
1461     struct VeryLargeType
1462     {
1463         static foreach (i; 500.iota)
1464         {
1465             mixin(format!"int v%s;"(i));
1466         }
1467 
1468         mixin(GenerateThis);
1469     }
1470 
1471     struct Wrapper
1472     {
1473         VeryLargeType field;
1474 
1475         mixin(GenerateThis);
1476     }
1477 
1478     auto builder = Wrapper.Builder();
1479 }