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