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