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