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