1 module clang; 2 3 4 import clang.c.index; 5 import clang.c.util: EnumD; 6 7 8 immutable bool[string] gPredefinedCursors; 9 10 version (Windows) 11 extern(C) private int _mktemp_s(char* nameTemplate, size_t sizeInChars) nothrow @safe @nogc; 12 13 shared static this() nothrow { 14 try { 15 16 const fileName = getTempFileName; 17 { 18 // create an empty file 19 import std.stdio: File; 20 auto f = File(fileName, "w"); 21 f.writeln; 22 f.flush; 23 f.detach; 24 } 25 26 auto tu = parse(fileName, 27 ["-xc"], 28 TranslationUnitFlags.DetailedPreprocessingRecord); 29 foreach(cursor; tu.cursor.children) { 30 gPredefinedCursors[cursor.spelling] = true; 31 } 32 33 } catch(Exception e) { 34 import std.stdio: stderr; 35 try 36 stderr.writeln("Error initialising libclang: ", e); 37 catch(Exception _) {} 38 } 39 } 40 41 42 string getTempFileName() @trusted { 43 import std.file: tempDir; 44 import std.path: buildPath; 45 import std.string: fromStringz; 46 47 char[] tmpnamBuf = "libclangXXXXXX\0".dup; 48 49 version (Posix) { 50 import core.sys.posix.stdlib: mkstemp; 51 mkstemp(&tmpnamBuf[0]); 52 } 53 else version (Windows) 54 _mktemp_s(&tmpnamBuf[0], tmpnamBuf.length); 55 56 return tempDir().buildPath(tempDir, fromStringz(&tmpnamBuf[0])); 57 } 58 59 60 mixin EnumD!("TranslationUnitFlags", CXTranslationUnit_Flags, "CXTranslationUnit_"); 61 mixin EnumD!("Language", CXLanguageKind, "CXLanguage_"); 62 63 64 TranslationUnit parse(in string fileName, 65 in TranslationUnitFlags translUnitflags = TranslationUnitFlags.None) 66 @safe 67 { 68 return parse(fileName, [], translUnitflags); 69 } 70 71 72 mixin EnumD!("ErrorCode", CXErrorCode, ""); 73 mixin EnumD!("DiagnosticSeverity", CXDiagnosticSeverity, "CXDiagnostic_"); 74 mixin EnumD!("TemplateArgumentKind", CXTemplateArgumentKind, "CXTemplateArgumentKind_"); 75 76 77 TranslationUnit parse(in string fileName, 78 in string[] commandLineArgs, 79 in TranslationUnitFlags translUnitflags = TranslationUnitFlags.None) 80 @safe 81 { 82 83 import std.string: toStringz; 84 import std.algorithm: map; 85 import std.array: array, join; 86 import std.conv: text; 87 88 // faux booleans 89 const excludeDeclarationsFromPCH = 0; 90 const displayDiagnostics = 0; 91 auto index = clang_createIndex(excludeDeclarationsFromPCH, displayDiagnostics); 92 CXUnsavedFile[] unsavedFiles; 93 const commandLineArgz = commandLineArgs 94 .map!(a => a.toStringz) 95 .array; 96 97 CXTranslationUnit cx; 98 const err = () @trusted { 99 return cast(ErrorCode)clang_parseTranslationUnit2( 100 index, 101 fileName.toStringz, 102 commandLineArgz.ptr, // .ptr since the length can be 0 103 cast(int)commandLineArgz.length, 104 unsavedFiles.ptr, // .ptr since the length can be 0 105 cast(uint)unsavedFiles.length, 106 translUnitflags, 107 &cx, 108 ); 109 }(); 110 111 if(err != ErrorCode.success) { 112 throw new Exception(text("Could not parse ", fileName, ": ", err)); 113 } 114 115 string[] errorMessages; 116 // throw if there are error diagnostics 117 foreach(i; 0 .. clang_getNumDiagnostics(cx)) { 118 auto diagnostic = clang_getDiagnostic(cx, i); 119 scope(exit) clang_disposeDiagnostic(diagnostic); 120 const severity = cast(DiagnosticSeverity) clang_getDiagnosticSeverity(diagnostic); 121 enum diagnosticOptions = CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn; 122 if(severity == DiagnosticSeverity.Error || severity == DiagnosticSeverity.Fatal) 123 errorMessages ~= clang_formatDiagnostic(diagnostic, diagnosticOptions).toString; 124 } 125 126 if(errorMessages.length > 0) 127 throw new Exception(text("Error parsing '", fileName, "':\n", 128 errorMessages.join("\n"))); 129 130 131 return TranslationUnit(cx); 132 } 133 134 string[] systemPaths() @safe { 135 import std.process: execute; 136 import std.string: splitLines, stripLeft; 137 import std.algorithm: map, countUntil; 138 import std.array: array; 139 140 version(Windows) 141 { 142 enum devnull = "NUL"; 143 } else { 144 enum devnull = "/dev/null"; 145 } 146 147 const res = () { 148 try 149 { 150 return execute(["clang", "-v", "-xc++", devnull, "-fsyntax-only"], ["LANG": "C"]); 151 } 152 catch (Exception e) 153 { 154 import std.typecons : Tuple; 155 return Tuple!(int, "status", string, "output")(-1, e.msg); 156 } 157 }(); 158 if(res.status != 0) throw new Exception("Failed to call clang:\n" ~ res.output); 159 160 auto lines = res.output.splitLines; 161 162 const startIndex = lines.countUntil("#include <...> search starts here:") + 1; 163 assert(startIndex > 0); 164 const endIndex = lines.countUntil("End of search list."); 165 assert(endIndex > 0); 166 167 return lines[startIndex .. endIndex].map!stripLeft.array; 168 } 169 170 171 mixin EnumD!("ChildVisitResult", CXChildVisitResult, "CXChildVisit_"); 172 alias CursorVisitor = ChildVisitResult delegate(Cursor cursor, Cursor parent); 173 174 struct TranslationUnit { 175 176 CXTranslationUnit cx; 177 Cursor cursor; 178 179 this(CXTranslationUnit cx) @safe nothrow { 180 this.cx = cx; 181 this.cursor = Cursor(clang_getTranslationUnitCursor(cx)); 182 } 183 } 184 185 string toString(CXString cxString) @safe pure nothrow { 186 import std.conv: to; 187 auto cstr = clang_getCString(cxString); 188 scope(exit) clang_disposeString(cxString); 189 return () @trusted { return cstr.to!string; }(); 190 } 191 192 string[] toStrings(CXStringSet* strings) @trusted pure nothrow { 193 import std.conv: to; 194 scope(exit) clang_disposeStringSet(strings); 195 string[] ret; 196 foreach(cxstr; strings.Strings[0 .. strings.Count]) { 197 // cannot use the toString above since it frees, and so 198 // does the dispose string set at scope exit, leading to 199 // a double free situation 200 auto cstr = clang_getCString(cxstr); 201 ret ~= cstr.to!string; 202 } 203 return ret; 204 } 205 206 mixin EnumD!("AccessSpecifier", CX_CXXAccessSpecifier, "CX_CXX"); 207 208 209 struct Cursor { 210 211 import std.traits: ReturnType; 212 213 mixin EnumD!("Kind", CXCursorKind, "CXCursor_"); 214 mixin EnumD!("StorageClass", CX_StorageClass, "CX_SC_"); 215 216 alias Hash = ReturnType!(clang_hashCursor); 217 218 CXCursor cx; 219 private Cursor[] _children; 220 Kind kind; 221 string spelling; 222 Type type; 223 Type underlyingType; 224 SourceRange sourceRange; 225 226 this(CXCursor cx) @safe pure nothrow { 227 this.cx = cx; 228 kind = cast(Kind) clang_getCursorKind(cx); 229 spelling = clang_getCursorSpelling(cx).toString; 230 type = Type(clang_getCursorType(cx)); 231 232 if(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl) 233 underlyingType = Type(clang_getTypedefDeclUnderlyingType(cx)); 234 235 sourceRange = SourceRange(clang_getCursorExtent(cx)); 236 } 237 238 private static extern(C) CXChildVisitResult ctorVisitor(CXCursor cursor, 239 CXCursor parent, 240 void* clientData_) 241 @safe nothrow 242 { 243 auto children = () @trusted { return cast(Cursor[]*) clientData_; }(); 244 *children ~= Cursor(cursor); 245 return CXChildVisit_Continue; 246 } 247 248 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 249 this(kind, spelling, Type()); 250 } 251 252 this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow { 253 this.kind = kind; 254 this.spelling = spelling; 255 this.type = type; 256 } 257 258 /// Lazily return the cursor's children 259 inout(Cursor)[] children() @safe @property nothrow inout { 260 if(_children.length) return _children; 261 262 inout(Cursor)[] ret; 263 // calling Cursor.visitChildren here would cause infinite recursion 264 // because cvisitor constructs a Cursor out of the parent 265 () @trusted { clang_visitChildren(cx, &ctorVisitor, &ret); }(); 266 return ret; 267 } 268 269 void children(Cursor[] cursors) @safe @property pure nothrow { 270 _children = cursors; 271 } 272 273 Type returnType() @safe pure nothrow const { 274 return Type(clang_getCursorResultType(cx)); 275 } 276 277 /** 278 For EnumConstantDecl cursors, return the numeric value 279 */ 280 auto enumConstantValue() @safe @nogc pure nothrow const { 281 assert(kind == Cursor.Kind.EnumConstantDecl); 282 return clang_getEnumConstantDeclValue(cx); 283 } 284 285 Language language() @safe @nogc pure nothrow const { 286 return cast(Language) clang_getCursorLanguage(cx); 287 } 288 289 Cursor canonical() @safe nothrow const { 290 return Cursor(clang_getCanonicalCursor(cx)); 291 } 292 293 /** 294 If this is the canonical cursor. Given forward declarations, there may 295 be several cursors for one entity. This returns true if this cursor 296 is the canonical one. 297 */ 298 bool isCanonical() @safe @nogc pure nothrow const { 299 return cast(bool) clang_equalCursors(cx, clang_getCanonicalCursor(cx)); 300 } 301 302 bool isDefinition() @safe @nogc pure nothrow const { 303 return cast(bool) clang_isCursorDefinition(cx); 304 } 305 306 bool isNull() @safe @nogc pure nothrow const { 307 return cast(bool) clang_Cursor_isNull(cx); 308 } 309 310 static Cursor nullCursor() @safe nothrow { 311 return Cursor(clang_getNullCursor()); 312 } 313 314 Cursor definition() @safe nothrow const { 315 return Cursor(clang_getCursorDefinition(cx)); 316 } 317 318 string toString() @safe pure nothrow const { 319 import std.conv: text; 320 try { 321 const returnTypeStr = kind == Kind.FunctionDecl 322 ? text(", ", returnType) 323 : ""; 324 325 return text("Cursor(", kind, `, "`, spelling, `", `, type, returnTypeStr, ")"); 326 } catch(Exception e) 327 assert(false, "Fatal error in Cursor.toString: " ~ e.msg); 328 } 329 330 bool isPredefined() @safe @nogc pure nothrow const { 331 return (spelling in gPredefinedCursors) !is null; 332 } 333 334 Cursor semanticParent() @safe nothrow const { 335 return Cursor(clang_getCursorSemanticParent(cx)); 336 } 337 338 Cursor lexicalParent() @safe nothrow const { 339 return Cursor(clang_getCursorLexicalParent(cx)); 340 } 341 342 bool isInvalid() @safe @nogc pure nothrow const { 343 return cast(bool) clang_isInvalid(cx.kind); 344 } 345 346 auto hash() @safe @nogc pure nothrow const { 347 return clang_hashCursor(cx); 348 } 349 350 string mangling() @safe pure nothrow const { 351 string mangle; 352 // for destructors, there may be multiple mangles, 353 // and the getMangling function doesn't always return 354 // the right one. To be honest, I don't know how to find 355 // the right one all the time, but in testing, the first 356 // one on this function, if it returns one, works more often. 357 // I wish I could explain more, I just know this passes the tests 358 // and the plain impl of just getMangling doesn't. 359 auto otherMangles = clang_Cursor_getCXXManglings(cx); 360 if(otherMangles) { 361 auto strings = toStrings(otherMangles); 362 if(strings.length) 363 mangle = strings[0]; 364 } 365 if(mangle is null) 366 mangle = clang_Cursor_getMangling(cx).toString; 367 return mangle; 368 } 369 370 bool isAnonymous() @safe @nogc pure nothrow const { 371 return cast(bool) clang_Cursor_isAnonymous(cx); 372 } 373 374 bool isBitField() @safe @nogc pure nothrow const { 375 return cast(bool) clang_Cursor_isBitField(cx); 376 } 377 378 int bitWidth() @safe @nogc pure nothrow const { 379 return clang_getFieldDeclBitWidth(cx); 380 } 381 382 auto accessSpecifier() @safe @nogc pure nothrow const { 383 return cast(AccessSpecifier) clang_getCXXAccessSpecifier(cx); 384 } 385 386 StorageClass storageClass() @safe @nogc pure nothrow const { 387 return cast(StorageClass) clang_Cursor_getStorageClass(cx); 388 } 389 390 bool isConstCppMethod() @safe @nogc pure nothrow const { 391 return cast(bool) clang_CXXMethod_isConst(cx); 392 } 393 394 bool isMoveConstructor() @safe @nogc pure nothrow const { 395 return cast(bool) clang_CXXConstructor_isMoveConstructor(cx); 396 } 397 398 bool isCopyConstructor() @safe @nogc pure nothrow const { 399 return cast(bool) clang_CXXConstructor_isCopyConstructor(cx); 400 } 401 402 bool isMacroFunction() @safe @nogc pure nothrow const { 403 return cast(bool) clang_Cursor_isMacroFunctionLike(cx); 404 } 405 406 bool isMacroBuiltin() @safe @nogc pure nothrow const { 407 return cast(bool) clang_Cursor_isMacroBuiltin(cx); 408 } 409 410 Cursor specializedCursorTemplate() @safe pure nothrow const { 411 return Cursor(clang_getSpecializedCursorTemplate(cx)); 412 } 413 414 TranslationUnit translationUnit() @safe nothrow const { 415 return TranslationUnit(clang_Cursor_getTranslationUnit(cx)); 416 } 417 418 Token[] tokens() @safe nothrow const { 419 import std.algorithm: map; 420 import std.array: array; 421 422 CXToken* tokens; 423 uint numTokens; 424 425 () @trusted { clang_tokenize(translationUnit.cx, sourceRange.cx, &tokens, &numTokens); }(); 426 // I hope this only deallocates the array 427 scope(exit) clang_disposeTokens(translationUnit.cx, tokens, numTokens); 428 429 auto tokenSlice = () @trusted { return tokens[0 .. numTokens]; }(); 430 431 return tokenSlice.map!(a => Token(a, translationUnit)).array; 432 } 433 434 alias templateParams = templateParameters; 435 436 const(Cursor)[] templateParameters() @safe nothrow const { 437 import std.algorithm: filter; 438 import std.array: array; 439 440 const amTemplate = 441 kind == Cursor.Kind.ClassTemplate 442 || kind == Cursor.Kind.TypeAliasTemplateDecl 443 || kind == Cursor.Kind.FunctionTemplate 444 ; 445 const templateCursor = amTemplate ? this : specializedCursorTemplate; 446 447 auto range = templateCursor 448 .children 449 .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter || 450 a.kind == Cursor.Kind.NonTypeTemplateParameter); 451 452 // Why is this @system? Who knows. 453 return () @trusted { return range.array; }(); 454 } 455 456 /** 457 If declared at file scope. 458 */ 459 bool isFileScope() @safe nothrow const { 460 return lexicalParent.kind == Cursor.Kind.TranslationUnit; 461 } 462 463 int numTemplateArguments() @safe @nogc pure nothrow const { 464 return clang_Cursor_getNumTemplateArguments(cx); 465 } 466 467 TemplateArgumentKind templateArgumentKind(int i) @safe @nogc pure nothrow const { 468 return cast(TemplateArgumentKind) clang_Cursor_getTemplateArgumentKind(cx, i); 469 } 470 471 Type templateArgumentType(int i) @safe pure nothrow const { 472 return Type(clang_Cursor_getTemplateArgumentType(cx, i)); 473 } 474 475 long templateArgumentValue(int i) @safe @nogc pure nothrow const { 476 return clang_Cursor_getTemplateArgumentValue(cx, i); 477 } 478 479 bool isVirtual() @safe @nogc pure nothrow const { 480 return cast(bool) clang_CXXMethod_isVirtual(cx); 481 } 482 483 bool isPureVirtual() @safe @nogc pure nothrow const { 484 return cast(bool) clang_CXXMethod_isPureVirtual(cx); 485 } 486 487 string displayName() @safe pure nothrow const { 488 return clang_getCursorDisplayName(cx).toString; 489 } 490 491 /** 492 For e.g. TypeRef or TemplateRef 493 */ 494 Cursor referencedCursor() @safe nothrow const { 495 return Cursor(clang_getCursorReferenced(cx)); 496 } 497 498 Cursor[] overriddenCursors() @trusted /* @safe with DIP1000 */ const { 499 import std.algorithm: map; 500 import std.array: array; 501 502 uint length; 503 CXCursor* cursors; 504 505 clang_getOverriddenCursors(cx, &cursors, &length); 506 scope(exit) clang_disposeOverriddenCursors(cursors); 507 508 return cursors[0 .. length].map!(a => Cursor(a)).array; 509 } 510 511 auto numOverloadedDecls() @safe @nogc pure nothrow const { 512 return clang_getNumOverloadedDecls(cx); 513 } 514 515 Cursor overloadedDecl(int i) @safe nothrow const { 516 return Cursor(clang_getOverloadedDecl(cx, cast(uint) i)); 517 } 518 519 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 520 return cast(bool) clang_equalCursors(cx, other.cx); 521 } 522 523 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 524 return cast(bool) clang_equalCursors(cx, other.cx); 525 } 526 527 void visitChildren(scope CursorVisitor visitor) @safe nothrow const { 528 scope clientData = ClientData(visitor); 529 // why isn't this @safe with dip10000??? 530 () @trusted { clang_visitChildren(cx, &cvisitor, &clientData); }(); 531 } 532 533 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 534 return opApplyN(block); 535 } 536 537 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 538 return opApplyN(block); 539 } 540 541 private int opApplyN(T)(scope T block) const { 542 import std.traits: Parameters; 543 544 int stop = 0; 545 546 enum numParams = Parameters!T.length; 547 548 visitChildren((cursor, parent) { 549 550 static if(numParams == 2) 551 stop = block(cursor, parent); 552 else static if(numParams == 1) 553 stop = block(cursor); 554 else 555 static assert(false); 556 557 return stop 558 ? ChildVisitResult.Break 559 : ChildVisitResult.Continue; 560 }); 561 562 return stop; 563 } 564 } 565 566 567 struct SourceRange { 568 569 CXSourceRange cx; 570 string path; 571 SourceLocation start; 572 SourceLocation end; 573 574 this(CXSourceRange cx) @safe pure nothrow { 575 this.cx = cx; 576 this.start = clang_getRangeStart(cx); 577 this.end = clang_getRangeEnd(cx); 578 this.path = start.path; 579 } 580 581 string toString() @safe pure const { 582 import std.conv: text; 583 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 584 } 585 } 586 587 struct SourceLocation { 588 CXSourceLocation cx; 589 string path; 590 uint line; 591 uint column; 592 uint offset; 593 594 this(CXSourceLocation cx) @safe pure nothrow { 595 this.cx = cx; 596 597 CXFile file; 598 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 599 this.path = clang_getFileName(file).toString; 600 601 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 602 } 603 604 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 605 if(path == other.path && line == other.line && column == other.column && 606 offset == other.offset) 607 return 0; 608 609 if(path < other.path) return -1; 610 if(path > other.path) return 1; 611 if(line < other.line) return -1; 612 if(line > other.line) return 1; 613 if(column < other.column) return -1; 614 if(column > other.column) return 1; 615 if(offset < other.offset) return -1; 616 if(offset > other.offset) return 1; 617 assert(false); 618 } 619 620 string toString() @safe pure nothrow const { 621 import std.conv: text; 622 return text(`"`, path, `" `, line, ":", column, ":", offset); 623 } 624 } 625 626 private struct ClientData { 627 /** 628 The D visitor delegate 629 */ 630 CursorVisitor dvisitor; 631 } 632 633 // This is the C function actually passed to libclang's clang_visitChildren 634 // The context (clientData) contains the D delegate that's then called on the 635 // (cursor, parent) pair 636 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 637 auto clientData = cast(ClientData*) clientData_; 638 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 639 } 640 641 642 struct Type { 643 644 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 645 646 CXType cx; 647 Kind kind; 648 string spelling; 649 650 this(CXType cx) @safe pure nothrow { 651 this.cx = cx; 652 this.kind = cast(Kind) cx.kind; 653 spelling = clang_getTypeSpelling(cx).toString; 654 } 655 656 this(in Type other) @trusted pure nothrow { 657 import std.algorithm: map; 658 import std.array: array; 659 660 this.cx.kind = other.cx.kind; 661 this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array; 662 663 this(this.cx); 664 } 665 666 this(in Kind kind) @safe @nogc pure nothrow { 667 this(kind, ""); 668 } 669 670 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 671 this.kind = kind; 672 this.spelling = spelling; 673 } 674 675 Type pointee() @safe pure nothrow const { 676 return Type(clang_getPointeeType(cx)); 677 } 678 679 Type unelaborate() @safe nothrow const { 680 return Type(clang_Type_getNamedType(cx)); 681 } 682 683 Type canonical() @safe pure nothrow const { 684 return Type(clang_getCanonicalType(cx)); 685 } 686 687 Type returnType() @safe pure const { 688 return Type(clang_getResultType(cx)); 689 } 690 691 // Returns a range of Type 692 auto paramTypes()() @safe pure const nothrow { 693 694 static struct Range { 695 const CXType cx; 696 const int numArgs; 697 int index = 0; 698 699 bool empty() { 700 return index < 0 || index >= numArgs; 701 } 702 703 void popFront() { 704 ++index; 705 } 706 707 Type front() { 708 return Type(clang_getArgType(cx, index)); 709 } 710 } 711 712 return Range(cx, clang_getNumArgTypes(cx)); 713 } 714 715 bool isVariadicFunction() @safe @nogc pure nothrow const { 716 return cast(bool) clang_isFunctionTypeVariadic(cx); 717 } 718 719 Type elementType() @safe pure nothrow const { 720 return Type(clang_getElementType(cx)); 721 } 722 723 long numElements() @safe @nogc pure nothrow const { 724 return clang_getNumElements(cx); 725 } 726 727 long arraySize() @safe @nogc pure nothrow const { 728 return clang_getArraySize(cx); 729 } 730 731 bool isConstQualified() @safe @nogc pure nothrow const { 732 return cast(bool) clang_isConstQualifiedType(cx); 733 } 734 735 bool isVolatileQualified() @safe @nogc pure nothrow const { 736 return cast(bool) clang_isVolatileQualifiedType(cx); 737 } 738 739 Cursor declaration() @safe pure nothrow const { 740 return Cursor(clang_getTypeDeclaration(cx)); 741 } 742 743 Type namedType() @safe pure nothrow const { 744 return Type(clang_Type_getNamedType(cx)); 745 } 746 747 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 748 return cast(bool) clang_equalTypes(cx, other.cx); 749 } 750 751 bool opEquals(in Type other) @safe @nogc pure nothrow const { 752 return cast(bool) clang_equalTypes(cx, other.cx); 753 } 754 755 bool isInvalid() @safe @nogc pure nothrow const { 756 return kind == Kind.Invalid; 757 } 758 759 long getSizeof() @safe @nogc pure nothrow const { 760 return clang_Type_getSizeOf(cx); 761 } 762 763 int numTemplateArguments() @safe @nogc pure nothrow const { 764 return clang_Type_getNumTemplateArguments(cx); 765 } 766 767 Type typeTemplateArgument(int i) @safe pure nothrow const { 768 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 769 } 770 771 string toString() @safe pure nothrow const { 772 import std.conv: text; 773 774 try { 775 return text("Type(", kind, `, "`, spelling, `")`); 776 } catch(Exception e) 777 assert(false, "Fatal error in Type.toString: " ~ e.msg); 778 } 779 } 780 781 782 struct Token { 783 784 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 785 786 Kind kind; 787 string spelling; 788 CXToken cx; 789 TranslationUnit translationUnit; 790 791 this(CXToken cx, TranslationUnit unit) @safe pure nothrow { 792 this.cx = cx; 793 this.translationUnit = unit; 794 this.kind = cast(Kind) clang_getTokenKind(cx); 795 this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx)); 796 } 797 798 this(Kind kind, string spelling) @safe @nogc pure nothrow { 799 this.kind = kind; 800 this.spelling = spelling; 801 } 802 803 string toString() @safe pure const { 804 import std.conv: text; 805 806 return text("Token(", kind, `, "`, spelling, `")`); 807 } 808 809 bool opEquals(in Token other) @safe pure nothrow const { 810 return kind == other.kind && spelling == other.spelling; 811 } 812 }