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     mixin EnumD!("Kind", CXCursorKind, "CXCursor_");
133     mixin EnumD!("StorageClass", CX_StorageClass, "CX_SC_");
134 
135     CXCursor cx;
136     private Cursor[] _children;
137     Kind kind;
138     string spelling;
139     Type type;
140     SourceRange sourceRange;
141 
142     this(CXCursor cx) @safe pure nothrow {
143         this.cx = cx;
144         kind = cast(Kind) clang_getCursorKind(cx);
145         spelling = clang_getCursorSpelling(cx).toString;
146         type = Type(clang_getCursorType(cx));
147         sourceRange = SourceRange(clang_getCursorExtent(cx));
148     }
149 
150     private static extern(C) CXChildVisitResult ctorVisitor(CXCursor cursor,
151                                                             CXCursor parent,
152                                                             void* clientData_)
153         @safe nothrow
154     {
155         auto children = () @trusted { return cast(Cursor[]*) clientData_; }();
156         *children ~= Cursor(cursor);
157         return CXChildVisit_Continue;
158     }
159 
160     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
161         this(kind, spelling, Type());
162     }
163 
164     this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow {
165         this.kind = kind;
166         this.spelling = spelling;
167         this.type = type;
168     }
169 
170     /// Lazily return the cursor's children
171     inout(Cursor)[] children() @safe @property nothrow inout {
172         if(_children.length) return _children;
173 
174         inout(Cursor)[] ret;
175         // calling Cursor.visitChildren here would cause infinite recursion
176         // because cvisitor constructs a Cursor out of the parent
177         () @trusted { clang_visitChildren(cx, &ctorVisitor, &ret); }();
178         return ret;
179     }
180 
181     void children(Cursor[] cursors) @safe @property pure nothrow {
182         _children = cursors;
183     }
184 
185     Type returnType() @safe pure nothrow const {
186         return Type(clang_getCursorResultType(cx));
187     }
188 
189     /**
190        For TypedefDecl cursors, return the underlying type
191      */
192     Type underlyingType() @safe pure nothrow const {
193         assert(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl,
194                "Not a TypedefDecl cursor");
195         return Type(clang_getTypedefDeclUnderlyingType(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     Cursor specializedCursorTemplate() @safe nothrow const {
313         return Cursor(clang_getSpecializedCursorTemplate(cx));
314     }
315 
316     bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const {
317         return cast(bool) clang_equalCursors(cx, other.cx);
318     }
319 
320     bool opEquals(in Cursor other) @safe @nogc pure nothrow const {
321         return cast(bool) clang_equalCursors(cx, other.cx);
322     }
323 
324     void visitChildren(CursorVisitor visitor) @safe nothrow const {
325         clang_visitChildren(cx, &cvisitor, new ClientData(visitor));
326     }
327 
328     int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const {
329         return opApplyN(block);
330     }
331 
332     int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const {
333         return opApplyN(block);
334     }
335 
336     private int opApplyN(T...)(int delegate(T args) @safe block) const {
337         int stop = 0;
338 
339         visitChildren((cursor, parent) {
340 
341             static if(T.length == 2)
342                 stop = block(cursor, parent);
343             else static if(T.length == 1)
344                 stop = block(cursor);
345             else
346                 static assert(false);
347 
348             return stop
349                 ? ChildVisitResult.Break
350                 : ChildVisitResult.Continue;
351         });
352 
353         return stop;
354     }
355 }
356 
357 
358 struct SourceRange {
359 
360     CXSourceRange cx;
361     string path;
362     SourceLocation start;
363     SourceLocation end;
364 
365     this(CXSourceRange cx) @safe pure nothrow {
366         this.cx = cx;
367         this.start = clang_getRangeStart(cx);
368         this.end = clang_getRangeEnd(cx);
369         this.path = start.path;
370     }
371 
372     string toString() @safe pure const {
373         import std.conv: text;
374         return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")");
375     }
376 }
377 
378 struct SourceLocation {
379     CXSourceLocation cx;
380     string path;
381     uint line;
382     uint column;
383     uint offset;
384 
385     this(CXSourceLocation cx) @safe pure nothrow {
386         this.cx = cx;
387 
388         CXFile file;
389         () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }();
390         this.path = clang_getFileName(file).toString;
391 
392         () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }();
393     }
394 
395     int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const {
396         if(path == other.path && line == other.line && column == other.column &&
397            offset == other.offset)
398             return 0;
399 
400         if(path < other.path) return -1;
401         if(path > other.path) return 1;
402         if(line < other.line) return -1;
403         if(line > other.line) return 1;
404         if(column < other.column) return -1;
405         if(column > other.column) return 1;
406         if(offset < other.offset) return -1;
407         if(offset > other.offset) return 1;
408         assert(false);
409     }
410 
411     string toString() @safe pure nothrow const {
412         import std.conv: text;
413         return text(`"`, path, `" `, line, ":", column, ":", offset);
414     }
415 }
416 
417 private struct ClientData {
418     /**
419        The D visitor delegate
420      */
421     CursorVisitor dvisitor;
422 }
423 
424 // This is the C function actually passed to libclang's clang_visitChildren
425 // The context (clientData) contains the D delegate that's then called on the
426 // (cursor, parent) pair
427 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) {
428     auto clientData = cast(ClientData*) clientData_;
429     return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent));
430 }
431 
432 
433 struct Type {
434 
435     mixin EnumD!("Kind", CXTypeKind, "CXType_");
436 
437     CXType cx;
438     Kind kind;
439     string spelling;
440 
441     this(CXType cx) @safe pure nothrow {
442         this.cx = cx;
443         this.kind = cast(Kind) cx.kind;
444         spelling = clang_getTypeSpelling(cx).toString;
445     }
446 
447     this(in Type other) @trusted pure nothrow {
448         import std.algorithm: map;
449         import std.array: array;
450 
451         this.cx.kind = other.cx.kind;
452         this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array;
453 
454         this(this.cx);
455     }
456 
457     this(in Kind kind) @safe @nogc pure nothrow {
458         this(kind, "");
459     }
460 
461     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
462         this.kind = kind;
463         this.spelling = spelling;
464     }
465 
466     Type pointee() @safe pure nothrow const {
467         return Type(clang_getPointeeType(cx));
468     }
469 
470     Type unelaborate() @safe nothrow const {
471         return Type(clang_Type_getNamedType(cx));
472     }
473 
474     Type canonical() @safe pure nothrow const {
475         return Type(clang_getCanonicalType(cx));
476     }
477 
478     Type returnType() @safe pure const {
479         return Type(clang_getResultType(cx));
480     }
481 
482     Type[] paramTypes() @safe pure const {
483         const numArgs = clang_getNumArgTypes(cx);
484         auto types = new Type[numArgs];
485 
486         foreach(i; 0 .. numArgs) {
487             types[i] = Type(clang_getArgType(cx, i));
488         }
489 
490         return types;
491     }
492 
493     bool isVariadicFunction() @safe @nogc pure nothrow const {
494         return cast(bool) clang_isFunctionTypeVariadic(cx);
495     }
496 
497     Type elementType() @safe pure nothrow const {
498         return Type(clang_getElementType(cx));
499     }
500 
501     long numElements() @safe @nogc pure nothrow const {
502         return clang_getNumElements(cx);
503     }
504 
505     long arraySize() @safe @nogc pure nothrow const {
506         return clang_getArraySize(cx);
507     }
508 
509     bool isConstQualified() @safe @nogc pure nothrow const {
510         return cast(bool) clang_isConstQualifiedType(cx);
511     }
512 
513     bool isVolatileQualified() @safe @nogc pure nothrow const {
514         return cast(bool) clang_isVolatileQualifiedType(cx);
515     }
516 
517     Cursor declaration() @safe pure nothrow const {
518         return Cursor(clang_getTypeDeclaration(cx));
519     }
520 
521     Type namedType() @safe pure nothrow const {
522         return Type(clang_Type_getNamedType(cx));
523     }
524 
525     bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const {
526         return cast(bool) clang_equalTypes(cx, other.cx);
527     }
528 
529     bool opEquals(in Type other) @safe @nogc pure nothrow const {
530         return cast(bool) clang_equalTypes(cx, other.cx);
531     }
532 
533     bool isInvalid() @safe @nogc pure nothrow const {
534         return kind == Kind.Invalid;
535     }
536 
537     long getSizeof() @safe @nogc pure nothrow const {
538         return clang_Type_getSizeOf(cx);
539     }
540 
541     int numTemplateArguments() @safe @nogc pure nothrow const {
542         return clang_Type_getNumTemplateArguments(cx);
543     }
544 
545     Type typeTemplateArgument(int i) @safe pure nothrow const {
546         return Type(clang_Type_getTemplateArgumentAsType(cx, i));
547     }
548 
549     string toString() @safe pure nothrow const {
550         import std.conv: text;
551         try {
552             const pointeeText = pointee.isInvalid ? "" : text(", ", pointee);
553             return text("Type(", kind, `, "`, spelling, pointeeText, `")`);
554         } catch(Exception e)
555             assert(false, "Fatal error in Type.toString: " ~ e.msg);
556     }
557 }