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