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