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