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