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