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  * `GenerateThis` creates a constructor with a parameter for every field.
27  */
28 @("creates a constructor")
29 unittest
30 {
31     class Class
32     {
33         int field;
34 
35         mixin(GenerateThis);
36     }
37 
38     auto obj = new Class(5);
39 
40     obj.field.shouldEqual(5);
41 }
42 
43 /**
44  * When the super class also has a generated constructor, it will be called first.
45  */
46 @("calls the super constructor if it exists")
47 unittest
48 {
49     class Class
50     {
51         int field;
52 
53         mixin(GenerateThis);
54     }
55 
56     class Child : Class
57     {
58         int field2;
59 
60         mixin(GenerateThis);
61     }
62 
63     auto obj = new Child(5, 8);
64 
65     obj.field.shouldEqual(5);
66     obj.field2.shouldEqual(8);
67 }
68 
69 /**
70  * Methods are ignored when generating constructors.
71  */
72 @("separates fields from methods")
73 unittest
74 {
75     class Class
76     {
77         int field;
78 
79         void method() { }
80 
81         mixin(GenerateThis);
82     }
83 
84     auto obj = new Class(5);
85 
86     obj.field.shouldEqual(5);
87 }
88 
89 /**
90  * When passing arrays to the constructor, these arrays are automatically `dup`-ed.
91  */
92 @("dups arrays")
93 unittest
94 {
95     class Class
96     {
97         int[] array;
98 
99         mixin(GenerateThis);
100     }
101 
102     auto array = [2, 3, 4];
103     auto obj = new Class(array);
104 
105     array[0] = 1;
106     obj.array[0].shouldEqual(2);
107 }
108 
109 /**
110  * Arrays passed to the constructor are `dup`-ed even if they're inside a Nullable.
111  */
112 @("dups arrays hidden behind Nullable")
113 unittest
114 {
115     import std.typecons : Nullable, nullable;
116 
117     class Class
118     {
119         Nullable!(int[]) array;
120 
121         mixin(GenerateThis);
122     }
123 
124     auto array = [2, 3, 4];
125     auto obj = new Class(array.nullable);
126 
127     array[0] = 1;
128     obj.array.get[0].shouldEqual(2);
129 
130     obj = new Class(Nullable!(int[]).init);
131     obj.array.isNull.shouldBeTrue;
132 }
133 
134 /**
135  * Associative arrays are also `dup`-ed.
136  */
137 @("dups associative arrays")
138 unittest
139 {
140     class Class
141     {
142         int[int] array;
143 
144         mixin(GenerateThis);
145     }
146 
147     auto array = [2: 3];
148     auto obj = new Class(array);
149 
150     array[2] = 4;
151     obj.array.shouldEqual([2: 3]);
152 }
153 
154 /**
155  * `@(This.Default!value)` defines a default value for the constructor parameter.
156  */
157 @("uses default value for default constructor parameter")
158 unittest
159 {
160     class Class
161     {
162         @(This.Default!5)
163         int value = 5;
164 
165         mixin(GenerateThis);
166     }
167 
168     auto obj1 = new Class();
169 
170     obj1.value.shouldEqual(5);
171 
172     auto obj2 = new Class(6);
173 
174     obj2.value.shouldEqual(6);
175 }
176 
177 /**
178  * When using `GenerateThis` in an empty struct, no constructor is created.
179  *
180  * This is because D does not allow empty constructor methods.
181  */
182 @("creates no constructor for an empty struct")
183 unittest
184 {
185     struct Struct
186     {
187         mixin(GenerateThis);
188     }
189 
190     auto strct = Struct();
191 }
192 
193 /**
194  * `@(This.Default!(lambda))` calls the lambda to generate the default value.
195  *
196  * This is to handle cases like `@(This.Default!(new Class))`, where D would allocate
197  * the class during startup and reuse the same reference for every constructor call.
198  */
199 @("properly generates new default values on each call")
200 unittest
201 {
202     import std.conv : to;
203 
204     class Class
205     {
206         @(This.Default!(() => new Object))
207         Object obj;
208 
209         mixin(GenerateThis);
210     }
211 
212     auto obj1 = new Class();
213     auto obj2 = new Class();
214 
215     (cast(void*) obj1.obj).shouldNotEqual(cast(void*) obj2.obj);
216 }
217 
218 /**
219  * When the superclass has a generated constructor, the order of parameters is:
220  *
221  * - Super class fields
222  * - Class fields
223  * - Class fields with default value
224  * - Super class fields with default value
225  */
226 @("establishes the parent-child parameter order: parent explicit, child explicit, child implicit, parent implicit.")
227 unittest
228 {
229     class Parent
230     {
231         int field1;
232 
233         @(This.Default!2)
234         int field2 = 2;
235 
236         mixin(GenerateThis);
237     }
238 
239     class Child : Parent
240     {
241         int field3;
242 
243         @(This.Default!4)
244         int field4 = 4;
245 
246         mixin(GenerateThis);
247     }
248 
249     auto obj = new Child(1, 2, 3, 4);
250 
251     obj.field1.shouldEqual(1);
252     obj.field3.shouldEqual(2);
253     obj.field4.shouldEqual(3);
254     obj.field2.shouldEqual(4);
255 }
256 
257 /**
258  * No constructor parameter is generated for static fields.
259  */
260 @("disregards static fields")
261 unittest
262 {
263     class Class
264     {
265         static int field1;
266         int field2;
267 
268         mixin(GenerateThis);
269     }
270 
271     auto obj = new Class(5);
272 
273     obj.field1.shouldEqual(0);
274     obj.field2.shouldEqual(5);
275 }
276 
277 /**
278  * Immutable arrays are supported as constructor parameters.
279  */
280 @("can initialize with immutable arrays")
281 unittest
282 {
283     class Class
284     {
285         immutable(Object)[] array;
286 
287         mixin(GenerateThis);
288     }
289 }
290 
291 /**
292  * `@(This.Private/Protected/Public)` can be used to define the visibility scope of the constructor.
293  */
294 @("can define scope for constructor")
295 unittest
296 {
297     @(This.Private)
298     class PrivateClass
299     {
300         mixin(GenerateThis);
301     }
302 
303     @(This.Protected)
304     class ProtectedClass
305     {
306         mixin(GenerateThis);
307     }
308 
309     @(This.Package)
310     class PackageClass
311     {
312         mixin(GenerateThis);
313     }
314 
315     @(This.Package("boilerplate"))
316     class SubPackageClass
317     {
318         mixin(GenerateThis);
319     }
320 
321     class PublicClass
322     {
323         mixin(GenerateThis);
324     }
325 
326     static assert(__traits(getProtection, PrivateClass.__ctor) == "private");
327     static assert(__traits(getProtection, ProtectedClass.__ctor) == "protected");
328     static assert(__traits(getProtection, PackageClass.__ctor) == "package");
329     // getProtection does not return the package name of a package() attribute
330     // static assert(__traits(getProtection, SubPackageClass.__ctor) == `package(boilerplate)`);
331     static assert(__traits(getProtection, PublicClass.__ctor) == "public");
332 }
333 
334 /**
335  * `@(This.Private/Protected/Public)` also assigns a visibility scope to the generated builder.
336  */
337 @("will assign the same scope to Builder")
338 unittest
339 {
340     @(This.Private)
341     class PrivateClass
342     {
343         mixin(GenerateThis);
344     }
345 
346     @(This.Protected)
347     class ProtectedClass
348     {
349         mixin(GenerateThis);
350     }
351 
352     @(This.Package)
353     class PackageClass
354     {
355         mixin(GenerateThis);
356     }
357 
358     @(This.Package("boilerplate"))
359     class SubPackageClass
360     {
361         mixin(GenerateThis);
362     }
363 
364     class PublicClass
365     {
366         mixin(GenerateThis);
367     }
368 
369     static assert(__traits(getProtection, PrivateClass.Builder) == "private");
370     static assert(__traits(getProtection, ProtectedClass.Builder) == "protected");
371     static assert(__traits(getProtection, PackageClass.Builder) == "package");
372     static assert(__traits(getProtection, PublicClass.Builder) == "public");
373 }
374 
375 /**
376  * `@(This.Default)` without a parameter uses the default value of the type.
377  */
378 @("empty default tag means T()")
379 unittest
380 {
381     class Class
382     {
383         @(This.Default)
384         string s;
385 
386         @(This.Default)
387         int i;
388 
389         mixin(GenerateThis);
390     }
391 
392     (new Class()).i.shouldEqual(0);
393     (new Class()).s.shouldEqual(string.init);
394 }
395 
396 /**
397  * `@(This.Exclude)` excludes a field from being set by the generated constructor.
398  */
399 @("can exclude fields from constructor")
400 unittest
401 {
402     class Class
403     {
404         @(This.Exclude)
405         int i = 5;
406 
407         mixin(GenerateThis);
408     }
409 
410     (new Class).i.shouldEqual(5);
411 }
412 
413 /**
414  * Even if the class holds a field that is not const, a const array can be passed to it
415  * as long as the `dup` of this field does not leak mutable references.
416  */
417 @("marks duppy parameters as const when this does not prevent dupping")
418 unittest
419 {
420 
421     struct Struct
422     {
423     }
424 
425     class Class
426     {
427         Struct[] values_;
428 
429         mixin(GenerateThis);
430     }
431 
432     const Struct[] constValues;
433     auto obj = new Class(constValues);
434 }
435 
436 /**
437  * Property functions are disregarded by the generated constructor
438  */
439 @("does not include property functions in constructor list")
440 unittest
441 {
442     class Class
443     {
444         int a;
445 
446         @property int foo() const
447         {
448             return 0;
449         }
450 
451         mixin(GenerateThis);
452     }
453 
454     static assert(__traits(compiles, new Class(0)));
455     static assert(!__traits(compiles, new Class(0, 0)));
456 }
457 
458 /**
459  * When no parameters need to be dupped, the generated constructor is `@nogc`.
460  */
461 @("declares @nogc on non-dupping constructors")
462 @nogc unittest
463 {
464     struct Struct
465     {
466         int a;
467 
468         mixin(GenerateThis);
469     }
470 
471     auto str = Struct(5);
472 }
473 
474 /**
475  * `@(This.Init!value)` defines a value for the field that will be assigned in the constructor,
476  * and excludes the field from being a constructor parameter.
477  */
478 @("can initialize fields using init value")
479 unittest
480 {
481     class Class
482     {
483         @(This.Init!5)
484         int field1;
485 
486         @(This.Init!(() => 8))
487         int field2;
488 
489         mixin(GenerateThis);
490     }
491 
492     auto obj = new Class;
493 
494     obj.field1.shouldEqual(5);
495     obj.field2.shouldEqual(8);
496 }
497 
498 /**
499  * `@(This.Init!lambda)`, like `@(This.Default!lambda)`, will be called automatically with `this`
500  * to determine the initializer value.
501  */
502 @("can initialize fields using init value, with lambda that accesses previous value")
503 unittest
504 {
505     class Class
506     {
507         int field1;
508 
509         @(This.Init!(self => self.field1 + 5))
510         int field2;
511 
512         mixin(GenerateThis);
513     }
514 
515     auto obj = new Class(5);
516 
517     obj.field1.shouldEqual(5);
518     obj.field2.shouldEqual(10);
519 }
520 
521 /**
522  * `@(This.Init!lambda)` can allocate runtime values.
523  */
524 @("can initialize fields with allocated types")
525 unittest
526 {
527     class Class1
528     {
529         @(This.Init!(self => new Object))
530         Object object;
531 
532         mixin(GenerateThis);
533     }
534 
535     class Class2
536     {
537         @(This.Init!(() => new Object))
538         Object object;
539 
540         mixin(GenerateThis);
541     }
542 
543     class Class3 : Class2
544     {
545         mixin(GenerateThis);
546     }
547 }
548 
549 /**
550  * `GenerateThis` creates a `.Builder()` property that can be used to incrementally construct the value.
551  *
552  * `builder.value` will call the generated constructor with the assigned values.
553  */
554 @("generates Builder class that gathers constructor parameters, then calls constructor with them")
555 unittest
556 {
557     static class Class
558     {
559         int field1;
560         int field2;
561         int field3;
562 
563         mixin(GenerateThis);
564     }
565 
566     auto obj = {
567         with (Class.Builder())
568         {
569             field1 = 1;
570             field2 = 2;
571             field3 = 3;
572             return value;
573         }
574     }();
575 
576     with (obj)
577     {
578         field1.shouldEqual(1);
579         field2.shouldEqual(2);
580         field3.shouldEqual(3);
581     }
582 }
583 
584 /**
585  * The order in which `Builder` fields are set is irrelevant.
586  */
587 @("builder field order doesn't matter")
588 unittest
589 {
590     static class Class
591     {
592         int field1;
593         int field2;
594         int field3;
595 
596         mixin(GenerateThis);
597     }
598 
599     auto obj = {
600         with (Class.Builder())
601         {
602             field3 = 1;
603             field1 = 2;
604             field2 = 3;
605             return value;
606         }
607     }();
608 
609     with (obj)
610     {
611         field1.shouldEqual(2);
612         field2.shouldEqual(3);
613         field3.shouldEqual(1);
614     }
615 }
616 
617 /**
618  * `Builder` fields with a `@(This.Default)` value can be omitted.
619  */
620 @("default fields can be left out when assigning builder")
621 unittest
622 {
623     static class Class
624     {
625         int field1;
626         @(This.Default!5)
627         int field2;
628         int field3;
629 
630         mixin(GenerateThis);
631     }
632 
633     // constructor is this(field1, field3, field2 = 5)
634     auto obj = {
635         with (Class.Builder())
636         {
637             field1 = 1;
638             field3 = 3;
639             return value;
640         }
641     }();
642 
643     with (obj)
644     {
645         field1.shouldEqual(1);
646         field2.shouldEqual(5);
647         field3.shouldEqual(3);
648     }
649 }
650 
651 /**
652  * `Builder` can be used with structs as well as classes.
653  */
654 @("supports Builder in structs")
655 unittest
656 {
657     struct Struct
658     {
659         int field1;
660         int field2;
661         int field3;
662 
663         mixin(GenerateThis);
664     }
665 
666     auto value = {
667         with (Struct.Builder())
668         {
669             field1 = 1;
670             field3 = 3;
671             field2 = 5;
672             return value;
673         }
674     }();
675 
676     static assert(is(typeof(value) == Struct));
677 
678     with (value)
679     {
680         field1.shouldEqual(1);
681         field2.shouldEqual(5);
682         field3.shouldEqual(3);
683     }
684 }
685 
686 /**
687  * `Builder` fields don't contain the trailing newline of a private field.
688  */
689 @("builder strips trailing underlines")
690 unittest
691 {
692     struct Struct
693     {
694         private int a_;
695 
696         mixin(GenerateThis);
697     }
698 
699     auto builder = Struct.Builder();
700 
701     builder.a = 1;
702 
703     auto value = builder.value;
704 
705     value.shouldEqual(Struct(1));
706 }
707 
708 /**
709  * When a field in the type has a generated constructor itself, its fields can be set directly
710  * on a `Builder` of the outer type.
711  */
712 @("builder supports nested initialization")
713 unittest
714 {
715     struct Struct1
716     {
717         int a;
718         int b;
719 
720         mixin(GenerateThis);
721     }
722 
723     struct Struct2
724     {
725         int c;
726         Struct1 struct1;
727         int d;
728 
729         mixin(GenerateThis);
730     }
731 
732     auto builder = Struct2.Builder();
733 
734     builder.struct1.a = 1;
735     builder.struct1.b = 2;
736     builder.c = 3;
737     builder.d = 4;
738 
739     auto value = builder.value;
740 
741     static assert(is(typeof(value) == Struct2));
742 
743     with (value)
744     {
745         struct1.a.shouldEqual(1);
746         struct1.b.shouldEqual(2);
747         c.shouldEqual(3);
748         d.shouldEqual(4);
749     }
750 }
751 
752 /**
753  * `Builder` can use `@(This.Default)` values for nested fields.
754  */
755 @("builder supports defaults for nested values")
756 unittest
757 {
758     struct Struct1
759     {
760         int a;
761         int b;
762 
763         mixin(GenerateThis);
764     }
765 
766     struct Struct2
767     {
768         int c;
769         @(This.Default!(Struct1(3, 4)))
770         Struct1 struct1;
771         int d;
772 
773         mixin(GenerateThis);
774     }
775 
776     auto builder = Struct2.Builder();
777 
778     builder.c = 1;
779     builder.d = 2;
780 
781     builder.value.shouldEqual(Struct2(1, 2, Struct1(3, 4)));
782 }
783 
784 /**
785  * `Builder` also allows assigning a single value directly for a nested `Builder` field.
786  */
787 @("builder supports direct value assignment for nested values")
788 unittest
789 {
790     struct Struct1
791     {
792         int a;
793         int b;
794 
795         mixin(GenerateThis);
796     }
797 
798     struct Struct2
799     {
800         int c;
801         Struct1 struct1;
802         int d;
803 
804         mixin(GenerateThis);
805     }
806 
807     auto builder = Struct2.Builder();
808 
809     builder.struct1 = Struct1(2, 3);
810     builder.c = 1;
811     builder.d = 4;
812 
813     builder.value.shouldEqual(Struct2(1, Struct1(2, 3), 4));
814 }
815 
816 /**
817  * When a `Builder` field is an array, it can be initialized by
818  * specifying the value for each desired index.
819  */
820 @("builder supports recursive array index initialization")
821 unittest
822 {
823     struct Struct1
824     {
825         int value;
826 
827         mixin(GenerateThis);
828     }
829 
830     struct Struct2
831     {
832         Struct1[] array;
833 
834         mixin(GenerateThis);
835     }
836 
837     auto builder = Struct2.Builder();
838 
839     builder.array[0].value = 1;
840     builder.array[1].value = 2;
841 
842     builder.value.shouldEqual(Struct2([Struct1(1), Struct1(2)]));
843 }
844 
845 /**
846  * `Builder` handles fields that are aliased to `this`.
847  */
848 @("builder supports nested struct with alias this")
849 unittest
850 {
851     struct Struct2
852     {
853         string text;
854 
855         alias text this;
856 
857         mixin(GenerateThis);
858     }
859 
860     struct Struct1
861     {
862         Struct2 nested;
863 
864         mixin(GenerateThis);
865     }
866 
867     auto builder = Struct1.Builder();
868 
869     builder.nested.text = "foo";
870 
871     builder.value.shouldEqual(Struct1(Struct2("foo")));
872 }
873 
874 /**
875  * When a `Builder` field is an array, it can also be initialized by appending values.
876  */
877 @("builder supports arrays as values")
878 unittest
879 {
880     struct Struct1
881     {
882         int value;
883 
884         mixin(GenerateThis);
885     }
886 
887     struct Struct2
888     {
889         Struct1[] array;
890 
891         mixin(GenerateThis);
892     }
893 
894     auto builder = Struct2.Builder();
895 
896     builder.array ~= Struct1(1);
897     builder.array ~= Struct1(2);
898 
899     builder.value.shouldEqual(Struct2([Struct1(1), Struct1(2)]));
900 }
901 
902 /**
903  * When a value has been assigned to a recursive `Builder` field, its fields can still
904  * be individually overridden.
905  * This uses the `BuilderFrom` reverse-`Builder` property.
906  */
907 @("builder supports overriding value assignment with field assignment later")
908 unittest
909 {
910     struct Struct1
911     {
912         int a;
913         int b;
914 
915         mixin(GenerateThis);
916     }
917 
918     struct Struct2
919     {
920         Struct1 struct1;
921 
922         mixin(GenerateThis);
923     }
924 
925     auto builder = Struct2.Builder();
926 
927     builder.struct1 = Struct1(2, 3);
928     builder.struct1.b = 4;
929 
930     builder.value.shouldEqual(Struct2(Struct1(2, 4)));
931 }
932 
933 /**
934  * When a recursive `Builder` field has already been directly assigned, it cannot be
935  * later overwritten with a whole-value assignment.
936  */
937 @("builder refuses overriding field assignment with value assignment")
938 unittest
939 {
940     import core.exception : AssertError;
941 
942     struct Struct1
943     {
944         int a;
945         int b;
946 
947         mixin(GenerateThis);
948     }
949 
950     struct Struct2
951     {
952         Struct1 struct1;
953 
954         mixin(GenerateThis);
955     }
956 
957     auto builder = Struct2.Builder();
958 
959     builder.struct1.b = 4;
960 
961     void set()
962     {
963         builder.struct1 = Struct1(2, 3);
964     }
965     set().shouldThrow!AssertError("Builder: cannot set field by value since a subfield has already been set.");
966 }
967 
968 /**
969  * `Builder` supports assigning const fields.
970  */
971 @("builder supports const args")
972 unittest
973 {
974     struct Struct
975     {
976         const int a;
977 
978         mixin(GenerateThis);
979     }
980 
981     with (Struct.Builder())
982     {
983         a = 5;
984 
985         value.shouldEqual(Struct(5));
986     }
987 }
988 
989 /**
990  * `Builder` supports assigning recursive values with a destructor.
991  */
992 @("builder supports fields with destructor")
993 unittest
994 {
995     static struct Struct1
996     {
997         ~this() pure @safe @nogc nothrow { }
998     }
999 
1000     struct Struct2
1001     {
1002         Struct1 struct1;
1003 
1004         mixin(GenerateThis);
1005     }
1006 
1007     with (Struct2.Builder())
1008     {
1009         struct1 = Struct1();
1010 
1011         value.shouldEqual(Struct2(Struct1()));
1012     }
1013 }
1014 
1015 /**
1016  * When a `Builder` field is `Nullable!T`, it can be directly assigned a `T`.
1017  */
1018 @("builder supports direct assignment to Nullables")
1019 unittest
1020 {
1021     import std.typecons : Nullable, nullable;
1022 
1023     struct Struct
1024     {
1025         const Nullable!int a;
1026 
1027         mixin(GenerateThis);
1028     }
1029 
1030     with (Struct.Builder())
1031     {
1032         a = 5;
1033 
1034         value.shouldEqual(Struct(5.nullable));
1035     }
1036 }
1037 
1038 /**
1039  * When a `Builder` field is `Nullable!T`, and `T` has a `Builder`, `T`'s fields can be directly assigned.
1040  */
1041 @("builder with passthrough assignment to Nullable structs")
1042 unittest
1043 {
1044     import std.typecons : Nullable, nullable;
1045 
1046     struct Struct1
1047     {
1048         int a;
1049 
1050         mixin(GenerateThis);
1051     }
1052 
1053     struct Struct2
1054     {
1055         @(This.Default)
1056         Nullable!Struct1 b;
1057 
1058         mixin(GenerateThis);
1059     }
1060 
1061     with (Struct2.Builder())
1062     {
1063         value.shouldEqual(Struct2(Nullable!Struct1()));
1064 
1065         b.a = 5;
1066 
1067         value.shouldEqual(Struct2(Nullable!Struct1(Struct1(5))));
1068     }
1069 }
1070 
1071 /**
1072  * When a `Builder` field is `Nullable!T`, and `T` has a `Builder`, `T` can still be assigned either as
1073  * `T` or `Nullable!T`.
1074  */
1075 @("builder with value assignment to Nullable struct field")
1076 unittest
1077 {
1078     import std.typecons : Nullable, nullable;
1079 
1080     struct Struct1
1081     {
1082         mixin(GenerateThis);
1083     }
1084 
1085     struct Struct2
1086     {
1087         @(This.Default)
1088         Nullable!Struct1 value;
1089 
1090         mixin(GenerateThis);
1091     }
1092 
1093     with (Struct2.Builder())
1094     {
1095         builderValue.shouldEqual(Struct2());
1096 
1097         value = Struct1();
1098 
1099         builderValue.shouldEqual(Struct2(Struct1().nullable));
1100     }
1101 
1102     with (Struct2.Builder())
1103     {
1104         value = Nullable!Struct1();
1105 
1106         builderValue.shouldEqual(Struct2());
1107     }
1108 }
1109 
1110 /**
1111  * A value with `GenerateThis` can be turned back into a builder using `BuilderFrom()`.
1112  * This can be used to reassign immutable fields.
1113  */
1114 @("builder supports reconstruction from value")
1115 unittest
1116 {
1117     import std.typecons : Nullable, nullable;
1118 
1119     struct Struct
1120     {
1121         private int a_;
1122 
1123         int[] b;
1124 
1125         mixin(GenerateThis);
1126     }
1127 
1128     const originalValue = Struct(2, [3]);
1129 
1130     with (originalValue.BuilderFrom())
1131     {
1132         a = 5;
1133 
1134         value.shouldEqual(Struct(5, [3]));
1135     }
1136 }
1137 
1138 /**
1139  * When a type already has a `value` field, `builderValue` can be used to get the builder value.
1140  */
1141 @("builder supports struct that already contains a value field")
1142 unittest
1143 {
1144     import std.typecons : Nullable, nullable;
1145 
1146     struct Struct
1147     {
1148         private int value_;
1149 
1150         mixin(GenerateThis);
1151     }
1152 
1153     with (Struct.Builder())
1154     {
1155         value = 5;
1156 
1157         builderValue.shouldEqual(Struct(5));
1158     }
1159 }
1160 
1161 /**
1162  * `Builder` will handle structs that contain structs with `@disable(this)`.
1163  */
1164 @("builder supports struct that contains struct that has @disable(this)")
1165 unittest
1166 {
1167     import std.typecons : Nullable, nullable;
1168 
1169     static struct Inner
1170     {
1171         private int i_;
1172 
1173         @disable this();
1174 
1175         mixin(GenerateThis);
1176     }
1177 
1178     static struct Struct
1179     {
1180         private Inner inner_;
1181 
1182         mixin(GenerateThis);
1183     }
1184 
1185     with (Struct.Builder())
1186     {
1187         inner.i = 3;
1188 
1189         value.shouldEqual(Struct(Inner(3)));
1190     }
1191 }
1192 
1193 @("destructors with code that is unsafe, system or throws exceptions")
1194 {
1195     struct S
1196     {
1197         ~this() { throw new Exception("test"); }
1198     }
1199 
1200     struct T
1201     {
1202         S s;
1203 
1204         mixin(GenerateThis);
1205     }
1206 }
1207 
1208 @("builder supports appending to transitive non-const fields")
1209 unittest
1210 {
1211     struct Struct1
1212     {
1213         int[] values;
1214 
1215         mixin(GenerateThis);
1216     }
1217 
1218     struct Struct2
1219     {
1220         Struct1[] array;
1221 
1222         mixin(GenerateThis);
1223     }
1224 
1225     auto builder = Struct2.Builder();
1226 
1227     builder.array ~= [Struct1([1]), Struct1([2])];
1228 
1229     builder.value.shouldEqual(Struct2([Struct1([1]), Struct1([2])]));
1230 }
1231 
1232 @("builder supports implicit nullable conversion")
1233 unittest
1234 {
1235     import std.typecons : Nullable, nullable;
1236 
1237     immutable struct Struct
1238     {
1239         Nullable!int a;
1240 
1241         mixin(GenerateThis);
1242     }
1243 
1244     auto builder = Struct.Builder();
1245     immutable int i;
1246 
1247     builder.a = i.nullable;
1248 }
1249 
1250 @("struct constructor with type substitution")
1251 unittest
1252 {
1253     import std.typecons : Nullable, nullable;
1254 
1255     immutable struct Struct
1256     {
1257         @(This.As!int)
1258         Nullable!int a;
1259 
1260         mixin(GenerateThis);
1261     }
1262 
1263     Struct(5).a.shouldEqual(Nullable!int(5));
1264 }
1265 
1266 @("class constructor with type substitution")
1267 unittest
1268 {
1269     import std.typecons : Nullable, nullable;
1270 
1271     class Class
1272     {
1273         @(This.As!int)
1274         Nullable!int a;
1275 
1276         mixin(GenerateThis);
1277     }
1278 
1279     (new Class(5)).a.shouldEqual(Nullable!int(5));
1280 }
1281 
1282 import std.string : format;
1283 
1284 mixin template GenerateThisTemplate()
1285 {
1286     private static generateThisImpl()
1287     {
1288         if (!__ctfe)
1289         {
1290             return null;
1291         }
1292 
1293         import boilerplate.constructor : filterCanFind, mapFormat, This;
1294         import boilerplate.util :
1295             bucketSort, GenNormalMemberTuple, needToDup,
1296             optionallyRemoveTrailingUnderline,
1297             removeTrailingUnderline, reorder, udaIndex;
1298         import std.algorithm : all, canFind, filter, map;
1299         import std.conv : to;
1300         import std.meta : Alias, aliasSeqOf, staticMap;
1301         import std.range : array, drop, empty, iota, zip;
1302         import std.string : endsWith, format, join;
1303         import std.traits : getUDAs;
1304         import std.typecons : Nullable;
1305 
1306         mixin GenNormalMemberTuple;
1307 
1308         string result = `import std.traits : getUDAs;`;
1309 
1310         string visibility = "public";
1311 
1312         foreach (uda; __traits(getAttributes, typeof(this)))
1313         {
1314             static if (is(typeof(uda) == ThisEnum))
1315             {
1316                 static if (uda == This.Protected)
1317                 {
1318                     visibility = "protected";
1319                 }
1320                 static if (uda == This.Private)
1321                 {
1322                     visibility = "private";
1323                 }
1324             }
1325             else static if (is(uda == This.Package))
1326             {
1327                 visibility = "package";
1328             }
1329             else static if (is(typeof(uda) == This.Package))
1330             {
1331                 visibility = "package(" ~ uda.packageMask ~ ")";
1332             }
1333         }
1334 
1335         string[] constructorAttributes = ["pure", "nothrow", "@safe", "@nogc"];
1336 
1337         static if (is(typeof(typeof(super).ConstructorInfo)))
1338         {
1339             enum argsPassedToSuper = typeof(super).ConstructorInfo.fields.length;
1340             enum members = typeof(super).ConstructorInfo.fields ~ [NormalMemberTuple];
1341 
1342             constructorAttributes = typeof(super).GeneratedConstructorAttributes_;
1343         }
1344         else
1345         {
1346             enum argsPassedToSuper = 0;
1347             static if (NormalMemberTuple.length > 0)
1348             {
1349                 enum members = [NormalMemberTuple];
1350             }
1351             else
1352             {
1353                 enum string[] members = null;
1354             }
1355         }
1356 
1357         string[] fields;
1358         string[] args;
1359         string[] argexprs;
1360         string[] defaultAssignments;
1361         bool[] fieldUseDefault;
1362         string[] fieldDefault;
1363         string[] fieldAttributes;
1364         string[] types;
1365         string[] directInitFields;
1366         int[] directInitIndex;
1367         bool[] directInitUseSelf;
1368 
1369         foreach (i; aliasSeqOf!(members.length.iota))
1370         {
1371             enum member = members[i];
1372             alias attributes = __traits(getAttributes, __traits(getMember, typeof(this), member));
1373 
1374             static if (i < argsPassedToSuper)
1375             {
1376                 enum bool useDefault = __traits(getMember, typeof(super).ConstructorInfo.FieldInfo, member).useDefault;
1377                 enum string memberTypeAsString = "typeof(super).ConstructorInfo.FieldInfo." ~ member ~ ".Type";
1378                 enum string default_ = "typeof(super).ConstructorInfo.FieldInfo." ~ member ~ ".fieldDefault";
1379                 enum string attributesStr = "typeof(super).ConstructorInfo.FieldInfo." ~ member ~ ".attributes";
1380             }
1381             else
1382             {
1383                 static if (udaIndex!(This.As, attributes) != -1)
1384                 {
1385                     enum string memberTypeAsString = "getUDAs!(this." ~ member ~ ", This.As)[0].Type";
1386                 }
1387                 else
1388                 {
1389                     enum string memberTypeAsString = "typeof(this." ~ member ~ ")";
1390                 }
1391                 enum bool useDefault = udaIndex!(This.Default, attributes) != -1;
1392                 enum string default_ = "getUDADefaultOrNothing!(typeof(this." ~ member ~ "),"
1393                     ~ " __traits(getAttributes, this." ~ member ~ "))";
1394                 enum string attributesStr = "__traits(getAttributes, this." ~ member ~ ")";
1395             }
1396 
1397             mixin(`alias Type = ` ~ memberTypeAsString ~ `;`);
1398 
1399             bool includeMember = false;
1400 
1401             enum isNullable = is(Type: Nullable!Arg, Arg);
1402 
1403             static if (!isNullable)
1404             {
1405                 enum bool dupExpr = needToDup!Type;
1406                 enum bool passExprAsConst = dupExpr && __traits(compiles, { Type value = const(Type).init.dup; });
1407             }
1408             else
1409             {
1410                 // unpack nullable for dup
1411                 enum bool dupExpr = needToDup!(typeof(Type.init.get));
1412                 enum bool passExprAsConst = dupExpr && __traits(compiles, { Type value = const(Type).init.get.dup; });
1413             }
1414 
1415             enum scopeAttributes = [__traits(getFunctionAttributes, {
1416                 static if (passExprAsConst) { const Type parameter = Type.init; }
1417                 else { Type parameter = Type.init; }
1418 
1419                 static if (isNullable) { auto value = parameter.get; }
1420                 else { auto value = parameter; }
1421 
1422                 static if (dupExpr)
1423                 {
1424                     Type dupped = value.dup;
1425                 }
1426             })];
1427             constructorAttributes = constructorAttributes.filterCanFind(scopeAttributes);
1428 
1429             bool forSuper = false;
1430 
1431             static if (i < argsPassedToSuper)
1432             {
1433                 includeMember = true;
1434                 forSuper = true;
1435             }
1436             else
1437             {
1438                 mixin("alias symbol = typeof(this)." ~ member ~ ";");
1439 
1440                 static assert (is(typeof(symbol)) && !__traits(isTemplate, symbol)); /* must have a resolvable type */
1441 
1442                 import boilerplate.util: isStatic;
1443 
1444                 includeMember = !mixin(isStatic(member));
1445 
1446                 static if (udaIndex!(This.Init, __traits(getAttributes, symbol)) != -1)
1447                 {
1448                     enum udaFieldIndex = udaIndex!(This.Init, __traits(getAttributes, symbol));
1449                     alias initArg = Alias!(__traits(getAttributes, symbol)[udaFieldIndex].value);
1450                     enum lambdaWithSelf = __traits(compiles, initArg(typeof(this).init));
1451                     enum nakedLambda = __traits(compiles, initArg());
1452 
1453                     directInitFields ~= member;
1454                     directInitIndex ~= udaFieldIndex;
1455                     directInitUseSelf ~= __traits(compiles,
1456                         __traits(getAttributes, symbol)[udaFieldIndex].value(typeof(this).init));
1457                     includeMember = false;
1458 
1459                     static if (lambdaWithSelf)
1460                     {
1461                         static if (__traits(compiles, initArg!(typeof(this))))
1462                         {
1463                             enum lambdaAttributes = [__traits(getFunctionAttributes, initArg!(typeof(this)))];
1464                         }
1465                         else
1466                         {
1467                             enum lambdaAttributes = [__traits(getFunctionAttributes, initArg)];
1468                         }
1469                         constructorAttributes = constructorAttributes.filterCanFind(lambdaAttributes);
1470                     }
1471                     else static if (nakedLambda)
1472                     {
1473                         enum lambdaAttributes = [__traits(getFunctionAttributes, initArg)];
1474 
1475                         constructorAttributes = constructorAttributes.filterCanFind(lambdaAttributes);
1476                     }
1477                 }
1478 
1479                 static if (udaIndex!(This.Exclude, __traits(getAttributes, symbol)) != -1)
1480                 {
1481                     includeMember = false;
1482                 }
1483             }
1484 
1485             if (!includeMember) continue;
1486 
1487             enum paramName = optionallyRemoveTrailingUnderline!member;
1488 
1489             string argexpr = paramName;
1490 
1491             if (dupExpr)
1492             {
1493                 constructorAttributes = constructorAttributes.filter!(q{a != "@nogc"}).array;
1494 
1495                 static if (isNullable)
1496                 {
1497                     argexpr = format!`%s.isNull ? %s.init : %s(%s.get.dup)`
1498                         (argexpr, memberTypeAsString, memberTypeAsString, argexpr);
1499                 }
1500                 else
1501                 {
1502                     argexpr = argexpr ~ ".dup";
1503                 }
1504             }
1505 
1506             fields ~= member;
1507             args ~= paramName;
1508             argexprs ~= argexpr;
1509             fieldUseDefault ~= useDefault;
1510             fieldDefault ~= default_;
1511             fieldAttributes ~= attributesStr;
1512             defaultAssignments ~= useDefault ? (` = ` ~ default_) : ``;
1513             types ~= passExprAsConst ? (`const ` ~ memberTypeAsString) : memberTypeAsString;
1514         }
1515 
1516         size_t establishParameterRank(size_t i)
1517         {
1518             // parent explicit, our explicit, our implicit, parent implicit
1519             const fieldOfParent = i < argsPassedToSuper;
1520             return fieldUseDefault[i] * 2 + (fieldUseDefault[i] == fieldOfParent);
1521         }
1522 
1523         auto constructorFieldOrder = fields.length.iota.array.bucketSort(&establishParameterRank);
1524 
1525         assert(fields.length == types.length);
1526         assert(fields.length == fieldUseDefault.length);
1527         assert(fields.length == fieldDefault.length);
1528 
1529         result ~= format!`
1530             public static alias ConstructorInfo =
1531                 saveConstructorInfo!(%s, %-(%s, %));`
1532         (
1533             fields.reorder(constructorFieldOrder),
1534             zip(
1535                 types.reorder(constructorFieldOrder),
1536                 fieldUseDefault.reorder(constructorFieldOrder),
1537                 fieldDefault.reorder(constructorFieldOrder),
1538                 fieldAttributes.reorder(constructorFieldOrder),
1539             )
1540             .map!(q{format!`ConstructorField!(%s, %s, %s, %s)`(a[0], a[1], a[2], a[3])})
1541             .array
1542         );
1543 
1544         // don't emit this(a = b, c = d) for structs -
1545         // the compiler complains that it collides with this(), which is reserved.
1546         if (is(typeof(this) == struct) && fieldUseDefault.all)
1547         {
1548             // If there are fields, their direct-construction types may diverge from ours
1549             // specifically, see the "struct with only default fields" test below
1550             if (!fields.empty)
1551             {
1552                 result ~= `static assert(
1553                     is(typeof(this.tupleof) == ConstructorInfo.Types),
1554                     "Structs with fields, that are all default, cannot use GenerateThis when their " ~
1555                     "constructor types would diverge from their native types: " ~
1556                     typeof(this).stringof ~ ".this" ~ typeof(this.tupleof).stringof ~ ", " ~
1557                     "but generated constructor would have been " ~ typeof(this).stringof ~ ".this"
1558                     ~ ConstructorInfo.Types.stringof
1559                 );`;
1560             }
1561         }
1562         else
1563         {
1564             result ~= visibility ~ ` this(`
1565                 ~ constructorFieldOrder.mapFormat!`%s %s%s`(types, args, defaultAssignments).join(`, `)
1566                 ~ format!`) %-(%s %)`(constructorAttributes);
1567 
1568             result ~= `{`;
1569 
1570             static if (is(typeof(typeof(super).ConstructorInfo)))
1571             {
1572                 result ~= `super(` ~ args[0 .. argsPassedToSuper].join(", ") ~ `);`;
1573             }
1574 
1575             result ~= fields.length.iota.drop(argsPassedToSuper).mapFormat!`this.%s = %s;`(fields, argexprs).join;
1576 
1577             foreach (i, field; directInitFields)
1578             {
1579                 if (directInitUseSelf[i])
1580                 {
1581                     result ~= format!`this.%s = __traits(getAttributes, this.%s)[%s].value(this);`
1582                         (field, field, directInitIndex[i]);
1583                 }
1584                 else
1585                 {
1586                     result ~= format!`this.%s = __traits(getAttributes, this.%s)[%s].value;`
1587                         (field, field, directInitIndex[i]);
1588                 }
1589             }
1590 
1591             result ~= `}`;
1592 
1593             result ~= `protected static enum string[] GeneratedConstructorAttributes_ = [`
1594                 ~ constructorAttributes.map!(q{`"` ~ a ~ `"`}).join(`, `)
1595                 ~ `];`;
1596         }
1597 
1598         result ~= visibility ~ ` static struct BuilderType(alias T = typeof(this))
1599         {
1600             import boilerplate.builder : BuilderImpl;
1601 
1602             mixin BuilderImpl!T;
1603         }`;
1604 
1605         result ~= visibility ~ ` static auto Builder()()
1606         {
1607             return BuilderType!()();
1608         }`;
1609 
1610         /**
1611          * We are allowed to read the private field values here.
1612          * We aren't actually leaking private or mutable information because:
1613          * - the constructor will dup it again anyways, if required
1614          * - we cannot read it from the Builder, because Builders are write-only
1615          * - if we can't read it off the current value, the builderValue will
1616          *   have the same type - so we can't read it off there either!
1617          */
1618         result ~= visibility ~ ` auto BuilderFrom(this This)()
1619         {
1620             import boilerplate.util : optionallyRemoveTrailingUnderline;
1621 
1622             auto builder = BuilderType!()();
1623 
1624             static foreach (field; ConstructorInfo.fields)
1625             {
1626                 mixin("builder." ~ optionallyRemoveTrailingUnderline!field ~ " = this." ~ field ~ ";");
1627             }
1628             return builder;
1629         }`;
1630 
1631         return result;
1632     }
1633 }
1634 
1635 public template ConstructorField(Type_, bool useDefault_, alias fieldDefault_, attributes_...)
1636 {
1637     public alias Type = Type_;
1638     public enum useDefault = useDefault_;
1639     public alias fieldDefault = fieldDefault_;
1640     public alias attributes = attributes_;
1641 }
1642 
1643 public template saveConstructorInfo(string[] fields_, Fields...)
1644 // if (fields_.length == Fields.length
1645 //     && allSatisfy!(ApplyLeft!(isInstanceOf, ConstructorField), Fields))
1646 {
1647     import std.format : format;
1648 
1649     public enum fields = fields_;
1650 
1651     private template FieldInfo_() {
1652         static foreach (i, field; fields)
1653         {
1654             mixin(format!q{public alias %s = Fields[%s];}(field, i));
1655         }
1656     }
1657 
1658     public alias FieldInfo = FieldInfo_!();
1659 
1660     mixin(
1661         format!q{public alias Types = AliasSeq!(%-(%s, %)); }
1662             (fields.map!(field => format!"FieldInfo.%s.Type"(field)).array));
1663 }
1664 
1665 enum ThisEnum
1666 {
1667     Private,
1668     Protected,
1669     Exclude
1670 }
1671 
1672 struct This
1673 {
1674     enum Private = ThisEnum.Private;
1675     enum Protected = ThisEnum.Protected;
1676     struct Package
1677     {
1678         string packageMask = null;
1679     }
1680     enum Exclude = ThisEnum.Exclude;
1681 
1682     // construct with value
1683     static struct Init(alias Alias)
1684     {
1685         static if (__traits(compiles, Alias()))
1686         {
1687             @property static auto value() { return Alias(); }
1688         }
1689         else
1690         {
1691             alias value = Alias;
1692         }
1693     }
1694 
1695     static struct Default(alias Alias)
1696     {
1697         static if (__traits(compiles, Alias()))
1698         {
1699             @property static auto value() { return Alias(); }
1700         }
1701         else
1702         {
1703             alias value = Alias;
1704         }
1705     }
1706 
1707     static struct As(Type_)
1708     {
1709         alias Type = Type_;
1710     }
1711 }
1712 
1713 public template getUDADefaultOrNothing(T, attributes...)
1714 {
1715     import boilerplate.util : udaIndex;
1716 
1717     template EnumTest()
1718     {
1719         enum EnumTest = attributes[udaIndex!(This.Default, attributes)].value;
1720     }
1721 
1722     static if (udaIndex!(This.Default, attributes) == -1)
1723     {
1724         enum getUDADefaultOrNothing = 0;
1725     }
1726     // @(This.Default)
1727     else static if (__traits(isSame, attributes[udaIndex!(This.Default, attributes)], This.Default))
1728     {
1729         enum getUDADefaultOrNothing = T.init;
1730     }
1731     else static if (__traits(compiles, EnumTest!()))
1732     {
1733         enum getUDADefaultOrNothing = attributes[udaIndex!(This.Default, attributes)].value;
1734     }
1735     else
1736     {
1737         @property static auto getUDADefaultOrNothing()
1738         {
1739             return attributes[udaIndex!(This.Default, attributes)].value;
1740         }
1741     }
1742 }
1743 
1744 @("struct with only default fields cannot use GenerateThis unless the default this() type matches the generated one")
1745 unittest
1746 {
1747     static assert(!__traits(compiles, {
1748         struct Foo
1749         {
1750             @(This.Default)
1751             int[] array;
1752 
1753             mixin(GenerateThis);
1754         }
1755 
1756         // because you would be able to do
1757         // const array = [2];
1758         // auto foo = Foo(array);
1759         // which would be an error, but work with a generated constructor
1760         // however, no constructor could be generated, as it would collide with this()
1761     }));
1762 
1763     // This works though.
1764     struct Bar
1765     {
1766         @(This.Default)
1767         const int[] array;
1768 
1769         mixin(GenerateThis);
1770     }
1771 
1772     const array = [2];
1773     auto bar = Bar(array);
1774 }
1775 
1776 @("very large types can be used")
1777 unittest
1778 {
1779     import std.format : format;
1780     import std.range : iota;
1781 
1782     struct VeryLargeType
1783     {
1784         static foreach (i; 500.iota)
1785         {
1786             mixin(format!"int v%s;"(i));
1787         }
1788 
1789         mixin(GenerateThis);
1790     }
1791 
1792     struct Wrapper
1793     {
1794         VeryLargeType field;
1795 
1796         mixin(GenerateThis);
1797     }
1798 
1799     auto builder = Wrapper.Builder();
1800 }
1801 
1802 @("const nullable assignment")
1803 unittest
1804 {
1805     import std.typecons : Nullable;
1806 
1807     // non-reference type
1808     struct Foo
1809     {
1810     }
1811 
1812     struct Bar
1813     {
1814         Nullable!Foo foo;
1815 
1816         mixin(GenerateThis);
1817     }
1818 
1819     auto builder = Bar.Builder();
1820 
1821     // trigger assignment bug where dmd tries to roundtrip over const(Foo), implicitly triggering .get
1822     // avoided by additional assignment overload in the Nullable case
1823     builder.foo = Nullable!(const Foo)();
1824 }
1825 
1826 // can't strip const, because int[] is a reference type and precludes it
1827 @("const nullable assignment with reference type")
1828 unittest
1829 {
1830     import std.typecons : Nullable, nullable;
1831 
1832     struct Foo
1833     {
1834         int[] reference;
1835     }
1836 
1837     struct Bar
1838     {
1839         Nullable!Foo foo;
1840 
1841         mixin(GenerateThis);
1842     }
1843 
1844     auto builder = Bar.Builder();
1845 
1846     int[] array = [2];
1847     auto foo = Foo(array);
1848 
1849     // direct assignment still works
1850     static assert(__traits(compiles, { builder.foo = foo.nullable; }));
1851     // but const assignment is blocked by opAssign(U)
1852     static assert(!__traits(compiles, { builder.foo = (cast(const) foo).nullable; }));
1853 }
1854 
1855 @("nullable null assignment to buildable field")
1856 unittest
1857 {
1858     import std.typecons : Nullable;
1859 
1860     struct Foo
1861     {
1862         mixin(GenerateThis);
1863     }
1864 
1865     struct Bar
1866     {
1867         Nullable!Foo foo;
1868 
1869         mixin(GenerateThis);
1870     }
1871 
1872     auto builder = Bar.Builder();
1873 
1874     builder.foo = Nullable!Foo();
1875 
1876     builder.value.shouldEqual(Bar(Nullable!Foo()));
1877 }
1878 
1879 @("@safe generated class constructor")
1880 unittest
1881 {
1882     class Foo
1883     {
1884         pure:
1885         @safe:
1886 
1887         int i;
1888 
1889         mixin(GenerateThis);
1890     }
1891 }
1892 
1893 // helper to avoid lambda, std.algorithm use in heavily-reused mixin GenerateThisTemplate
1894 public string[] filterCanFind(string[] array, string[] other) nothrow pure @safe
1895 {
1896     import std.algorithm : canFind, filter;
1897 
1898     return array.filter!(a => other.canFind(a)).array;
1899 }
1900 
1901 // ditto
1902 public string[] mapFormat(string fmt, Range, T...)(Range range, T args)
1903 {
1904     import std.algorithm : map;
1905     import std.format : format;
1906     import std.range : iota, join;
1907 
1908     enum argElements = T.length.iota.map!(k => format!"args[%s][i]"(k)).join(", ");
1909 
1910     return range.map!((i) {
1911         return mixin("format!fmt(" ~ argElements ~ ")");
1912     }).array;
1913 }