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