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