1 /**
2  * Copyright: Copyright (c) 2011 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 1, 2012
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module clang.Cursor;
8 
9 import std.array : appender, Appender;
10 import std.conv : to;
11 import std..string;
12 
13 import clang.c.Index;
14 import clang.Index;
15 import clang.File;
16 import clang.SourceLocation;
17 import clang.SourceRange;
18 import clang.Token;
19 import clang.TranslationUnit;
20 import clang.Type;
21 import clang.Util;
22 import clang.Visitor;
23 
24 struct Cursor
25 {
26     mixin CX;
27 
28     private static const CXCursorKind[string] predefined;
29 
30     static this()
31     {
32         predefined = queryPredefined();
33     }
34 
35     @property static Cursor empty ()
36     {
37         auto r = clang_getNullCursor();
38         return Cursor(r);
39     }
40 
41     @property string spelling () const
42     {
43         return toD(clang_getCursorSpelling(cx));
44     }
45 
46     @property CXCursorKind kind () const
47     {
48         return clang_getCursorKind(cx);
49     }
50 
51     @property bool isPreprocessor () const
52     {
53         CXCursorKind kind = clang_getCursorKind(cx);
54         return CXCursorKind.firstPreprocessing <= kind &&
55             kind <= CXCursorKind.lastPreprocessing;
56     }
57 
58     @property SourceLocation location () const
59     {
60         return SourceLocation(clang_getCursorLocation(cx));
61     }
62 
63     @property File file () const
64     {
65         return location.file;
66     }
67 
68     @property string path () const
69     {
70         return file.name;
71     }
72 
73     @property Token[] tokens() const
74     {
75         CXTranslationUnit translUnit = clang_Cursor_getTranslationUnit(cx);
76 
77         return TranslationUnit.tokenize(translUnit, extent);
78     }
79 
80     @property SourceRange extent() const
81     {
82         return SourceRange(clang_getCursorExtent(cx));
83     }
84 
85     @property Type type () const
86     {
87         auto r = clang_getCursorType(cx);
88         return Type(r);
89     }
90 
91     @property Type underlyingType() const
92     {
93         return Type(clang_getTypedefDeclUnderlyingType(cx));
94     }
95 
96     @property Cursor underlyingCursor() const
97     {
98         foreach (child; all)
99         {
100             if (child.kind == CXCursorKind.typeRef)
101                 return child.referenced;
102 
103             if (child.isDeclaration &&
104                 child.kind != CXCursorKind.parmDecl)
105             {
106                 return child;
107             }
108         }
109 
110         return empty;
111     }
112 
113     @property bool isDeclaration ()
114     {
115         return clang_isDeclaration(cx.kind) != 0;
116     }
117 
118     @property DeclarationVisitor declarations ()
119     {
120         return DeclarationVisitor(cx);
121     }
122 
123     @property ObjcCursor objc ()
124     {
125         return ObjcCursor(this);
126     }
127 
128     @property FunctionCursor func ()
129     {
130         return FunctionCursor(this);
131     }
132 
133     @property EnumCursor enum_ ()
134     {
135         return EnumCursor(this);
136     }
137 
138     @property bool isValid ()
139     {
140         return !clang_isInvalid(cx.kind);
141     }
142 
143     @property bool isEmpty ()
144     {
145         return clang_Cursor_isNull(cx) != 0;
146     }
147 
148     @property Visitor all () const
149     {
150         return Visitor(this);
151     }
152 
153     @property InOrderVisitor allInOrder () const
154     {
155         return InOrderVisitor(this);
156     }
157 
158     private Cursor[] childrenImpl(T)(bool ignorePredefined) const
159     {
160         import std.array : appender;
161 
162         Cursor[] result;
163         auto app = appender(result);
164 
165         if (ignorePredefined && isTranslationUnit)
166         {
167             foreach (cursor, _; T(this))
168             {
169                 if (!cursor.isPredefined)
170                     app.put(cursor);
171             }
172         }
173         else
174         {
175             foreach (cursor, _; T(this))
176                 app.put(cursor);
177         }
178 
179         return app.data;
180     }
181 
182     Cursor[] children(bool ignorePredefined = false) const
183     {
184         return childrenImpl!Visitor(ignorePredefined);
185     }
186 
187     Cursor[] childrenInOrder(bool ignorePredefined = false) const
188     {
189         return childrenImpl!InOrderVisitor(ignorePredefined);
190     }
191 
192     Cursor child() const
193     {
194         foreach (child; all)
195             return child;
196 
197         return Cursor.empty;
198     }
199 
200     Cursor findChild(CXCursorKind kind) const
201     {
202         foreach (child; all)
203         {
204             if (child.kind == kind)
205                 return child;
206         }
207 
208         return Cursor.empty();
209     }
210 
211     Cursor[] filterChildren(CXCursorKind kind)
212     {
213         import std.array;
214 
215         auto result = Appender!(Cursor[])();
216 
217         foreach (child; all)
218         {
219             if (child.kind == kind)
220                 result.put(child);
221         }
222 
223         return result.data();
224     }
225 
226     Cursor[] filterChildren(CXCursorKind[] kinds ...)
227     {
228         import std.array;
229 
230         auto result = Appender!(Cursor[])();
231 
232         foreach (child; all)
233         {
234             foreach (kind; kinds)
235             {
236                 if (child.kind == kind)
237                 {
238                     result.put(child);
239                     break;
240                 }
241             }
242         }
243 
244         return result.data();
245     }
246 
247     Cursor semanticParent() const
248     {
249         return Cursor(clang_getCursorSemanticParent(cast(CXCursor) cx));
250     }
251 
252     Cursor lexicalParent() const
253     {
254         return Cursor(clang_getCursorLexicalParent(cast(CXCursor) cx));
255     }
256 
257     @property CXLanguageKind language ()
258     {
259         return clang_getCursorLanguage(cx);
260     }
261 
262     equals_t opEquals (in Cursor cursor) const
263     {
264         return clang_equalCursors(cast(CXCursor) cursor.cx, cast(CXCursor) cx) != 0;
265     }
266 
267     hash_t toHash () const
268     {
269         return clang_hashCursor(cast(CXCursor) cx);
270     }
271 
272     bool isDefinition () const
273     {
274         return clang_isCursorDefinition(cast(CXCursor) cx) != 0;
275     }
276 
277     bool isTranslationUnit() const
278     {
279         return clang_isTranslationUnit(kind) != 0;
280     }
281 
282     File includedFile()
283     {
284         return File(clang_getIncludedFile(cx));
285     }
286 
287     string includedPath ()
288     {
289         auto file = clang_getIncludedFile(cx);
290         return toD(clang_getFileName(file));
291     }
292 
293     private static CXCursorKind[string] queryPredefined()
294     {
295         CXCursorKind[string] result;
296 
297         Index index = Index(false, false);
298         TranslationUnit unit = TranslationUnit.parseString(
299             index,
300             "",
301             []);
302 
303         foreach (cursor; unit.cursor.children)
304             result[cursor.spelling] = cursor.kind;
305 
306         auto version_ = clangVersion();
307 
308         if (version_.major == 3 && version_.minor == 7)
309             result["__int64"] = CXCursorKind.macroDefinition;
310 
311         return result;
312     }
313 
314     bool isPredefined() const
315     {
316         auto xkind = spelling in predefined;
317         return xkind !is null && *xkind == kind;
318     }
319 
320     TranslationUnit translationUnit ()
321     {
322         return TranslationUnit(clang_Cursor_getTranslationUnit(cx));
323     }
324 
325     @property Cursor definition () const
326     {
327         return Cursor(clang_getCursorDefinition(cast(CXCursor) cx));
328     }
329 
330     Cursor referenced () const
331     {
332         return Cursor(clang_getCursorReferenced(cast(CXCursor) cx));
333     }
334 
335     Cursor canonical () const
336     {
337         return Cursor(clang_getCanonicalCursor(cast(CXCursor) cx));
338     }
339 
340     int bitFieldWidth() const
341     {
342         return clang_getFieldDeclBitWidth(cast(CXCursor) cx);
343     }
344 
345     bool isBitField() const
346     {
347         return clang_Cursor_isBitField(cast(CXCursor) cx) != 0;
348     }
349 
350     Cursor opCast(T)() const if (is(T == Cursor))
351     {
352         return this;
353     }
354 
355     bool opCast(T)() if (is(T == bool))
356     {
357         return !isEmpty && isValid;
358     }
359 
360     void dumpAST(ref Appender!string result, size_t indent, File* file)
361     {
362         import std.format;
363         import std.array : replicate;
364         import std.algorithm.comparison : min;
365 
366         string stripPrefix(string x)
367         {
368             immutable string prefix = "";
369             immutable size_t prefixSize = prefix.length;
370             return x.startsWith(prefix) ? x[prefixSize..$] : x;
371         }
372 
373         string prettyTokens(Token[] tokens, size_t limit = 5)
374         {
375             string prettyToken(Token token)
376             {
377                 immutable string prefix = "CXToken_";
378                 immutable size_t prefixSize = prefix.length;
379                 auto x = to!string(token.kind);
380                 return format(
381                     "%s \"%s\"",
382                     x.startsWith(prefix) ? x[prefixSize .. $] : x,
383                     token.spelling);
384             }
385 
386             auto result = appender!string("[");
387 
388             if (tokens.length != 0)
389             {
390                 result.put(prettyToken(tokens[0]));
391 
392                 foreach (Token token; tokens[1..min($, limit)])
393                 {
394                     result.put(", ");
395                     result.put(prettyToken(token));
396                 }
397             }
398 
399             if (tokens.length > limit)
400                 result.put(", ..]");
401             else
402                 result.put("]");
403 
404             return result.data;
405         }
406 
407         immutable size_t step = 4;
408 
409         result.put(" ".replicate(indent));
410         formattedWrite(
411             result,
412             "%s \"%s\" [%d..%d] %s\n",
413             stripPrefix(to!string(kind)),
414             spelling,
415             extent.start.offset,
416             extent.end.offset,
417             prettyTokens(tokens));
418 
419         if (file)
420         {
421             foreach (cursor, _; allInOrder)
422             {
423                 if (!cursor.isPredefined() && cursor.file == *file)
424                     cursor.dumpAST(result, indent + step);
425             }
426         }
427         else
428         {
429             foreach (cursor, _; allInOrder)
430             {
431                 if (!cursor.isPredefined())
432                     cursor.dumpAST(result, indent + step);
433             }
434         }
435     }
436 
437     void dumpAST(ref Appender!string result, size_t indent)
438     {
439         dumpAST(result, indent, null);
440     }
441 
442     string dumpAST()
443     {
444         auto result = appender!string();
445         dumpAST(result, 0);
446         return result.data;
447     }
448 
449     @property string toString()
450     {
451         import std.format : format;
452         return format("Cursor(kind = %s, spelling = %s)", kind, spelling);
453     }
454 }
455 
456 struct ObjcCursor
457 {
458     Cursor cursor;
459     alias cursor this;
460 
461     @property ObjCInstanceMethodVisitor instanceMethods ()
462     {
463         return ObjCInstanceMethodVisitor(cursor);
464     }
465 
466     @property ObjCClassMethodVisitor classMethods ()
467     {
468         return ObjCClassMethodVisitor(cursor);
469     }
470 
471     @property ObjCPropertyVisitor properties ()
472     {
473         return ObjCPropertyVisitor(cursor);
474     }
475 
476     @property Cursor superClass ()
477     {
478         foreach (cursor, parent ; TypedVisitor!(CXCursorKind.objCSuperClassRef)(cursor))
479             return cursor;
480 
481         return Cursor.empty;
482     }
483 
484     @property ObjCProtocolVisitor protocols ()
485     {
486         return ObjCProtocolVisitor(cursor);
487     }
488 
489     @property Cursor category ()
490     {
491         assert(cursor.kind == CXCursorKind.objCCategoryDecl);
492 
493         foreach (c, _ ; TypedVisitor!(CXCursorKind.objCClassRef)(cursor))
494             return c;
495 
496         assert(0, "This cursor does not have a class reference.");
497     }
498 }
499 
500 struct FunctionCursor
501 {
502     Cursor cursor;
503     alias cursor this;
504 
505     @property Type resultType ()
506     {
507         auto r = clang_getCursorResultType(cx);
508         return Type(r);
509     }
510 
511     @property bool isVariadic ()
512     {
513         return type.func.isVariadic;
514     }
515 
516     @property ParamVisitor parameters ()
517     {
518         return ParamVisitor(cx);
519     }
520 }
521 
522 struct ParamCursor
523 {
524     Cursor cursor;
525     alias cursor this;
526 }
527 
528 struct EnumCursor
529 {
530     Cursor cursor;
531     alias cursor this;
532 
533     @property string value ()
534     {
535         //return type.kind.isUnsigned ? unsignedValue.toString : signedValue.toString;
536         return signedValue.to!string;
537     }
538 
539     @property long signedValue ()
540     {
541         return clang_getEnumConstantDeclValue(cx);
542     }
543 
544     @property ulong unsignedValue ()
545     {
546         return clang_getEnumConstantDeclUnsignedValue(cx);
547     }
548 }