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