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     bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const {
436         return cast(bool) clang_equalCursors(cx, other.cx);
437     }
438 
439     bool opEquals(in Cursor other) @safe @nogc pure nothrow const {
440         return cast(bool) clang_equalCursors(cx, other.cx);
441     }
442 
443     void visitChildren(scope CursorVisitor visitor) @safe nothrow const {
444         scope clientData = ClientData(visitor);
445         // why isn't this @safe with dip10000???
446         () @trusted { clang_visitChildren(cx, &cvisitor, &clientData); }();
447     }
448 
449     int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const {
450         return opApplyN(block);
451     }
452 
453     int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const {
454         return opApplyN(block);
455     }
456 
457     private int opApplyN(T)(scope T block) const {
458         import std.traits: Parameters;
459 
460         int stop = 0;
461 
462         enum numParams = Parameters!T.length;
463 
464         visitChildren((cursor, parent) {
465 
466             static if(numParams == 2)
467                 stop = block(cursor, parent);
468             else static if(numParams == 1)
469                 stop = block(cursor);
470             else
471                 static assert(false);
472 
473             return stop
474                 ? ChildVisitResult.Break
475                 : ChildVisitResult.Continue;
476         });
477 
478         return stop;
479     }
480 }
481 
482 
483 struct SourceRange {
484 
485     CXSourceRange cx;
486     string path;
487     SourceLocation start;
488     SourceLocation end;
489 
490     this(CXSourceRange cx) @safe pure nothrow {
491         this.cx = cx;
492         this.start = clang_getRangeStart(cx);
493         this.end = clang_getRangeEnd(cx);
494         this.path = start.path;
495     }
496 
497     string toString() @safe pure const {
498         import std.conv: text;
499         return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")");
500     }
501 }
502 
503 struct SourceLocation {
504     CXSourceLocation cx;
505     string path;
506     uint line;
507     uint column;
508     uint offset;
509 
510     this(CXSourceLocation cx) @safe pure nothrow {
511         this.cx = cx;
512 
513         CXFile file;
514         () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }();
515         this.path = clang_getFileName(file).toString;
516 
517         () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }();
518     }
519 
520     int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const {
521         if(path == other.path && line == other.line && column == other.column &&
522            offset == other.offset)
523             return 0;
524 
525         if(path < other.path) return -1;
526         if(path > other.path) return 1;
527         if(line < other.line) return -1;
528         if(line > other.line) return 1;
529         if(column < other.column) return -1;
530         if(column > other.column) return 1;
531         if(offset < other.offset) return -1;
532         if(offset > other.offset) return 1;
533         assert(false);
534     }
535 
536     string toString() @safe pure nothrow const {
537         import std.conv: text;
538         return text(`"`, path, `" `, line, ":", column, ":", offset);
539     }
540 }
541 
542 private struct ClientData {
543     /**
544        The D visitor delegate
545      */
546     CursorVisitor dvisitor;
547 }
548 
549 // This is the C function actually passed to libclang's clang_visitChildren
550 // The context (clientData) contains the D delegate that's then called on the
551 // (cursor, parent) pair
552 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) {
553     auto clientData = cast(ClientData*) clientData_;
554     return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent));
555 }
556 
557 
558 struct Type {
559 
560     mixin EnumD!("Kind", CXTypeKind, "CXType_");
561 
562     CXType cx;
563     Kind kind;
564     string spelling;
565 
566     this(CXType cx) @safe pure nothrow {
567         this.cx = cx;
568         this.kind = cast(Kind) cx.kind;
569         spelling = clang_getTypeSpelling(cx).toString;
570     }
571 
572     this(in Type other) @trusted pure nothrow {
573         import std.algorithm: map;
574         import std.array: array;
575 
576         this.cx.kind = other.cx.kind;
577         this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array;
578 
579         this(this.cx);
580     }
581 
582     this(in Kind kind) @safe @nogc pure nothrow {
583         this(kind, "");
584     }
585 
586     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
587         this.kind = kind;
588         this.spelling = spelling;
589     }
590 
591     Type pointee() @safe pure nothrow const {
592         return Type(clang_getPointeeType(cx));
593     }
594 
595     Type unelaborate() @safe nothrow const {
596         return Type(clang_Type_getNamedType(cx));
597     }
598 
599     Type canonical() @safe pure nothrow const {
600         return Type(clang_getCanonicalType(cx));
601     }
602 
603     Type returnType() @safe pure const {
604         return Type(clang_getResultType(cx));
605     }
606 
607     // Returns a range of Type
608     auto paramTypes()() @safe pure const nothrow {
609 
610         static struct Range {
611             const CXType cx;
612             const int numArgs;
613             int index = 0;
614 
615             bool empty() {
616                 return index < 0 || index >= numArgs;
617             }
618 
619             void popFront() {
620                 ++index;
621             }
622 
623             Type front() {
624                 return Type(clang_getArgType(cx, index));
625             }
626         }
627 
628         return Range(cx, clang_getNumArgTypes(cx));
629     }
630 
631     bool isVariadicFunction() @safe @nogc pure nothrow const {
632         return cast(bool) clang_isFunctionTypeVariadic(cx);
633     }
634 
635     Type elementType() @safe pure nothrow const {
636         return Type(clang_getElementType(cx));
637     }
638 
639     long numElements() @safe @nogc pure nothrow const {
640         return clang_getNumElements(cx);
641     }
642 
643     long arraySize() @safe @nogc pure nothrow const {
644         return clang_getArraySize(cx);
645     }
646 
647     bool isConstQualified() @safe @nogc pure nothrow const {
648         return cast(bool) clang_isConstQualifiedType(cx);
649     }
650 
651     bool isVolatileQualified() @safe @nogc pure nothrow const {
652         return cast(bool) clang_isVolatileQualifiedType(cx);
653     }
654 
655     Cursor declaration() @safe pure nothrow const {
656         return Cursor(clang_getTypeDeclaration(cx));
657     }
658 
659     Type namedType() @safe pure nothrow const {
660         return Type(clang_Type_getNamedType(cx));
661     }
662 
663     bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const {
664         return cast(bool) clang_equalTypes(cx, other.cx);
665     }
666 
667     bool opEquals(in Type other) @safe @nogc pure nothrow const {
668         return cast(bool) clang_equalTypes(cx, other.cx);
669     }
670 
671     bool isInvalid() @safe @nogc pure nothrow const {
672         return kind == Kind.Invalid;
673     }
674 
675     long getSizeof() @safe @nogc pure nothrow const {
676         return clang_Type_getSizeOf(cx);
677     }
678 
679     int numTemplateArguments() @safe @nogc pure nothrow const {
680         return clang_Type_getNumTemplateArguments(cx);
681     }
682 
683     Type typeTemplateArgument(int i) @safe pure nothrow const {
684         return Type(clang_Type_getTemplateArgumentAsType(cx, i));
685     }
686 
687     string toString() @safe pure nothrow const {
688         import std.conv: text;
689 
690         try {
691             return text("Type(", kind, `, "`, spelling, `")`);
692         } catch(Exception e)
693             assert(false, "Fatal error in Type.toString: " ~ e.msg);
694     }
695 }
696 
697 
698 struct Token {
699 
700     mixin EnumD!("Kind", CXTokenKind, "CXToken_");
701 
702     Kind kind;
703     string spelling;
704     CXToken cx;
705     TranslationUnit translationUnit;
706 
707     this(CXToken cx, TranslationUnit unit) @safe pure nothrow {
708         this.cx = cx;
709         this.translationUnit = unit;
710         this.kind = cast(Kind) clang_getTokenKind(cx);
711         this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx));
712     }
713 
714     this(Kind kind, string spelling) @safe @nogc pure nothrow {
715         this.kind = kind;
716         this.spelling = spelling;
717     }
718 
719     string toString() @safe pure const {
720         import std.conv: text;
721 
722         return text("Token(", kind, `, "`, spelling, `")`);
723     }
724 
725     bool opEquals(in Token other) @safe pure nothrow const {
726         return kind == other.kind && spelling == other.spelling;
727     }
728 }