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