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 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     bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const {
313         return cast(bool) clang_equalCursors(cx, other.cx);
314     }
315 
316     bool opEquals(in Cursor other) @safe @nogc pure nothrow const {
317         return cast(bool) clang_equalCursors(cx, other.cx);
318     }
319 
320     void visitChildren(CursorVisitor visitor) @safe nothrow const {
321         clang_visitChildren(cx, &cvisitor, new ClientData(visitor));
322     }
323 
324     int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const {
325         return opApplyN(block);
326     }
327 
328     int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const {
329         return opApplyN(block);
330     }
331 
332     private int opApplyN(T...)(int delegate(T args) @safe block) const {
333         int stop = 0;
334 
335         visitChildren((cursor, parent) {
336 
337             static if(T.length == 2)
338                 stop = block(cursor, parent);
339             else static if(T.length == 1)
340                 stop = block(cursor);
341             else
342                 static assert(false);
343 
344             return stop
345                 ? ChildVisitResult.Break
346                 : ChildVisitResult.Continue;
347         });
348 
349         return stop;
350     }
351 }
352 
353 
354 struct SourceRange {
355     CXSourceRange cx;
356     string path;
357     SourceLocation start;
358     SourceLocation end;
359 
360     this(CXSourceRange cx) @safe pure nothrow {
361         this.cx = cx;
362         this.start = clang_getRangeStart(cx);
363         this.end = clang_getRangeEnd(cx);
364         this.path = start.path;
365     }
366 
367     string toString() @safe pure const {
368         import std.conv: text;
369         return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")");
370     }
371 }
372 
373 struct SourceLocation {
374     CXSourceLocation cx;
375     string path;
376     uint line;
377     uint column;
378     uint offset;
379 
380     this(CXSourceLocation cx) @safe pure nothrow {
381         this.cx = cx;
382 
383         CXFile file;
384         () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }();
385         this.path = clang_getFileName(file).toString;
386 
387         () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }();
388     }
389 
390     int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const {
391         if(path == other.path && line == other.line && column == other.column &&
392            offset == other.offset)
393             return 0;
394 
395         if(path < other.path) return -1;
396         if(path > other.path) return 1;
397         if(line < other.line) return -1;
398         if(line > other.line) return 1;
399         if(column < other.column) return -1;
400         if(column > other.column) return 1;
401         if(offset < other.offset) return -1;
402         if(offset > other.offset) return 1;
403         assert(false);
404     }
405 
406     string toString() @safe pure nothrow const {
407         import std.conv: text;
408         return text(`"`, path, `" `, line, ":", column, ":", offset);
409     }
410 }
411 
412 private struct ClientData {
413     /**
414        The D visitor delegate
415      */
416     CursorVisitor dvisitor;
417 }
418 
419 // This is the C function actually passed to libclang's clang_visitChildren
420 // The context (clientData) contains the D delegate that's then called on the
421 // (cursor, parent) pair
422 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) {
423     auto clientData = cast(ClientData*) clientData_;
424     return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent));
425 }
426 
427 
428 struct Type {
429 
430     mixin EnumD!("Kind", CXTypeKind, "CXType_");
431 
432     CXType cx;
433     Kind kind;
434     string spelling;
435 
436     this(CXType cx) @safe pure nothrow {
437         this.cx = cx;
438         this.kind = cast(Kind) cx.kind;
439         spelling = clang_getTypeSpelling(cx).toString;
440     }
441 
442     this(in Type other) @trusted pure nothrow {
443         import std.algorithm: map;
444         import std.array: array;
445 
446         this.cx.kind = other.cx.kind;
447         this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array;
448 
449         this(this.cx);
450     }
451 
452     this(in Kind kind) @safe @nogc pure nothrow {
453         this(kind, "");
454     }
455 
456     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
457         this.kind = kind;
458         this.spelling = spelling;
459     }
460 
461     Type pointee() @safe pure nothrow const {
462         return Type(clang_getPointeeType(cx));
463     }
464 
465     Type unelaborate() @safe nothrow const {
466         return Type(clang_Type_getNamedType(cx));
467     }
468 
469     Type canonical() @safe pure nothrow const {
470         return Type(clang_getCanonicalType(cx));
471     }
472 
473     Type returnType() @safe pure const {
474         return Type(clang_getResultType(cx));
475     }
476 
477     Type[] paramTypes() @safe pure const {
478         const numArgs = clang_getNumArgTypes(cx);
479         auto types = new Type[numArgs];
480 
481         foreach(i; 0 .. numArgs) {
482             types[i] = Type(clang_getArgType(cx, i));
483         }
484 
485         return types;
486     }
487 
488     bool isVariadicFunction() @safe @nogc pure nothrow const {
489         return cast(bool) clang_isFunctionTypeVariadic(cx);
490     }
491 
492     Type elementType() @safe pure nothrow const {
493         return Type(clang_getElementType(cx));
494     }
495 
496     long numElements() @safe @nogc pure nothrow const {
497         return clang_getNumElements(cx);
498     }
499 
500     long arraySize() @safe @nogc pure nothrow const {
501         return clang_getArraySize(cx);
502     }
503 
504     bool isConstQualified() @safe @nogc pure nothrow const {
505         return cast(bool) clang_isConstQualifiedType(cx);
506     }
507 
508     bool isVolatileQualified() @safe @nogc pure nothrow const {
509         return cast(bool) clang_isVolatileQualifiedType(cx);
510     }
511 
512     Cursor declaration() @safe nothrow const {
513         return Cursor(clang_getTypeDeclaration(cx));
514     }
515 
516     Type namedType() @safe pure nothrow const {
517         return Type(clang_Type_getNamedType(cx));
518     }
519 
520     bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const {
521         return cast(bool) clang_equalTypes(cx, other.cx);
522     }
523 
524     bool opEquals(in Type other) @safe @nogc pure nothrow const {
525         return cast(bool) clang_equalTypes(cx, other.cx);
526     }
527 
528     bool isInvalid() @safe @nogc pure nothrow const {
529         return kind == Kind.Invalid;
530     }
531 
532     string toString() @safe pure nothrow const {
533         import std.conv: text;
534         try {
535             const pointeeText = pointee.isInvalid ? "" : text(", ", pointee);
536             return text("Type(", kind, `, "`, spelling, pointeeText, `")`);
537         } catch(Exception e)
538             assert(false, "Fatal error in Type.toString: " ~ e.msg);
539     }
540 }