1 /**
2 Date: 2015-2016, Joakim Brännström
3 License: MPL-2, Mozilla Public License 2.0
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 Extracted information of types.
7 
8 TODO replace the fmt with a specialized formatting struct for the purpose
9 needed by TypeKind.  fmt is namely a typeless strict that do not encode the
10 assumed number or arguments when it is used.  In other words it do not contain
11 information regarding the number of '%s'.
12 */
13 module cpptooling.data.kind;
14 
15 import std.conv : to;
16 import std..string : format;
17 import std.traits;
18 import std.typecons : Nullable, Flag;
19 import logger = std.experimental.logger;
20 
21 import cpptooling.data.symbol.types : USRType;
22 
23 version (unittest) {
24     import unit_threaded : Name, shouldEqual;
25 } else {
26     private struct Name {
27         string name_;
28     }
29 }
30 
31 alias ArrayInfoIndex = Nullable!long;
32 
33 struct FuncInfoParam {
34     USRType usr;
35     TypeAttr attr;
36     string id;
37     Flag!"isVariadic" isVariadic;
38 }
39 
40 /// Convert an array of indexes to a string representation
41 string toRepr(const(ArrayInfoIndex[]) indexes) @safe pure {
42     import std.algorithm : map, joiner;
43     import std.conv : text;
44 
45     // dfmt off
46     return indexes
47         // a null is a dynamic index
48         .map!(a => a.isNull ? "[]" : "[" ~ text(a.get) ~ "]")
49         .joiner
50         .text;
51     // dfmt on
52 }
53 
54 bool isIncompleteArray(const(ArrayInfoIndex)[] indexes) @safe pure nothrow @nogc {
55     foreach (index; indexes) {
56         if (index.isNull)
57             return true;
58     }
59 
60     return false;
61 }
62 
63 /** Type representation and information.
64  */
65 struct TypeKind {
66     import std.traits : isSomeString;
67     import taggedalgebraic : TaggedAlgebraic;
68     import cpptooling.data.kind_type_format;
69 
70     this(T)(T info, USRType usr) @safe if (!is(T == TypeKind)) {
71         this.info = info;
72         this.usr = usr;
73     }
74 
75     this(T)(T info) @safe if (!is(T == TypeKind)) {
76         this(info, USRType(""));
77     }
78 
79     invariant {
80         final switch (this.info.kind) with (TypeKind.Info) {
81         case Kind.ctor:
82             // Assuming that a ctor or dtor must always have a id, otherwise
83             // unusable
84             goto case;
85         case Kind.dtor:
86             assert(info.id.length > 0);
87             break;
88         case Kind.array:
89         case Kind.func:
90         case Kind.funcSignature:
91         case Kind.primitive:
92         case Kind.record:
93         case Kind.simple:
94         case Kind.typeRef:
95             break;
96         case Kind.funcPtr:
97             assert(info.attrs.length > 0);
98             break;
99         case Kind.pointer:
100             assert(info.attrs.length > 0);
101             break;
102         case Kind.null_:
103             break;
104         }
105     }
106 
107     /// Formatting information needed to reproduce the type and identifier.
108     static @safe pure nothrow @nogc union InternalInfo {
109         typeof(null) null_;
110         PrimitiveInfo primitive;
111         SimpleInfo simple;
112         ArrayInfo array;
113 
114         FuncInfo func;
115         FuncPtrInfo funcPtr;
116         FuncSignatureInfo funcSignature;
117 
118         RecordInfo record;
119         CtorInfo ctor;
120         DtorInfo dtor;
121         PointerInfo pointer;
122         TypeRefInfo typeRef;
123     }
124 
125     alias Info = TaggedAlgebraic!InternalInfo;
126 
127     Info info;
128     USRType usr;
129 
130 pure @safe nothrow @nogc:
131 
132     /** The type 'const int x[2][3]'
133      */
134     static struct ArrayInfo {
135         /// int %s%s
136         ArrayFmt fmt;
137         /// [2, 3]
138         ArrayInfoIndex[] indexes;
139         /// usr for 'int'
140         USRType element;
141     }
142 
143     /** The type 'extern int (*e_g)(int pa)'.
144      *
145      * attrs is only for the pointers, never the final pointee.
146      * In the example shown about it would have length 1.
147      *
148      * TODO improve formatting with more separation, f.e return, ptr and args.
149      * TODO add a USRType for the FuncPrototype.
150      */
151     static struct FuncPtrInfo {
152         /// int (%s %s)(int pa)
153         FuncPtrFmt fmt;
154         /// USRs up the pointee
155         USRType pointee;
156         /// attributes of the pointer hierarchy. attr[0] is the right most ptr.
157         TypeAttr[] attrs;
158     }
159 
160     /** The type of a function signature, 'void foo(int)'.
161      */
162     static struct FuncSignatureInfo {
163         /// void %s(int)
164         FuncSignatureFmt fmt;
165         USRType return_;
166         TypeAttr returnAttr;
167         FuncInfoParam[] params;
168     }
169 
170     /** The type of a function prototype, 'void foo(int)'.
171      *
172      * TODO consider changing the chain to be a FuncInfo referencing a FuncSignatureInfo.
173      *
174      * This coupled with FuncSignatureInfo having the USR of the signature
175      * would mean that it would be possible to merge/detect/find all those
176      * FuncInfo with the same symbol mangling/signature.
177      *
178      * Which is needed when doing cross-translation unit analyse to find
179      * connections between "points of interest.
180      *
181      * It would also lower the amount of data in a FuncInfo.
182      *
183      */
184     static struct FuncInfo {
185         /// void %s(int)
186         FuncFmt fmt;
187         USRType return_;
188         TypeAttr returnAttr;
189         FuncInfoParam[] params;
190     }
191 
192     /** The type of a ctor prototype, 'Class::Class(int)'
193      */
194     static struct CtorInfo {
195         /// %s(int)
196         CtorFmt fmt;
197         /// Class
198         string id;
199         FuncInfoParam[] params;
200     }
201 
202     /** The type of a dtor prototype, 'Class::~Class()'
203      */
204     static struct DtorInfo {
205         /// ~%s()
206         DtorFmt fmt;
207         /// identifier, in the example it would be 'Class'
208         string id;
209     }
210 
211     /** The type of a pointer (may be recursive), 'const int ** const x'
212      *
213      * attrs is only for the pointers, never the final pointee.
214      * In the example shown about it would have length 2.
215      */
216     static struct PointerInfo {
217         /// int%s %s
218         PtrFmt fmt;
219         /// USRs up the pointee
220         USRType pointee;
221         /// attributes of the pointer hierarchy. attr[0] is the left most ptr.
222         TypeAttr[] attrs;
223     }
224 
225     /** Representation of a typedef, 'typedef int tx'
226      *
227      * canonicalType is the final resolved in a chain of typedef's.
228      */
229     static struct TypeRefInfo {
230         /// tx %s
231         SimpleFmt fmt;
232         /// usr of the child type
233         USRType typeRef;
234         /// usr of the canonical type
235         USRType canonicalRef;
236     }
237 
238     /** Represent a primitive type.
239      *
240      * Similar to a $(D SimpleInfo) but used to distinguish primitive types
241      * from "other" simple representations.
242      *
243      * The type 'int x' would be:
244      */
245     static struct PrimitiveInfo {
246         /// int %s
247         SimpleFmt fmt;
248     }
249 
250     /** Textual representation of simple types.
251      *
252      * A simple type is one that do not need the features or distinction of the
253      * other infos.
254      *
255      * The type 'const int x' would be:
256      */
257     static struct SimpleInfo {
258         /// int %s
259         SimpleFmt fmt;
260     }
261 
262     /** The type 'const A'
263      */
264     static struct RecordInfo {
265         /// A %s
266         SimpleFmt fmt;
267     }
268 }
269 
270 /// Attributes for a type.
271 @safe @nogc struct TypeAttr {
272     import std.typecons : Flag;
273     import std.format : FormatSpec;
274 
275     Flag!"isConst" isConst;
276     Flag!"isRef" isRef;
277     Flag!"isPtr" isPtr;
278     Flag!"isFuncPtr" isFuncPtr;
279     Flag!"isArray" isArray;
280     Flag!"isDefinition" isDefinition;
281 
282     /// Returns: a string range of the attributes
283     auto stringRange() const {
284         import std.range : chain, only;
285         import std.algorithm : filter;
286 
287         // dfmt off
288         return chain(only(isConst ? "const" : null),
289               only(isRef ? "ref" : null),
290               only(isPtr ? "ptr" : null),
291               only(isFuncPtr ? "funcPtr" : null),
292               only(isArray ? "array" : null))
293             .filter!(a => a !is null);
294         // dfmt on
295     }
296 
297     ///
298     void toString(Writer, Char)(scope Writer w, FormatSpec!Char fmt = "%s") const {
299         import std.algorithm : joiner, copy;
300 
301         // dfmt off
302         this.stringRange
303             .joiner(";")
304             .copy(w);
305         // dfmt on
306     }
307 
308     string toString() @safe pure const {
309         import std.exception : assumeUnique;
310         import std.format : FormatSpec;
311 
312         char[] buf;
313         buf.reserve(100);
314         auto fmt = FormatSpec!char("%s");
315         toString((const(char)[] s) { buf ~= s; }, fmt);
316         auto trustedUnique(T)(T t) @trusted {
317             return assumeUnique(t);
318         }
319 
320         return trustedUnique(buf);
321     }
322 }
323 
324 /** Resolve the canonical type.
325  *
326  * TODO merge with resolvePointeeType. (It wasn't done as of this writing
327  * because I'm not sure they will stay similare enough to allow a merge).
328  *
329  * TODO I think that the resuilt from array/funcPtr/pointee should be checked
330  * if they are a typedef. May be a bug that complicates the result at other
331  * places.
332  *
333  * Params:
334  *   LookupT = a type supporting the method "kind" taking a USR and returning a
335  *             TypeKind.
336  *   type = the type to resolve
337  *   attr_ = attributes for the type, depending on the result from the lookup
338  *           either this attributes are used or those from the lookup
339  *   lookup = see $(D LookupT)
340  *
341  * Returns: TypeKindAttr of the canonical type.
342  */
343 auto resolveCanonicalType(LookupT)(TypeKind type, TypeAttr attr, LookupT lookup)
344         if (__traits(hasMember, LookupT, "kind")) {
345     import std.algorithm : among;
346     import std.range : only, dropOne;
347     import cpptooling.data : TypeKindAttr;
348 
349     auto rval = only(TypeKindAttr.init).dropOne;
350     auto found = typeof(lookup.kind(USRType.init)).init;
351 
352     final switch (type.info.kind) with (TypeKind.Info) {
353     case Kind.array:
354         found = lookup.kind(type.info.element);
355         break;
356     case Kind.funcPtr:
357         found = lookup.kind(type.info.pointee);
358         break;
359     case Kind.pointer:
360         found = lookup.kind(type.info.pointee);
361         break;
362     case Kind.typeRef:
363         found = lookup.kind(type.info.canonicalRef);
364         break;
365     case Kind.ctor:
366     case Kind.dtor:
367     case Kind.func:
368     case Kind.funcSignature:
369     case Kind.primitive:
370     case Kind.record:
371     case Kind.simple:
372         rval = only(TypeKindAttr(type, attr));
373         break;
374     case Kind.null_:
375         break;
376     }
377 
378     foreach (item; found) {
379         rval = only(TypeKindAttr(item.get, attr));
380     }
381 
382     return rval;
383 }
384 
385 /** Resolve the typeref type.
386  *
387  * TODO merge with resolvePointeeType. (It wasn't done as of this writing
388  * because I'm not sure they will stay similare enough to allow a merge).
389  *
390  * Params:
391  *   LookupT = a type supporting taking a USR and returning a TypeKind.
392  *   type = the type to resolve
393  *   lookup = see $(D LookupT)
394  *
395  * Returns: TypeKind of the canonical type.
396  */
397 TypeKind resolveTypeRef(LookupT)(const TypeKind type, LookupT lookup) {
398     import std.algorithm : among;
399     import std.range : only, dropOne;
400     import cpptooling.data : TypeKindAttr;
401 
402     if (type.info.kind == TypeKind.Info.Kind.typeRef) {
403         foreach (a; lookup(type.info.canonicalRef)) {
404             TypeKind t = a;
405             return t;
406         }
407     }
408 
409     return type;
410 }
411 
412 /** Resolve the pointe type.
413  *
414  * Params:
415  *   LookupT = a type supporting the method "kind" taking a USR as parameter,
416  *      returning the result as a TypeKind wrapped in a range.
417  *   type = the type to resolve
418  *   attr_ = attributes for the type, depending on the result from the lookup
419  *      either this attributes are used or those from the lookup
420  *   lookup = see $(D LookupT)
421  *
422  * Returns: TypeKindAttr of the pointee type.
423  */
424 auto resolvePointeeType(LookupT)(TypeKind type, TypeAttr attr, LookupT lookup)
425         if (__traits(hasMember, LookupT, "kind")) {
426     import std.algorithm : among;
427     import std.range : only, dropOne;
428     import cpptooling.data : TypeKindAttr;
429 
430     auto rval = only(TypeKindAttr.init).dropOne;
431     auto found = typeof(lookup.kind(USRType.init)).init;
432 
433     final switch (type.info.kind) with (TypeKind.Info) {
434     case Kind.array:
435         found = lookup.kind(type.info.element);
436         break;
437     case Kind.funcPtr:
438         found = lookup.kind(type.info.pointee);
439         break;
440     case Kind.pointer:
441         found = lookup.kind(type.info.pointee);
442         break;
443     case Kind.typeRef:
444     case Kind.ctor:
445     case Kind.dtor:
446     case Kind.func:
447     case Kind.funcSignature:
448     case Kind.primitive:
449     case Kind.record:
450     case Kind.simple:
451         rval = only(TypeKindAttr(type, attr));
452         break;
453     case Kind.null_:
454         break;
455     }
456 
457     foreach (item; found) {
458         rval = only(TypeKindAttr(item.get, attr));
459     }
460 
461     return rval;
462 }
463 
464 // Test instantiation
465 @safe unittest {
466     TypeKind tk;
467 }