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