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