1 module clang;
2 
3 
4 import clang.c.index;
5 import clang.c.util: EnumD;
6 
7 
8 immutable bool[string] gPredefinedCursors;
9 
10 version (Windows)
11     extern(C) private int _mktemp_s(char* nameTemplate, size_t sizeInChars) nothrow @safe @nogc;
12 
13 shared static this() nothrow {
14     try {
15 
16         const fileName = () {
17             import std.file: tempDir;
18             import std.path: buildPath;
19             char[] tmpnamBuf = "libclangXXXXXX\0".dup;
20             version (Posix) {
21                 import core.sys.posix.stdlib: mkstemp;
22                 mkstemp(tmpnamBuf.ptr);
23             }
24             else version (Windows)
25                 _mktemp_s(tmpnamBuf.ptr, tmpnamBuf.length);
26             return tempDir().buildPath(tmpnamBuf[0 .. $-1]);
27         }();
28         {
29             // create an empty file
30             import std.stdio: File;
31             auto f = File(fileName, "w");
32             f.writeln;
33             f.flush;
34             f.detach;
35         }
36 
37         auto tu = parse(fileName,
38                         ["-xc"],
39                         TranslationUnitFlags.DetailedPreprocessingRecord);
40         foreach(cursor; tu.cursor.children) {
41             gPredefinedCursors[cursor.spelling] = true;
42         }
43 
44     } catch(Exception e) {
45         import std.stdio: stderr;
46         try
47             stderr.writeln("Error initialising libclang: ", e);
48         catch(Exception _) {}
49     }
50 }
51 
52 mixin EnumD!("TranslationUnitFlags", CXTranslationUnit_Flags, "CXTranslationUnit_");
53 mixin EnumD!("Language", CXLanguageKind, "CXLanguage_");
54 
55 
56 TranslationUnit parse(in string fileName,
57                       in TranslationUnitFlags translUnitflags = TranslationUnitFlags.None)
58     @safe
59 {
60     return parse(fileName, [], translUnitflags);
61 }
62 
63 
64 mixin EnumD!("ErrorCode", CXErrorCode, "");
65 mixin EnumD!("DiagnosticSeverity", CXDiagnosticSeverity, "CXDiagnostic_");
66 mixin EnumD!("TemplateArgumentKind", CXTemplateArgumentKind, "CXTemplateArgumentKind_");
67 
68 
69 TranslationUnit parse(in string fileName,
70                       in string[] commandLineArgs,
71                       in TranslationUnitFlags translUnitflags = TranslationUnitFlags.None)
72     @safe
73 {
74 
75     import std.string: toStringz;
76     import std.algorithm: map;
77     import std.array: array, join;
78     import std.conv: text;
79 
80     // faux booleans
81     const excludeDeclarationsFromPCH = 0;
82     const displayDiagnostics = 0;
83     auto index = clang_createIndex(excludeDeclarationsFromPCH, displayDiagnostics);
84     CXUnsavedFile[] unsavedFiles;
85     const commandLineArgz = commandLineArgs
86         .map!(a => a.toStringz)
87         .array;
88 
89     CXTranslationUnit cx;
90     const err = () @trusted {
91         return cast(ErrorCode)clang_parseTranslationUnit2(
92             index,
93             fileName.toStringz,
94             commandLineArgz.ptr, // .ptr since the length can be 0
95             cast(int)commandLineArgz.length,
96             unsavedFiles.ptr,  // .ptr since the length can be 0
97             cast(uint)unsavedFiles.length,
98             translUnitflags,
99             &cx,
100         );
101     }();
102 
103     if(err != ErrorCode.success) {
104         throw new Exception(text("Could not parse ", fileName, ": ", err));
105     }
106 
107     string[] errorMessages;
108     // throw if there are error diagnostics
109     foreach(i; 0 .. clang_getNumDiagnostics(cx)) {
110         auto diagnostic = clang_getDiagnostic(cx, i);
111         scope(exit) clang_disposeDiagnostic(diagnostic);
112         const severity = cast(DiagnosticSeverity) clang_getDiagnosticSeverity(diagnostic);
113         enum diagnosticOptions = CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn;
114         if(severity == DiagnosticSeverity.Error || severity == DiagnosticSeverity.Fatal)
115             errorMessages ~= clang_formatDiagnostic(diagnostic, diagnosticOptions).toString;
116     }
117 
118     if(errorMessages.length > 0)
119         throw new Exception(text("Error parsing '", fileName, "':\n",
120                                  errorMessages.join("\n")));
121 
122 
123     return TranslationUnit(cx);
124 }
125 
126 string[] systemPaths() @safe {
127     import std.process: execute;
128     import std.string: splitLines, stripLeft;
129     import std.algorithm: map, countUntil;
130     import std.array: array;
131 
132     version(Windows)
133     {
134         enum devnull = "NUL";
135     } else {
136         enum devnull = "/dev/null";
137     }
138 
139     const res = () {
140         try
141         {
142             return execute(["clang", "-v", "-xc++", devnull, "-fsyntax-only"], ["LANG": "C"]);
143         }
144         catch (Exception e)
145         {
146             import std.typecons : Tuple;
147             return Tuple!(int, "status", string, "output")(-1, e.msg);
148         }
149     }();
150     if(res.status != 0) throw new Exception("Failed to call clang:\n" ~ res.output);
151 
152     auto lines = res.output.splitLines;
153 
154     const startIndex = lines.countUntil("#include <...> search starts here:") + 1;
155     assert(startIndex > 0);
156     const endIndex = lines.countUntil("End of search list.");
157     assert(endIndex > 0);
158 
159     return lines[startIndex .. endIndex].map!stripLeft.array;
160 }
161 
162 
163 mixin EnumD!("ChildVisitResult", CXChildVisitResult, "CXChildVisit_");
164 alias CursorVisitor = ChildVisitResult delegate(Cursor cursor, Cursor parent);
165 
166 struct TranslationUnit {
167 
168     CXTranslationUnit cx;
169     Cursor cursor;
170 
171     this(CXTranslationUnit cx) @safe nothrow {
172         this.cx = cx;
173         this.cursor = Cursor(clang_getTranslationUnitCursor(cx));
174     }
175 }
176 
177 string toString(CXString cxString) @safe pure nothrow {
178     import std.conv: to;
179     auto cstr = clang_getCString(cxString);
180     scope(exit) clang_disposeString(cxString);
181     return () @trusted { return cstr.to!string; }();
182 }
183 
184 string[] toStrings(CXStringSet* strings) @trusted pure nothrow {
185     scope(exit) clang_disposeStringSet(strings);
186     string[] ret;
187     foreach(cxstr; strings.Strings[0 .. strings.Count])
188         ret ~= cxstr.toString;
189     return ret;
190 }
191 
192 mixin EnumD!("AccessSpecifier", CX_CXXAccessSpecifier, "CX_CXX");
193 
194 
195 struct Cursor {
196 
197     import std.traits: ReturnType;
198 
199     mixin EnumD!("Kind", CXCursorKind, "CXCursor_");
200     mixin EnumD!("StorageClass", CX_StorageClass, "CX_SC_");
201 
202     alias Hash = ReturnType!(clang_hashCursor);
203 
204     CXCursor cx;
205     private Cursor[] _children;
206     Kind kind;
207     string spelling;
208     Type type;
209     Type underlyingType;
210     SourceRange sourceRange;
211 
212     this(CXCursor cx) @safe pure nothrow {
213         this.cx = cx;
214         kind = cast(Kind) clang_getCursorKind(cx);
215         spelling = clang_getCursorSpelling(cx).toString;
216         type = Type(clang_getCursorType(cx));
217 
218         if(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl)
219             underlyingType = Type(clang_getTypedefDeclUnderlyingType(cx));
220 
221         sourceRange = SourceRange(clang_getCursorExtent(cx));
222     }
223 
224     private static extern(C) CXChildVisitResult ctorVisitor(CXCursor cursor,
225                                                             CXCursor parent,
226                                                             void* clientData_)
227         @safe nothrow
228     {
229         auto children = () @trusted { return cast(Cursor[]*) clientData_; }();
230         *children ~= Cursor(cursor);
231         return CXChildVisit_Continue;
232     }
233 
234     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
235         this(kind, spelling, Type());
236     }
237 
238     this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow {
239         this.kind = kind;
240         this.spelling = spelling;
241         this.type = type;
242     }
243 
244     /// Lazily return the cursor's children
245     inout(Cursor)[] children() @safe @property nothrow inout {
246         if(_children.length) return _children;
247 
248         inout(Cursor)[] ret;
249         // calling Cursor.visitChildren here would cause infinite recursion
250         // because cvisitor constructs a Cursor out of the parent
251         () @trusted { clang_visitChildren(cx, &ctorVisitor, &ret); }();
252         return ret;
253     }
254 
255     void children(Cursor[] cursors) @safe @property pure nothrow {
256         _children = cursors;
257     }
258 
259     Type returnType() @safe pure nothrow const {
260         return Type(clang_getCursorResultType(cx));
261     }
262 
263     /**
264        For EnumConstantDecl cursors, return the numeric value
265      */
266     auto enumConstantValue() @safe @nogc pure nothrow const {
267         assert(kind == Cursor.Kind.EnumConstantDecl);
268         return clang_getEnumConstantDeclValue(cx);
269     }
270 
271     Language language() @safe @nogc pure nothrow const {
272         return cast(Language) clang_getCursorLanguage(cx);
273     }
274 
275     Cursor canonical() @safe nothrow const {
276         return Cursor(clang_getCanonicalCursor(cx));
277     }
278 
279     /**
280        If this is the canonical cursor. Given forward declarations, there may
281        be several cursors for one entity. This returns true if this cursor
282        is the canonical one.
283      */
284     bool isCanonical() @safe @nogc pure nothrow const {
285         return cast(bool) clang_equalCursors(cx, clang_getCanonicalCursor(cx));
286     }
287 
288     bool isDefinition() @safe @nogc pure nothrow const {
289         return cast(bool) clang_isCursorDefinition(cx);
290     }
291 
292     bool isNull() @safe @nogc pure nothrow const {
293         return cast(bool) clang_Cursor_isNull(cx);
294     }
295 
296     static Cursor nullCursor() @safe nothrow {
297         return Cursor(clang_getNullCursor());
298     }
299 
300     Cursor definition() @safe nothrow const {
301         return Cursor(clang_getCursorDefinition(cx));
302     }
303 
304     string toString() @safe pure nothrow const {
305         import std.conv: text;
306         try {
307             const returnTypeStr = kind == Kind.FunctionDecl
308                 ? text(", ", returnType)
309                 : "";
310 
311             return text("Cursor(", kind, `, "`, spelling, `", `, type, returnTypeStr, ")");
312         } catch(Exception e)
313             assert(false, "Fatal error in Cursor.toString: " ~ e.msg);
314     }
315 
316     bool isPredefined() @safe @nogc pure nothrow const {
317         return (spelling in gPredefinedCursors) !is null;
318     }
319 
320     Cursor semanticParent() @safe nothrow const {
321         return Cursor(clang_getCursorSemanticParent(cx));
322     }
323 
324     Cursor lexicalParent() @safe nothrow const {
325         return Cursor(clang_getCursorLexicalParent(cx));
326     }
327 
328     bool isInvalid() @safe @nogc pure nothrow const {
329         return cast(bool) clang_isInvalid(cx.kind);
330     }
331 
332     auto hash() @safe @nogc pure nothrow const {
333         return clang_hashCursor(cx);
334     }
335 
336     string mangling() @safe pure nothrow const {
337         return clang_Cursor_getMangling(cx).toString;
338     }
339 
340     bool isAnonymous() @safe @nogc pure nothrow const {
341         return cast(bool) clang_Cursor_isAnonymous(cx);
342     }
343 
344     bool isBitField() @safe @nogc pure nothrow const {
345         return cast(bool) clang_Cursor_isBitField(cx);
346     }
347 
348     int bitWidth() @safe @nogc pure nothrow const {
349         return clang_getFieldDeclBitWidth(cx);
350     }
351 
352     auto accessSpecifier() @safe @nogc pure nothrow const {
353         return cast(AccessSpecifier) clang_getCXXAccessSpecifier(cx);
354     }
355 
356     StorageClass storageClass() @safe @nogc pure nothrow const {
357         return cast(StorageClass) clang_Cursor_getStorageClass(cx);
358     }
359 
360     bool isConstCppMethod() @safe @nogc pure nothrow const {
361         return cast(bool) clang_CXXMethod_isConst(cx);
362     }
363 
364     bool isMoveConstructor() @safe @nogc pure nothrow const {
365         return cast(bool) clang_CXXConstructor_isMoveConstructor(cx);
366     }
367 
368     bool isCopyConstructor() @safe @nogc pure nothrow const {
369         return cast(bool) clang_CXXConstructor_isCopyConstructor(cx);
370     }
371 
372     bool isMacroFunction() @safe @nogc pure nothrow const {
373         return cast(bool) clang_Cursor_isMacroFunctionLike(cx);
374     }
375 
376     bool isMacroBuiltin() @safe @nogc pure nothrow const {
377         return cast(bool) clang_Cursor_isMacroBuiltin(cx);
378     }
379 
380     Cursor specializedCursorTemplate() @safe pure nothrow const {
381         return Cursor(clang_getSpecializedCursorTemplate(cx));
382     }
383 
384     TranslationUnit translationUnit() @safe nothrow const {
385         return TranslationUnit(clang_Cursor_getTranslationUnit(cx));
386     }
387 
388     Token[] tokens() @safe nothrow const {
389         import std.algorithm: map;
390         import std.array: array;
391 
392         CXToken* tokens;
393         uint numTokens;
394 
395         () @trusted { clang_tokenize(translationUnit.cx, sourceRange.cx, &tokens, &numTokens); }();
396         // I hope this only deallocates the array
397         scope(exit) clang_disposeTokens(translationUnit.cx, tokens, numTokens);
398 
399         auto tokenSlice = () @trusted { return tokens[0 .. numTokens]; }();
400 
401         return tokenSlice.map!(a => Token(a, translationUnit)).array;
402     }
403 
404     alias templateParams = templateParameters;
405 
406     const(Cursor)[] templateParameters() @safe nothrow const {
407         import std.algorithm: filter;
408         import std.array: array;
409 
410         const amTemplate =
411             kind == Cursor.Kind.ClassTemplate
412             || kind == Cursor.Kind.TypeAliasTemplateDecl
413             || kind == Cursor.Kind.FunctionTemplate
414             ;
415         const templateCursor = amTemplate ? this : specializedCursorTemplate;
416 
417         auto range = templateCursor
418             .children
419             .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter ||
420                           a.kind == Cursor.Kind.NonTypeTemplateParameter);
421 
422         // Why is this @system? Who knows.
423         return () @trusted { return range.array; }();
424     }
425 
426     /**
427        If declared at file scope.
428      */
429     bool isFileScope() @safe nothrow const {
430         return lexicalParent.kind == Cursor.Kind.TranslationUnit;
431     }
432 
433     int numTemplateArguments() @safe @nogc pure nothrow const {
434         return clang_Cursor_getNumTemplateArguments(cx);
435     }
436 
437     TemplateArgumentKind templateArgumentKind(int i) @safe @nogc pure nothrow const {
438         return cast(TemplateArgumentKind) clang_Cursor_getTemplateArgumentKind(cx, i);
439     }
440 
441     Type templateArgumentType(int i) @safe pure nothrow const {
442         return Type(clang_Cursor_getTemplateArgumentType(cx, i));
443     }
444 
445     long templateArgumentValue(int i) @safe @nogc pure nothrow const {
446         return clang_Cursor_getTemplateArgumentValue(cx, i);
447     }
448 
449     bool isVirtual() @safe @nogc pure nothrow const {
450         return cast(bool) clang_CXXMethod_isVirtual(cx);
451     }
452 
453     bool isPureVirtual() @safe @nogc pure nothrow const {
454         return cast(bool) clang_CXXMethod_isPureVirtual(cx);
455     }
456 
457     string displayName() @safe pure nothrow const {
458         return clang_getCursorDisplayName(cx).toString;
459     }
460 
461     /**
462        For e.g. TypeRef or TemplateRef
463      */
464     Cursor referencedCursor() @safe nothrow const {
465         return Cursor(clang_getCursorReferenced(cx));
466     }
467 
468     Cursor[] overriddenCursors() @trusted /* @safe with DIP1000 */ const {
469         import std.algorithm: map;
470         import std.array: array;
471 
472         uint length;
473         CXCursor* cursors;
474 
475         clang_getOverriddenCursors(cx, &cursors, &length);
476         scope(exit) clang_disposeOverriddenCursors(cursors);
477 
478         return cursors[0 .. length].map!(a => Cursor(a)).array;
479     }
480 
481     auto numOverloadedDecls() @safe @nogc pure nothrow const {
482         return clang_getNumOverloadedDecls(cx);
483     }
484 
485     Cursor overloadedDecl(int i) @safe nothrow const {
486         return Cursor(clang_getOverloadedDecl(cx, cast(uint) i));
487     }
488 
489     bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const {
490         return cast(bool) clang_equalCursors(cx, other.cx);
491     }
492 
493     bool opEquals(in Cursor other) @safe @nogc pure nothrow const {
494         return cast(bool) clang_equalCursors(cx, other.cx);
495     }
496 
497     void visitChildren(scope CursorVisitor visitor) @safe nothrow const {
498         scope clientData = ClientData(visitor);
499         // why isn't this @safe with dip10000???
500         () @trusted { clang_visitChildren(cx, &cvisitor, &clientData); }();
501     }
502 
503     int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const {
504         return opApplyN(block);
505     }
506 
507     int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const {
508         return opApplyN(block);
509     }
510 
511     private int opApplyN(T)(scope T block) const {
512         import std.traits: Parameters;
513 
514         int stop = 0;
515 
516         enum numParams = Parameters!T.length;
517 
518         visitChildren((cursor, parent) {
519 
520             static if(numParams == 2)
521                 stop = block(cursor, parent);
522             else static if(numParams == 1)
523                 stop = block(cursor);
524             else
525                 static assert(false);
526 
527             return stop
528                 ? ChildVisitResult.Break
529                 : ChildVisitResult.Continue;
530         });
531 
532         return stop;
533     }
534 }
535 
536 
537 struct SourceRange {
538 
539     CXSourceRange cx;
540     string path;
541     SourceLocation start;
542     SourceLocation end;
543 
544     this(CXSourceRange cx) @safe pure nothrow {
545         this.cx = cx;
546         this.start = clang_getRangeStart(cx);
547         this.end = clang_getRangeEnd(cx);
548         this.path = start.path;
549     }
550 
551     string toString() @safe pure const {
552         import std.conv: text;
553         return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")");
554     }
555 }
556 
557 struct SourceLocation {
558     CXSourceLocation cx;
559     string path;
560     uint line;
561     uint column;
562     uint offset;
563 
564     this(CXSourceLocation cx) @safe pure nothrow {
565         this.cx = cx;
566 
567         CXFile file;
568         () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }();
569         this.path = clang_getFileName(file).toString;
570 
571         () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }();
572     }
573 
574     int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const {
575         if(path == other.path && line == other.line && column == other.column &&
576            offset == other.offset)
577             return 0;
578 
579         if(path < other.path) return -1;
580         if(path > other.path) return 1;
581         if(line < other.line) return -1;
582         if(line > other.line) return 1;
583         if(column < other.column) return -1;
584         if(column > other.column) return 1;
585         if(offset < other.offset) return -1;
586         if(offset > other.offset) return 1;
587         assert(false);
588     }
589 
590     string toString() @safe pure nothrow const {
591         import std.conv: text;
592         return text(`"`, path, `" `, line, ":", column, ":", offset);
593     }
594 }
595 
596 private struct ClientData {
597     /**
598        The D visitor delegate
599      */
600     CursorVisitor dvisitor;
601 }
602 
603 // This is the C function actually passed to libclang's clang_visitChildren
604 // The context (clientData) contains the D delegate that's then called on the
605 // (cursor, parent) pair
606 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) {
607     auto clientData = cast(ClientData*) clientData_;
608     return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent));
609 }
610 
611 
612 struct Type {
613 
614     mixin EnumD!("Kind", CXTypeKind, "CXType_");
615 
616     CXType cx;
617     Kind kind;
618     string spelling;
619 
620     this(CXType cx) @safe pure nothrow {
621         this.cx = cx;
622         this.kind = cast(Kind) cx.kind;
623         spelling = clang_getTypeSpelling(cx).toString;
624     }
625 
626     this(in Type other) @trusted pure nothrow {
627         import std.algorithm: map;
628         import std.array: array;
629 
630         this.cx.kind = other.cx.kind;
631         this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array;
632 
633         this(this.cx);
634     }
635 
636     this(in Kind kind) @safe @nogc pure nothrow {
637         this(kind, "");
638     }
639 
640     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
641         this.kind = kind;
642         this.spelling = spelling;
643     }
644 
645     Type pointee() @safe pure nothrow const {
646         return Type(clang_getPointeeType(cx));
647     }
648 
649     Type unelaborate() @safe nothrow const {
650         return Type(clang_Type_getNamedType(cx));
651     }
652 
653     Type canonical() @safe pure nothrow const {
654         return Type(clang_getCanonicalType(cx));
655     }
656 
657     Type returnType() @safe pure const {
658         return Type(clang_getResultType(cx));
659     }
660 
661     // Returns a range of Type
662     auto paramTypes()() @safe pure const nothrow {
663 
664         static struct Range {
665             const CXType cx;
666             const int numArgs;
667             int index = 0;
668 
669             bool empty() {
670                 return index < 0 || index >= numArgs;
671             }
672 
673             void popFront() {
674                 ++index;
675             }
676 
677             Type front() {
678                 return Type(clang_getArgType(cx, index));
679             }
680         }
681 
682         return Range(cx, clang_getNumArgTypes(cx));
683     }
684 
685     bool isVariadicFunction() @safe @nogc pure nothrow const {
686         return cast(bool) clang_isFunctionTypeVariadic(cx);
687     }
688 
689     Type elementType() @safe pure nothrow const {
690         return Type(clang_getElementType(cx));
691     }
692 
693     long numElements() @safe @nogc pure nothrow const {
694         return clang_getNumElements(cx);
695     }
696 
697     long arraySize() @safe @nogc pure nothrow const {
698         return clang_getArraySize(cx);
699     }
700 
701     bool isConstQualified() @safe @nogc pure nothrow const {
702         return cast(bool) clang_isConstQualifiedType(cx);
703     }
704 
705     bool isVolatileQualified() @safe @nogc pure nothrow const {
706         return cast(bool) clang_isVolatileQualifiedType(cx);
707     }
708 
709     Cursor declaration() @safe pure nothrow const {
710         return Cursor(clang_getTypeDeclaration(cx));
711     }
712 
713     Type namedType() @safe pure nothrow const {
714         return Type(clang_Type_getNamedType(cx));
715     }
716 
717     bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const {
718         return cast(bool) clang_equalTypes(cx, other.cx);
719     }
720 
721     bool opEquals(in Type other) @safe @nogc pure nothrow const {
722         return cast(bool) clang_equalTypes(cx, other.cx);
723     }
724 
725     bool isInvalid() @safe @nogc pure nothrow const {
726         return kind == Kind.Invalid;
727     }
728 
729     long getSizeof() @safe @nogc pure nothrow const {
730         return clang_Type_getSizeOf(cx);
731     }
732 
733     int numTemplateArguments() @safe @nogc pure nothrow const {
734         return clang_Type_getNumTemplateArguments(cx);
735     }
736 
737     Type typeTemplateArgument(int i) @safe pure nothrow const {
738         return Type(clang_Type_getTemplateArgumentAsType(cx, i));
739     }
740 
741     string toString() @safe pure nothrow const {
742         import std.conv: text;
743 
744         try {
745             return text("Type(", kind, `, "`, spelling, `")`);
746         } catch(Exception e)
747             assert(false, "Fatal error in Type.toString: " ~ e.msg);
748     }
749 }
750 
751 
752 struct Token {
753 
754     mixin EnumD!("Kind", CXTokenKind, "CXToken_");
755 
756     Kind kind;
757     string spelling;
758     CXToken cx;
759     TranslationUnit translationUnit;
760 
761     this(CXToken cx, TranslationUnit unit) @safe pure nothrow {
762         this.cx = cx;
763         this.translationUnit = unit;
764         this.kind = cast(Kind) clang_getTokenKind(cx);
765         this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx));
766     }
767 
768     this(Kind kind, string spelling) @safe @nogc pure nothrow {
769         this.kind = kind;
770         this.spelling = spelling;
771     }
772 
773     string toString() @safe pure const {
774         import std.conv: text;
775 
776         return text("Token(", kind, `, "`, spelling, `")`);
777     }
778 
779     bool opEquals(in Token other) @safe pure nothrow const {
780         return kind == other.kind && spelling == other.spelling;
781     }
782 }