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