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