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 string mangle; 328 // for destructors, there may be multiple mangles, 329 // and the getMangling function doesn't always return 330 // the right one. To be honest, I don't know how to find 331 // the right one all the time, but in testing, the first 332 // one on this function, if it returns one, works more often. 333 // I wish I could explain more, I just know this passes the tests 334 // and the plain impl of just getMangling doesn't. 335 auto otherMangles = clang_Cursor_getCXXManglings(cx); 336 if(otherMangles) { 337 auto strings = toStrings(otherMangles); 338 if(strings.length) 339 mangle = strings[0]; 340 } 341 if(mangle is null) 342 mangle = clang_Cursor_getMangling(cx).toString; 343 return mangle; 344 } 345 346 bool isAnonymous() @safe @nogc pure nothrow const { 347 return cast(bool) clang_Cursor_isAnonymous(cx); 348 } 349 350 bool isBitField() @safe @nogc pure nothrow const { 351 return cast(bool) clang_Cursor_isBitField(cx); 352 } 353 354 int bitWidth() @safe @nogc pure nothrow const { 355 return clang_getFieldDeclBitWidth(cx); 356 } 357 358 auto accessSpecifier() @safe @nogc pure nothrow const { 359 return cast(AccessSpecifier) clang_getCXXAccessSpecifier(cx); 360 } 361 362 StorageClass storageClass() @safe @nogc pure nothrow const { 363 return cast(StorageClass) clang_Cursor_getStorageClass(cx); 364 } 365 366 bool isConstCppMethod() @safe @nogc pure nothrow const { 367 return cast(bool) clang_CXXMethod_isConst(cx); 368 } 369 370 bool isMoveConstructor() @safe @nogc pure nothrow const { 371 return cast(bool) clang_CXXConstructor_isMoveConstructor(cx); 372 } 373 374 bool isCopyConstructor() @safe @nogc pure nothrow const { 375 return cast(bool) clang_CXXConstructor_isCopyConstructor(cx); 376 } 377 378 bool isMacroFunction() @safe @nogc pure nothrow const { 379 return cast(bool) clang_Cursor_isMacroFunctionLike(cx); 380 } 381 382 bool isMacroBuiltin() @safe @nogc pure nothrow const { 383 return cast(bool) clang_Cursor_isMacroBuiltin(cx); 384 } 385 386 Cursor specializedCursorTemplate() @safe pure nothrow const { 387 return Cursor(clang_getSpecializedCursorTemplate(cx)); 388 } 389 390 TranslationUnit translationUnit() @safe nothrow const { 391 return TranslationUnit(clang_Cursor_getTranslationUnit(cx)); 392 } 393 394 Token[] tokens() @safe nothrow const { 395 import std.algorithm: map; 396 import std.array: array; 397 398 CXToken* tokens; 399 uint numTokens; 400 401 auto tu = clang_Cursor_getTranslationUnit(cx); 402 403 () @trusted { clang_tokenize(tu, sourceRange.cx, &tokens, &numTokens); }(); 404 // I hope this only deallocates the array 405 scope(exit) clang_disposeTokens(tu, tokens, numTokens); 406 407 auto tokenSlice = () @trusted { return tokens[0 .. numTokens]; }(); 408 409 return tokenSlice.map!(a => Token(a, tu)).array; 410 } 411 412 alias templateParams = templateParameters; 413 414 const(Cursor)[] templateParameters() @safe nothrow const { 415 import std.algorithm: filter; 416 import std.array: array; 417 418 const amTemplate = 419 kind == Cursor.Kind.ClassTemplate 420 || kind == Cursor.Kind.TypeAliasTemplateDecl 421 || kind == Cursor.Kind.FunctionTemplate 422 ; 423 const templateCursor = amTemplate ? this : specializedCursorTemplate; 424 425 auto range = templateCursor 426 .children 427 .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter || 428 a.kind == Cursor.Kind.NonTypeTemplateParameter); 429 430 // Why is this @system? Who knows. 431 return () @trusted { return range.array; }(); 432 } 433 434 /** 435 If declared at file scope. 436 */ 437 bool isFileScope() @safe nothrow const { 438 return lexicalParent.kind == Cursor.Kind.TranslationUnit; 439 } 440 441 int numTemplateArguments() @safe @nogc pure nothrow const { 442 return clang_Cursor_getNumTemplateArguments(cx); 443 } 444 445 TemplateArgumentKind templateArgumentKind(int i) @safe @nogc pure nothrow const { 446 return cast(TemplateArgumentKind) clang_Cursor_getTemplateArgumentKind(cx, i); 447 } 448 449 Type templateArgumentType(int i) @safe pure nothrow const { 450 return Type(clang_Cursor_getTemplateArgumentType(cx, i)); 451 } 452 453 long templateArgumentValue(int i) @safe @nogc pure nothrow const { 454 return clang_Cursor_getTemplateArgumentValue(cx, i); 455 } 456 457 bool isVirtual() @safe @nogc pure nothrow const { 458 return cast(bool) clang_CXXMethod_isVirtual(cx); 459 } 460 461 bool isPureVirtual() @safe @nogc pure nothrow const { 462 return cast(bool) clang_CXXMethod_isPureVirtual(cx); 463 } 464 465 string displayName() @safe pure nothrow const { 466 return clang_getCursorDisplayName(cx).toString; 467 } 468 469 /** 470 For e.g. TypeRef or TemplateRef 471 */ 472 Cursor referencedCursor() @safe nothrow const { 473 return Cursor(clang_getCursorReferenced(cx)); 474 } 475 476 Cursor[] overriddenCursors() @trusted /* @safe with DIP1000 */ const { 477 import std.algorithm: map; 478 import std.array: array; 479 480 uint length; 481 CXCursor* cursors; 482 483 clang_getOverriddenCursors(cx, &cursors, &length); 484 scope(exit) clang_disposeOverriddenCursors(cursors); 485 486 return cursors[0 .. length].map!(a => Cursor(a)).array; 487 } 488 489 auto numOverloadedDecls() @safe @nogc pure nothrow const { 490 return clang_getNumOverloadedDecls(cx); 491 } 492 493 Cursor overloadedDecl(int i) @safe nothrow const { 494 return Cursor(clang_getOverloadedDecl(cx, cast(uint) i)); 495 } 496 497 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 498 return cast(bool) clang_equalCursors(cx, other.cx); 499 } 500 501 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 502 return cast(bool) clang_equalCursors(cx, other.cx); 503 } 504 505 void visitChildren(scope CursorVisitor visitor) @safe nothrow const { 506 scope clientData = ClientData(visitor); 507 // why isn't this @safe with dip10000??? 508 () @trusted { clang_visitChildren(cx, &cvisitor, &clientData); }(); 509 } 510 511 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 512 return opApplyN(block); 513 } 514 515 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 516 return opApplyN(block); 517 } 518 519 private int opApplyN(T)(scope T block) const { 520 import std.traits: Parameters; 521 522 int stop = 0; 523 524 enum numParams = Parameters!T.length; 525 526 visitChildren((cursor, parent) { 527 528 static if(numParams == 2) 529 stop = block(cursor, parent); 530 else static if(numParams == 1) 531 stop = block(cursor); 532 else 533 static assert(false); 534 535 return stop 536 ? ChildVisitResult.Break 537 : ChildVisitResult.Continue; 538 }); 539 540 return stop; 541 } 542 543 private string _spellingCreate() @safe pure nothrow const { 544 return clang_getCursorSpelling(cx).toString; 545 } 546 547 private SourceRange _sourceRangeCreate() @safe pure nothrow const { 548 return SourceRange(clang_getCursorExtent(cx)); 549 } 550 } 551 552 553 struct SourceRange { 554 555 import clang.util: Lazy; 556 557 CXSourceRange cx; 558 private SourceLocation _start; 559 private SourceLocation _end; 560 561 mixin Lazy!_start; 562 mixin Lazy!_end; 563 564 this(CXSourceRange cx) @safe @nogc pure nothrow { 565 this.cx = cx; 566 } 567 568 string path() @safe nothrow const { 569 return start.path; 570 } 571 572 string toString() @safe pure const { 573 import std.conv: text; 574 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 575 } 576 577 private auto _startCreate() @safe pure nothrow const { 578 return SourceLocation(clang_getRangeStart(cx)); 579 } 580 581 private auto _endCreate() @safe pure nothrow const { 582 return SourceLocation(clang_getRangeEnd(cx)); 583 } 584 } 585 586 587 struct SourceLocation { 588 589 CXSourceLocation cx; 590 string path; 591 uint line; 592 uint column; 593 uint offset; 594 595 this(CXSourceLocation cx) @safe pure nothrow { 596 this.cx = cx; 597 598 CXFile file; 599 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 600 this.path = clang_getFileName(file).toString; 601 602 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 603 } 604 605 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 606 if(path == other.path && line == other.line && column == other.column && 607 offset == other.offset) 608 return 0; 609 610 if(path < other.path) return -1; 611 if(path > other.path) return 1; 612 if(line < other.line) return -1; 613 if(line > other.line) return 1; 614 if(column < other.column) return -1; 615 if(column > other.column) return 1; 616 if(offset < other.offset) return -1; 617 if(offset > other.offset) return 1; 618 assert(false); 619 } 620 621 string toString() @safe pure nothrow const { 622 import std.conv: text; 623 return text(`"`, path, `" `, line, ":", column, ":", offset); 624 } 625 } 626 627 private struct ClientData { 628 /** 629 The D visitor delegate 630 */ 631 CursorVisitor dvisitor; 632 } 633 634 // This is the C function actually passed to libclang's clang_visitChildren 635 // The context (clientData) contains the D delegate that's then called on the 636 // (cursor, parent) pair 637 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 638 auto clientData = cast(ClientData*) clientData_; 639 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 640 } 641 642 643 struct Type { 644 645 import clang.util: Lazy; 646 647 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 648 649 CXType cx; 650 Kind kind; 651 private string _spelling; 652 653 mixin Lazy!_spelling; 654 655 this(CXType cx) @safe @nogc pure nothrow { 656 this.cx = cx; 657 this.kind = cast(Kind) cx.kind; 658 } 659 660 this(in Type other) @trusted pure nothrow { 661 import std.algorithm: map; 662 import std.array: array; 663 664 this.cx.kind = other.cx.kind; 665 this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array; 666 667 this(this.cx); 668 } 669 670 this(in Kind kind) @safe @nogc pure nothrow { 671 this(kind, ""); 672 } 673 674 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 675 this.kind = kind; 676 _spelling = spelling; 677 } 678 679 Type pointee() @safe pure nothrow const { 680 return Type(clang_getPointeeType(cx)); 681 } 682 683 Type unelaborate() @safe nothrow const { 684 return Type(clang_Type_getNamedType(cx)); 685 } 686 687 Type canonical() @safe pure nothrow const { 688 return Type(clang_getCanonicalType(cx)); 689 } 690 691 Type returnType() @safe pure const { 692 return Type(clang_getResultType(cx)); 693 } 694 695 // Returns a range of Type 696 auto paramTypes()() @safe pure const nothrow { 697 698 static struct Range { 699 const CXType cx; 700 const int numArgs; 701 int index = 0; 702 703 bool empty() { 704 return index < 0 || index >= numArgs; 705 } 706 707 void popFront() { 708 ++index; 709 } 710 711 Type front() { 712 return Type(clang_getArgType(cx, index)); 713 } 714 } 715 716 return Range(cx, clang_getNumArgTypes(cx)); 717 } 718 719 bool isVariadicFunction() @safe @nogc pure nothrow const { 720 return cast(bool) clang_isFunctionTypeVariadic(cx); 721 } 722 723 Type elementType() @safe pure nothrow const { 724 return Type(clang_getElementType(cx)); 725 } 726 727 long numElements() @safe @nogc pure nothrow const { 728 return clang_getNumElements(cx); 729 } 730 731 long arraySize() @safe @nogc pure nothrow const { 732 return clang_getArraySize(cx); 733 } 734 735 bool isConstQualified() @safe @nogc pure nothrow const scope { 736 return cast(bool) clang_isConstQualifiedType(cx); 737 } 738 739 bool isVolatileQualified() @safe @nogc pure nothrow const scope { 740 return cast(bool) clang_isVolatileQualifiedType(cx); 741 } 742 743 Cursor declaration() @safe pure nothrow const { 744 return Cursor(clang_getTypeDeclaration(cx)); 745 } 746 747 Type namedType() @safe pure nothrow const { 748 return Type(clang_Type_getNamedType(cx)); 749 } 750 751 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 752 return cast(bool) clang_equalTypes(cx, other.cx); 753 } 754 755 bool opEquals(in Type other) @safe @nogc pure nothrow const { 756 return cast(bool) clang_equalTypes(cx, other.cx); 757 } 758 759 bool isInvalid() @safe @nogc pure nothrow const { 760 return kind == Kind.Invalid; 761 } 762 763 long getSizeof() @safe @nogc pure nothrow const { 764 return clang_Type_getSizeOf(cx); 765 } 766 767 int numTemplateArguments() @safe @nogc pure nothrow const { 768 return clang_Type_getNumTemplateArguments(cx); 769 } 770 771 Type typeTemplateArgument(int i) @safe pure nothrow const { 772 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 773 } 774 775 string toString() @safe pure nothrow const { 776 import std.conv: text; 777 778 try { 779 return text("Type(", kind, `, "`, spelling, `")`); 780 } catch(Exception e) 781 assert(false, "Fatal error in Type.toString: " ~ e.msg); 782 } 783 784 private string _spellingCreate() @safe pure nothrow const { 785 return clang_getTypeSpelling(cx).toString; 786 } 787 } 788 789 790 struct Token { 791 792 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 793 794 Kind kind; 795 string spelling; 796 CXToken cxToken; 797 CXTranslationUnit cxTU; 798 799 this(CXToken cxToken, CXTranslationUnit cxTU) @safe pure nothrow { 800 this.cxToken = cxToken; 801 this.cxTU = cxTU; 802 this.kind = cast(Kind) clang_getTokenKind(cxToken); 803 this.spelling = .toString(clang_getTokenSpelling(cxTU, cxToken)); 804 } 805 806 this(Kind kind, string spelling) @safe @nogc pure nothrow { 807 this.kind = kind; 808 this.spelling = spelling; 809 } 810 811 string toString() @safe pure const { 812 import std.conv: text; 813 814 return text("Token(", kind, `, "`, spelling, `")`); 815 } 816 817 bool opEquals(in Token other) @safe pure nothrow const { 818 return kind == other.kind && spelling == other.spelling; 819 } 820 }