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 = buildPath(tempDir, "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 tmpnamBuf.idup;
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 
186 string toString(CXString cxString) @safe pure nothrow {
187     import std.string: fromStringz;
188 
189     scope(exit) clang_disposeString(cxString);
190     auto cstr = clang_getCString(cxString);
191     return  () @trusted { return cstr.fromStringz.idup; }();
192 }
193 
194 
195 string[] toStrings(CXStringSet* strings) @safe pure nothrow {
196     import std.string: fromStringz;
197     import std.array: appender;
198 
199     scope(exit) clang_disposeStringSet(strings);
200 
201     auto app = appender!(string[]);
202     app.reserve(strings.Count);
203 
204     foreach(cxstr; () @trusted { return strings.Strings[0 .. strings.Count]; }()) {
205         // cannot use the toString above since it frees, and so
206         // does the dispose string set at scope exit, leading to
207         // a double free situation
208         auto cstr = clang_getCString(cxstr);
209         auto str = () @trusted { return cstr.fromStringz.idup; }();
210         app ~= str;
211     }
212 
213     return app.data;
214 }
215 
216 
217 mixin EnumD!("AccessSpecifier", CX_CXXAccessSpecifier, "CX_CXX");
218 
219 
220 struct Cursor {
221 
222     import clang.util: Lazy;
223     import std.traits: ReturnType;
224 
225     mixin EnumD!("Kind", CXCursorKind, "CXCursor_");
226     mixin EnumD!("StorageClass", CX_StorageClass, "CX_SC_");
227 
228     alias Hash = ReturnType!clang_hashCursor;
229 
230     CXCursor cx;
231     private Cursor[] _children;
232     Kind kind;
233     private string _spelling;
234     Type type;
235     Type underlyingType;
236     SourceRange sourceRange;
237 
238     mixin Lazy!_spelling;
239 
240     this(CXCursor cx) @safe @nogc pure nothrow {
241         this.cx = cx;
242         kind = cast(Kind) clang_getCursorKind(cx);
243         type = Type(clang_getCursorType(cx));
244 
245         if(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl)
246             underlyingType = Type(clang_getTypedefDeclUnderlyingType(cx));
247 
248         sourceRange = SourceRange(clang_getCursorExtent(cx));
249     }
250 
251     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
252         this(kind, spelling, Type());
253     }
254 
255     this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow {
256         this.kind = kind;
257         this._spelling = spelling;
258         this._spellingInit = true;
259         this.type = type;
260     }
261 
262     /// Lazily return the cursor's children
263     auto children(this This)() @property {
264         import std.array: appender;
265 
266         if(_children.length) return _children;
267 
268         auto app = appender!(Cursor[]);
269         app.reserve(10); // hacky but speeds things up, faster than counting the right number
270         // calling Cursor.visitChildren here would cause infinite recursion
271         // because cvisitor constructs a Cursor out of the parent
272         () @trusted { clang_visitChildren(cx, &childrenVisitor, &app); }();
273         () @trusted { cast(Cursor[]) _children = app.data; }();
274 
275         return _children;
276     }
277 
278     private static extern(C) CXChildVisitResult childrenVisitor(CXCursor cursor,
279                                                                 CXCursor parent,
280                                                                 void* clientData)
281         @safe nothrow
282     {
283         import std.array: Appender;
284 
285         auto app = () @trusted { return cast(Appender!(Cursor[])*) clientData; }();
286         *app ~= Cursor(cursor);
287 
288         return CXChildVisit_Continue;
289     }
290 
291     void children(Cursor[] cursors) @safe @property pure nothrow {
292         _children = cursors;
293     }
294 
295     void setSpelling(string str) @safe @nogc @property pure nothrow {
296         _spelling = str;
297         _spellingInit = true;
298     }
299 
300     Type returnType() @safe pure nothrow const {
301         return Type(clang_getCursorResultType(cx));
302     }
303 
304     /**
305        For EnumConstantDecl cursors, return the numeric value
306      */
307     auto enumConstantValue() @safe @nogc pure nothrow const {
308         assert(kind == Cursor.Kind.EnumConstantDecl);
309         return clang_getEnumConstantDeclValue(cx);
310     }
311 
312     Language language() @safe @nogc pure nothrow const {
313         return cast(Language) clang_getCursorLanguage(cx);
314     }
315 
316     Cursor canonical() @safe nothrow const {
317         return Cursor(clang_getCanonicalCursor(cx));
318     }
319 
320     /**
321        If this is the canonical cursor. Given forward declarations, there may
322        be several cursors for one entity. This returns true if this cursor
323        is the canonical one.
324      */
325     bool isCanonical() @safe @nogc pure nothrow const {
326         return cast(bool) clang_equalCursors(cx, clang_getCanonicalCursor(cx));
327     }
328 
329     bool isDefinition() @safe @nogc pure nothrow const {
330         return cast(bool) clang_isCursorDefinition(cx);
331     }
332 
333     bool isNull() @safe @nogc pure nothrow const {
334         return cast(bool) clang_Cursor_isNull(cx);
335     }
336 
337     static Cursor nullCursor() @safe nothrow {
338         return Cursor(clang_getNullCursor());
339     }
340 
341     Cursor definition() @safe nothrow const {
342         return Cursor(clang_getCursorDefinition(cx));
343     }
344 
345     string toString() @safe pure nothrow const {
346         import std.conv: text;
347         try {
348             const returnTypeStr = kind == Kind.FunctionDecl
349                 ? text(", ", returnType)
350                 : "";
351 
352             return text("Cursor(", kind, `, "`, spelling, `", `, type, returnTypeStr, ")");
353         } catch(Exception e)
354             assert(false, "Fatal error in Cursor.toString: " ~ e.msg);
355     }
356 
357     bool isPredefined() @safe pure nothrow const {
358         return (spelling in gPredefinedCursors) !is null;
359     }
360 
361     Cursor semanticParent() @safe nothrow const {
362         return Cursor(clang_getCursorSemanticParent(cx));
363     }
364 
365     Cursor lexicalParent() @safe nothrow const {
366         return Cursor(clang_getCursorLexicalParent(cx));
367     }
368 
369     bool isInvalid() @safe @nogc pure nothrow const {
370         return cast(bool) clang_isInvalid(cx.kind);
371     }
372 
373     auto hash() @safe @nogc pure nothrow const {
374         return clang_hashCursor(cx);
375     }
376 
377     string mangling() @safe pure nothrow const {
378         string mangle;
379         // for destructors, there may be multiple mangles,
380         // and the getMangling function doesn't always return
381         // the right one. To be honest, I don't know how to find
382         // the right one all the time, but in testing, the first
383         // one on this function, if it returns one, works more often.
384         // I wish I could explain more, I just know this passes the tests
385         // and the plain impl of just getMangling doesn't.
386         auto otherMangles = clang_Cursor_getCXXManglings(cx);
387         if(otherMangles) {
388             auto strings = toStrings(otherMangles);
389             if(strings.length)
390                 mangle = strings[0];
391         }
392         if(mangle is null)
393             mangle = clang_Cursor_getMangling(cx).toString;
394         return mangle;
395     }
396 
397     bool isAnonymous() @safe @nogc pure nothrow const {
398         return cast(bool) clang_Cursor_isAnonymous(cx);
399     }
400 
401     bool isBitField() @safe @nogc pure nothrow const {
402         return cast(bool) clang_Cursor_isBitField(cx);
403     }
404 
405     int bitWidth() @safe @nogc pure nothrow const {
406         return clang_getFieldDeclBitWidth(cx);
407     }
408 
409     auto accessSpecifier() @safe @nogc pure nothrow const {
410         return cast(AccessSpecifier) clang_getCXXAccessSpecifier(cx);
411     }
412 
413     StorageClass storageClass() @safe @nogc pure nothrow const {
414         return cast(StorageClass) clang_Cursor_getStorageClass(cx);
415     }
416 
417     bool isConstCppMethod() @safe @nogc pure nothrow const {
418         return cast(bool) clang_CXXMethod_isConst(cx);
419     }
420 
421     bool isMoveConstructor() @safe @nogc pure nothrow const {
422         return cast(bool) clang_CXXConstructor_isMoveConstructor(cx);
423     }
424 
425     bool isCopyConstructor() @safe @nogc pure nothrow const {
426         return cast(bool) clang_CXXConstructor_isCopyConstructor(cx);
427     }
428 
429     bool isMacroFunction() @safe @nogc pure nothrow const {
430         return cast(bool) clang_Cursor_isMacroFunctionLike(cx);
431     }
432 
433     bool isMacroBuiltin() @safe @nogc pure nothrow const {
434         return cast(bool) clang_Cursor_isMacroBuiltin(cx);
435     }
436 
437     Cursor specializedCursorTemplate() @safe pure nothrow const {
438         return Cursor(clang_getSpecializedCursorTemplate(cx));
439     }
440 
441     TranslationUnit translationUnit() @safe nothrow const {
442         return TranslationUnit(clang_Cursor_getTranslationUnit(cx));
443     }
444 
445     Token[] tokens() @safe nothrow const {
446         import std.algorithm: map;
447         import std.array: array;
448 
449         CXToken* tokens;
450         uint numTokens;
451 
452         auto tu = clang_Cursor_getTranslationUnit(cx);
453 
454         () @trusted { clang_tokenize(tu, sourceRange.cx, &tokens, &numTokens); }();
455         // I hope this only deallocates the array
456         scope(exit) clang_disposeTokens(tu, tokens, numTokens);
457 
458         auto tokenSlice = () @trusted { return tokens[0 .. numTokens]; }();
459 
460         return tokenSlice.map!(a => Token(a, tu)).array;
461     }
462 
463     alias templateParams = templateParameters;
464 
465     const(Cursor)[] templateParameters() @safe nothrow const {
466         import std.algorithm: filter;
467         import std.array: array;
468 
469         const amTemplate =
470             kind == Cursor.Kind.ClassTemplate
471             || kind == Cursor.Kind.TypeAliasTemplateDecl
472             || kind == Cursor.Kind.FunctionTemplate
473             ;
474         const templateCursor = amTemplate ? this : specializedCursorTemplate;
475 
476         auto range = templateCursor
477             .children
478             .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter ||
479                           a.kind == Cursor.Kind.NonTypeTemplateParameter);
480 
481         // Why is this @system? Who knows.
482         return () @trusted { return range.array; }();
483     }
484 
485     /**
486        If declared at file scope.
487      */
488     bool isFileScope() @safe nothrow const {
489         return lexicalParent.kind == Cursor.Kind.TranslationUnit;
490     }
491 
492     int numTemplateArguments() @safe @nogc pure nothrow const {
493         return clang_Cursor_getNumTemplateArguments(cx);
494     }
495 
496     TemplateArgumentKind templateArgumentKind(int i) @safe @nogc pure nothrow const {
497         return cast(TemplateArgumentKind) clang_Cursor_getTemplateArgumentKind(cx, i);
498     }
499 
500     Type templateArgumentType(int i) @safe pure nothrow const {
501         return Type(clang_Cursor_getTemplateArgumentType(cx, i));
502     }
503 
504     long templateArgumentValue(int i) @safe @nogc pure nothrow const {
505         return clang_Cursor_getTemplateArgumentValue(cx, i);
506     }
507 
508     bool isVirtual() @safe @nogc pure nothrow const {
509         return cast(bool) clang_CXXMethod_isVirtual(cx);
510     }
511 
512     bool isPureVirtual() @safe @nogc pure nothrow const {
513         return cast(bool) clang_CXXMethod_isPureVirtual(cx);
514     }
515 
516     string displayName() @safe pure nothrow const {
517         return clang_getCursorDisplayName(cx).toString;
518     }
519 
520     /**
521        For e.g. TypeRef or TemplateRef
522      */
523     Cursor referencedCursor() @safe nothrow const {
524         return Cursor(clang_getCursorReferenced(cx));
525     }
526 
527     Cursor[] overriddenCursors() @trusted /* @safe with DIP1000 */ const {
528         import std.algorithm: map;
529         import std.array: array;
530 
531         uint length;
532         CXCursor* cursors;
533 
534         clang_getOverriddenCursors(cx, &cursors, &length);
535         scope(exit) clang_disposeOverriddenCursors(cursors);
536 
537         return cursors[0 .. length].map!(a => Cursor(a)).array;
538     }
539 
540     auto numOverloadedDecls() @safe @nogc pure nothrow const {
541         return clang_getNumOverloadedDecls(cx);
542     }
543 
544     Cursor overloadedDecl(int i) @safe nothrow const {
545         return Cursor(clang_getOverloadedDecl(cx, cast(uint) i));
546     }
547 
548     bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const {
549         return cast(bool) clang_equalCursors(cx, other.cx);
550     }
551 
552     bool opEquals(in Cursor other) @safe @nogc pure nothrow const {
553         return cast(bool) clang_equalCursors(cx, other.cx);
554     }
555 
556     void visitChildren(scope CursorVisitor visitor) @safe nothrow const {
557         scope clientData = ClientData(visitor);
558         // why isn't this @safe with dip10000???
559         () @trusted { clang_visitChildren(cx, &cvisitor, &clientData); }();
560     }
561 
562     int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const {
563         return opApplyN(block);
564     }
565 
566     int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const {
567         return opApplyN(block);
568     }
569 
570     private int opApplyN(T)(scope T block) const {
571         import std.traits: Parameters;
572 
573         int stop = 0;
574 
575         enum numParams = Parameters!T.length;
576 
577         visitChildren((cursor, parent) {
578 
579             static if(numParams == 2)
580                 stop = block(cursor, parent);
581             else static if(numParams == 1)
582                 stop = block(cursor);
583             else
584                 static assert(false);
585 
586             return stop
587                 ? ChildVisitResult.Break
588                 : ChildVisitResult.Continue;
589         });
590 
591         return stop;
592     }
593 
594     private string _spellingCreate() @safe pure nothrow const {
595         return clang_getCursorSpelling(cx).toString;
596     }
597 }
598 
599 
600 struct SourceRange {
601 
602     import clang.util: Lazy;
603 
604     CXSourceRange cx;
605     private SourceLocation _start;
606     private SourceLocation _end;
607 
608     mixin Lazy!_start;
609     mixin Lazy!_end;
610 
611     this(CXSourceRange cx) @safe @nogc pure nothrow {
612         this.cx = cx;
613     }
614 
615     string path() @safe nothrow const {
616         return start.path;
617     }
618 
619     string toString() @safe pure const {
620         import std.conv: text;
621         return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")");
622     }
623 
624     private auto _startCreate() @safe pure nothrow const {
625         return SourceLocation(clang_getRangeStart(cx));
626     }
627 
628     private auto _endCreate() @safe pure nothrow const {
629         return SourceLocation(clang_getRangeEnd(cx));
630     }
631 }
632 
633 
634 struct SourceLocation {
635 
636     CXSourceLocation cx;
637     string path;
638     uint line;
639     uint column;
640     uint offset;
641 
642     this(CXSourceLocation cx) @safe pure nothrow {
643         this.cx = cx;
644 
645         CXFile file;
646         () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }();
647         this.path = clang_getFileName(file).toString;
648 
649        () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }();
650     }
651 
652     int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const {
653         if(path == other.path && line == other.line && column == other.column &&
654            offset == other.offset)
655             return 0;
656 
657         if(path < other.path) return -1;
658         if(path > other.path) return 1;
659         if(line < other.line) return -1;
660         if(line > other.line) return 1;
661         if(column < other.column) return -1;
662         if(column > other.column) return 1;
663         if(offset < other.offset) return -1;
664         if(offset > other.offset) return 1;
665         assert(false);
666     }
667 
668     string toString() @safe pure nothrow const {
669         import std.conv: text;
670         return text(`"`, path, `" `, line, ":", column, ":", offset);
671     }
672 }
673 
674 private struct ClientData {
675     /**
676        The D visitor delegate
677      */
678     CursorVisitor dvisitor;
679 }
680 
681 // This is the C function actually passed to libclang's clang_visitChildren
682 // The context (clientData) contains the D delegate that's then called on the
683 // (cursor, parent) pair
684 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) {
685     auto clientData = cast(ClientData*) clientData_;
686     return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent));
687 }
688 
689 
690 struct Type {
691 
692     import clang.util: Lazy;
693 
694     mixin EnumD!("Kind", CXTypeKind, "CXType_");
695 
696     CXType cx;
697     Kind kind;
698     private string _spelling;
699 
700     mixin Lazy!_spelling;
701 
702     this(CXType cx) @safe @nogc pure nothrow {
703         this.cx = cx;
704         this.kind = cast(Kind) cx.kind;
705     }
706 
707     this(in Type other) @trusted pure nothrow {
708         import std.algorithm: map;
709         import std.array: array;
710 
711         this.cx.kind = other.cx.kind;
712         this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array;
713 
714         this(this.cx);
715     }
716 
717     this(in Kind kind) @safe @nogc pure nothrow {
718         this(kind, "");
719     }
720 
721     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
722         this.kind = kind;
723         _spelling = spelling;
724     }
725 
726     Type pointee() @safe pure nothrow const {
727         return Type(clang_getPointeeType(cx));
728     }
729 
730     Type unelaborate() @safe nothrow const {
731         return Type(clang_Type_getNamedType(cx));
732     }
733 
734     Type canonical() @safe pure nothrow const {
735         return Type(clang_getCanonicalType(cx));
736     }
737 
738     Type returnType() @safe pure const {
739         return Type(clang_getResultType(cx));
740     }
741 
742     // Returns a range of Type
743     auto paramTypes()() @safe pure const nothrow {
744 
745         static struct Range {
746             const CXType cx;
747             const int numArgs;
748             int index = 0;
749 
750             bool empty() {
751                 return index < 0 || index >= numArgs;
752             }
753 
754             void popFront() {
755                 ++index;
756             }
757 
758             Type front() {
759                 return Type(clang_getArgType(cx, index));
760             }
761         }
762 
763         return Range(cx, clang_getNumArgTypes(cx));
764     }
765 
766     bool isVariadicFunction() @safe @nogc pure nothrow const {
767         return cast(bool) clang_isFunctionTypeVariadic(cx);
768     }
769 
770     Type elementType() @safe pure nothrow const {
771         return Type(clang_getElementType(cx));
772     }
773 
774     long numElements() @safe @nogc pure nothrow const {
775         return clang_getNumElements(cx);
776     }
777 
778     long arraySize() @safe @nogc pure nothrow const {
779         return clang_getArraySize(cx);
780     }
781 
782     bool isConstQualified() @safe @nogc pure nothrow const scope {
783         return cast(bool) clang_isConstQualifiedType(cx);
784     }
785 
786     bool isVolatileQualified() @safe @nogc pure nothrow const scope {
787         return cast(bool) clang_isVolatileQualifiedType(cx);
788     }
789 
790     Cursor declaration() @safe pure nothrow const {
791         return Cursor(clang_getTypeDeclaration(cx));
792     }
793 
794     Type namedType() @safe pure nothrow const {
795         return Type(clang_Type_getNamedType(cx));
796     }
797 
798     bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const {
799         return cast(bool) clang_equalTypes(cx, other.cx);
800     }
801 
802     bool opEquals(in Type other) @safe @nogc pure nothrow const {
803         return cast(bool) clang_equalTypes(cx, other.cx);
804     }
805 
806     bool isInvalid() @safe @nogc pure nothrow const {
807         return kind == Kind.Invalid;
808     }
809 
810     long getSizeof() @safe @nogc pure nothrow const {
811         return clang_Type_getSizeOf(cx);
812     }
813 
814     int numTemplateArguments() @safe @nogc pure nothrow const {
815         return clang_Type_getNumTemplateArguments(cx);
816     }
817 
818     Type typeTemplateArgument(int i) @safe pure nothrow const {
819         return Type(clang_Type_getTemplateArgumentAsType(cx, i));
820     }
821 
822     string toString() @safe pure nothrow const {
823         import std.conv: text;
824 
825         try {
826             return text("Type(", kind, `, "`, spelling, `")`);
827         } catch(Exception e)
828             assert(false, "Fatal error in Type.toString: " ~ e.msg);
829     }
830 
831     private string _spellingCreate() @safe pure nothrow const {
832         return clang_getTypeSpelling(cx).toString;
833     }
834 }
835 
836 
837 struct Token {
838 
839     mixin EnumD!("Kind", CXTokenKind, "CXToken_");
840 
841     Kind kind;
842     string spelling;
843     CXToken cxToken;
844     CXTranslationUnit cxTU;
845 
846     this(CXToken cxToken, CXTranslationUnit cxTU) @safe pure nothrow {
847         this.cxToken = cxToken;
848         this.cxTU = cxTU;
849         this.kind = cast(Kind) clang_getTokenKind(cxToken);
850         this.spelling = .toString(clang_getTokenSpelling(cxTU, cxToken));
851     }
852 
853     this(Kind kind, string spelling) @safe @nogc pure nothrow {
854         this.kind = kind;
855         this.spelling = spelling;
856     }
857 
858     string toString() @safe pure const {
859         import std.conv: text;
860 
861         return text("Token(", kind, `, "`, spelling, `")`);
862     }
863 
864     bool opEquals(in Token other) @safe pure nothrow const {
865         return kind == other.kind && spelling == other.spelling;
866     }
867 }