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