1 /**
2 Date: 2015-2017, 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 module cpptooling.data.kind_type;
9 
10 import std.conv : to;
11 import std..string : format;
12 import std.traits;
13 import std.typecons : Tuple, Nullable, Flag;
14 import logger = std.experimental.logger;
15 
16 import cpptooling.data.symbol.types : USRType;
17 import cpptooling.data.type : LocationTag;
18 
19 public import cpptooling.data.kind;
20 
21 struct TypeKindAttr {
22     TypeKind kind;
23     TypeAttr attr;
24 }
25 
26 struct TypeResult {
27     TypeKindAttr type;
28     LocationTag location;
29 }
30 
31 struct TypeResults {
32     TypeResult primary;
33     TypeResult[] extra;
34 }
35 
36 /** Merge rhs into lhs.
37  */
38 ref TypeResults mergeExtra(ref return TypeResults lhs, const ref TypeResults rhs) {
39     lhs.extra ~= rhs.extra;
40     return lhs;
41 }
42 
43 /// Pretty loggning with indentation.
44 void logTypeAttr(const ref TypeAttr attr, const uint indent = 0,
45         in uint extra_space = 0, in string func = __FUNCTION__, in uint line = __LINE__) @safe pure {
46     import std.array : array;
47     import std.range : repeat;
48     import logger = std.experimental.logger;
49     import clang.info;
50 
51     // dfmt off
52     debug {
53         string indent_ = repeat(' ', indent + extra_space).array();
54         logger.logf!(-1, "", "", "", "")
55             (logger.LogLevel.trace,
56              "%d%s %s [%s:%d]",
57              indent,
58              indent_,
59              attr,
60              func,
61              line);
62     }
63     // dfmt on
64 }
65 
66 /// Pretty loggning with indentation.
67 void logTypeResult(ref const(TypeResult) result, in uint indent,
68         in string func = __FUNCTION__, in uint line = __LINE__) @safe pure nothrow {
69     import std.array : array;
70     import std.conv : to;
71     import std.range : repeat;
72     import logger = std.experimental.logger;
73 
74     // dfmt off
75     try {
76     debug {
77         string indent_ = repeat(' ', indent).array();
78         string extra;
79         switch (result.type.kind.info.kind) with (TypeKind.Info) {
80             case Kind.typeRef:
81                 extra = "|ex ref:" ~ result.type.kind.info.typeRef ~ "|ex canonical:" ~ result.type.kind.info.canonicalRef;
82                 break;
83             case Kind.funcPtr:
84             case Kind.pointer:
85                 extra = "|ex usr:" ~ result.type.kind.info.pointee;
86                 break;
87             case Kind.array:
88                 extra = "|ex elem:" ~ result.type.kind.info.element;
89                 break;
90             default:
91         }
92 
93         logger.logf!(-1, "", "", "", "")
94             (logger.LogLevel.trace,
95              "%d%s %s|%s|repr:%s|loc:%s %s|usr:%s|%s%s [%s:%d]",
96              indent,
97              indent_,
98              to!string(result.type.kind.info.kind),
99              result.type.kind.splitTypeId,
100              result.type.toStringDecl("x"),
101              (result.location.kind == LocationTag.Kind.loc) ? (result.location.file.length == 0 ? "no" : "yes") : "noloc",
102              (result.type.attr.isDefinition ? "def" : "decl"),
103              result.type.kind.usr,
104              result.type.attr,
105              extra,
106              func,
107              line);
108 
109         switch (result.type.kind.info.kind) with (TypeKind.Info) {
110             case Kind.func:
111                 foreach (r; result.type.kind.info.params) {
112                     logTypeAttr(r.attr, indent, 1, func, line);
113                 }
114                 break;
115             case Kind.pointer:
116                 foreach (r; result.type.kind.info.attrs) {
117                     logTypeAttr(r, indent, 1, func, line);
118                 }
119                 break;
120             default:
121         }
122     }
123     } catch (Exception ex) {
124     }
125     // dfmt on
126 }
127 
128 /// Pretty loggning with indentation.
129 void logTypeResult(ref const(TypeResults) results, in uint indent = 0,
130         in string func = __FUNCTION__, in uint line = __LINE__) @safe pure nothrow {
131     import std.range : chain, only;
132 
133     // dfmt off
134     debug {
135         foreach (const ref result; chain(only(results.primary), results.extra)) {
136             logTypeResult(result, indent, func, line);
137         }
138     }
139     // dfmt on
140 }
141 
142 /// Pretty loggning with indentation.
143 void logTypeResult(ref const(Nullable!TypeResults) results, in uint indent = 0,
144         in string func = __FUNCTION__, in uint line = __LINE__) @safe pure nothrow {
145     debug {
146         if (!results.isNull) {
147             logTypeResult(results.get, indent, func, line);
148         }
149     }
150 }
151 
152 //TODO remove, this is not good. keep it focused on SimleInfo.
153 TypeKindAttr makeSimple(string txt, TypeAttr attr = TypeAttr.init) pure @safe nothrow {
154     import cpptooling.data : SimpleFmt, TypeId;
155 
156     TypeKind t;
157     t.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(txt)));
158 
159     return TypeKindAttr(t, attr);
160 }
161 
162 private auto toCvPtrQ(T)(ref T app, const(TypeAttr)[] attrs) {
163     import cpptooling.data;
164 
165     foreach (attr; attrs) {
166         // TODO merge isPtr/isRef to an enum in the data structure for
167         // attributes
168         // should be either, never both
169         assert(!(attr.isPtr && attr.isRef));
170 
171         CvPtrQ a;
172         a.cvQual = attr.isConst ? CvQ.const_ : CvQ();
173         if (attr.isPtr)
174             a.ptrQual = PtrQ.ptr;
175         else if (attr.isRef)
176             a.ptrQual = PtrQ.ref_;
177 
178         app.put(a);
179     }
180 }
181 
182 /** Combine type attributes, kind and identifier to produce a declaration.
183  * TODO reconsider the function name.
184  *  - Don't encode the return type in the name (?)
185  *  - Is it really a Decl-> declaration? Maybe more appropriate would be
186  *    "merge", of type and attributes?
187  */
188 auto toStringDecl(const TypeKind t, const TypeAttr ta, string id) @safe pure {
189     import std.array : appender, Appender;
190     import cpptooling.data;
191 
192     static void oneArg(T)(ref Appender!string app, ref T fmt, ref const TypeAttr ta, DeclId id) {
193         fmt.toString(app, ta.isConst ? CvQ.const_ : CvQ(), id);
194     }
195 
196     static void twoArg(T0, T1)(ref Appender!string app, ref T0 fmt,
197             ref const TypeAttr ta, DeclId id, T1 data1) {
198         fmt.toString(app, app, ta.isConst ? CvQ.const_ : CvQ(), data1, id);
199     }
200 
201     auto txt = appender!string();
202 
203     // TODO sort them by alphabetic order
204 
205     final switch (t.info.kind) with (TypeKind.Info) {
206     case Kind.primitive:
207         auto info = cast(const TypeKind.PrimitiveInfo) t.info;
208         oneArg(txt, info.fmt, ta, DeclId(id));
209         break;
210     case Kind.record:
211         auto info = cast(const TypeKind.RecordInfo) t.info;
212         oneArg(txt, info.fmt, ta, DeclId(id));
213         break;
214     case Kind.simple:
215         auto info = cast(const TypeKind.SimpleInfo) t.info;
216         oneArg(txt, info.fmt, ta, DeclId(id));
217         break;
218     case Kind.typeRef:
219         auto info = cast(const TypeKind.TypeRefInfo) t.info;
220         oneArg(txt, info.fmt, ta, DeclId(id));
221         break;
222     case Kind.array:
223         auto info = cast(const TypeKind.ArrayInfo) t.info;
224         ArraySize sz;
225 
226         foreach (a; info.indexes) {
227             if (a.isNull) {
228                 sz ~= ArraySize.Size();
229             } else {
230                 sz ~= ArraySize.Size(ArraySize.Kind.const_, a.get);
231             }
232         }
233 
234         info.fmt.toString(txt, txt, ta.isConst ? CvQ.const_ : CvQ(), DeclId(id), sz);
235         break;
236     case Kind.func:
237         auto info = cast(const TypeKind.FuncInfo) t.info;
238         info.fmt.toString(txt, txt, DeclId(id));
239         break;
240     case Kind.funcSignature:
241         auto info = cast(const TypeKind.FuncSignatureInfo) t.info;
242         info.fmt.toString(txt, txt);
243         break;
244     case Kind.funcPtr:
245         auto ptrs = appender!(CvPtrQ[])();
246         toCvPtrQ(ptrs, t.info.attrs);
247 
248         auto info = cast(const TypeKind.FuncPtrInfo) t.info;
249         twoArg(txt, info.fmt, ta, DeclId(id), ptrs.data);
250         break;
251     case Kind.pointer:
252         auto ptrs = appender!(CvPtrQ[])();
253         toCvPtrQ(ptrs, t.info.attrs);
254 
255         auto info = cast(const TypeKind.PointerInfo) t.info;
256         twoArg(txt, info.fmt, ta, DeclId(id), ptrs.data);
257         break;
258     case Kind.ctor:
259         auto info = cast(const TypeKind.CtorInfo) t.info;
260         info.fmt.toString(txt, DeclId(id));
261         break;
262     case Kind.dtor:
263         auto info = cast(const TypeKind.DtorInfo) t.info;
264         info.fmt.toString(txt, DeclId(id));
265         break;
266     case Kind.null_:
267         debug {
268             logger.error("Type is null. Identifier ", id);
269         }
270         txt.put(id);
271         break;
272     }
273 
274     return txt.data;
275 }
276 
277 /// ditto
278 auto toStringDecl(const TypeKind t, const TypeAttr ta) {
279     import std..string : strip;
280 
281     // TODO consider changing the implementation of to NOT take an id.
282     // Would avoid the strip....
283     return t.toStringDecl(ta, "").strip;
284 }
285 
286 /// if a type can be cast to a TypeKindAttr.
287 auto toStringDecl(T)(const T value, string id)
288         if (is(typeof(cast(TypeKindAttr) value) == TypeKindAttr)) {
289     return (cast(TypeKindAttr) value).kind.toStringDecl(value.attr, id);
290 }
291 
292 /// ditto
293 auto toStringDecl(T)(const T value)
294         if (is(typeof(cast(TypeKindAttr) value) == TypeKindAttr)) {
295     return (cast(TypeKindAttr) value).kind.toStringDecl(value.attr);
296 }
297 
298 /** Split the TypeId from the formatter in a Left/Right.
299  *
300  * TODO duplicate code between this and toStringDecl.
301  */
302 auto splitTypeId(ref const TypeKind t) @safe pure {
303     import std.array : appender, Appender;
304     import cpptooling.data;
305 
306     TypeIdLR rval;
307 
308     final switch (t.info.kind) with (TypeKind.Info) {
309     case Kind.primitive:
310         auto info = cast(const TypeKind.PrimitiveInfo) t.info;
311         rval.left = info.fmt.typeId;
312         break;
313     case Kind.record:
314         auto info = cast(const TypeKind.RecordInfo) t.info;
315         rval.left = info.fmt.typeId;
316         break;
317     case Kind.simple:
318         auto info = cast(const TypeKind.SimpleInfo) t.info;
319         rval.left = info.fmt.typeId;
320         break;
321     case Kind.typeRef:
322         auto info = cast(const TypeKind.TypeRefInfo) t.info;
323         rval.left = info.fmt.typeId;
324         break;
325     case Kind.array:
326         auto info = cast(const TypeKind.ArrayInfo) t.info;
327         ArraySize sz;
328 
329         foreach (a; info.indexes) {
330             if (a.isNull) {
331                 sz ~= ArraySize.Size();
332             } else {
333                 sz ~= ArraySize.Size(ArraySize.Kind.const_, a.get);
334             }
335         }
336 
337         auto wl = appender!string();
338         auto wr = appender!string();
339         info.fmt.toString(wl, wr, CvQ(), DeclId(null), sz);
340         rval = TypeIdLR(Left(wl.data), Right(wr.data));
341         break;
342     case Kind.funcSignature:
343         auto info = cast(const TypeKind.FuncSignatureInfo) t.info;
344         auto wl = appender!string();
345         auto wr = appender!string();
346         info.fmt.toString(wl, wr);
347         rval = TypeIdLR(Left(wl.data), Right(wr.data));
348         break;
349     case Kind.func:
350         auto info = cast(const TypeKind.FuncInfo) t.info;
351         auto wl = appender!string();
352         auto wr = appender!string();
353         info.fmt.toString(wl, wr, DeclId(null));
354         rval = TypeIdLR(Left(wl.data), Right(wr.data));
355         break;
356     case Kind.funcPtr:
357         auto ptrs = appender!(CvPtrQ[])();
358         toCvPtrQ(ptrs, t.info.attrs);
359 
360         auto info = cast(const TypeKind.FuncPtrInfo) t.info;
361         auto wl = appender!string();
362         auto wr = appender!string();
363         info.fmt.toString(wl, wr, CvQ(), ptrs.data, DeclId(null));
364         rval = TypeIdLR(Left(wl.data), Right(wr.data));
365         break;
366     case Kind.pointer:
367         auto ptrs = appender!(CvPtrQ[])();
368         toCvPtrQ(ptrs, t.info.attrs);
369 
370         auto info = cast(const TypeKind.PointerInfo) t.info;
371         auto wl = appender!string();
372         auto wr = appender!string();
373         info.fmt.toString(wl, wr, CvQ(), ptrs.data, DeclId(null));
374         rval = TypeIdLR(Left(wl.data), Right(wr.data));
375         break;
376     case Kind.ctor:
377         // have no TypeId
378         break;
379     case Kind.dtor:
380         // have no TypeId
381         break;
382     case Kind.null_:
383         debug logger.error("Type is null");
384         break;
385     }
386 
387     return rval;
388 }
389 
390 /// ditto
391 auto splitTypeId(ref const TypeKind t, const uint indent = 0) @safe pure
392 out (result) {
393     import dextool.logger : trace;
394     import std.conv : to;
395 
396     debug {
397         trace(result.to!string(), indent);
398     }
399 }
400 body {
401     return splitTypeId(t);
402 }