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, Nullable;
14 import logger = std.experimental.logger;
15 
16 import sumtype;
17 
18 import cpptooling.data.symbol.types : USRType;
19 import cpptooling.data.type : LocationTag;
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, ref TypeResults rhs) {
41     lhs.extra ~= rhs.extra;
42     return lhs;
43 }
44 
45 /// Pretty loggning with indentation.
46 void logTypeAttr(TypeAttr attr, const uint indent = 0, in uint extra_space = 0,
47         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(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         result.type.kind.info.match!((const TypeKind.TypeRefInfo t) {
82                 extra = "|ex ref:" ~ t.typeRef ~ "|ex canonical:" ~ t.canonicalRef;
83                                      },
84                                      (const TypeKind.FuncPtrInfo t) {
85                 extra = "|ex usr:" ~ t.pointee;
86                                      },
87                                      (const TypeKind.PointerInfo t) {
88                 extra = "|ex usr:" ~ t.pointee;
89                                      },
90                                      (const TypeKind.ArrayInfo t) {
91                 extra = "|ex elem:" ~ t.element;
92                                      },
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              result.type.kind.info.match!(a => typeof(a).stringof),
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         result.type.kind.info.match!((const TypeKind.FuncInfo t) {
112                 foreach (r; t.params) {
113                     logTypeAttr(r.attr, indent, 1, func, line);
114                 }
115                                      },
116                                      (const TypeKind.PointerInfo t) {
117                 foreach (r; t.attrs) {
118                     logTypeAttr(r, indent, 1, func, line);
119                 }
120                                      }, (_) {});
121     }
122     } catch (Exception ex) {
123     }
124     // dfmt on
125 }
126 
127 /// Pretty loggning with indentation.
128 void logTypeResult(const TypeResults results, in uint indent = 0,
129         in string func = __FUNCTION__, in uint line = __LINE__) @safe pure nothrow {
130     // dfmt off
131     debug {
132         logTypeResult(results.primary, indent, func, line);
133         foreach (ref result; results.extra) {
134             logTypeResult(result, indent, func, line);
135         }
136     }
137     // dfmt on
138 }
139 
140 /// Pretty loggning with indentation.
141 void logTypeResult(const Nullable!TypeResults results, in uint indent = 0,
142         in string func = __FUNCTION__, in uint line = __LINE__) @safe pure nothrow {
143     debug {
144         if (!results.isNull) {
145             logTypeResult(results.get, indent, func, line);
146         }
147     }
148 }
149 
150 //TODO remove, this is not good. keep it focused on SimleInfo.
151 TypeKindAttr makeSimple(string txt, TypeAttr attr = TypeAttr.init) pure @trusted nothrow {
152     import cpptooling.data : SimpleFmt, TypeId;
153 
154     TypeKind t;
155     t.info = TypeKind.Info(TypeKind.SimpleInfo(SimpleFmt(TypeId(txt))));
156 
157     return TypeKindAttr(t, attr);
158 }
159 
160 private auto toCvPtrQ(T)(ref T app, const(TypeAttr)[] attrs) {
161     import cpptooling.data;
162 
163     foreach (attr; attrs) {
164         // TODO merge isPtr/isRef to an enum in the data structure for
165         // attributes
166         // should be either, never both
167         assert(!(attr.isPtr && attr.isRef));
168 
169         CvPtrQ a;
170         a.cvQual = attr.isConst ? CvQ.const_ : CvQ();
171         if (attr.isPtr)
172             a.ptrQual = PtrQ.ptr;
173         else if (attr.isRef)
174             a.ptrQual = PtrQ.ref_;
175 
176         app.put(a);
177     }
178 }
179 
180 /** Combine type attributes, kind and identifier to produce a declaration.
181  * TODO reconsider the function name.
182  *  - Don't encode the return type in the name (?)
183  *  - Is it really a Decl-> declaration? Maybe more appropriate would be
184  *    "merge", of type and attributes?
185  *
186  * trusted: shouldn't be needed but because of changes to dmd-2.094.0
187  */
188 auto toStringDecl(const TypeKind t, TypeAttr ta, string id) @safe pure {
189     import std.array : appender, Appender;
190     import cpptooling.data;
191 
192     auto buf = appender!(char[])();
193     void txt(const(char)[] s) @safe pure {
194         buf.put(s);
195     }
196 
197     void oneArg(T)(ref T fmt, ref const TypeAttr ta, DeclId id) {
198         fmt.toString(&txt, ta.isConst ? CvQ.const_ : CvQ(), id);
199     }
200 
201     void twoArg(T0, T1)(ref T0 fmt, ref const TypeAttr ta, DeclId id, T1 data1) {
202         fmt.toString(&txt, &txt, ta.isConst ? CvQ.const_ : CvQ(), data1, id);
203     }
204 
205     t.info.match!((const TypeKind.PrimitiveInfo t) => oneArg(t.fmt, ta,
206             DeclId(id)), (const TypeKind.RecordInfo t) => oneArg(t.fmt, ta,
207             DeclId(id)), (const TypeKind.SimpleInfo t) => oneArg(t.fmt, ta,
208             DeclId(id)), (const TypeKind.TypeRefInfo t) => oneArg(t.fmt, ta,
209             DeclId(id)), (const TypeKind.ArrayInfo t) {
210         ArraySize sz;
211 
212         foreach (a; t.indexes) {
213             if (a.isNull) {
214                 sz ~= ArraySize.Size();
215             } else {
216                 sz ~= ArraySize.Size(ArraySize.Kind.const_, a.get);
217             }
218         }
219 
220         t.fmt.toString(&txt, &txt, ta.isConst ? CvQ.const_ : CvQ(), DeclId(id), sz);
221     }, (const TypeKind.FuncInfo t) => t.fmt.toString(&txt, &txt, DeclId(id)),
222             (const TypeKind.FuncSignatureInfo t) => t.fmt.toString(&txt,
223                 &txt), (const TypeKind.FuncPtrInfo t) {
224         auto ptrs = appender!(CvPtrQ[])();
225         toCvPtrQ(ptrs, t.attrs);
226 
227         twoArg(t.fmt, ta, DeclId(id), ptrs.data);
228     }, (const TypeKind.PointerInfo t) {
229         auto ptrs = appender!(CvPtrQ[])();
230         toCvPtrQ(ptrs, t.attrs);
231 
232         twoArg(t.fmt, ta, DeclId(id), ptrs.data);
233     }, (const TypeKind.CtorInfo t) => t.fmt.toString(&txt, DeclId(id)),
234             (const TypeKind.DtorInfo t) => t.fmt.toString(&txt, DeclId(id)), (Void t) {
235         debug {
236             logger.error("Type is null. Identifier ", id);
237         }
238         txt(id);
239     });
240 
241     return buf.data.idup;
242 }
243 
244 /// ditto
245 auto toStringDecl(const TypeKind t, const TypeAttr ta) @safe {
246     import std..string : strip;
247 
248     // TODO consider changing the implementation of to NOT take an id.
249     // Would avoid the strip....
250     return t.toStringDecl(ta, "").strip;
251 }
252 
253 /// if a type can be cast to a TypeKindAttr.
254 auto toStringDecl(T)(T value, string id) @safe
255         if (is(typeof(cast(TypeKindAttr) value) == TypeKindAttr)) {
256     return (cast(const TypeKindAttr) value).kind.toStringDecl(value.attr, id);
257 }
258 
259 /// ditto
260 auto toStringDecl(T)(T value) @safe
261         if (is(typeof(cast(TypeKindAttr) value) == TypeKindAttr)) {
262     return (cast(const TypeKindAttr) value).kind.toStringDecl(value.attr);
263 }
264 
265 /** Split the TypeId from the formatter in a Left/Right.
266  *
267  * TODO duplicate code between this and toStringDecl.
268  */
269 auto splitTypeId(const TypeKind t) @safe pure {
270     import std.array : appender, Appender;
271     import cpptooling.data;
272 
273     TypeIdLR rval;
274 
275     auto bufWl = appender!(char[])();
276     void wl(const(char)[] s) @safe pure {
277         bufWl.put(s);
278     }
279 
280     auto bufWr = appender!(char[])();
281     void wr(const(char)[] s) @safe pure {
282         bufWr.put(s);
283     }
284 
285     t.info.match!((const TypeKind.PrimitiveInfo t) { rval.left = t.fmt.typeId; },
286             (const TypeKind.RecordInfo t) { rval.left = t.fmt.typeId; },
287             (const TypeKind.SimpleInfo t) { rval.left = t.fmt.typeId; },
288             (const TypeKind.TypeRefInfo t) { rval.left = t.fmt.typeId; },
289             (const TypeKind.ArrayInfo t) {
290         ArraySize sz;
291 
292         foreach (a; t.indexes) {
293             if (a.isNull) {
294                 sz ~= ArraySize.Size();
295             } else {
296                 sz ~= ArraySize.Size(ArraySize.Kind.const_, a.get);
297             }
298         }
299 
300         t.fmt.toString(&wl, &wr, CvQ(), DeclId(null), sz);
301         rval = TypeIdLR(Left(bufWl.data.idup), Right(bufWr.data.idup));
302     }, (const TypeKind.FuncSignatureInfo t) {
303         t.fmt.toString(&wl, &wr);
304         rval = TypeIdLR(Left(bufWl.data.idup), Right(bufWr.data.idup));
305     }, (const TypeKind.FuncInfo t) {
306         t.fmt.toString(&wl, &wr, DeclId(null));
307         rval = TypeIdLR(Left(bufWl.data.idup), Right(bufWr.data.idup));
308     }, (const TypeKind.FuncPtrInfo t) {
309         auto ptrs = appender!(CvPtrQ[])();
310         toCvPtrQ(ptrs, t.attrs);
311 
312         t.fmt.toString(&wl, &wr, CvQ(), ptrs.data, DeclId(null));
313         rval = TypeIdLR(Left(bufWl.data.idup), Right(bufWr.data.idup));
314     }, (const TypeKind.PointerInfo t) {
315         auto ptrs = appender!(CvPtrQ[])();
316         toCvPtrQ(ptrs, t.attrs);
317 
318         t.fmt.toString(&wl, &wr, CvQ(), ptrs.data, DeclId(null));
319         rval = TypeIdLR(Left(bufWl.data.idup), Right(bufWr.data.idup));
320     }, (_) {});
321 
322     return rval;
323 }
324 
325 /// ditto
326 auto splitTypeId(const TypeKind t, in uint indent = 0) @safe pure
327 out (result) {
328     import std.conv : to;
329 
330     debug {
331         logger.trace(result.to!string(), indent);
332     }
333 }
334 do {
335     return splitTypeId(t);
336 }