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