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 }