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 = buildPath(tempDir, "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 tmpnamBuf.idup; 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 186 string toString(CXString cxString) @safe pure nothrow { 187 import std.string: fromStringz; 188 189 scope(exit) clang_disposeString(cxString); 190 auto cstr = clang_getCString(cxString); 191 return () @trusted { return cstr.fromStringz.idup; }(); 192 } 193 194 195 string[] toStrings(CXStringSet* strings) @safe pure nothrow { 196 import std.string: fromStringz; 197 import std.array: appender; 198 199 scope(exit) clang_disposeStringSet(strings); 200 201 auto app = appender!(string[]); 202 app.reserve(strings.Count); 203 204 foreach(cxstr; () @trusted { return strings.Strings[0 .. strings.Count]; }()) { 205 // cannot use the toString above since it frees, and so 206 // does the dispose string set at scope exit, leading to 207 // a double free situation 208 auto cstr = clang_getCString(cxstr); 209 auto str = () @trusted { return cstr.fromStringz.idup; }(); 210 app ~= str; 211 } 212 213 return app.data; 214 } 215 216 217 mixin EnumD!("AccessSpecifier", CX_CXXAccessSpecifier, "CX_CXX"); 218 219 220 struct Cursor { 221 222 import clang.util: Lazy; 223 import std.traits: ReturnType; 224 225 mixin EnumD!("Kind", CXCursorKind, "CXCursor_"); 226 mixin EnumD!("StorageClass", CX_StorageClass, "CX_SC_"); 227 228 alias Hash = ReturnType!clang_hashCursor; 229 230 CXCursor cx; 231 private Cursor[] _children; 232 Kind kind; 233 private string _spelling; 234 Type type; 235 Type underlyingType; 236 SourceRange sourceRange; 237 238 mixin Lazy!_spelling; 239 240 this(CXCursor cx) @safe @nogc pure nothrow { 241 this.cx = cx; 242 kind = cast(Kind) clang_getCursorKind(cx); 243 type = Type(clang_getCursorType(cx)); 244 245 if(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl) 246 underlyingType = Type(clang_getTypedefDeclUnderlyingType(cx)); 247 248 sourceRange = SourceRange(clang_getCursorExtent(cx)); 249 } 250 251 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 252 this(kind, spelling, Type()); 253 } 254 255 this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow { 256 this.kind = kind; 257 this._spelling = spelling; 258 this._spellingInit = true; 259 this.type = type; 260 } 261 262 /// Lazily return the cursor's children 263 auto children(this This)() @property { 264 import std.array: appender; 265 266 if(_children.length) return _children; 267 268 auto app = appender!(Cursor[]); 269 app.reserve(10); // hacky but speeds things up, faster than counting the right number 270 // calling Cursor.visitChildren here would cause infinite recursion 271 // because cvisitor constructs a Cursor out of the parent 272 () @trusted { clang_visitChildren(cx, &childrenVisitor, &app); }(); 273 () @trusted { cast(Cursor[]) _children = app.data; }(); 274 275 return _children; 276 } 277 278 private static extern(C) CXChildVisitResult childrenVisitor(CXCursor cursor, 279 CXCursor parent, 280 void* clientData) 281 @safe nothrow 282 { 283 import std.array: Appender; 284 285 auto app = () @trusted { return cast(Appender!(Cursor[])*) clientData; }(); 286 *app ~= Cursor(cursor); 287 288 return CXChildVisit_Continue; 289 } 290 291 void children(Cursor[] cursors) @safe @property pure nothrow { 292 _children = cursors; 293 } 294 295 void setSpelling(string str) @safe @nogc @property pure nothrow { 296 _spelling = str; 297 _spellingInit = true; 298 } 299 300 Type returnType() @safe pure nothrow const { 301 return Type(clang_getCursorResultType(cx)); 302 } 303 304 /** 305 For EnumConstantDecl cursors, return the numeric value 306 */ 307 auto enumConstantValue() @safe @nogc pure nothrow const { 308 assert(kind == Cursor.Kind.EnumConstantDecl); 309 return clang_getEnumConstantDeclValue(cx); 310 } 311 312 Language language() @safe @nogc pure nothrow const { 313 return cast(Language) clang_getCursorLanguage(cx); 314 } 315 316 Cursor canonical() @safe nothrow const { 317 return Cursor(clang_getCanonicalCursor(cx)); 318 } 319 320 /** 321 If this is the canonical cursor. Given forward declarations, there may 322 be several cursors for one entity. This returns true if this cursor 323 is the canonical one. 324 */ 325 bool isCanonical() @safe @nogc pure nothrow const { 326 return cast(bool) clang_equalCursors(cx, clang_getCanonicalCursor(cx)); 327 } 328 329 bool isDefinition() @safe @nogc pure nothrow const { 330 return cast(bool) clang_isCursorDefinition(cx); 331 } 332 333 bool isNull() @safe @nogc pure nothrow const { 334 return cast(bool) clang_Cursor_isNull(cx); 335 } 336 337 static Cursor nullCursor() @safe nothrow { 338 return Cursor(clang_getNullCursor()); 339 } 340 341 Cursor definition() @safe nothrow const { 342 return Cursor(clang_getCursorDefinition(cx)); 343 } 344 345 string toString() @safe pure nothrow const { 346 import std.conv: text; 347 try { 348 const returnTypeStr = kind == Kind.FunctionDecl 349 ? text(", ", returnType) 350 : ""; 351 352 return text("Cursor(", kind, `, "`, spelling, `", `, type, returnTypeStr, ")"); 353 } catch(Exception e) 354 assert(false, "Fatal error in Cursor.toString: " ~ e.msg); 355 } 356 357 bool isPredefined() @safe pure nothrow const { 358 return (spelling in gPredefinedCursors) !is null; 359 } 360 361 Cursor semanticParent() @safe nothrow const { 362 return Cursor(clang_getCursorSemanticParent(cx)); 363 } 364 365 Cursor lexicalParent() @safe nothrow const { 366 return Cursor(clang_getCursorLexicalParent(cx)); 367 } 368 369 bool isInvalid() @safe @nogc pure nothrow const { 370 return cast(bool) clang_isInvalid(cx.kind); 371 } 372 373 auto hash() @safe @nogc pure nothrow const { 374 return clang_hashCursor(cx); 375 } 376 377 string mangling() @safe pure nothrow const { 378 string mangle; 379 // for destructors, there may be multiple mangles, 380 // and the getMangling function doesn't always return 381 // the right one. To be honest, I don't know how to find 382 // the right one all the time, but in testing, the first 383 // one on this function, if it returns one, works more often. 384 // I wish I could explain more, I just know this passes the tests 385 // and the plain impl of just getMangling doesn't. 386 auto otherMangles = clang_Cursor_getCXXManglings(cx); 387 if(otherMangles) { 388 auto strings = toStrings(otherMangles); 389 if(strings.length) 390 mangle = strings[0]; 391 } 392 if(mangle is null) 393 mangle = clang_Cursor_getMangling(cx).toString; 394 return mangle; 395 } 396 397 bool isAnonymous() @safe @nogc pure nothrow const { 398 return cast(bool) clang_Cursor_isAnonymous(cx); 399 } 400 401 bool isBitField() @safe @nogc pure nothrow const { 402 return cast(bool) clang_Cursor_isBitField(cx); 403 } 404 405 int bitWidth() @safe @nogc pure nothrow const { 406 return clang_getFieldDeclBitWidth(cx); 407 } 408 409 auto accessSpecifier() @safe @nogc pure nothrow const { 410 return cast(AccessSpecifier) clang_getCXXAccessSpecifier(cx); 411 } 412 413 StorageClass storageClass() @safe @nogc pure nothrow const { 414 return cast(StorageClass) clang_Cursor_getStorageClass(cx); 415 } 416 417 bool isConstCppMethod() @safe @nogc pure nothrow const { 418 return cast(bool) clang_CXXMethod_isConst(cx); 419 } 420 421 bool isMoveConstructor() @safe @nogc pure nothrow const { 422 return cast(bool) clang_CXXConstructor_isMoveConstructor(cx); 423 } 424 425 bool isCopyConstructor() @safe @nogc pure nothrow const { 426 return cast(bool) clang_CXXConstructor_isCopyConstructor(cx); 427 } 428 429 bool isMacroFunction() @safe @nogc pure nothrow const { 430 return cast(bool) clang_Cursor_isMacroFunctionLike(cx); 431 } 432 433 bool isMacroBuiltin() @safe @nogc pure nothrow const { 434 return cast(bool) clang_Cursor_isMacroBuiltin(cx); 435 } 436 437 Cursor specializedCursorTemplate() @safe pure nothrow const { 438 return Cursor(clang_getSpecializedCursorTemplate(cx)); 439 } 440 441 TranslationUnit translationUnit() @safe nothrow const { 442 return TranslationUnit(clang_Cursor_getTranslationUnit(cx)); 443 } 444 445 Token[] tokens() @safe nothrow const { 446 import std.algorithm: map; 447 import std.array: array; 448 449 CXToken* tokens; 450 uint numTokens; 451 452 auto tu = clang_Cursor_getTranslationUnit(cx); 453 454 () @trusted { clang_tokenize(tu, sourceRange.cx, &tokens, &numTokens); }(); 455 // I hope this only deallocates the array 456 scope(exit) clang_disposeTokens(tu, tokens, numTokens); 457 458 auto tokenSlice = () @trusted { return tokens[0 .. numTokens]; }(); 459 460 return tokenSlice.map!(a => Token(a, tu)).array; 461 } 462 463 alias templateParams = templateParameters; 464 465 const(Cursor)[] templateParameters() @safe nothrow const { 466 import std.algorithm: filter; 467 import std.array: array; 468 469 const amTemplate = 470 kind == Cursor.Kind.ClassTemplate 471 || kind == Cursor.Kind.TypeAliasTemplateDecl 472 || kind == Cursor.Kind.FunctionTemplate 473 ; 474 const templateCursor = amTemplate ? this : specializedCursorTemplate; 475 476 auto range = templateCursor 477 .children 478 .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter || 479 a.kind == Cursor.Kind.NonTypeTemplateParameter); 480 481 // Why is this @system? Who knows. 482 return () @trusted { return range.array; }(); 483 } 484 485 /** 486 If declared at file scope. 487 */ 488 bool isFileScope() @safe nothrow const { 489 return lexicalParent.kind == Cursor.Kind.TranslationUnit; 490 } 491 492 int numTemplateArguments() @safe @nogc pure nothrow const { 493 return clang_Cursor_getNumTemplateArguments(cx); 494 } 495 496 TemplateArgumentKind templateArgumentKind(int i) @safe @nogc pure nothrow const { 497 return cast(TemplateArgumentKind) clang_Cursor_getTemplateArgumentKind(cx, i); 498 } 499 500 Type templateArgumentType(int i) @safe pure nothrow const { 501 return Type(clang_Cursor_getTemplateArgumentType(cx, i)); 502 } 503 504 long templateArgumentValue(int i) @safe @nogc pure nothrow const { 505 return clang_Cursor_getTemplateArgumentValue(cx, i); 506 } 507 508 bool isVirtual() @safe @nogc pure nothrow const { 509 return cast(bool) clang_CXXMethod_isVirtual(cx); 510 } 511 512 bool isPureVirtual() @safe @nogc pure nothrow const { 513 return cast(bool) clang_CXXMethod_isPureVirtual(cx); 514 } 515 516 string displayName() @safe pure nothrow const { 517 return clang_getCursorDisplayName(cx).toString; 518 } 519 520 /** 521 For e.g. TypeRef or TemplateRef 522 */ 523 Cursor referencedCursor() @safe nothrow const { 524 return Cursor(clang_getCursorReferenced(cx)); 525 } 526 527 Cursor[] overriddenCursors() @trusted /* @safe with DIP1000 */ const { 528 import std.algorithm: map; 529 import std.array: array; 530 531 uint length; 532 CXCursor* cursors; 533 534 clang_getOverriddenCursors(cx, &cursors, &length); 535 scope(exit) clang_disposeOverriddenCursors(cursors); 536 537 return cursors[0 .. length].map!(a => Cursor(a)).array; 538 } 539 540 auto numOverloadedDecls() @safe @nogc pure nothrow const { 541 return clang_getNumOverloadedDecls(cx); 542 } 543 544 Cursor overloadedDecl(int i) @safe nothrow const { 545 return Cursor(clang_getOverloadedDecl(cx, cast(uint) i)); 546 } 547 548 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 549 return cast(bool) clang_equalCursors(cx, other.cx); 550 } 551 552 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 553 return cast(bool) clang_equalCursors(cx, other.cx); 554 } 555 556 void visitChildren(scope CursorVisitor visitor) @safe nothrow const { 557 scope clientData = ClientData(visitor); 558 // why isn't this @safe with dip10000??? 559 () @trusted { clang_visitChildren(cx, &cvisitor, &clientData); }(); 560 } 561 562 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 563 return opApplyN(block); 564 } 565 566 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 567 return opApplyN(block); 568 } 569 570 private int opApplyN(T)(scope T block) const { 571 import std.traits: Parameters; 572 573 int stop = 0; 574 575 enum numParams = Parameters!T.length; 576 577 visitChildren((cursor, parent) { 578 579 static if(numParams == 2) 580 stop = block(cursor, parent); 581 else static if(numParams == 1) 582 stop = block(cursor); 583 else 584 static assert(false); 585 586 return stop 587 ? ChildVisitResult.Break 588 : ChildVisitResult.Continue; 589 }); 590 591 return stop; 592 } 593 594 private string _spellingCreate() @safe pure nothrow const { 595 return clang_getCursorSpelling(cx).toString; 596 } 597 } 598 599 600 struct SourceRange { 601 602 import clang.util: Lazy; 603 604 CXSourceRange cx; 605 private SourceLocation _start; 606 private SourceLocation _end; 607 608 mixin Lazy!_start; 609 mixin Lazy!_end; 610 611 this(CXSourceRange cx) @safe @nogc pure nothrow { 612 this.cx = cx; 613 } 614 615 string path() @safe nothrow const { 616 return start.path; 617 } 618 619 string toString() @safe pure const { 620 import std.conv: text; 621 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 622 } 623 624 private auto _startCreate() @safe pure nothrow const { 625 return SourceLocation(clang_getRangeStart(cx)); 626 } 627 628 private auto _endCreate() @safe pure nothrow const { 629 return SourceLocation(clang_getRangeEnd(cx)); 630 } 631 } 632 633 634 struct SourceLocation { 635 636 CXSourceLocation cx; 637 string path; 638 uint line; 639 uint column; 640 uint offset; 641 642 this(CXSourceLocation cx) @safe pure nothrow { 643 this.cx = cx; 644 645 CXFile file; 646 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 647 this.path = clang_getFileName(file).toString; 648 649 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 650 } 651 652 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 653 if(path == other.path && line == other.line && column == other.column && 654 offset == other.offset) 655 return 0; 656 657 if(path < other.path) return -1; 658 if(path > other.path) return 1; 659 if(line < other.line) return -1; 660 if(line > other.line) return 1; 661 if(column < other.column) return -1; 662 if(column > other.column) return 1; 663 if(offset < other.offset) return -1; 664 if(offset > other.offset) return 1; 665 assert(false); 666 } 667 668 string toString() @safe pure nothrow const { 669 import std.conv: text; 670 return text(`"`, path, `" `, line, ":", column, ":", offset); 671 } 672 } 673 674 private struct ClientData { 675 /** 676 The D visitor delegate 677 */ 678 CursorVisitor dvisitor; 679 } 680 681 // This is the C function actually passed to libclang's clang_visitChildren 682 // The context (clientData) contains the D delegate that's then called on the 683 // (cursor, parent) pair 684 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 685 auto clientData = cast(ClientData*) clientData_; 686 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 687 } 688 689 690 struct Type { 691 692 import clang.util: Lazy; 693 694 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 695 696 CXType cx; 697 Kind kind; 698 private string _spelling; 699 700 mixin Lazy!_spelling; 701 702 this(CXType cx) @safe @nogc pure nothrow { 703 this.cx = cx; 704 this.kind = cast(Kind) cx.kind; 705 } 706 707 this(in Type other) @trusted pure nothrow { 708 import std.algorithm: map; 709 import std.array: array; 710 711 this.cx.kind = other.cx.kind; 712 this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array; 713 714 this(this.cx); 715 } 716 717 this(in Kind kind) @safe @nogc pure nothrow { 718 this(kind, ""); 719 } 720 721 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 722 this.kind = kind; 723 _spelling = spelling; 724 } 725 726 Type pointee() @safe pure nothrow const { 727 return Type(clang_getPointeeType(cx)); 728 } 729 730 Type unelaborate() @safe nothrow const { 731 return Type(clang_Type_getNamedType(cx)); 732 } 733 734 Type canonical() @safe pure nothrow const { 735 return Type(clang_getCanonicalType(cx)); 736 } 737 738 Type returnType() @safe pure const { 739 return Type(clang_getResultType(cx)); 740 } 741 742 // Returns a range of Type 743 auto paramTypes()() @safe pure const nothrow { 744 745 static struct Range { 746 const CXType cx; 747 const int numArgs; 748 int index = 0; 749 750 bool empty() { 751 return index < 0 || index >= numArgs; 752 } 753 754 void popFront() { 755 ++index; 756 } 757 758 Type front() { 759 return Type(clang_getArgType(cx, index)); 760 } 761 } 762 763 return Range(cx, clang_getNumArgTypes(cx)); 764 } 765 766 bool isVariadicFunction() @safe @nogc pure nothrow const { 767 return cast(bool) clang_isFunctionTypeVariadic(cx); 768 } 769 770 Type elementType() @safe pure nothrow const { 771 return Type(clang_getElementType(cx)); 772 } 773 774 long numElements() @safe @nogc pure nothrow const { 775 return clang_getNumElements(cx); 776 } 777 778 long arraySize() @safe @nogc pure nothrow const { 779 return clang_getArraySize(cx); 780 } 781 782 bool isConstQualified() @safe @nogc pure nothrow const scope { 783 return cast(bool) clang_isConstQualifiedType(cx); 784 } 785 786 bool isVolatileQualified() @safe @nogc pure nothrow const scope { 787 return cast(bool) clang_isVolatileQualifiedType(cx); 788 } 789 790 Cursor declaration() @safe pure nothrow const { 791 return Cursor(clang_getTypeDeclaration(cx)); 792 } 793 794 Type namedType() @safe pure nothrow const { 795 return Type(clang_Type_getNamedType(cx)); 796 } 797 798 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 799 return cast(bool) clang_equalTypes(cx, other.cx); 800 } 801 802 bool opEquals(in Type other) @safe @nogc pure nothrow const { 803 return cast(bool) clang_equalTypes(cx, other.cx); 804 } 805 806 bool isInvalid() @safe @nogc pure nothrow const { 807 return kind == Kind.Invalid; 808 } 809 810 long getSizeof() @safe @nogc pure nothrow const { 811 return clang_Type_getSizeOf(cx); 812 } 813 814 int numTemplateArguments() @safe @nogc pure nothrow const { 815 return clang_Type_getNumTemplateArguments(cx); 816 } 817 818 Type typeTemplateArgument(int i) @safe pure nothrow const { 819 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 820 } 821 822 string toString() @safe pure nothrow const { 823 import std.conv: text; 824 825 try { 826 return text("Type(", kind, `, "`, spelling, `")`); 827 } catch(Exception e) 828 assert(false, "Fatal error in Type.toString: " ~ e.msg); 829 } 830 831 private string _spellingCreate() @safe pure nothrow const { 832 return clang_getTypeSpelling(cx).toString; 833 } 834 } 835 836 837 struct Token { 838 839 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 840 841 Kind kind; 842 string spelling; 843 CXToken cxToken; 844 CXTranslationUnit cxTU; 845 846 this(CXToken cxToken, CXTranslationUnit cxTU) @safe pure nothrow { 847 this.cxToken = cxToken; 848 this.cxTU = cxTU; 849 this.kind = cast(Kind) clang_getTokenKind(cxToken); 850 this.spelling = .toString(clang_getTokenSpelling(cxTU, cxToken)); 851 } 852 853 this(Kind kind, string spelling) @safe @nogc pure nothrow { 854 this.kind = kind; 855 this.spelling = spelling; 856 } 857 858 string toString() @safe pure const { 859 import std.conv: text; 860 861 return text("Token(", kind, `, "`, spelling, `")`); 862 } 863 864 bool opEquals(in Token other) @safe pure nothrow const { 865 return kind == other.kind && spelling == other.spelling; 866 } 867 }