1 /**
2    Information about types and symbols at compile-time,
3    similar to std.traits.
4  */
5 module mirror.traits;
6 
7 
8 /// Usable as a predicate to std.meta.Filter
9 enum isEnum(T) = is(T == enum);
10 
11 /// Usable as a predicate to std.meta.Filter
12 enum isStruct(T) = is(T == struct);
13 
14 /// Usable as a predicate to std.meta.Filter
15 enum isInterface(T) = is(T == interface);
16 
17 /// Usable as a predicate to std.meta.Filter
18 enum isClass(T) = is(T == class);
19 
20 /**
21    If a type is a class or an interface.
22    Usable as a predicate to std.meta.Filter
23 */
24 enum isOOP(T) = is(T == class) || is(T == interface);
25 
26 
27 template moduleOf(alias T) {
28     import std.traits: moduleName;
29     mixin(`import `, moduleName!T, `;`);
30     mixin(`alias moduleOf = `, moduleName!T, `;`);
31 }
32 
33 
34 template isPrivate(alias symbol) {
35     // If a module contains an alias to a basic type, e.g. `alias L = long;`,
36     // then __traits(getProtection, member) fails to compile
37     static if(__traits(compiles, __traits(getProtection, symbol)))
38         enum isPrivate = __traits(getProtection, symbol) == "private";
39     else
40         enum isPrivate = true;  // if it doesn't compile, treat it as private
41 }
42 
43 
44 /**
45    Retrieves the "fundamental type" of a type T.  For most types, this
46    will be exactly the same as T itself.  For arrays or pointers, it
47    removes as many "layers" of array or pointer indirections to get to
48    the most basic atomic type possible.  Examples of inputs and
49    outputs:
50 
51    * T -> T
52    * T[] -> T
53    * T[][] -> T
54    * T* -> T
55 
56  */
57 template FundamentalType(T) {
58 
59     import std.traits: isArray, isPointer, PointerTarget;
60     import std.range: ElementEncodingType;
61 
62     static if(isArray!T)
63         alias removeOneIndirection = ElementEncodingType!T;
64     else static if(isPointer!T)
65         alias removeOneIndirection = PointerTarget!T;
66 
67     private enum isArrayOrPointer(U) = isArray!U || isPointer!U;
68 
69     static if(isArrayOrPointer!T) {
70         static if(isArrayOrPointer!removeOneIndirection)
71             alias FundamentalType = FundamentalType!removeOneIndirection;
72         else
73             alias FundamentalType = removeOneIndirection;
74     } else
75         alias FundamentalType = T;
76 }
77 
78 
79 /**
80    Returns an AliasSeq of all field types of `T`, depth-first
81    recursively.
82  */
83 alias RecursiveFieldTypes(T) = RecursiveFieldTypesImpl!T;
84 
85 
86 private template RecursiveFieldTypesImpl(T, alreadySeen...) {
87 
88     import mirror.traits: isStruct, isClass;
89     import std.meta: staticMap, AliasSeq, NoDuplicates, Filter,
90         templateNot, staticIndexOf;
91 
92     enum isStructOrClass(U) = isStruct!(FundamentalType!U) || isClass!(FundamentalType!U);
93 
94     static if(isStructOrClass!T) {
95 
96         private alias fields = AliasSeq!(T.tupleof);
97         private alias publicFields = Filter!(templateNot!isPrivate, fields);
98         private alias type(alias symbol) = typeof(symbol);
99         private alias types = staticMap!(type, fields);
100 
101         private template recurse(U) {
102 
103             static if(isStructOrClass!U) {
104 
105                 // only recurse if the type hasn't been seen yet to
106                 // prevent infinite recursion
107                 enum shouldRecurse = staticIndexOf!(U, alreadySeen) == -1;
108 
109                 static if(shouldRecurse)
110                     alias recurse = AliasSeq!(U, RecursiveFieldTypesImpl!(FundamentalType!U, NoDuplicates!(T, types, alreadySeen)));
111                 else
112                     alias recurse = AliasSeq!();
113             } else
114                 alias recurse = U;
115         }
116 
117         alias RecursiveFieldTypesImpl = NoDuplicates!(staticMap!(recurse, types));
118     } else
119         alias RecursiveFieldTypesImpl = T;
120 }
121 
122 
123 /**
124    An std.meta.AliasSeq of `T` and all its recursive
125    subtypes.
126  */
127 template RecursiveTypeTree(T...) {
128     import std.meta: staticMap, NoDuplicates;
129     alias RecursiveTypeTree = NoDuplicates!(T, staticMap!(RecursiveFieldTypes, T));
130 }
131 
132 
133 /**
134    Whether or not `F` is a property function
135  */
136 template isProperty(alias F) {
137     import std.traits: functionAttributes, FunctionAttribute;
138     enum isProperty = functionAttributes!F & FunctionAttribute.property;
139 }
140 
141 
142 /**
143    All member function symbols in T with overloads represented
144    separately.
145  */
146 template MemberFunctions(T) if(isStruct!T || isClass!T || isInterface!T)
147 {
148     import mirror.meta: functionsByOverload, Protection;
149     import std.meta: Filter, staticMap;
150 
151     private enum isPublic(alias F) = F.protection != Protection.private_;
152     private alias symbolOf(alias S) = S.symbol;
153 
154     alias MemberFunctions = Filter!(isMemberFunction,
155                                     staticMap!(symbolOf,
156                                                Filter!(isPublic,
157                                                        functionsByOverload!(T, PublicMembers!T))));
158 }
159 
160 
161 // must be a global template
162 private template isMemberFunction(alias F) {
163     import std.algorithm: startsWith;
164 
165     static if(__traits(compiles, __traits(identifier, F))) {
166         enum name = __traits(identifier, F);
167         alias parent = __traits(parent, F);
168 
169         static if(isOOP!parent) {
170             private static bool isWantedFunction(string name) {
171                 import std.algorithm: among;
172                 return
173                     !name.among("toString", "toHash", "opCmp", "opEquals", "factory")
174                     && !name.startsWith("__")
175                     ;
176             }
177         } else {
178             bool isWantedFunction(string name) { return true; }
179         }
180         private bool isOperator(string name) {
181             return name.startsWith("op") && name.length > 2 && name[2] >= 'A';
182         }
183 
184         enum isOp = isOperator(name);
185         enum isMemberFunction = isWantedFunction(name) && !isOperator(name);
186 
187     } else
188         enum isMemberFunction = false;
189 }
190 
191 
192 template PublicMembers(alias A) {
193     import mirror.traits: isPrivate;
194     import std.meta: Filter, staticMap, Alias, AliasSeq;
195 
196     package template member(string name) {
197 
198         enum identifier = name;
199 
200         static if(__traits(compiles, Alias!(__traits(getMember, A, name)))) {
201 
202             alias symbol = Alias!(__traits(getMember, A, name));
203 
204             static if(is(symbol))
205                 alias Type = symbol;
206             else static if(is(typeof(symbol)))
207                 alias Type = typeof(symbol);
208             else
209                 alias Type = void;
210 
211         } else
212             alias symbol = void;
213     }
214 
215     private alias members = staticMap!(member, __traits(allMembers, A));
216 
217     // In the `member` template above, if it's not possible to get a member from `A`,
218     // then the symbol is an empty AliasSeq. An example of such a situation can be
219     // found in `modules.problems` from the tests directory, where this causes things
220     // to not compile: `version = OopsVersion;`.
221     // So we filter out such members.
222     private enum hasSymbol(alias member) = !is(member == void);
223     private alias goodMembers = Filter!(hasSymbol, members);
224 
225     private enum notPrivate(alias member) = !isPrivate!(member.symbol);
226 
227     alias PublicMembers = Filter!(notPrivate, goodMembers);
228 }
229 
230 
231 package template memberIsSomeFunction(alias member) {
232     import std.traits: isSomeFunction;
233     enum memberIsSomeFunction = isSomeFunction!(member.symbol);
234 }
235 
236 
237 package template memberIsRegularFunction(alias member) {
238     static if(memberIsSomeFunction!member) {
239         import std.algorithm: startsWith;
240         enum memberIsRegularFunction =
241             !member.identifier.startsWith("_sharedStaticCtor")
242             && !member.identifier.startsWith("_staticCtor")
243             ;
244     } else
245         enum memberIsRegularFunction = false;
246 }