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 Version: Initial created: Jan 30, 2012
7 Copyright (c) 2012 Jacob Carlborg. All rights reserved.
8 
9 # Interaction flow
10 Pass1, implicit anonymous struct and unions.
11 Pass2, struct or union decl who has no name.
12 Pass3, anonymous instantiated types.
13 Pass4, generic, last decision point for deriving data from the cursor.
14 PassType, derive type from the cursors type.
15 
16 # Location information
17 The "source" location is only, always the definition.
18 Declarations are "using" locations.
19 
20 ## CS101 refresher.
21 A definition is the information needed to create an instance of the type.
22 
23 A declaration is a subset of the information that makes it possible to use in most scenarios.
24 The most telling example of an useful declaration is a function declaration, "void foo();".
25 Useful most everywhere.
26 But during linking it must be defined _somewhere_ or a linker error will ensue.
27 
28 # Future optimization
29  - Skip the primitive types by having them prepoulated in the Container.
30  - Minimize the amoung of data that is propagated by changing TypeResults to
31     ensure only unique USR's exist in it.
32  - Skip pass4+ if the USR already exist in the container.
33 */
34 module cpptooling.analyzer.clang.type;
35 
36 import std.algorithm : among;
37 import std.conv : to;
38 import std.string : format;
39 import std.traits;
40 import std.typecons : Flag, Yes, No, Tuple;
41 import logger = std.experimental.logger;
42 
43 import clang.c.Index : CXTypeKind, CXCursorKind;
44 import clang.Cursor : Cursor;
45 import clang.Type : Type;
46 
47 public import cpptooling.data.kind_type;
48 import cpptooling.analyzer.clang.cursor_logger : logNode;
49 import cpptooling.analyzer.clang.type_logger : logType;
50 import cpptooling.data : SimpleFmt, TypeId, TypeIdLR;
51 import cpptooling.data : Location, LocationTag;
52 import cpptooling.data.symbol : Container, USRType;
53 
54 import dextool.nullable;
55 
56 private string nextSequence() @safe {
57     import std.conv : text;
58     import cpptooling.utility.global_unique : nextNumber;
59 
60     return text(nextNumber);
61 }
62 
63 /// Returns: Filter node to only return those that are a typeref.
64 private auto filterByTypeRef(T)(auto ref T in_) {
65     import std.algorithm : filter;
66 
67     return in_.filter!(a => a.isTypeRef);
68 }
69 
70 ///
71 private bool isTypeRef(Cursor c) {
72     // CXCursorKind.CXCursor_TypeRef is the first node, thus >=...
73     // it is not in any other way "special".
74 
75     return c.kind >= CXCursorKind.typeRef && c.kind <= CXCursorKind.lastRef;
76 }
77 
78 /** Iteratively try to construct a USR that is reproducable from the cursor.
79  *
80  * Only use when c.usr may return the empty string.
81  *
82  * Fallback case, using location to make it unique.
83  *
84  * strategy 1
85  *  try and derive a location from the lexical parent.
86  * strategy 2
87  *  handled by putBacktrackLocation when loc_.kind is null_.
88  *  putBacktrackLocation will then use nextSequence to generate a _for sure_
89  *  unique ID.
90  */
91 private void makeFallbackUSR(Writer)(scope Writer w, ref const(Cursor) c, in uint this_indent) @safe {
92     // strategy 1
93     auto loc_ = backtrackLocation(c);
94 
95     // strategy 2
96     putBacktrackLocation(w, c, loc_);
97 }
98 
99 /// ditto
100 /// Returns: fallback USR from the cursor.
101 private USRType makeFallbackUSR(ref const(Cursor) c, in uint this_indent) @safe
102 out (result) {
103     logger.trace(result, this_indent);
104     assert(result.length > 0);
105 }
106 body {
107     import std.array : appender;
108 
109     auto app = appender!string();
110     makeFallbackUSR((const(char)[] s) { app.put(s); }, c, this_indent);
111 
112     return USRType(app.data);
113 }
114 
115 /// Make a USR, never failing.
116 USRType makeEnsuredUSR(const(Cursor) c, in uint this_indent) @safe
117 out (result) {
118     logger.trace(result, this_indent);
119     assert(result.length > 0);
120 }
121 body {
122     import std.array : appender;
123 
124     auto usr = USRType(c.usr);
125     if (usr.length > 0) {
126         return usr;
127     }
128 
129     auto app = appender!string();
130     makeFallbackUSR((const(char)[] s) { app.put(s); }, c, this_indent);
131     app.put("§");
132     app.put(nextSequence);
133 
134     return USRType(app.data);
135 }
136 
137 private void assertTypeResult(const ref TypeResults results) {
138     import std.range : chain, only;
139 
140     foreach (const ref result; chain(only(results.primary), results.extra)) {
141         assert(result.type.toStringDecl("x").length > 0);
142         assert(result.type.kind.usr.length > 0);
143         if (result.type.kind.info.kind != TypeKind.Info.Kind.primitive
144                 && result.location.kind != LocationTag.Kind.noloc) {
145             assert(result.location.file.length > 0);
146         }
147     }
148 }
149 
150 struct BacktrackLocation {
151     static import clang.SourceLocation;
152     import taggedalgebraic : TaggedAlgebraic, Void;
153     import cpptooling.data.type : Location;
154 
155     union TagType {
156         Void null_;
157         cpptooling.data.type.Location loc;
158     }
159 
160     alias Tag = TaggedAlgebraic!TagType;
161 
162     Tag tag;
163 
164     /// Number of nodes backtracked through until a valid was found
165     int backtracked;
166 }
167 
168 /** Lexical backtrack from the argument cursor to first cursor with a valid
169  * location.
170  *
171  * using a for loop to ensure it is NOT an infinite loop.
172  * hoping 100 is enough for all type of nesting to reach at least translation
173  * unit.
174  *
175  * Return: Location and nr of backtracks needed.
176  */
177 private BacktrackLocation backtrackLocation(ref const(Cursor) c) @safe {
178     import cpptooling.data.type : Location;
179 
180     BacktrackLocation rval;
181 
182     Cursor parent = c;
183     for (rval.backtracked = 0; rval.tag.kind == BacktrackLocation.Tag.Kind.null_
184             && rval.backtracked < 100; ++rval.backtracked) {
185         auto loc = parent.location;
186         auto spell = loc.spelling;
187         if (spell.file is null) {
188             // do nothing
189         } else if (spell.file.name.length != 0) {
190             rval.tag = Location(spell.file.name, spell.line, spell.column);
191         } else if (parent.isTranslationUnit) {
192             rval.tag = Location(spell.file.name, spell.line, spell.column);
193             break;
194         }
195 
196         parent = () @trusted { return parent.lexicalParent; }();
197     }
198 
199     return rval;
200 }
201 
202 /// TODO consider if .offset should be used too. But may make it harder to
203 /// reverse engineer a location.
204 private void putBacktrackLocation(Writer)(scope Writer app, ref const(Cursor) c,
205         BacktrackLocation back_loc) @safe {
206     import std.range.primitives : put;
207 
208     static import cpptooling.data.type;
209 
210     // using a suffix that do NOT exist in the clang USR standard.
211     // TODO lookup the algorithm for clang USR to see if $ is valid.
212     enum marker = '§';
213 
214     final switch (back_loc.tag.kind) with (BacktrackLocation.Tag) {
215     case Kind.loc:
216         auto loc = cast(cpptooling.data.type.Location) back_loc.tag;
217         app.put(loc.toString);
218         break;
219     case Kind.null_:
220         app.put(nextSequence);
221         break;
222     }
223 
224     put(app, marker);
225     put(app, back_loc.backtracked.to!string);
226     if (c.isValid) {
227         put(app, () @trusted { return c.spelling; }());
228     }
229 }
230 
231 LocationTag makeLocation(ref const(Cursor) c) @safe
232 out (result) {
233     import std.utf : validate;
234 
235     validate(result.file);
236 }
237 body {
238     import std.array : appender;
239 
240     auto loc = c.location.spelling;
241     auto rval = Location(loc.file.name, loc.line, loc.column);
242 
243     if (rval.file.length > 0) {
244         return LocationTag(rval);
245     }
246 
247     auto loc_ = backtrackLocation(c);
248 
249     if (loc_.tag.kind == BacktrackLocation.Tag.Kind.null_) {
250         return LocationTag(null);
251     }
252 
253     auto app = appender!string();
254     putBacktrackLocation((const(char)[] s) { app.put(s); }, c, loc_);
255 
256     rval = Location(app.data, loc.line, loc.column);
257 
258     return LocationTag(rval);
259 }
260 
261 TypeAttr makeTypeAttr(ref Type type, ref const(Cursor) c) {
262     TypeAttr attr;
263 
264     attr.isConst = cast(Flag!"isConst") type.isConst;
265     attr.isRef = cast(Flag!"isRef")(type.kind == CXTypeKind.lValueReference);
266     attr.isPtr = cast(Flag!"isPtr")(type.kind == CXTypeKind.pointer);
267     attr.isArray = cast(Flag!"isArray") type.isArray;
268     attr.isDefinition = cast(Flag!"isDefinition") c.isDefinition;
269 
270     return attr;
271 }
272 
273 TypeKindAttr makeTypeKindAttr(ref Type type, ref const(Cursor) c) {
274     TypeKindAttr tka;
275     tka.attr = makeTypeAttr(type, c);
276 
277     return tka;
278 }
279 
280 TypeKindAttr makeTypeKindAttr(ref Type type, ref TypeKind tk, ref const(Cursor) c) {
281     auto tka = makeTypeKindAttr(type, c);
282     tka.kind = tk;
283 
284     return tka;
285 }
286 
287 /** Deduct the type the node represents.
288  *
289  * pass 1, implicit anonymous structs and unions.
290  * pass 2, implicit types aka no spelling exist for them.
291  * pass 3, instansiated anonymous types and typedef of anonymous.
292  * pass 4, normal nodes, typedefs and references.
293  * passType, collect type information. The final result in most cases.
294  *
295  * TODO add "in" to parameter c.
296  *
297  * Params:
298  *  c = cursor to retrieve from.
299  *  container = container holding type symbols.
300  *  indent = ?
301  */
302 Nullable!TypeResults retrieveType(ref const(Cursor) c,
303         ref const(Container) container, in uint indent = 0)
304 in {
305     logNode(c, indent);
306 
307     // unable to derive anything useful from a typeref when based on nothing else.
308     // __va_list is an examle (found in stdarg.h).
309     if (indent == 0 && isRefNode(c.kind)) {
310         assert(false);
311     }
312 }
313 out (result) {
314     logTypeResult(result, indent);
315 
316     // ensure no invalid data is returned
317     if (!result.isNull && indent == 0) {
318         assertTypeResult(result.get);
319     }
320 }
321 body {
322     import std.range;
323 
324     Nullable!TypeResults rval;
325 
326     // bail early
327     if (c.kind.among(CXCursorKind.macroDefinition)) {
328         return rval;
329     }
330 
331     foreach (pass; only(&pass1, &pass2, &pass3)) {
332         auto r = pass(c, indent + 1);
333         if (!r.isNull) {
334             rval = typeof(return)(TypeResults(r.get, null));
335             return rval;
336         }
337     }
338 
339     rval = pass4(c, container, indent + 1);
340     return rval;
341 }
342 
343 /** Pass 1, implicit anonymous types for struct and union.
344  *
345  * TODO merge with pass2. Code duplication
346  */
347 private Nullable!TypeResult pass1(ref const(Cursor) c, uint indent)
348 in {
349     logNode(c, indent);
350 }
351 body {
352     Nullable!TypeResult rval;
353 
354     if (!c.isAnonymous) {
355         return rval;
356     }
357 
358     switch (c.kind) with (CXCursorKind) {
359     case structDecl:
360         goto case;
361     case unionDecl:
362         auto type = c.type;
363         rval = TypeResult();
364         rval.type = makeTypeKindAttr(type, c);
365 
366         string spell = type.spelling;
367         rval.type.kind.info = TypeKind.RecordInfo(SimpleFmt(TypeId(spell)));
368         rval.type.kind.usr = USRType(c.usr);
369         rval.location = makeLocation(c);
370         break;
371     default:
372     }
373 
374     return rval;
375 }
376 
377 /** Pass 2, detect anonymous types who has "no name".
378  *
379  * Only struct, enum, union can possibly have this attribute.
380  * The types name.
381  *
382  * TODO consider using the identifier as the spelling.
383  *
384  * Example:
385  * ---
386  * struct (implicit name) { <-- and spelling is ""
387  * } Struct;
388  *
389  * union (implicit name) { <-- and spelling is ""
390  * } Union;
391  *
392  * typedef enum {
393  *  X <--- this one
394  * } Enum; <--- not this one, covered by "other" pass
395  * ---
396  */
397 private Nullable!TypeResult pass2(ref const(Cursor) c, uint indent)
398 in {
399     logNode(c, indent);
400 }
401 body {
402     Nullable!TypeResult rval;
403 
404     if (c.spelling.length != 0) {
405         return rval;
406     }
407 
408     switch (c.kind) with (CXCursorKind) {
409     case structDecl:
410         goto case;
411     case unionDecl:
412         auto type = c.type;
413         rval = TypeResult(makeTypeKindAttr(type, c), LocationTag.init);
414 
415         rval.type.kind.info = TypeKind.RecordInfo(SimpleFmt(TypeId(nextSequence)));
416         rval.type.kind.usr = USRType(c.usr);
417         rval.location = makeLocation(c);
418         break;
419     case enumDecl:
420         auto type = c.type;
421         rval = TypeResult(makeTypeKindAttr(type, c), LocationTag.init);
422 
423         rval.type.kind.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(nextSequence)));
424         rval.type.kind.usr = USRType(c.usr);
425         rval.location = makeLocation(c);
426         break;
427     default:
428     }
429 
430     return rval;
431 }
432 
433 /** Detect anonymous types that have an instansiation.
434  *
435  * Continuation of Pass 2.
436  * Kept separate from Pass 3 to keep the passes logically "small".
437  * Less cognitive load to understand what the passes do.
438  *
439  * Examle:
440  * ---
441  * struct {
442  * } Struct;
443  * ---
444  */
445 private Nullable!TypeResult pass3(ref const(Cursor) c, uint indent)
446 in {
447     logNode(c, indent);
448 }
449 body {
450     Nullable!TypeResult rval;
451 
452     switch (c.kind) with (CXCursorKind) {
453     case fieldDecl:
454         goto case;
455     case varDecl:
456         import std.range : takeOne;
457 
458         foreach (child; c.children.takeOne) {
459             rval = pass2(child, indent + 1);
460         }
461         break;
462     default:
463     }
464 
465     return rval;
466 }
467 
468 /**
469  */
470 private Nullable!TypeResults pass4(ref const(Cursor) c,
471         ref const(Container) container, in uint this_indent)
472 in {
473     logNode(c, this_indent);
474 }
475 out (result) {
476     logTypeResult(result, this_indent);
477 }
478 body {
479     auto indent = this_indent + 1;
480     Nullable!TypeResults rval;
481 
482     switch (c.kind) with (CXCursorKind) {
483     case typedefDecl:
484         rval = retrieveTypeDef(c, container, indent);
485         break;
486 
487     case typeAliasTemplateDecl:
488     case typeAliasDecl:
489         rval = retrieveTypeAlias(c, container, indent);
490         break;
491 
492     case fieldDecl:
493     case varDecl:
494         rval = retrieveInstanceDecl(c, container, indent);
495         break;
496 
497     case parmDecl:
498         rval = retrieveParam(c, container, indent);
499         break;
500 
501     case templateTypeParameter:
502         rval = retrieveTemplateParam(c, container, indent);
503         break;
504 
505     case classTemplatePartialSpecialization:
506     case classTemplate:
507         rval = retrieveClassTemplate(c, container, indent);
508         break;
509 
510     case structDecl:
511     case unionDecl:
512     case classDecl:
513     case enumDecl:
514         auto type = c.type;
515         rval = passType(c, type, container, indent);
516         break;
517 
518     case cxxMethod:
519     case functionDecl:
520         rval = retrieveFunc(c, container, indent);
521         break;
522 
523     case constructor:
524         auto type = c.type;
525         rval = typeToCtor(c, type, container, indent);
526         break;
527 
528     case destructor:
529         auto type = c.type;
530         rval = typeToDtor(c, type, indent);
531         break;
532 
533     case integerLiteral:
534         auto type = c.type;
535         rval = passType(c, type, container, indent);
536         break;
537 
538     case cxxBaseSpecifier:
539         rval = retrieveClassBaseSpecifier(c, container, indent);
540         break;
541 
542     case declRefExpr:
543     case typeRef:
544     case templateRef:
545     case namespaceRef:
546     case memberRef:
547     case labelRef:
548         auto refc = c.referenced;
549         rval = retrieveType(refc, container, indent);
550         break;
551 
552     case noDeclFound:
553         // nothing to do
554         break;
555 
556     case nonTypeTemplateParameter:
557         auto type = c.type;
558         rval = typeToSimple(c, type, indent);
559         break;
560 
561     case unexposedDecl:
562         rval = retrieveUnexposed(c, container, indent);
563         if (rval.isNull) {
564             logger.trace("Not implemented type retrieval for node ", c.usr);
565         }
566         break;
567 
568     default:
569         // skip for now, may implement in the future
570         logger.trace("Not implemented type retrieval for node ", c.usr);
571     }
572 
573     return rval;
574 }
575 
576 //TODO add comment, I don't understand what the function is intended to do from
577 // the function name.
578 private bool isUnexposedDeclWithUSR(CXCursorKind kind) {
579     switch (kind) with (CXCursorKind) {
580     case typedefDecl:
581     case templateTypeParameter:
582     case classTemplate:
583     case structDecl:
584     case unionDecl:
585     case classDecl:
586     case enumDecl:
587         return true;
588     default:
589         return false;
590     }
591 }
592 
593 private bool canConvertNodeDeclToType(CXCursorKind kind) {
594     switch (kind) with (CXCursorKind) {
595     case typedefDecl:
596     case templateTypeParameter:
597     case classTemplate:
598     case structDecl:
599     case unionDecl:
600     case classDecl:
601     case enumDecl:
602     case cxxMethod:
603     case functionDecl:
604     case constructor:
605     case destructor:
606     case integerLiteral:
607         return true;
608     default:
609         return false;
610     }
611 }
612 
613 private bool isRefNode(CXCursorKind kind) {
614     switch (kind) with (CXCursorKind) {
615     case typeRef:
616     case cxxBaseSpecifier:
617     case templateRef:
618     case namespaceRef:
619     case memberRef:
620     case labelRef:
621         return true;
622     default:
623         return false;
624     }
625 }
626 
627 private Nullable!TypeResults retrieveUnexposed(ref const(Cursor) c,
628         ref const(Container) container, in uint this_indent)
629 in {
630     logNode(c, this_indent);
631     assert(c.kind.among(CXCursorKind.unexposedDecl, CXCursorKind.nonTypeTemplateParameter));
632 }
633 out (result) {
634     logTypeResult(result, this_indent);
635 }
636 body {
637     import std.range : takeOne;
638 
639     auto indent = this_indent + 1;
640     Nullable!TypeResults rval;
641 
642     foreach (child; c.children.takeOne) {
643         switch (child.kind) with (CXCursorKind) {
644         case cxxMethod:
645         case functionDecl:
646             rval = pass4(child, container, indent);
647             if (!rval.isNull && rval.primary.type.kind.info.kind != TypeKind.Info.Kind.func) {
648                 // cases like typeof(x) y;
649                 // fix in the future
650                 rval.nullify;
651             }
652             break;
653 
654         default:
655         }
656     }
657 
658     return rval;
659 }
660 
661 private Nullable!TypeResults passType(ref const(Cursor) c, ref Type type,
662         ref const(Container) container, in uint this_indent)
663 in {
664     logNode(c, this_indent);
665     logType(type, this_indent);
666 
667     //TODO investigate if the below assumption is as it should be.
668     // Not suposed to be handled here.
669     // A typedef cursor shall have been detected and then handled by inspecting the child.
670     // MAYBE move primitive type detection here.
671     //assert(type.kind != CXTypeKind.CXType_Typedef);
672 }
673 out (result) {
674     logTypeResult(result, this_indent);
675 }
676 body {
677     import std.range : takeOne;
678 
679     auto indent = 1 + this_indent;
680     Nullable!TypeResults rval;
681 
682     switch (type.kind) with (CXTypeKind) {
683     case functionNoProto:
684     case functionProto:
685         rval = typeToFuncProto(c, type, container, indent);
686         break;
687 
688     case blockPointer:
689         rval = typeToFuncPtr(c, type, container, indent);
690         break;
691 
692         // handle ref and ptr the same way
693     case lValueReference:
694     case pointer:
695         //TODO fix architecture so this check isn't needed.
696         //Should be possible to merge typeToFunPtr and typeToPointer
697         if (type.isFunctionPointerType) {
698             rval = typeToFuncPtr(c, type, container, indent);
699         } else {
700             rval = typeToPointer(c, type, container, indent);
701         }
702         break;
703 
704     case constantArray:
705     case incompleteArray:
706         rval = typeToArray(c, type, container, indent);
707         break;
708 
709     case record:
710         rval = typeToRecord(c, type, indent);
711         break;
712 
713     case typedef_:
714         // unable to represent a typedef as a typedef.
715         // Falling back on representing as a Simple.
716         // Note: the usr from the cursor is null.
717         rval = typeToFallBackTypeDef(c, type, indent);
718         break;
719 
720     case unexposed:
721         debug {
722             logger.trace("Unexposed, investigate if any other action should be taken");
723         }
724         if (!c.kind.among(CXCursorKind.functionDecl, CXCursorKind.cxxMethod)) {
725             // see retrieveUnexposed for why
726             rval = typeToSimple(c, type, indent);
727         } else if (type.isFunctionType) {
728             rval = typeToFuncProto(c, type, container, indent);
729         }
730         break;
731 
732     default:
733         rval = typeToSimple(c, type, indent);
734     }
735 
736     return rval;
737 }
738 
739 /** Create a representation of a typeRef for the cursor.
740 */
741 private TypeResults typeToTypeRef(ref const(Cursor) c, ref Type type,
742         USRType type_ref, USRType canonical_ref, in uint this_indent)
743 in {
744     logNode(c, this_indent);
745     logType(type, this_indent);
746 }
747 out (result) {
748     logTypeResult(result, this_indent);
749 }
750 body {
751     const uint indent = this_indent + 1;
752     string spell = type.spelling;
753 
754     // ugly hack
755     if (type.isConst && spell.length > 6 && spell[0 .. 6] == "const ") {
756         spell = spell[6 .. $];
757     }
758 
759     TypeKind.TypeRefInfo info;
760     info.fmt = SimpleFmt(TypeId(spell));
761     info.typeRef = type_ref;
762     info.canonicalRef = canonical_ref;
763 
764     TypeResults rval;
765     rval.primary.type.attr = makeTypeAttr(type, c);
766     rval.primary.type.kind.info = info;
767 
768     // a typedef like __va_list has a null usr
769     if (c.usr.length == 0) {
770         rval.primary.type.kind.usr = makeFallbackUSR(c, indent);
771     } else {
772         rval.primary.type.kind.usr = c.usr;
773     }
774 
775     rval.primary.location = makeLocation(c);
776 
777     return rval;
778 }
779 
780 /** Use fallback strategy for typedef's via Simple.
781  *
782  * A typedef referencing a type that it isn't possible to derive information
783  * from to correctly represent (pointer, record, primitive etc).
784  *
785  * The fall back strategy is in that case to represent the type textually as a Simple.
786  * The TypeKind->typeRef then references this simple type.
787  */
788 private Nullable!TypeResults typeToFallBackTypeDef(ref const(Cursor) c,
789         ref Type type, in uint this_indent)
790 in {
791     logNode(c, this_indent);
792     logType(type, this_indent);
793 }
794 out (result) {
795     logTypeResult(result, this_indent);
796 }
797 body {
798     string spell = type.spelling;
799 
800     // ugly hack to remove const
801     if (type.isConst && spell.length > 6 && spell[0 .. 6] == "const ") {
802         spell = spell[6 .. $];
803     }
804 
805     auto rval = makeTypeKindAttr(type, c);
806 
807     auto info = TypeKind.SimpleInfo(SimpleFmt(TypeId(spell)));
808     rval.kind.info = info;
809 
810     // a typedef like __va_list has a null usr
811     if (c.usr.length == 0) {
812         rval.kind.usr = makeFallbackUSR(c, this_indent + 1);
813     } else {
814         rval.kind.usr = c.usr;
815     }
816 
817     auto loc = makeLocation(c);
818 
819     return typeof(return)(TypeResults(TypeResult(rval, loc), null));
820 }
821 
822 private Nullable!TypeResults typeToSimple(ref const(Cursor) c, ref Type type, in uint this_indent)
823 in {
824     logNode(c, this_indent);
825     logType(type, this_indent);
826 }
827 out (result) {
828     logTypeResult(result, this_indent);
829 }
830 body {
831     auto rval = makeTypeKindAttr(type, c);
832     LocationTag loc;
833 
834     auto maybe_primitive = translateCursorType(type.kind);
835 
836     if (maybe_primitive.isNull) {
837         string spell = type.spelling;
838         rval.kind.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(spell)));
839 
840         rval.kind.usr = c.usr;
841         if (rval.kind.usr.length == 0) {
842             rval.kind.usr = makeFallbackUSR(c, this_indent + 1);
843         }
844         loc = makeLocation(c);
845     } else {
846         string spell = maybe_primitive.get;
847         rval.kind.info = TypeKind.PrimitiveInfo(SimpleFmt(TypeId(spell)));
848 
849         rval.kind.usr = USRType(maybe_primitive.get);
850         loc = LocationTag(null);
851     }
852 
853     return typeof(return)(TypeResults(TypeResult(rval, loc), null));
854 }
855 
856 /// A function proto signature?
857 /// Workaround by checking if the return type is valid.
858 private bool isFuncProtoTypedef(ref const(Cursor) c) {
859     auto result_t = c.type.func.resultType;
860     return result_t.isValid;
861 }
862 
863 private Nullable!TypeResults typeToTypedef(ref const(Cursor) c, ref Type type,
864         USRType typeRef, USRType canonicalRef, ref const(Container) container, in uint this_indent)
865 in {
866     logNode(c, this_indent);
867     logType(type, this_indent);
868     assert(type.kind.among(CXTypeKind.typedef_) || c.kind == CXCursorKind.typeAliasTemplateDecl);
869 }
870 out (result) {
871     logTypeResult(result, this_indent);
872 }
873 body {
874     /// Make a string that represent the type.
875     static string makeSpelling(ref const(Cursor) c, ref Type type) {
876         import std.array : array;
877         import std.algorithm : canFind, map, joiner;
878         import std.range : retro, chain, only;
879         import std.utf : byChar;
880 
881         string spell = type.spelling;
882 
883         if (type.isConst && spell.length > 6 && spell[0 .. 6] == "const ") {
884             spell = spell[6 .. $];
885         }
886 
887         if (!spell.canFind("::")) {
888             // if it isn't contained in a namespace then perform a backtracking of
889             // the scope to ensure it isn't needed.  Implicit struct or enums need
890             // this check.
891             // Example: typedef struct {} Struct;
892 
893             import cpptooling.analyzer.clang.cursor_backtrack : backtrackScopeRange;
894 
895             // dfmt off
896             spell = cast(string) chain(only(spell), backtrackScopeRange(c).map!(a => a.spelling))
897                 .array
898                 .retro
899                 .joiner("::")
900                 .byChar
901                 .array;
902             // dfmt on
903         }
904 
905         return spell;
906     }
907 
908     auto spell = makeSpelling(c, type);
909 
910     TypeKind.TypeRefInfo info;
911     info.fmt = SimpleFmt(TypeId(spell));
912     info.typeRef = typeRef;
913     info.canonicalRef = canonicalRef;
914 
915     TypeResults rval;
916     rval.primary.type.attr = makeTypeAttr(type, c);
917     rval.primary.type.kind.info = info;
918 
919     // a typedef like __va_list has a null usr
920     if (c.usr.length == 0) {
921         rval.primary.type.kind.usr = makeFallbackUSR(c, this_indent + 1);
922     } else {
923         rval.primary.type.kind.usr = c.usr;
924     }
925 
926     rval.primary.location = makeLocation(c);
927 
928     return typeof(return)(rval);
929 }
930 
931 /** Make a Record from a declaration or definition.
932  */
933 private Nullable!TypeResults typeToRecord(ref const(Cursor) c, ref Type type, in uint indent)
934 in {
935     logNode(c, indent);
936     logType(type, indent);
937     assert(type.kind == CXTypeKind.record);
938 }
939 out (result) {
940     logTypeResult(result, indent);
941 }
942 body {
943     string spell = type.spelling;
944 
945     // ugly hack needed when canonicalType has been used to get the type of a
946     // cursor
947     if (type.isConst && spell.length > 6 && spell[0 .. 6] == "const ") {
948         spell = spell[6 .. $];
949     }
950 
951     TypeKind.RecordInfo info;
952     info.fmt = SimpleFmt(TypeId(spell));
953 
954     auto rval = makeTypeKindAttr(type, c);
955     rval.kind.info = info;
956     rval.kind.usr = c.usr;
957     auto loc = makeLocation(c);
958 
959     if (rval.kind.usr.length == 0) {
960         rval.kind.usr = makeFallbackUSR(c, indent + 1);
961     }
962 
963     return typeof(return)(TypeResults(TypeResult(rval, loc), null));
964 }
965 
966 /** Represent a pointer type hierarchy.
967  *
968  * Returns: TypeResults.primary.attr is the pointed at attribute.
969  */
970 private Nullable!TypeResults typeToPointer(ref const(Cursor) c, ref Type type,
971         ref const(Container) container, const uint this_indent)
972 in {
973     logNode(c, this_indent);
974     logType(type, this_indent);
975     assert(type.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference));
976 }
977 out (result) {
978     logTypeResult(result, this_indent);
979     with (TypeKind.Info.Kind) {
980         assert(result.primary.type.kind.info.kind.among(funcPtr, pointer));
981     }
982 }
983 body {
984     import cpptooling.data : PtrFmt, Left, Right;
985 
986     immutable indent = this_indent + 1;
987 
988     static auto getPointee(ref const(Cursor) c, ref Type type,
989             ref const(Container) container, in uint indent) {
990         auto pointee = type.pointeeType;
991         auto c_pointee = pointee.declaration;
992 
993         debug {
994             logNode(c_pointee, indent);
995             logType(pointee, indent);
996         }
997 
998         TypeResults rval;
999 
1000         // find the underlying type information
1001         if (c_pointee.kind == CXCursorKind.typedefDecl) {
1002             rval = retrieveType(c_pointee, container, indent).get;
1003         } else if (pointee.kind == CXTypeKind.unexposed) {
1004             pointee = type.canonicalType;
1005             while (pointee.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)) {
1006                 pointee = pointee.pointeeType;
1007             }
1008             rval = passType(c, pointee, container, indent).get;
1009 
1010             if (rval.primary.type.kind.info.kind == TypeKind.Info.Kind.record
1011                     && c_pointee.kind.isUnexposedDeclWithUSR) {
1012                 // if the current pointers type is for a declaration use this
1013                 // usr instead of the one from pointee.
1014                 // Solves the case of a forward declared class in a namespace.
1015                 // The retrieved data is only correct if it is from the
1016                 // canonical type but the USR is wrong.
1017                 string usr = c_pointee.usr;
1018                 rval.primary.type.kind.usr = usr;
1019 
1020                 // TODO investigate if a usr null checking is needed.
1021                 // I think it is unnecessary but unsure at this point.
1022                 // It is possible to run a full scan of google mock and all
1023                 // internal tests without this check.
1024                 // If this hasn't been changed for 6 month remove this comment.
1025                 // Written at 2016-07-01, remove by 2017-02-01.
1026             }
1027         } else if (c_pointee.kind == CXCursorKind.noDeclFound) {
1028             while (pointee.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)) {
1029                 pointee = pointee.pointeeType;
1030             }
1031 
1032             auto c_decl = pointee.declaration;
1033 
1034             if (c_decl.kind == CXCursorKind.noDeclFound) {
1035                 // primitive types do not have a declaration cursor.
1036                 // find the underlying primitive type.
1037                 rval = passType(c, pointee, container, indent).get;
1038             } else {
1039                 rval = retrieveType(c_decl, container, indent).get;
1040             }
1041         } else {
1042             rval = retrieveType(c_pointee, container, indent).get;
1043         }
1044 
1045         return rval;
1046     }
1047 
1048     auto pointee = getPointee(c, type, container, indent);
1049 
1050     auto attrs = retrievePointeeAttr(type, indent);
1051 
1052     TypeKind.PointerInfo info;
1053     info.pointee = pointee.primary.type.kind.usr;
1054     info.attrs = attrs.ptrs;
1055 
1056     switch (pointee.primary.type.kind.info.kind) with (TypeKind.Info) {
1057     case Kind.array:
1058         auto type_id = pointee.primary.type.kind.splitTypeId(indent);
1059         info.fmt = PtrFmt(TypeIdLR(Left(type_id.left ~ "("), Right(")" ~ type_id.right)));
1060         break;
1061     default:
1062         info.fmt = PtrFmt(pointee.primary.type.kind.splitTypeId(indent));
1063     }
1064 
1065     TypeResults rval;
1066     rval.primary.type.kind.info = info;
1067     // somehow pointee.primary.attr is wrong, somehow. Don't undestand why.
1068     // TODO remove this hack
1069     rval.primary.type.attr = attrs.base;
1070     // a pointer is always itselfs definition because they are always unique
1071     rval.primary.type.attr.isDefinition = Yes.isDefinition;
1072 
1073     // must be unique even when analyzing many translation units.
1074     // Could maybe work if static/anonymous namespace influenced the USR.
1075     rval.primary.type.kind.usr = makeFallbackUSR(c, indent);
1076     rval.primary.location = makeLocation(c);
1077 
1078     rval.extra = [pointee.primary] ~ pointee.extra;
1079 
1080     return typeof(return)(rval);
1081 }
1082 
1083 /** Represent a function pointer type.
1084  *
1085  * Return: correct formatting and attributes for a function pointer.
1086  */
1087 private Nullable!TypeResults typeToFuncPtr(ref const(Cursor) c, ref Type type,
1088         ref const(Container) container, const uint this_indent)
1089 in {
1090     logNode(c, this_indent);
1091     logType(type, this_indent);
1092     assert(type.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference));
1093     assert(type.isFunctionPointerType);
1094 }
1095 out (result) {
1096     logTypeResult(result, this_indent);
1097     with (TypeKind.Info.Kind) {
1098         // allow catching the logical error in debug build
1099         assert(!result.primary.type.kind.info.kind.among(ctor, dtor, record, simple, array));
1100     }
1101 }
1102 body {
1103     import cpptooling.data : FuncPtrFmt, Left, Right;
1104 
1105     immutable indent = this_indent + 1;
1106 
1107     // find the underlying function prototype
1108     auto pointee_type = type;
1109     while (pointee_type.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)) {
1110         pointee_type = pointee_type.pointeeType;
1111     }
1112     debug {
1113         logType(pointee_type, indent);
1114     }
1115 
1116     auto attrs = retrievePointeeAttr(type, indent);
1117     auto pointee = typeToFuncProto(c, pointee_type, container, indent + 1);
1118 
1119     TypeKind.FuncPtrInfo info;
1120     info.pointee = pointee.primary.type.kind.usr;
1121     info.attrs = attrs.ptrs;
1122     info.fmt = () {
1123         auto tid = pointee.primary.type.kind.splitTypeId(indent);
1124         return FuncPtrFmt(TypeIdLR(Left(tid.left ~ "("), Right(")" ~ tid.right)));
1125     }();
1126 
1127     TypeResults rval;
1128     rval.primary.type.kind.info = info;
1129     rval.primary.type.kind.usr = makeFallbackUSR(c, indent);
1130     rval.primary.location = makeLocation(c);
1131     // somehow pointee.primary.attr is wrong, somehow. Don't undestand why.
1132     // TODO remove this hack
1133     rval.primary.type.attr = attrs.base;
1134 
1135     rval.extra = [pointee.primary] ~ pointee.extra;
1136 
1137     return typeof(return)(rval);
1138 }
1139 
1140 private Nullable!TypeResults typeToFuncProto(InfoT = TypeKind.FuncInfo)(
1141         ref const(Cursor) c, ref Type type, ref const(Container) container, in uint this_indent)
1142         if (is(InfoT == TypeKind.FuncInfo) || is(InfoT == TypeKind.FuncSignatureInfo))
1143 in {
1144     logNode(c, this_indent);
1145     logType(type, this_indent);
1146     assert(type.isFunctionType || type.isTypedef || type.kind == CXTypeKind.functionNoProto);
1147 }
1148 out (result) {
1149     logTypeResult(result, this_indent);
1150 }
1151 body {
1152     import std.array : array;
1153     import std.algorithm : map;
1154     import std.string : strip;
1155     import cpptooling.data : FuncFmt, Left, Right, FuncSignatureFmt;
1156 
1157     const auto indent = this_indent + 1;
1158 
1159     // TODO redesign. This is brittle and ugly.
1160     // return by value instead of splitting two ways like this.
1161     TypeKindAttr retrieveReturn(ref TypeResults rval) {
1162         auto result_type = type.func.resultType;
1163         auto result_decl = result_type.declaration;
1164         debug {
1165             logNode(result_decl, indent);
1166             logType(result_type, indent);
1167         }
1168 
1169         auto this_node = passType(result_decl, result_type, container, indent + 1).get;
1170 
1171         if (result_decl.kind == CXCursorKind.noDeclFound) {
1172             rval = this_node;
1173         } else {
1174             rval = retrieveType(result_decl, container, indent + 1).get;
1175 
1176             // use the attributes derived from this node because it is not
1177             // preserved in the result_decl. This is a problem when the return
1178             // type is a typedef.  The const attribute isn't preserved.
1179             rval.primary.type.attr = this_node.primary.type.attr;
1180 
1181             rval.extra ~= this_node.primary;
1182             rval.extra ~= this_node.extra;
1183         }
1184 
1185         return rval.primary.type;
1186     }
1187 
1188     TypeResults rval;
1189     TypeResults return_rval;
1190 
1191     auto return_t = retrieveReturn(return_rval);
1192     auto params = extractParams(c, type, container, indent);
1193     TypeResult primary;
1194     primary.type = makeTypeKindAttr(type, c);
1195 
1196     // a C++ member function must be queried for constness via a different API
1197     primary.type.attr.isConst = cast(Flag!"isConst") c.func.isConst;
1198 
1199     InfoT info;
1200     static if (is(InfoT == TypeKind.FuncInfo)) {
1201         info.fmt = FuncFmt(TypeIdLR(Left(return_t.toStringDecl.strip),
1202                 Right("(" ~ params.params.joinParamId ~ ")")));
1203     } else {
1204         info.fmt = FuncSignatureFmt(TypeIdLR(Left(return_t.toStringDecl.strip),
1205                 Right("(" ~ params.params.joinParamId ~ ")")));
1206     }
1207     info.return_ = return_t.kind.usr;
1208     info.returnAttr = return_t.attr;
1209     info.params = params.params.map!(a => FuncInfoParam(a.result.type.kind.usr,
1210             a.result.type.attr, a.id, a.isVariadic)).array();
1211 
1212     primary.type.kind.info = info;
1213     primary.location = makeLocation(c);
1214 
1215     primary.type.kind.usr = c.usr;
1216     if (primary.type.kind.usr.length == 0) {
1217         primary.type.kind.usr = makeFallbackUSR(c, indent);
1218     } else if (c.kind.among(CXCursorKind.varDecl, CXCursorKind.fieldDecl,
1219             CXCursorKind.templateTypeParameter, CXCursorKind.parmDecl)) {
1220         // TODO consider how the knowledge of the field could be "moved" out of
1221         // this function.
1222         // Instances must result in a unique USR. Otherwise it is impossible to
1223         // differentiate between the type and field.
1224         primary.type.kind.usr = makeFallbackUSR(c, indent);
1225     }
1226 
1227     rval.primary = primary;
1228     rval.extra ~= params.params.map!(a => a.result).array() ~ params.extra;
1229     rval.extra ~= return_rval.primary;
1230     rval.extra ~= return_rval.extra;
1231 
1232     return typeof(return)(rval);
1233 }
1234 
1235 private Nullable!TypeResults typeToCtor(ref const(Cursor) c, ref Type type,
1236         ref const(Container) container, in uint indent)
1237 in {
1238     logNode(c, indent);
1239     logType(type, indent);
1240     assert(c.kind == CXCursorKind.constructor);
1241 }
1242 out (result) {
1243     logTypeResult(result, indent);
1244 }
1245 body {
1246     import std.algorithm : map;
1247     import std.array : array;
1248     import cpptooling.data : CtorFmt;
1249 
1250     TypeResults rval;
1251     auto params = extractParams(c, type, container, indent);
1252     TypeResult primary;
1253     primary.type = makeTypeKindAttr(type, c);
1254 
1255     TypeKind.CtorInfo info;
1256     info.fmt = CtorFmt(TypeId(format("(%s)", params.params.joinParamId())));
1257     info.params = params.params.map!(a => FuncInfoParam(a.result.type.kind.usr,
1258             a.result.type.attr, a.id, a.isVariadic)).array();
1259     info.id = c.spelling;
1260 
1261     primary.type.kind.info = info;
1262     primary.type.kind.usr = c.usr;
1263     primary.location = makeLocation(c);
1264 
1265     rval.primary = primary;
1266     rval.extra ~= params.params.map!(a => a.result).array() ~ params.extra;
1267 
1268     return typeof(return)(rval);
1269 }
1270 
1271 private Nullable!TypeResults typeToDtor(ref const(Cursor) c, ref Type type, in uint indent)
1272 in {
1273     logNode(c, indent);
1274     logType(type, indent);
1275     assert(c.kind == CXCursorKind.destructor);
1276 }
1277 out (result) {
1278     logTypeResult(result, indent);
1279 }
1280 body {
1281     TypeResults rval;
1282     auto primary = makeTypeKindAttr(type, c);
1283 
1284     TypeKind.DtorInfo info;
1285     info.id = c.spelling[1 .. $]; // remove the leading ~
1286 
1287     primary.kind.info = info;
1288     primary.kind.usr = c.usr;
1289 
1290     rval.primary.location = makeLocation(c);
1291     rval.primary.type = primary;
1292 
1293     return typeof(return)(rval);
1294 }
1295 
1296 //TODO change the array to an appender, less GC pressure
1297 private alias PointerTypeAttr = Tuple!(TypeAttr[], "ptrs", TypeAttr, "base");
1298 
1299 /** Retrieve the attributes of the pointers until base condition.
1300  *
1301  * [$] is the value pointed at.
1302  *
1303  * Params:
1304  *  underlying = the value type, injected at correct position.
1305  *  type = a pointer or reference type.
1306  *  indent = indent for the log strings.
1307  * Return: An array of attributes for the pointers.
1308  */
1309 private PointerTypeAttr retrievePointeeAttr(ref Type type, in uint this_indent)
1310 in {
1311     logType(type, this_indent);
1312 }
1313 out (result) {
1314     import std.range : chain, only;
1315 
1316     foreach (r; chain(only(result.base), result.ptrs)) {
1317         logTypeAttr(r, this_indent);
1318     }
1319 }
1320 body {
1321     auto indent = this_indent + 1;
1322     PointerTypeAttr rval;
1323     auto decl_c = type.declaration;
1324 
1325     if (type.kind.among(CXTypeKind.pointer, CXTypeKind.lValueReference)) {
1326         // recursive
1327         auto pointee = type.pointeeType;
1328         rval = retrievePointeeAttr(pointee, indent);
1329         // current appended so right most ptr is at position 0.
1330         rval.ptrs ~= makeTypeAttr(type, decl_c);
1331     } else {
1332         // Base condition.
1333         rval.base = makeTypeAttr(type, decl_c);
1334     }
1335 
1336     return rval;
1337 }
1338 
1339 /// TODO this function is horrible. Refactor
1340 private Nullable!TypeResults typeToArray(ref const(Cursor) c, ref Type type,
1341         ref const(Container) container, const uint this_indent)
1342 in {
1343     logNode(c, this_indent);
1344     logType(type, this_indent);
1345 }
1346 out (result) {
1347     logTypeResult(result, this_indent);
1348     assert(result.primary.type.kind.info.kind == TypeKind.Info.Kind.array);
1349 }
1350 body {
1351     import std.format : format;
1352     import cpptooling.data : ArrayFmt, LocationTag, Location;
1353 
1354     immutable indent = this_indent + 1;
1355 
1356     static void gatherIndexesToElement(Type start, ref ArrayInfoIndex[] indexes, ref Type element) {
1357         Type curr = start;
1358 
1359         while (curr.kind.among(CXTypeKind.constantArray, CXTypeKind.incompleteArray)) {
1360             auto arr = curr.array;
1361 
1362             switch (curr.kind) with (CXTypeKind) {
1363             case constantArray:
1364                 indexes ~= ArrayInfoIndex(arr.size);
1365                 break;
1366             case incompleteArray:
1367                 indexes ~= ArrayInfoIndex();
1368                 break;
1369             default:
1370                 break;
1371             }
1372 
1373             curr = arr.elementType;
1374         }
1375 
1376         element = curr;
1377     }
1378 
1379     static void determineElement(Type ele_type, ref const(ArrayInfoIndex[]) indexes,
1380             ref const(Cursor) c, ref const(Container) container, ref USRType primary_usr,
1381             ref LocationTag primary_loc, ref TypeResults element, const uint indent) {
1382         auto index_decl = ele_type.declaration;
1383 
1384         if (index_decl.kind == CXCursorKind.noDeclFound) {
1385             // on purpuse not checking if it is null before using
1386             element = passType(c, ele_type, container, indent).get;
1387 
1388             if (element.primary.type.kind.usr.length != 0) {
1389                 primary_usr = element.primary.type.kind.usr;
1390             } else {
1391                 primary_usr = makeFallbackUSR(c, indent);
1392             }
1393             primary_loc = element.primary.location;
1394         } else {
1395             // on purpuse not checking if it is null before using
1396             element = retrieveType(index_decl, container, indent).get;
1397 
1398             primary_usr = element.primary.type.kind.usr;
1399             primary_loc = element.primary.location;
1400         }
1401         // let the indexing affect the USR as to not collide with none-arrays of
1402         // the same type.
1403         primary_usr = primary_usr ~ indexes.toRepr;
1404 
1405         switch (primary_loc.kind) {
1406         case LocationTag.Kind.noloc:
1407             // TODO this is stupid ... fix it. Shouldn't be needed but happens
1408             // when it is an array of primary types.
1409             // Probably the correct fix is the contract in retrieveType to check
1410             // that if it is an array at primary types it do NOT check for length.
1411             primary_loc = makeLocation(c);
1412             break;
1413         default:
1414         }
1415     }
1416 
1417     // step 1, find indexing and element type
1418     ArrayInfoIndex[] index_nr;
1419     Type element_type = type;
1420 
1421     gatherIndexesToElement(type, index_nr, element_type);
1422 
1423     // step 2, determine element
1424     TypeResults element;
1425     USRType primary_usr;
1426     LocationTag primary_loc;
1427 
1428     determineElement(element_type, index_nr, c, container, primary_usr,
1429             primary_loc, element, indent);
1430 
1431     // step 3, put together the result
1432 
1433     TypeKind.ArrayInfo info;
1434     info.element = element.primary.type.kind.usr;
1435     info.indexes = index_nr;
1436     info.fmt = ArrayFmt(element.primary.type.kind.splitTypeId(indent));
1437 
1438     TypeResults rval;
1439 
1440     if (!element.primary.type.kind.info.kind.among(TypeKind.Info.Kind.pointer,
1441             TypeKind.Info.Kind.funcPtr)) {
1442         auto elem_t = type.array.elementType;
1443         auto decl_c = elem_t.declaration;
1444         rval.primary.type.attr = makeTypeAttr(elem_t, decl_c);
1445     } else {
1446         rval.primary.type.attr = element.primary.type.attr;
1447     }
1448 
1449     rval.primary.type.kind.usr = primary_usr;
1450     rval.primary.location = primary_loc;
1451     rval.primary.type.kind.info = info;
1452     rval.extra ~= [element.primary] ~ element.extra;
1453 
1454     return typeof(return)(rval);
1455 }
1456 
1457 /** Retrieve the type of an instance declaration.
1458  *
1459  * Questions to consider:
1460  *  - Is the type a typeref?
1461  *  - Is it a function pointer?
1462  *  - Is the type a primitive type?
1463  */
1464 private Nullable!TypeResults retrieveInstanceDecl(ref const(Cursor) c,
1465         ref const(Container) container, in uint this_indent)
1466 in {
1467     logNode(c, this_indent);
1468     with (CXCursorKind) {
1469         assert(c.kind.among(varDecl, fieldDecl, templateTypeParameter, parmDecl));
1470     }
1471 }
1472 out (result) {
1473     logTypeResult(result, this_indent);
1474 }
1475 body {
1476     import std.range : takeOne;
1477 
1478     const auto indent = this_indent + 1;
1479     auto c_type = c.type;
1480 
1481     auto handlePointer(ref Nullable!TypeResults rval) {
1482         switch (c_type.kind) with (CXTypeKind) {
1483             // Is it a pointer?
1484             // Then preserve the pointer structure but dig deeper for the
1485             // pointed at type.
1486         case lValueReference:
1487         case pointer:
1488             // must retrieve attributes from the pointed at type thus need a
1489             // more convulated deduction
1490             rval = passType(c, c_type, container, indent);
1491             foreach (tref; c.children.takeOne) {
1492                 auto child = retrieveType(tref, container, indent);
1493                 if (!child.isNull) {
1494                     rval.extra ~= [child.primary] ~ child.extra;
1495                 }
1496             }
1497             break;
1498 
1499         default:
1500         }
1501     }
1502 
1503     auto handleTypedef(ref Nullable!TypeResults rval) {
1504         import std.algorithm : until;
1505         import cpptooling.analyzer.clang.cursor_visitor : visitBreathFirst;
1506 
1507         // example of tree analyzed:
1508         // VarDecl -> TypedefDecl
1509         // VarDecl -> TypeRef -> TypedefDecl
1510         foreach (child; c.visitBreathFirst.until!(a => a.depth == 3)) {
1511             if (child.kind == CXCursorKind.typeRef) {
1512                 rval = retrieveType(child, container, indent);
1513                 break;
1514             } else if (child.kind == CXCursorKind.typedefDecl) {
1515                 rval = retrieveType(child, container, indent);
1516                 break;
1517             }
1518         }
1519 
1520         if (!rval.isNull) {
1521             // depend on the underlying cursor
1522             auto old_def = rval.primary.type.attr.isDefinition;
1523 
1524             rval.primary.type.attr = makeTypeAttr(c_type, c);
1525 
1526             rval.primary.type.attr.isDefinition = old_def;
1527         }
1528     }
1529 
1530     auto handleTypeWithDecl(ref Nullable!TypeResults rval) {
1531         auto c_type_decl = c_type.declaration;
1532         if (c_type_decl.isValid) {
1533             auto type = c_type_decl.type;
1534             rval = passType(c_type_decl, type, container, indent);
1535         }
1536     }
1537 
1538     auto handleArray(ref Nullable!TypeResults rval) {
1539         // must check for array:nes before Typedef because of the case when it
1540         // is an array of typedef's
1541         if (c_type.isArray) {
1542             rval = typeToArray(c, c_type, container, indent);
1543         }
1544     }
1545 
1546     auto fallback(ref Nullable!TypeResults rval) {
1547         rval = passType(c, c_type, container, indent);
1548     }
1549 
1550     auto ensureUSR(ref Nullable!TypeResults rval) {
1551         if (!rval.isNull && rval.primary.type.kind.usr.length == 0) {
1552             rval.primary.type.kind.usr = makeFallbackUSR(c, this_indent);
1553         }
1554     }
1555 
1556     Nullable!TypeResults rval;
1557     foreach (idx, f; [&handlePointer, &handleArray, &handleTypedef,
1558             &handleTypeWithDecl, &fallback]) {
1559         debug {
1560             import std.conv : to;
1561 
1562             logger.trace(idx.to!string(), this_indent);
1563         }
1564 
1565         f(rval);
1566         if (!rval.isNull) {
1567             break;
1568         }
1569     }
1570 
1571     ensureUSR(rval);
1572 
1573     return rval;
1574 }
1575 
1576 private Nullable!TypeResults retrieveTypeAlias(ref const(Cursor) c,
1577         ref const(Container) container, in uint this_indent)
1578 in {
1579     logNode(c, this_indent);
1580     assert(c.kind.among(CXCursorKind.typeAliasDecl, CXCursorKind.typeAliasTemplateDecl));
1581 }
1582 out (result) {
1583     logTypeResult(result, this_indent);
1584 }
1585 body {
1586     const uint indent = this_indent + 1;
1587 
1588     Nullable!TypeResults rval;
1589 
1590     foreach (child; c.children) {
1591         if (!child.kind.among(CXCursorKind.typeRef, CXCursorKind.typeAliasDecl)) {
1592             continue;
1593         }
1594 
1595         auto tref = pass4(child, container, indent);
1596 
1597         auto type = c.type;
1598         // duplicated code from retrieveTypeDef -> handleTyperef
1599         // TODO consider if this can be harmonized with Typedef.
1600         // Maybe this is a special case?
1601         // Shouldn't be per se locked to a TypeDefDecl but rather the concept
1602         // of a type that is an alias for another.
1603         if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) {
1604             rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1605                     tref.primary.type.kind.info.canonicalRef, container, indent);
1606         } else {
1607             rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1608                     tref.primary.type.kind.usr, container, indent);
1609         }
1610         rval.extra = [tref.primary] ~ tref.extra;
1611     }
1612 
1613     if (rval.isNull && c.kind == CXCursorKind.typeAliasDecl) {
1614         auto type = c.type;
1615         rval = typeToSimple(c, type, indent);
1616     }
1617 
1618     return rval;
1619 }
1620 
1621 private Nullable!TypeResults retrieveTypeDef(ref const(Cursor) c,
1622         ref const(Container) container, in uint this_indent)
1623 in {
1624     logNode(c, this_indent);
1625     assert(c.kind == CXCursorKind.typedefDecl);
1626 }
1627 out (result) {
1628     logTypeResult(result, this_indent);
1629 }
1630 body {
1631     import std.range : takeOne;
1632 
1633     const uint indent = this_indent + 1;
1634 
1635     void handleTyperef(ref Nullable!TypeResults rval) {
1636         import std.algorithm : filter;
1637 
1638         if (isFuncProtoTypedef(c)) {
1639             // this case is handled by handleTyperefFuncProto
1640             return;
1641         }
1642 
1643         // any TypeRef children and thus need to traverse the tree?
1644         foreach (child; c.children.filterByTypeRef.filter!(a => a.kind == CXCursorKind.typeRef)
1645                 .takeOne) {
1646             auto tref = pass4(child, container, indent);
1647 
1648             auto type = c.type;
1649             if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) {
1650                 rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1651                         tref.primary.type.kind.info.canonicalRef, container, indent);
1652             } else {
1653                 rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1654                         tref.primary.type.kind.usr, container, indent);
1655             }
1656             rval.extra = [tref.primary] ~ tref.extra;
1657         }
1658     }
1659 
1660     void handleDecl(ref Nullable!TypeResults rval) {
1661         auto child_ = c.children.takeOne;
1662         if (child_.length == 0 || !child_[0].kind.canConvertNodeDeclToType) {
1663             return;
1664         }
1665 
1666         auto c_child = child_[0];
1667         auto tref = retrieveType(c_child, container, indent);
1668 
1669         auto type = c.type;
1670         if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) {
1671             rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1672                     tref.primary.type.kind.info.canonicalRef, container, indent);
1673         } else {
1674             rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1675                     tref.primary.type.kind.usr, container, indent);
1676         }
1677         rval.extra = [tref.primary] ~ tref.extra;
1678     }
1679 
1680     auto handleTypeRefToTypeDeclFuncProto(ref Nullable!TypeResults rval) {
1681         static bool isFuncProto(ref const(Cursor) c) {
1682             //TODO consider merging or improving isFuncProtoTypedef with this
1683             if (!isFuncProtoTypedef(c)) {
1684                 return false;
1685             }
1686 
1687             if (c.children.length == 0) {
1688                 return false;
1689             }
1690 
1691             auto child_t = c.children[0].type;
1692             if (!child_t.isFunctionType || child_t.isPointer) {
1693                 return false;
1694             }
1695 
1696             return true;
1697         }
1698 
1699         if (!isFuncProto(c)) {
1700             return;
1701         }
1702 
1703         auto child = c.children[0];
1704         auto ref_child = child.referenced;
1705         if (ref_child.kind != CXCursorKind.typedefDecl) {
1706             return;
1707         }
1708 
1709         auto tref = retrieveType(ref_child, container, indent);
1710 
1711         // TODO consolidate code. Copied from handleDecl
1712         auto type = c.type;
1713         if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) {
1714             rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1715                     tref.primary.type.kind.info.canonicalRef, container, indent);
1716         } else {
1717             rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1718                     tref.primary.type.kind.usr, container, indent);
1719         }
1720         rval.extra = [tref.primary] ~ tref.extra;
1721     }
1722 
1723     auto handleFuncProto(ref Nullable!TypeResults rval) {
1724         if (!isFuncProtoTypedef(c)) {
1725             return;
1726         }
1727 
1728         auto type = c.type;
1729         auto func = typeToFuncProto!(TypeKind.FuncSignatureInfo)(c, type, container, indent);
1730 
1731         rval = typeToTypedef(c, type, func.primary.type.kind.usr,
1732                 func.primary.type.kind.usr, container, indent);
1733         rval.extra = [func.primary] ~ func.extra;
1734     }
1735 
1736     auto underlying(ref Nullable!TypeResults rval) {
1737         // TODO this function is convoluted and complex. Consider how it can be rewritten.
1738 
1739         auto underlying = c.typedefUnderlyingType;
1740         auto underlying_decl_c = underlying.declaration;
1741 
1742         Nullable!TypeResults tref;
1743         // assuming that there are typedef nodes that have no declaration.
1744         if (underlying_decl_c.isValid) {
1745             tref = passType(underlying_decl_c, underlying, container, indent);
1746         } else {
1747             tref = passType(c, underlying, container, indent);
1748             // ensure it is unique
1749             tref.primary.type.kind.usr = makeFallbackUSR(c, indent);
1750         }
1751 
1752         USRType canonical_usr;
1753         if (tref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) {
1754             canonical_usr = tref.primary.type.kind.info.canonicalRef;
1755         } else {
1756             canonical_usr = tref.primary.type.kind.usr;
1757         }
1758 
1759         auto type = c.type;
1760         rval = typeToTypedef(c, type, tref.primary.type.kind.usr,
1761                 canonical_usr, container, indent);
1762         rval.extra = [tref.primary] ~ tref.extra;
1763     }
1764 
1765     void handleArray(ref Nullable!TypeResults rval) {
1766         // a constant array typedef has an integerLiteral as child.
1767         // handleDecl is built on the assumption that the first child of a
1768         // declaration that is a typedef is the "reference". As can be seen it
1769         // is wrong in the case for a constant array.
1770         auto underlying_t = c.typedefUnderlyingType;
1771 
1772         if (underlying_t.isArray) {
1773             underlying(rval);
1774         }
1775     }
1776 
1777     // TODO investigate if this can be removed, aka always covered by underlying.
1778     auto fallback(ref Nullable!TypeResults rval) {
1779         // fallback, unable to represent as a typedef ref'ing a type
1780         auto type = c.type;
1781         rval = passType(c, type, container, indent);
1782     }
1783 
1784     typeof(return) rval;
1785     foreach (idx, f; [&handleTypeRefToTypeDeclFuncProto, &handleArray,
1786             &handleTyperef, &handleFuncProto, &handleDecl, &underlying, &fallback]) {
1787         debug {
1788             import std.conv : to;
1789 
1790             logger.trace(idx.to!string(), this_indent);
1791         }
1792         f(rval);
1793         if (!rval.isNull) {
1794             break;
1795         }
1796     }
1797 
1798     return rval;
1799 }
1800 
1801 /** Retrieve the type representation of a FuncDecl or CXXMethod.
1802  *
1803  * case a. A typedef of a function signature.
1804  * When it is instantiated it results in a FunctionDecl with a TypeRef.
1805  * Note in the example that the child node is a TypeRef.
1806  * Using the resultType to distinguish between a typedef function signature and
1807  * a function returning a function ptr.
1808  *
1809  * Example:
1810  * FunctionDecl "tiger" [Keyword "extern", Identifier "func_type", Identifier "tiger"] c:@F@tiger
1811  *   TypeRef "func_type" [Identifier "func_type"]
1812  *
1813  * case b. A function with a return type which is a TypeRef to a TypedefDecl.
1814  * The first child node is a TypeRef.
1815  * This case should NOT be confused with case a.
1816  *
1817  * case c. A function declared "the normal way", void foo();
1818  *
1819  * solve case a.
1820  * Try resolving the type of the first child node.
1821  * If the canonical type is a function, good. Case a.
1822  * Otherwise case b and c.
1823  */
1824 private Nullable!TypeResults retrieveFunc(ref const(Cursor) c,
1825         ref const(Container) container, const uint this_indent)
1826 in {
1827     logNode(c, this_indent);
1828     assert(c.kind.among(CXCursorKind.functionDecl, CXCursorKind.cxxMethod));
1829 }
1830 out (result) {
1831     logTypeResult(result, this_indent);
1832 }
1833 body {
1834     import std.algorithm : filter;
1835     import std.range : chain, only;
1836     import cpptooling.data : FuncFmt;
1837 
1838     immutable indent = this_indent + 1;
1839     typeof(return) rval;
1840 
1841     // distinguish between a child node that is for the return value from those
1842     // cases when it is a function derived from a typedef:ed function signature.
1843     auto result_decl_usr = c.func.resultType.declaration.usr;
1844 
1845     foreach (child; c.children.filterByTypeRef.filter!((a) {
1846             auto tmp = a.referenced.usr;
1847             return tmp != result_decl_usr;
1848         })) {
1849         if (child.kind != CXCursorKind.typeRef) {
1850             break;
1851         }
1852         auto retrieved_ref = retrieveType(child, container, indent);
1853 
1854         if (retrieved_ref.isNull) {
1855             continue;
1856         }
1857 
1858         if (retrieved_ref.primary.type.kind.info.kind == TypeKind.Info.Kind.func) {
1859             // fast path
1860             rval = retrieved_ref;
1861         } else if (retrieved_ref.primary.type.kind.info.kind == TypeKind.Info.Kind.typeRef) {
1862             // check the canonical type
1863             foreach (k; chain(only(retrieved_ref.primary), retrieved_ref.extra)) {
1864                 if (k.type.kind.usr != retrieved_ref.primary.type.kind.info.canonicalRef) {
1865                     continue;
1866                 }
1867 
1868                 if (k.type.kind.info.kind == TypeKind.Info.Kind.func) {
1869                     rval = retrieved_ref;
1870                 } else if (k.type.kind.info.kind == TypeKind.Info.Kind.funcSignature) {
1871                     // function declaration of a typedef'ed signature
1872                     rval = retrieved_ref;
1873                     rval.extra ~= rval.primary;
1874 
1875                     auto prim = k;
1876                     auto info = k.type.kind.info;
1877                     prim.type.kind.info = TypeKind.FuncInfo(FuncFmt(k.type.kind.splitTypeId(indent)),
1878                             info.return_, info.returnAttr, info.params);
1879                     prim.location = makeLocation(c);
1880                     prim.type.kind.usr = makeFallbackUSR(c, this_indent);
1881                     rval.primary = prim;
1882                 }
1883             }
1884         }
1885     }
1886 
1887     if (rval.isNull) {
1888         auto type = c.type;
1889         rval = passType(c, type, container, indent);
1890     }
1891 
1892     return rval;
1893 }
1894 
1895 /** Only able to uniquely represent the class template.
1896  *
1897  * TODO Unable to instansiate.
1898  */
1899 private Nullable!TypeResults retrieveClassTemplate(ref const(Cursor) c,
1900         ref const(Container) container, in uint indent)
1901 in {
1902     import std.algorithm : among;
1903 
1904     logNode(c, indent);
1905     assert(c.kind.among(CXCursorKind.classTemplate,
1906             CXCursorKind.classTemplatePartialSpecialization));
1907 }
1908 body {
1909     TypeResults rval;
1910 
1911     auto type = c.type;
1912     rval.primary.type = makeTypeKindAttr(type, c);
1913     rval.primary.type.kind.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(c.spelling)));
1914     rval.primary.type.kind.usr = c.usr;
1915     rval.primary.location = makeLocation(c);
1916 
1917     return typeof(return)(rval);
1918 }
1919 
1920 private Nullable!TypeResults retrieveClassBaseSpecifier(ref const(Cursor) c,
1921         ref const(Container) container, in uint this_indent)
1922 in {
1923     logNode(c, this_indent);
1924     assert(c.kind == CXCursorKind.cxxBaseSpecifier);
1925 }
1926 body {
1927     auto indent = this_indent + 1;
1928 
1929     // when the cursor references a definition. easy
1930     bool tryReferenced(ref Nullable!TypeResults rval) {
1931         logger.trace("", this_indent);
1932         auto c_ref = c.referenced;
1933 
1934         if (c_ref.kind == CXCursorKind.noDeclFound) {
1935             return false;
1936         }
1937 
1938         rval = retrieveType(c_ref, container, indent);
1939 
1940         return true;
1941     }
1942 
1943     // no definition exist. e.g in the cases of a template instantiation.
1944     bool reconstructFromCursor(ref Nullable!TypeResults rval_) {
1945         logger.trace("", this_indent);
1946 
1947         TypeResults rval;
1948 
1949         auto type = c.type;
1950         rval.primary.type = makeTypeKindAttr(type, c);
1951 
1952         rval.primary.type.kind.info = TypeKind.SimpleInfo(SimpleFmt(TypeId(c.spelling)));
1953         rval.primary.type.kind.usr = makeEnsuredUSR(c, indent);
1954         rval.primary.location = makeLocation(c);
1955 
1956         rval_ = Nullable!TypeResults(rval);
1957 
1958         return true;
1959     }
1960 
1961     Nullable!TypeResults rval;
1962 
1963     foreach (idx, f; [&tryReferenced, &reconstructFromCursor]) {
1964         if (f(rval)) {
1965             break;
1966         }
1967     }
1968 
1969     return typeof(return)(rval);
1970 }
1971 
1972 /** Extract the type of a parameter cursor.
1973  *
1974  * TODO if nothing changes remove either retrieveParam or retrieveInstanceDecl,
1975  * code duplication.
1976  */
1977 private Nullable!TypeResults retrieveParam(ref const(Cursor) c,
1978         ref const(Container) container, in uint this_indent)
1979 in {
1980     logNode(c, this_indent);
1981     // TODO add assert for the types allowed
1982 }
1983 out (result) {
1984     logTypeResult(result, this_indent);
1985 }
1986 body {
1987     return retrieveInstanceDecl(c, container, this_indent + 1);
1988 }
1989 
1990 /** Only able to uniquely represent the class template.
1991  *
1992  * TODO Unable to instansiate.
1993  */
1994 private Nullable!TypeResults retrieveTemplateParam(ref const(Cursor) c,
1995         ref const(Container) container, in uint this_indent)
1996 in {
1997     logNode(c, this_indent);
1998     // TODO add assert for the types allowed
1999 }
2000 body {
2001     import std.range : takeOne;
2002 
2003     uint indent = this_indent + 1;
2004     Nullable!TypeResults rval;
2005 
2006     if (c.spelling.length == 0) {
2007         //TODO could probably be a random name, the location or something.
2008         // Example when it occurs:
2009         // template <typename/*here*/> class allocator;
2010         return rval;
2011     }
2012 
2013     auto type = c.type;
2014     rval = retrieveParam(c, container, indent);
2015 
2016     return rval;
2017 }
2018 
2019 private alias ExtractParamsResult = Tuple!(TypeResult, "result", string, "id",
2020         Flag!"isVariadic", "isVariadic");
2021 private alias ExtractParamsResults = Tuple!(ExtractParamsResult[], "params",
2022         TypeResult[], "extra");
2023 
2024 private ExtractParamsResults extractParams(ref const(Cursor) c, ref Type type,
2025         ref const(Container) container, in uint this_indent)
2026 in {
2027     logNode(c, this_indent);
2028     logType(type, this_indent);
2029     assert(type.isFunctionType || type.isTypedef || type.kind == CXTypeKind.functionNoProto);
2030 }
2031 out (result) {
2032     foreach (p; result.params) {
2033         logger.trace(p.result.type.toStringDecl(p.id), this_indent);
2034     }
2035 
2036     foreach (e; result.extra) {
2037         logTypeResult(e, this_indent);
2038     }
2039 }
2040 body {
2041     const auto indent = this_indent + 1;
2042 
2043     void appendParams(ref const(Cursor) c, ref ExtractParamsResults rval) {
2044         import std.range : enumerate;
2045 
2046         foreach (idx, p; c.children.enumerate) {
2047             if (p.kind != CXCursorKind.parmDecl) {
2048                 logNode(p, this_indent);
2049                 continue;
2050             }
2051 
2052             auto tka = retrieveType(p, container, indent);
2053             auto id = p.spelling;
2054             rval.params ~= ExtractParamsResult(tka.primary, id, No.isVariadic);
2055             rval.extra ~= tka.extra;
2056         }
2057 
2058         if (type.func.isVariadic) {
2059             import clang.SourceLocation;
2060 
2061             TypeResult result;
2062 
2063             auto info = TypeKind.SimpleInfo(SimpleFmt(TypeId("...")));
2064             result.type.kind.info = info;
2065             result.type.kind.usr = "..." ~ c.location.toString();
2066             result.location = makeLocation(c);
2067 
2068             // TODO remove this ugly hack
2069             // space as id to indicate it is empty
2070             rval.params ~= ExtractParamsResult(result, " ", Yes.isVariadic);
2071         }
2072     }
2073 
2074     ExtractParamsResults rval;
2075 
2076     if (c.kind == CXCursorKind.typeRef) {
2077         auto cref = c.referenced;
2078         appendParams(cref, rval);
2079     } else {
2080         appendParams(c, rval);
2081     }
2082 
2083     return rval;
2084 }
2085 
2086 /// Join an array slice of PTuples to a parameter string of "type" "id"
2087 private string joinParamId(ExtractParamsResult[] r) {
2088     import std.algorithm : joiner, map, filter;
2089     import std.conv : text;
2090     import std.range : enumerate;
2091 
2092     static string getTypeId(ref ExtractParamsResult p, ulong uid) {
2093         if (p.id.length == 0) {
2094             //TODO decide if to autogenerate for unnamed parameters here or later
2095             //return p.tka.toStringDecl("x" ~ text(uid));
2096             return p.result.type.toStringDecl("");
2097         } else {
2098             return p.result.type.toStringDecl(p.id);
2099         }
2100     }
2101 
2102     // using cache to avoid calling getName twice.
2103     return r.enumerate
2104         .map!(a => getTypeId(a.value, a.index))
2105         .filter!(a => a.length > 0)
2106         .joiner(", ").text();
2107 
2108 }
2109 
2110 private Nullable!string translateCursorType(CXTypeKind kind)
2111 in {
2112     import std.conv : to;
2113 
2114     logger.trace(to!string(kind));
2115 }
2116 out (result) {
2117     logger.trace(!result.isNull, result);
2118 }
2119 body {
2120     Nullable!string r;
2121 
2122     with (CXTypeKind) switch (kind) {
2123     case invalid:
2124         break;
2125     case unexposed:
2126         break;
2127     case void_:
2128         r = "void";
2129         break;
2130     case bool_:
2131         r = "bool";
2132         break;
2133     case charU:
2134         r = "unsigned char";
2135         break;
2136     case uChar:
2137         r = "unsigned char";
2138         break;
2139     case char16:
2140         break;
2141     case char32:
2142         break;
2143     case uShort:
2144         r = "unsigned short";
2145         break;
2146     case uInt:
2147         r = "unsigned int";
2148         break;
2149     case uLong:
2150         r = "unsigned long";
2151         break;
2152     case uLongLong:
2153         r = "unsigned long long";
2154         break;
2155     case uInt128:
2156         break;
2157     case charS:
2158         r = "char";
2159         break;
2160     case sChar:
2161         r = "char";
2162         break;
2163     case wChar:
2164         r = "wchar_t";
2165         break;
2166     case short_:
2167         r = "short";
2168         break;
2169     case int_:
2170         r = "int";
2171         break;
2172     case long_:
2173         r = "long";
2174         break;
2175     case longLong:
2176         r = "long long";
2177         break;
2178     case int128:
2179         break;
2180     case float_:
2181         r = "float";
2182         break;
2183     case double_:
2184         r = "double";
2185         break;
2186     case longDouble:
2187         r = "long double";
2188         break;
2189     case nullPtr:
2190         r = "null";
2191         break;
2192     case overload:
2193         break;
2194     case dependent:
2195         break;
2196 
2197     case objCId:
2198     case objCClass:
2199     case objCSel:
2200         break;
2201 
2202     case complex:
2203     case pointer:
2204     case blockPointer:
2205     case lValueReference:
2206     case rValueReference:
2207     case record:
2208     case enum_:
2209     case typedef_:
2210     case functionNoProto:
2211     case functionProto:
2212     case vector:
2213     case incompleteArray:
2214     case variableArray:
2215     case dependentSizedArray:
2216     case memberPointer:
2217     case auto_:
2218 
2219         /**
2220      * \brief Represents a type that was referred to using an elaborated type keyword.
2221      *
2222      * E.g., struct S, or via a qualified name, e.g., N::M::type, or both.
2223      */
2224     case elaborated:
2225         break;
2226 
2227     default:
2228         logger.trace("Unhandled type kind ", to!string(kind));
2229     }
2230 
2231     return r;
2232 }