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