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