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     SourceRange sourceRange;
145 
146     this(CXCursor cx) @safe pure nothrow {
147         this.cx = cx;
148         kind = cast(Kind) clang_getCursorKind(cx);
149         spelling = clang_getCursorSpelling(cx).toString;
150         type = Type(clang_getCursorType(cx));
151         sourceRange = SourceRange(clang_getCursorExtent(cx));
152     }
153 
154     private static extern(C) CXChildVisitResult ctorVisitor(CXCursor cursor,
155                                                             CXCursor parent,
156                                                             void* clientData_)
157         @safe nothrow
158     {
159         auto children = () @trusted { return cast(Cursor[]*) clientData_; }();
160         *children ~= Cursor(cursor);
161         return CXChildVisit_Continue;
162     }
163 
164     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
165         this(kind, spelling, Type());
166     }
167 
168     this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow {
169         this.kind = kind;
170         this.spelling = spelling;
171         this.type = type;
172     }
173 
174     /// Lazily return the cursor's children
175     inout(Cursor)[] children() @safe @property nothrow inout {
176         if(_children.length) return _children;
177 
178         inout(Cursor)[] ret;
179         // calling Cursor.visitChildren here would cause infinite recursion
180         // because cvisitor constructs a Cursor out of the parent
181         () @trusted { clang_visitChildren(cx, &ctorVisitor, &ret); }();
182         return ret;
183     }
184 
185     void children(Cursor[] cursors) @safe @property pure nothrow {
186         _children = cursors;
187     }
188 
189     Type returnType() @safe pure nothrow const {
190         return Type(clang_getCursorResultType(cx));
191     }
192 
193     /**
194        For TypedefDecl cursors, return the underlying type
195      */
196     Type underlyingType() @safe pure nothrow const {
197         assert(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl,
198                "Not a TypedefDecl cursor");
199         return Type(clang_getTypedefDeclUnderlyingType(cx));
200     }
201 
202     /**
203        For EnumConstantDecl cursors, return the numeric value
204      */
205     auto enumConstantValue() @safe @nogc pure nothrow const {
206         assert(kind == Cursor.Kind.EnumConstantDecl);
207         return clang_getEnumConstantDeclValue(cx);
208     }
209 
210     Language language() @safe @nogc pure nothrow const {
211         return cast(Language) clang_getCursorLanguage(cx);
212     }
213 
214     Cursor canonical() @safe nothrow const {
215         return Cursor(clang_getCanonicalCursor(cx));
216     }
217 
218     /**
219        If this is the canonical cursor. Given forward declarations, there may
220        be several cursors for one entity. This returns true if this cursor
221        is the canonical one.
222      */
223     bool isCanonical() @safe @nogc pure nothrow const {
224         return cast(bool) clang_equalCursors(cx, clang_getCanonicalCursor(cx));
225     }
226 
227     bool isDefinition() @safe @nogc pure nothrow const {
228         return cast(bool) clang_isCursorDefinition(cx);
229     }
230 
231     bool isNull() @safe @nogc pure nothrow const {
232         return cast(bool) clang_Cursor_isNull(cx);
233     }
234 
235     static Cursor nullCursor() @safe nothrow {
236         return Cursor(clang_getNullCursor());
237     }
238 
239     Cursor definition() @safe nothrow const {
240         return Cursor(clang_getCursorDefinition(cx));
241     }
242 
243     string toString() @safe pure nothrow const {
244         import std.conv: text;
245         try {
246             const returnTypeStr = kind == Kind.FunctionDecl
247                 ? text(", ", returnType)
248                 : "";
249 
250             return text("Cursor(", kind, `, "`, spelling, `", `, type, returnTypeStr, ")");
251         } catch(Exception e)
252             assert(false, "Fatal error in Cursor.toString: " ~ e.msg);
253     }
254 
255     bool isPredefined() @safe @nogc pure nothrow const {
256         // FIXME
257         return false;
258     }
259 
260     Cursor semanticParent() @safe nothrow const {
261         return Cursor(clang_getCursorSemanticParent(cx));
262     }
263 
264     Cursor lexicalParent() @safe nothrow const {
265         return Cursor(clang_getCursorLexicalParent(cx));
266     }
267 
268     bool isInvalid() @safe @nogc pure nothrow const {
269         return cast(bool) clang_isInvalid(cx.kind);
270     }
271 
272     auto hash() @safe @nogc pure nothrow const {
273         return clang_hashCursor(cx);
274     }
275 
276     string mangling() @safe pure nothrow const {
277         return clang_Cursor_getMangling(cx).toString;
278     }
279 
280     bool isAnonymous() @safe @nogc pure nothrow const {
281         return cast(bool) clang_Cursor_isAnonymous(cx);
282     }
283 
284     bool isBitField() @safe @nogc pure nothrow const {
285         return cast(bool) clang_Cursor_isBitField(cx);
286     }
287 
288     int bitWidth() @safe @nogc pure nothrow const {
289         return clang_getFieldDeclBitWidth(cx);
290     }
291 
292     auto accessSpecifier() @safe @nogc pure nothrow const {
293         return cast(AccessSpecifier) clang_getCXXAccessSpecifier(cx);
294     }
295 
296     StorageClass storageClass() @safe @nogc pure nothrow const {
297         return cast(StorageClass) clang_Cursor_getStorageClass(cx);
298     }
299 
300     bool isConstCppMethod() @safe @nogc pure nothrow const {
301         return cast(bool) clang_CXXMethod_isConst(cx);
302     }
303 
304     bool isMoveConstructor() @safe @nogc pure nothrow const {
305         return cast(bool) clang_CXXConstructor_isMoveConstructor(cx);
306     }
307 
308     bool isCopyConstructor() @safe @nogc pure nothrow const {
309         return cast(bool) clang_CXXConstructor_isCopyConstructor(cx);
310     }
311 
312     bool isMacroFunction() @safe @nogc pure nothrow const {
313         return cast(bool) clang_Cursor_isMacroFunctionLike(cx);
314     }
315 
316     Cursor specializedCursorTemplate() @safe 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     const(Cursor)[] templateParams() @safe nothrow const {
341         import std.algorithm: filter;
342         import std.array: array;
343 
344         const templateCursor = kind == Cursor.Kind.ClassTemplate
345             ? this
346             : specializedCursorTemplate;
347 
348         auto range = templateCursor
349             .children
350             .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter || a.kind == Cursor.Kind.NonTypeTemplateParameter);
351 
352         // Why is this @system? Who knows.
353         return () @trusted { return range.array; }();
354     }
355 
356     bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const {
357         return cast(bool) clang_equalCursors(cx, other.cx);
358     }
359 
360     bool opEquals(in Cursor other) @safe @nogc pure nothrow const {
361         return cast(bool) clang_equalCursors(cx, other.cx);
362     }
363 
364     void visitChildren(CursorVisitor visitor) @safe nothrow const {
365         clang_visitChildren(cx, &cvisitor, new ClientData(visitor));
366     }
367 
368     int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const {
369         return opApplyN(block);
370     }
371 
372     int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const {
373         return opApplyN(block);
374     }
375 
376     private int opApplyN(T...)(int delegate(T args) @safe block) const {
377         int stop = 0;
378 
379         visitChildren((cursor, parent) {
380 
381             static if(T.length == 2)
382                 stop = block(cursor, parent);
383             else static if(T.length == 1)
384                 stop = block(cursor);
385             else
386                 static assert(false);
387 
388             return stop
389                 ? ChildVisitResult.Break
390                 : ChildVisitResult.Continue;
391         });
392 
393         return stop;
394     }
395 }
396 
397 
398 struct SourceRange {
399 
400     CXSourceRange cx;
401     string path;
402     SourceLocation start;
403     SourceLocation end;
404 
405     this(CXSourceRange cx) @safe pure nothrow {
406         this.cx = cx;
407         this.start = clang_getRangeStart(cx);
408         this.end = clang_getRangeEnd(cx);
409         this.path = start.path;
410     }
411 
412     string toString() @safe pure const {
413         import std.conv: text;
414         return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")");
415     }
416 }
417 
418 struct SourceLocation {
419     CXSourceLocation cx;
420     string path;
421     uint line;
422     uint column;
423     uint offset;
424 
425     this(CXSourceLocation cx) @safe pure nothrow {
426         this.cx = cx;
427 
428         CXFile file;
429         () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }();
430         this.path = clang_getFileName(file).toString;
431 
432         () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }();
433     }
434 
435     int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const {
436         if(path == other.path && line == other.line && column == other.column &&
437            offset == other.offset)
438             return 0;
439 
440         if(path < other.path) return -1;
441         if(path > other.path) return 1;
442         if(line < other.line) return -1;
443         if(line > other.line) return 1;
444         if(column < other.column) return -1;
445         if(column > other.column) return 1;
446         if(offset < other.offset) return -1;
447         if(offset > other.offset) return 1;
448         assert(false);
449     }
450 
451     string toString() @safe pure nothrow const {
452         import std.conv: text;
453         return text(`"`, path, `" `, line, ":", column, ":", offset);
454     }
455 }
456 
457 private struct ClientData {
458     /**
459        The D visitor delegate
460      */
461     CursorVisitor dvisitor;
462 }
463 
464 // This is the C function actually passed to libclang's clang_visitChildren
465 // The context (clientData) contains the D delegate that's then called on the
466 // (cursor, parent) pair
467 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) {
468     auto clientData = cast(ClientData*) clientData_;
469     return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent));
470 }
471 
472 
473 struct Type {
474 
475     mixin EnumD!("Kind", CXTypeKind, "CXType_");
476 
477     CXType cx;
478     Kind kind;
479     string spelling;
480 
481     this(CXType cx) @safe pure nothrow {
482         this.cx = cx;
483         this.kind = cast(Kind) cx.kind;
484         spelling = clang_getTypeSpelling(cx).toString;
485     }
486 
487     this(in Type other) @trusted pure nothrow {
488         import std.algorithm: map;
489         import std.array: array;
490 
491         this.cx.kind = other.cx.kind;
492         this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array;
493 
494         this(this.cx);
495     }
496 
497     this(in Kind kind) @safe @nogc pure nothrow {
498         this(kind, "");
499     }
500 
501     this(in Kind kind, in string spelling) @safe @nogc pure nothrow {
502         this.kind = kind;
503         this.spelling = spelling;
504     }
505 
506     Type pointee() @safe pure nothrow const {
507         return Type(clang_getPointeeType(cx));
508     }
509 
510     Type unelaborate() @safe nothrow const {
511         return Type(clang_Type_getNamedType(cx));
512     }
513 
514     Type canonical() @safe pure nothrow const {
515         return Type(clang_getCanonicalType(cx));
516     }
517 
518     Type returnType() @safe pure const {
519         return Type(clang_getResultType(cx));
520     }
521 
522     Type[] paramTypes() @safe pure const {
523         const numArgs = clang_getNumArgTypes(cx);
524         auto types = new Type[numArgs];
525 
526         foreach(i; 0 .. numArgs) {
527             types[i] = Type(clang_getArgType(cx, i));
528         }
529 
530         return types;
531     }
532 
533     bool isVariadicFunction() @safe @nogc pure nothrow const {
534         return cast(bool) clang_isFunctionTypeVariadic(cx);
535     }
536 
537     Type elementType() @safe pure nothrow const {
538         return Type(clang_getElementType(cx));
539     }
540 
541     long numElements() @safe @nogc pure nothrow const {
542         return clang_getNumElements(cx);
543     }
544 
545     long arraySize() @safe @nogc pure nothrow const {
546         return clang_getArraySize(cx);
547     }
548 
549     bool isConstQualified() @safe @nogc pure nothrow const {
550         return cast(bool) clang_isConstQualifiedType(cx);
551     }
552 
553     bool isVolatileQualified() @safe @nogc pure nothrow const {
554         return cast(bool) clang_isVolatileQualifiedType(cx);
555     }
556 
557     Cursor declaration() @safe pure nothrow const {
558         return Cursor(clang_getTypeDeclaration(cx));
559     }
560 
561     Type namedType() @safe pure nothrow const {
562         return Type(clang_Type_getNamedType(cx));
563     }
564 
565     bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const {
566         return cast(bool) clang_equalTypes(cx, other.cx);
567     }
568 
569     bool opEquals(in Type other) @safe @nogc pure nothrow const {
570         return cast(bool) clang_equalTypes(cx, other.cx);
571     }
572 
573     bool isInvalid() @safe @nogc pure nothrow const {
574         return kind == Kind.Invalid;
575     }
576 
577     long getSizeof() @safe @nogc pure nothrow const {
578         return clang_Type_getSizeOf(cx);
579     }
580 
581     int numTemplateArguments() @safe @nogc pure nothrow const {
582         return clang_Type_getNumTemplateArguments(cx);
583     }
584 
585     Type typeTemplateArgument(int i) @safe pure nothrow const {
586         return Type(clang_Type_getTemplateArgumentAsType(cx, i));
587     }
588 
589     string toString() @safe pure nothrow const {
590         import std.conv: text;
591         try {
592             const pointeeText = pointee.isInvalid ? "" : text(", ", pointee);
593             return text("Type(", kind, `, "`, spelling, pointeeText, `")`);
594         } catch(Exception e)
595             assert(false, "Fatal error in Type.toString: " ~ e.msg);
596     }
597 }
598 
599 
600 struct Token {
601 
602     mixin EnumD!("Kind", CXTokenKind, "CXToken_");
603 
604     Kind kind;
605     string spelling;
606     CXToken cx;
607     TranslationUnit translationUnit;
608 
609     this(CXToken cx, TranslationUnit unit) @safe pure nothrow {
610         this.cx = cx;
611         this.translationUnit = unit;
612         this.kind = cast(Kind) clang_getTokenKind(cx);
613         this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx));
614     }
615 
616     this(Kind kind, string spelling) @safe @nogc pure nothrow {
617         this.kind = kind;
618         this.spelling = spelling;
619     }
620 
621     string toString() @safe pure const {
622         import std.conv: text;
623 
624         return text("Token(", kind, `, "`, spelling, `")`);
625     }
626 
627     bool opEquals(in Token other) @safe pure nothrow const {
628         return kind == other.kind && spelling == other.spelling;
629     }
630 }