The OpenD Programming Language

1 /++
2 Base reflection utilities.
3 
4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
5 Authors: Ilia Ki
6 Macros:
7 +/
8 module mir.reflection;
9 
10 import std.meta;
11 import std.traits: Parameters, isSomeFunction, FunctionAttribute, functionAttributes, EnumMembers, isAggregateType;
12 import mir.internal.meta: hasUDA, getUDAs;
13 import mir.functional: Tuple;
14 import mir.exception: toMutable;
15 
16 deprecated
17 package alias isSomeStruct = isAggregateType;
18 
19 /++
20 Attribute to force member serialization for static fields, compiletime `enum` members and non-property methods.
21 +/
22 enum reflectSerde;
23 
24 /++
25 Match types like `std.typeconst: Nullable`.
26 +/
27 template isStdNullable(T)
28 {
29     import std.traits : hasMember;
30 
31     private __gshared T* aggregate;
32 
33     enum bool isStdNullable =
34         hasMember!(T, "isNull") &&
35         hasMember!(T, "get") &&
36         hasMember!(T, "nullify") &&
37         is(typeof(__traits(getMember, aggregate, "isNull")()) == bool) &&
38         !is(typeof(__traits(getMember, aggregate, "get")()) == void) &&
39         is(typeof(__traits(getMember, aggregate, "nullify")()) == void);
40 }
41 
42 ///
43 version(mir_core_test) unittest
44 {
45     import std.typecons;
46     static assert(isStdNullable!(Nullable!double));
47 }
48 
49 ///
50 version(mir_core_test) unittest
51 {
52     import mir.algebraic;
53     static assert(isStdNullable!(Nullable!double));
54 }
55 
56 ///  Attribute for deprecated API
57 struct reflectDeprecated(string target)
58 {
59     ///
60     string msg;
61 
62     /++
63     Number in an issue tracker. Not mandatory.
64     +/
65     uint issueNumber = uint.max;
66     /++
67     Should be kind of version number if one can be given.
68     Can be something else if that's not possible. Not mandatory.
69     +/
70     string removalTime;
71 }
72 
73 /// Attribute to rename methods, types and functions
74 template ReflectName(string target)
75 {
76     ///
77     struct ReflectName(Args...)
78     {
79         ///
80         string name;
81     }
82 }
83 
84 /// ditto
85 template reflectName(string target = null, Args...)
86 {
87     ///
88     auto reflectName(string name)
89     {
90         alias TargetName = ReflectName!target;
91         return TargetName!Args(name);
92     }
93 }
94 
95 ///
96 version(mir_core_test) unittest
97 {
98     enum E { A, B, C }
99 
100     struct S
101     {
102         @reflectName("A")
103         int a;
104 
105         @reflectName!"c++"("B")
106         int b;
107 
108         @reflectName!("C",  double)("cd")
109         @reflectName!("C",  float)("cf")
110         F c(F)()
111         {
112             return b;
113         }
114     }
115 
116     import std.traits: hasUDA;
117 
118     alias UniName = ReflectName!null;
119     alias CppName = ReflectName!"c++";
120     alias CName = ReflectName!"C";
121 
122     static assert(hasUDA!(S.a, UniName!()("A")));
123     static assert(hasUDA!(S.b, CppName!()("B")));
124 
125     // static assert(hasUDA!(S.c, ReflectName)); // doesn't work for now
126     static assert(hasUDA!(S.c, CName));
127     static assert(hasUDA!(S.c, CName!double));
128     static assert(hasUDA!(S.c, CName!float));
129     static assert(hasUDA!(S.c, CName!double("cd")));
130     static assert(hasUDA!(S.c, CName!float("cf")));
131 }
132 
133 /// Attribute to rename methods, types and functions
134 template ReflectMeta(string target, string[] fields)
135 {
136     ///
137     struct ReflectMeta(Args...)
138     {
139         ///
140         Args args;
141         static foreach(i, field; fields)
142             mixin(`alias ` ~ field ~` = args[` ~ i.stringof ~`];`);
143     }
144 }
145 
146 /// ditto
147 template reflectMeta(string target, string[] fields)
148 {
149     ///
150     auto reflectMeta(Args...)(Args args)
151         if (args.length <= fields.length)
152     {
153         alias TargetMeta = ReflectMeta!(target, fields);
154         return TargetMeta!Args(args);
155     }
156 }
157 
158 ///
159 version(mir_core_test) unittest
160 {
161     enum E { A, B, C }
162 
163     struct S
164     {
165         int a;
166         @reflectMeta!("c++", ["type"])(E.C)
167         int b;
168     }
169 
170     import std.traits: hasUDA;
171 
172     alias CppMeta = ReflectMeta!("c++", ["type"]);
173 
174     static assert(CppMeta!E(E.C).type == E.C);
175     static assert(!hasUDA!(S.a, CppMeta!E(E.A)));
176     static assert(hasUDA!(S.b, CppMeta!E(E.C)));
177 }
178 
179 /++
180 Attribute to ignore a reflection target
181 +/
182 template reflectIgnore(string target)
183 {
184     enum reflectIgnore;
185 }
186 
187 ///
188 version(mir_core_test) unittest
189 {
190     struct S
191     {
192         @reflectIgnore!"c++"
193         int a;
194     }
195 
196     import std.traits: hasUDA;
197     static assert(hasUDA!(S.a, reflectIgnore!"c++"));
198 }
199 
200 /// Attribute for documentation and unittests
201 struct ReflectDoc(string target)
202 {
203     ///
204     string text;
205     ///
206     reflectUnittest!target test;
207 
208     ///
209     @safe pure nothrow @nogc
210     this(string text)
211     {
212         this.text = text;
213     }
214 
215     ///
216     @safe pure nothrow @nogc
217     this(string text, reflectUnittest!target test)
218     {
219         this.text = text;
220         this.test = test;
221     }
222 
223     ///
224     void toString(W)(scope ref W w) scope const
225     {
226         w.put(cast()this.text);
227 
228         if (this.test.text.length)
229         {
230             w.put("\nExample usage:\n");
231             w.put(cast()this.test.text);
232         }
233     }
234 
235     ///
236     @safe pure nothrow
237     string toString()() scope const
238     {
239         return this.text ~ "\nExample usage:\n" ~ this.test.text;
240     }
241 }
242 
243 /++
244 Attribute for documentation.
245 +/
246 template reflectDoc(string target = null)
247 {
248     ///
249     ReflectDoc!target reflectDoc(string text)
250     {
251         return ReflectDoc!target(text);
252     }
253 
254     ///
255     ReflectDoc!target reflectDoc(string text, reflectUnittest!target test)
256     {
257         return ReflectDoc!target(text, test);
258     }
259 }
260 
261 /++
262 +/
263 template reflectGetDocs(string target, alias symbol)
264 {
265     static if (hasUDA!(symbol, ReflectDoc!target))
266         static immutable(ReflectDoc!target[]) reflectGetDocs = [getUDAs!(symbol, ReflectDoc!target)];
267     else
268         static immutable(ReflectDoc!target[]) reflectGetDocs = null;
269 }
270 
271 /// ditto
272 template reflectGetDocs(string target)
273 {
274     ///
275     alias reflectGetDocs(alias symbol) = .reflectGetDocs!(target, symbol);
276 
277     /// ditto
278     immutable(ReflectDoc!target)[] reflectGetDocs(T)(T value)
279         @safe pure nothrow @nogc
280         if (is(T == enum))
281     {
282         foreach (i, member; EnumMembers!T)
283         {{
284             alias all = __traits(getAttributes, EnumMembers!T[i]);
285         }}
286         static immutable ReflectDoc!target[][EnumMembers!T.length] docs = [staticMap!(reflectGetDocs, EnumMembers!T)];
287         import mir.enums: getEnumIndex;
288         uint index = void;
289         if (getEnumIndex(value, index))
290             return docs[index];
291         assert(0);
292     }
293 }
294 
295 ///
296 version(mir_core_test) unittest
297 {
298     enum E
299     {
300         @reflectDoc("alpha")
301         a,
302         @reflectDoc!"C#"("Beta", reflectUnittest!"C#"("some c# code"))
303         @reflectDoc("beta")
304         b,
305         c,
306     }
307 
308     alias Doc = ReflectDoc!null;
309     alias CSDoc = ReflectDoc!"C#";
310 
311     static assert(reflectGetDocs!null(E.a) == [Doc("alpha")]);
312     static assert(reflectGetDocs!"C#"(E.b) == [CSDoc("Beta", reflectUnittest!"C#"("some c# code"))]);
313     static assert(reflectGetDocs!null(E.b) == [Doc("beta")]);
314     static assert(reflectGetDocs!null(E.c) is null);
315 
316     struct S
317     {
318         @reflectDoc("alpha")
319         @reflectDoc!"C#"("Alpha")
320         int a;
321     }
322 
323     static assert(reflectGetDocs!(null, S.a) == [Doc("alpha")]);
324     static assert(reflectGetDocs!("C#", S.a) == [CSDoc("Alpha")]);
325 
326     import std.conv: to;
327     static assert(CSDoc("Beta", reflectUnittest!"C#"("some c# code")).to!string == "Beta\nExample usage:\nsome c# code");
328 }
329 
330 /++
331 Attribute for extern unit-test.
332 +/
333 struct reflectUnittest(string target)
334 {
335     ///
336     string text;
337 
338 @safe pure nothrow @nogc:
339 
340     this(string text)
341     {
342         this.text = text;
343     }
344 
345     this(const typeof(this) other)
346     {
347         this.text = other.text;
348     }
349 }
350 
351 /++
352 +/
353 template reflectGetUnittest(string target, alias symbol)
354 {
355     static if (hasUDA!(symbol, reflectUnittest!target))
356         enum string reflectGetUnittest = getUDA!(symbol, reflectUnittest).text;
357     else
358         enum string reflectGetUnittest = null;
359 }
360 
361 /// ditto
362 template reflectGetUnittest(string target)
363 {
364     ///
365     alias reflectGetUnittest(alias symbol) = .reflectGetUnittest!(target, symbol);
366 
367     ///
368     string reflectGetUnittest(T)(T value)
369         if (is(T == enum))
370     {
371         foreach (i, member; EnumMembers!T)
372         {{
373             alias all = __traits(getAttributes, EnumMembers!T[i]);
374         }}
375         static immutable string[EnumMembers!T.length] tests = [staticMap!(reflectGetUnittest, EnumMembers!T)];
376         import mir.enums: getEnumIndex;
377         uint index = void;
378         if (getEnumIndex(value, index))
379             return tests[index];
380         assert(0);
381     }
382 }
383 
384 ///
385 version(mir_core_test) unittest
386 {
387     enum E
388     {
389         @reflectUnittest!"c++"("assert(E::a == 0);")
390         a,
391         @reflectUnittest!"c++"("assert(E::b == 1);")
392         b,
393         c,
394     }
395 
396     static assert(reflectGetUnittest!"c++"(E.a) == "assert(E::a == 0);");
397     static assert(reflectGetUnittest!"c++"(E.b) == "assert(E::b == 1);");
398     static assert(reflectGetUnittest!"c++"(E.c) is null);
399 
400     struct S
401     {
402         @reflectUnittest!"c++"("alpha")
403         int a;
404     }
405 
406     static assert(reflectGetUnittest!("c++", S.a) == "alpha");
407 }
408 
409 /++
410 Returns: single UDA.
411 +/
412 template getUDA(alias symbol, alias attribute)
413 {
414     private alias all = getUDAs!(symbol, attribute);
415     static if (all.length != 1)
416         static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required, " ~ "got " ~ all.length.stringof);
417     else
418     {
419         static if (is(typeof(all[0])))
420             enum getUDA = all[0];
421         else
422             alias getUDA = all[0];
423     }
424 }
425 
426 /// ditto
427 template getUDA(T, string member, alias attribute)
428 {
429     private alias all = getUDAs!(T, member, attribute);
430     static if (all.length != 1)
431         static assert(0, "Exactly one " ~ attribute.stringof ~ " attribute is required, " ~ "got " ~ all.length.stringof);
432     else
433     {
434         static if (is(typeof(all[0])))
435             enum getUDA = all[0];
436         else
437             alias getUDA = all[0];
438     }
439 }
440 
441 /++
442 Checks if T has a field member.
443 +/
444 enum bool isOriginalMember(T, string member) = __traits(identifier, __traits(getMember, T, member)) == member;
445 
446 ///
447 version(mir_core_test) unittest
448 {
449     struct D
450     {
451         int a;
452         alias b = a;
453     }
454 
455     static assert(isOriginalMember!(D, "a"));
456     static assert(!isOriginalMember!(D, "b"));
457 }
458 
459 /++
460 Checks if T has a field member.
461 +/
462 enum bool hasField(T, string member) = __traits(compiles, (ref T aggregate) { return __traits(getMember, aggregate, member).offsetof; });
463 
464 deprecated("use 'hasField' instead") alias isField = hasField;
465 
466 ///
467 version(mir_core_test) unittest
468 {
469     struct D
470     {
471         int gi;
472     }
473 
474     struct I
475     {
476         int f;
477 
478         D base;
479         alias base this;
480 
481         void gi(double ) @property {}
482         void gi(uint ) @property {}
483     }
484 
485     struct S
486     {
487         int d;
488 
489         I i;
490         alias i this;
491 
492         int gm() @property {return 0;}
493         int gc() const @property {return 0;}
494         void gs(int) @property {}
495     }
496 
497     static assert(!hasField!(S, "gi"));
498     static assert(!hasField!(S, "gs"));
499     static assert(!hasField!(S, "gc"));
500     static assert(!hasField!(S, "gm"));
501     static assert(!hasField!(S, "gi"));
502     static assert(hasField!(S, "d"));
503     static assert(hasField!(S, "f"));
504     static assert(hasField!(S, "i"));
505 }
506 
507 ///  with classes
508 version(mir_core_test) unittest
509 {
510     class I
511     {
512         int f;
513 
514         void gi(double ) @property {}
515         void gi(uint ) @property {}
516     }
517 
518     class S
519     {
520         int d;
521 
522         I i;
523         alias i this;
524 
525         int gm() @property {return 0;}
526         int gc() const @property {return 0;}
527         void gs(int) @property {}
528     }
529 
530     static assert(!hasField!(S, "gi"));
531     static assert(!hasField!(S, "gs"));
532     static assert(!hasField!(S, "gc"));
533     static assert(!hasField!(S, "gm"));
534     static assert(hasField!(S, "d"));
535     static assert(hasField!(S, "f"));
536     static assert(hasField!(S, "i"));
537 }
538 
539 /++
540 Checks if member is property.
541 +/
542 template isProperty(T, string member)
543 {
544     private __gshared T* aggregate;
545 
546     static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
547     {
548         static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
549         {
550             enum bool isProperty = isPropertyImpl!(__traits(getMember, *aggregate, member));
551         }
552         else
553         {
554             enum bool isProperty = false;
555         }
556     }
557     else
558         enum bool isProperty = false;
559 }
560 
561 ///
562 version(mir_core_test) unittest
563 {
564     struct D
565     {
566         int y;
567 
568         void gf(double ) @property {}
569         void gf(uint ) @property {}
570     }
571 
572     struct I
573     {
574         int f;
575 
576         D base;
577         alias base this;
578 
579         void gi(double ) @property {}
580         void gi(uint ) @property {}
581     }
582 
583     struct S
584     {
585         int d;
586 
587         I i;
588         alias i this;
589 
590         int gm() @property {return 0;}
591         int gc() const @property {return 0;}
592         void gs(int) @property {}
593     }
594 
595     static assert(isProperty!(S, "gf"));
596     static assert(isProperty!(S, "gi"));
597     static assert(isProperty!(S, "gs"));
598     static assert(isProperty!(S, "gc"));
599     static assert(isProperty!(S, "gm"));
600     static assert(!isProperty!(S, "d"));
601     static assert(!isProperty!(S, "f"));
602     static assert(!isProperty!(S, "y"));
603 }
604 
605 version(mir_core_test) unittest
606 {
607     struct S
608     {
609         @reflectSerde enum s = "str";
610         enum t = "str";
611     }
612     static assert(hasUDA!(S, "s", reflectSerde));
613     static assert(!hasUDA!(S, "t", reflectSerde));
614 }
615 
616 /++
617 Returns: list of the setter properties.
618 
619 Note: The implementation ignores templates.
620 +/
621 template getSetters(T, string member)
622 {
623     static if (__traits(hasMember, T, member))
624         alias getSetters = Filter!(hasSingleArgument, Filter!(isPropertyImpl, __traits(getOverloads, T, member, true)));
625     else
626         alias getSetters = AliasSeq!();
627 }
628 
629 ///
630 version(mir_core_test) unittest
631 {
632     struct I
633     {
634         int f;
635 
636         void gi(double ) @property {}
637         void gi(uint ) @property {}
638     }
639 
640     struct S
641     {
642         int d;
643 
644         I i;
645         alias i this;
646 
647         int gm() @property {return 0;}
648         int gc() const @property {return 0;}
649         void gs(int) @property {}
650     }
651 
652     static assert(getSetters!(S, "gi").length == 2);
653     static assert(getSetters!(S, "gs").length == 1);
654     static assert(getSetters!(S, "gc").length == 0);
655     static assert(getSetters!(S, "gm").length == 0);
656     static assert(getSetters!(S, "d").length == 0);
657     static assert(getSetters!(S, "f").length == 0);
658 }
659 
660 /++
661 Returns: list of the serializable (public getters) members.
662 +/
663 enum string[] SerializableMembers(T) = [Filter!(ApplyLeft!(Serializable, T), SerdeFieldsAndProperties!T)];
664 
665 ///
666 version(mir_core_test) unittest
667 {
668     struct D
669     {
670         int y;
671 
672         int gf() @property {return 0;}
673     }
674 
675     struct I
676     {
677         int f;
678 
679         D base;
680         alias base this;
681 
682         int gi() @property {return 0;}
683     }
684 
685     struct S
686     {
687         int d;
688 
689         package int p;
690 
691         enum s = "str";
692         @reflectSerde enum t = "str";
693 
694         int gm() @property {return 0;}
695 
696         private int q;
697 
698         I i;
699         alias i this;
700 
701         int gc() const @property {return 0;}
702         void gs(int) @property {}
703     }
704 
705     static assert(SerializableMembers!S == ["y", "gf", "f", "gi", "d", "t", "gm", "gc"]);
706     static assert(SerializableMembers!(const S) == ["y", "f", "d", "t", "gc"]);
707 }
708 
709 /++
710 Returns: list of the deserializable (public setters) members.
711 +/
712 enum string[] DeserializableMembers(T) = [Filter!(ApplyLeft!(Deserializable, T), SerdeFieldsAndProperties!T)];
713 
714 ///
715 version(mir_core_test) unittest
716 {
717     struct I
718     {
719         int f;
720         void ga(int) @property {}
721     }
722 
723     struct S
724     {
725         int d;
726         package int p;
727 
728         int gm() @property {return 0;}
729         void gm(int) @property {}
730 
731         private int q;
732 
733         I i;
734         alias i this;
735 
736 
737         void gc(int, int) @property {}
738         void gc(int) @property {}
739     }
740 
741     S s;
742     // s.gc(0);
743 
744     static assert (DeserializableMembers!S == ["f", "ga", "d", "gm", "gc"]);
745     static assert (DeserializableMembers!(const S) == []);
746 }
747 
748 // This trait defines what members should be serialized -
749 // public members that are either readable and writable or getter properties
750 private template Serializable(T, string member)
751 {
752     static if (!isPublic!(T, member))
753         enum Serializable = false;
754     else
755         enum Serializable = isReadable!(T, member); // any readable is good
756 }
757 
758 private enum bool hasSingleArgument(alias fun) = Parameters!fun.length == 1;
759 private enum bool hasZeroArguments(alias fun) = Parameters!fun.length == 0;
760 
761 // This trait defines what members should be serialized -
762 // public members that are either readable and writable or setter properties
763 private template Deserializable(T, string member)
764 {
765     static if (!isPublic!(T, member))
766         enum Deserializable = false;
767     else
768     static if (isReadableAndWritable!(T, member))
769         enum Deserializable = true;
770     else
771     static if (getSetters!(T, member).length == 1)
772         enum Deserializable =  is(typeof((ref T val){ __traits(getMember, val, member) = Parameters!(getSetters!(T, member)[0])[0].init; }));
773     else
774         enum Deserializable = false;
775 }
776 
777 private enum SerdeFieldsAndProperties(T) = Reverse!(NoDuplicates!(Reverse!(SerdeFieldsAndPropertiesImpl!T)));
778 
779 
780 private static immutable exlMembers = [
781     "opAssign",
782     "opCast",
783     "opCmp",
784     "opEquals",
785     "opPostMove",
786     "toHash",
787     "toString",
788     "trustedGet",
789     "deserializeFromAsdf",
790     "deserializeFromIon",
791 ];
792 
793 private auto filterMembers(string[] members)
794 {
795     string[] ret;
796 
797     L: foreach(member; members)
798     {
799         if (member.length >= 2 && (member[0 .. 2] == "__" || member[$ - 2 .. $] == "__"))
800             continue;
801         foreach(exlMember; exlMembers)
802             if (exlMember == member)
803                 continue L;
804         ret ~= member;
805     }
806     return ret;
807 };
808 
809 private template allMembers(T)
810 {
811     import std.meta: aliasSeqOf;
812     static if (isAggregateType!T)
813         alias allMembers = aliasSeqOf!(filterMembers([__traits(allMembers, T)]));
814     else
815         alias allMembers = AliasSeq!();
816 }
817 
818 private template SerdeFieldsAndPropertiesImpl(T)
819 {
820     alias isProperty = ApplyLeft!(.isProperty, T);
821     alias hasField = ApplyLeft!(.hasField, T);
822     alias isOriginalMember = ApplyLeft!(.isOriginalMember, T);
823     private __gshared T* aggregate;
824     template hasReflectSerde(string member)
825     {
826         static if (is(typeof(__traits(getMember, *aggregate, member))))
827             enum hasReflectSerde = hasUDA!(T, member, reflectSerde);
828         else
829             enum hasReflectSerde = false;
830     }
831     alias isMember = templateAnd!(templateOr!(hasField, isProperty, hasReflectSerde), isOriginalMember);
832     static if (!is(immutable T == Tuple!Types, Types...) && __traits(getAliasThis, T).length)
833     {
834         alias A = typeof(__traits(getMember, aggregate, __traits(getAliasThis, T)));
835         static if (isAggregateType!T)
836             alias baseMembers = SerdeFieldsAndPropertiesImpl!A;
837         else
838             alias baseMembers = AliasSeq!();
839         alias members = Erase!(__traits(getAliasThis, T)[0], __traits(allMembers, T));
840         alias SerdeFieldsAndPropertiesImpl = AliasSeq!(baseMembers, Filter!(isMember, members));
841     }
842     else
843     {
844         import mir.algebraic;
845         static if (isVariant!T)
846             alias members = staticMap!(allMembers, T.AllowedTypes);
847         else
848             alias members = allMembers!T;
849         alias SerdeFieldsAndPropertiesImpl = AliasSeq!(Filter!(isMember, members));
850     }
851 }
852 
853 // check if the member is readable
854 private template isReadable(T, string member)
855 {
856     private __gshared T* aggregate;
857     enum bool isReadable = __traits(compiles, { static fun(T)(auto ref T t) {} fun(__traits(getMember, *aggregate, member)); });
858 }
859 
860 // check if the member is readable/writeble?
861 private template isReadableAndWritable(T, string member)
862 {
863     private __gshared T* aggregate;
864     enum bool isReadableAndWritable = __traits(compiles, __traits(getMember, *aggregate, member) = __traits(getMember, *aggregate, member));
865 }
866 
867 package template isPublic(T, string member)
868 {
869     private __gshared T* aggregate;
870     static if (__traits(compiles, { auto _ = __traits(getProtection, __traits(getMember, *aggregate, member)); }))
871         enum bool isPublic = !__traits(getProtection, __traits(getMember, *aggregate, member)).privateOrPackage;
872     else
873         enum bool isPublic = false;
874 }
875 
876 version(unittest)
877 {
878     final class ZipArchive
879     {
880     public:
881         static const ushort zip64ExtractVersion = 45;
882     }
883 }
884 
885 version (mir_core_test) @nogc nothrow pure @safe unittest
886 {
887     static assert(!isPublic!(ZipArchive, "zip64ExtractVersion"));
888 }
889 
890 // check if the member is property
891 private template isSetter(T, string member)
892 {
893     private __gshared T* aggregate;
894     static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
895     {
896         static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
897         {
898             enum bool isSetter = getSetters!(T, member).length > 0;;
899         }
900         else
901         {
902             enum bool isSetter = false;
903         }
904     }
905     else
906         enum bool isSetter = false;
907 }
908 
909 private template isGetter(T, string member)
910 {
911     private __gshared T* aggregate;
912     static if (__traits(compiles, isSomeFunction!(__traits(getMember, *aggregate, member))))
913     {
914         static if (isSomeFunction!(__traits(getMember, *aggregate, member)))
915         {
916             enum bool isGetter = Filter!(hasZeroArguments, Filter!(isPropertyImpl, __traits(getOverloads, T, member, true))).length == 1;
917         }
918         else
919         {
920             enum bool isGetter = false;
921         }
922     }
923     else
924         enum bool isGetter = false;
925 }
926 
927 private enum bool isPropertyImpl(alias member) = (functionAttributes!member & FunctionAttribute.property) != 0;
928 
929 private bool privateOrPackage()(string protection)
930 {
931     return protection == "private" || protection == "package";
932 }