1 module clang; 2 3 import clang.c.index; 4 import clang.c.util: EnumD; 5 6 mixin EnumD!("TranslationUnitFlags", CXTranslationUnit_Flags, "CXTranslationUnit_"); 7 mixin EnumD!("Language", CXLanguageKind, "CXLanguage_"); 8 9 TranslationUnit parse(in string fileName, 10 in TranslationUnitFlags translUnitflags = TranslationUnitFlags.None) 11 @safe 12 { 13 return parse(fileName, [], translUnitflags); 14 } 15 16 17 mixin EnumD!("ErrorCode", CXErrorCode, ""); 18 mixin EnumD!("DiagnosticSeverity", CXDiagnosticSeverity, "CXDiagnostic_"); 19 mixin EnumD!("TemplateArgumentKind", CXTemplateArgumentKind, "CXTemplateArgumentKind_"); 20 21 22 TranslationUnit parse(in string fileName, 23 in string[] commandLineArgs, 24 in TranslationUnitFlags translUnitflags = TranslationUnitFlags.None) 25 @safe 26 { 27 28 import std.string: toStringz; 29 import std.algorithm: map; 30 import std.array: array, join; 31 import std.conv: text; 32 33 // faux booleans 34 const excludeDeclarationsFromPCH = 0; 35 const displayDiagnostics = 0; 36 auto index = clang_createIndex(excludeDeclarationsFromPCH, displayDiagnostics); 37 CXUnsavedFile[] unsavedFiles; 38 const commandLineArgz = commandLineArgs 39 .map!(a => a.toStringz) 40 .array; 41 42 CXTranslationUnit cx; 43 const err = () @trusted { 44 return cast(ErrorCode)clang_parseTranslationUnit2( 45 index, 46 fileName.toStringz, 47 commandLineArgz.ptr, // .ptr since the length can be 0 48 cast(int)commandLineArgz.length, 49 unsavedFiles.ptr, // .ptr since the length can be 0 50 cast(uint)unsavedFiles.length, 51 translUnitflags, 52 &cx, 53 ); 54 }(); 55 56 if(err != ErrorCode.success) { 57 throw new Exception(text("Could not parse ", fileName, ": ", err)); 58 } 59 60 string[] errorMessages; 61 // throw if there are error diagnostics 62 foreach(i; 0 .. clang_getNumDiagnostics(cx)) { 63 auto diagnostic = clang_getDiagnostic(cx, i); 64 scope(exit) clang_disposeDiagnostic(diagnostic); 65 const severity = cast(DiagnosticSeverity) clang_getDiagnosticSeverity(diagnostic); 66 enum diagnosticOptions = CXDiagnostic_DisplaySourceLocation | CXDiagnostic_DisplayColumn; 67 if(severity == DiagnosticSeverity.Error || severity == DiagnosticSeverity.Fatal) 68 errorMessages ~= clang_formatDiagnostic(diagnostic, diagnosticOptions).toString; 69 } 70 71 if(errorMessages.length > 0) 72 throw new Exception(text("Error parsing '", fileName, "':\n", 73 errorMessages.join("\n"))); 74 75 76 return TranslationUnit(cx); 77 } 78 79 string[] systemPaths() @safe { 80 import std.process: execute; 81 import std.string: splitLines, stripLeft; 82 import std.algorithm: map, countUntil; 83 import std.array: array; 84 85 version(Windows) 86 { 87 enum devnull = "NUL"; 88 } else { 89 enum devnull = "/dev/null"; 90 } 91 92 const res = () { 93 try 94 { 95 return execute(["clang", "-v", "-xc++", devnull, "-fsyntax-only"], ["LANG": "C"]); 96 } 97 catch (Exception e) 98 { 99 import std.typecons : Tuple; 100 return Tuple!(int, "status", string, "output")(-1, e.msg); 101 } 102 }(); 103 if(res.status != 0) throw new Exception("Failed to call clang:\n" ~ res.output); 104 105 auto lines = res.output.splitLines; 106 107 const startIndex = lines.countUntil("#include <...> search starts here:") + 1; 108 assert(startIndex > 0); 109 const endIndex = lines.countUntil("End of search list."); 110 assert(endIndex > 0); 111 112 return lines[startIndex .. endIndex].map!stripLeft.array; 113 } 114 115 116 mixin EnumD!("ChildVisitResult", CXChildVisitResult, "CXChildVisit_"); 117 alias CursorVisitor = ChildVisitResult delegate(Cursor cursor, Cursor parent); 118 119 struct TranslationUnit { 120 121 CXTranslationUnit cx; 122 Cursor cursor; 123 124 this(CXTranslationUnit cx) @safe nothrow { 125 this.cx = cx; 126 this.cursor = Cursor(clang_getTranslationUnitCursor(cx)); 127 } 128 } 129 130 string toString(CXString cxString) @safe pure nothrow { 131 import std.conv: to; 132 auto cstr = clang_getCString(cxString); 133 scope(exit) clang_disposeString(cxString); 134 return () @trusted { return cstr.to!string; }(); 135 } 136 137 string[] toStrings(CXStringSet* strings) @trusted pure nothrow { 138 scope(exit) clang_disposeStringSet(strings); 139 string[] ret; 140 foreach(cxstr; strings.Strings[0 .. strings.Count]) 141 ret ~= cxstr.toString; 142 return ret; 143 } 144 145 mixin EnumD!("AccessSpecifier", CX_CXXAccessSpecifier, "CX_CXX"); 146 147 148 struct Cursor { 149 150 import std.traits: ReturnType; 151 152 mixin EnumD!("Kind", CXCursorKind, "CXCursor_"); 153 mixin EnumD!("StorageClass", CX_StorageClass, "CX_SC_"); 154 155 alias Hash = ReturnType!(clang_hashCursor); 156 157 CXCursor cx; 158 private Cursor[] _children; 159 Kind kind; 160 string spelling; 161 Type type; 162 Type underlyingType; 163 SourceRange sourceRange; 164 165 this(CXCursor cx) @safe pure nothrow { 166 this.cx = cx; 167 kind = cast(Kind) clang_getCursorKind(cx); 168 spelling = clang_getCursorSpelling(cx).toString; 169 type = Type(clang_getCursorType(cx)); 170 171 if(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl) 172 underlyingType = Type(clang_getTypedefDeclUnderlyingType(cx)); 173 174 sourceRange = SourceRange(clang_getCursorExtent(cx)); 175 } 176 177 private static extern(C) CXChildVisitResult ctorVisitor(CXCursor cursor, 178 CXCursor parent, 179 void* clientData_) 180 @safe nothrow 181 { 182 auto children = () @trusted { return cast(Cursor[]*) clientData_; }(); 183 *children ~= Cursor(cursor); 184 return CXChildVisit_Continue; 185 } 186 187 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 188 this(kind, spelling, Type()); 189 } 190 191 this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow { 192 this.kind = kind; 193 this.spelling = spelling; 194 this.type = type; 195 } 196 197 /// Lazily return the cursor's children 198 inout(Cursor)[] children() @safe @property nothrow inout { 199 if(_children.length) return _children; 200 201 inout(Cursor)[] ret; 202 // calling Cursor.visitChildren here would cause infinite recursion 203 // because cvisitor constructs a Cursor out of the parent 204 () @trusted { clang_visitChildren(cx, &ctorVisitor, &ret); }(); 205 return ret; 206 } 207 208 void children(Cursor[] cursors) @safe @property pure nothrow { 209 _children = cursors; 210 } 211 212 Type returnType() @safe pure nothrow const { 213 return Type(clang_getCursorResultType(cx)); 214 } 215 216 /** 217 For EnumConstantDecl cursors, return the numeric value 218 */ 219 auto enumConstantValue() @safe @nogc pure nothrow const { 220 assert(kind == Cursor.Kind.EnumConstantDecl); 221 return clang_getEnumConstantDeclValue(cx); 222 } 223 224 Language language() @safe @nogc pure nothrow const { 225 return cast(Language) clang_getCursorLanguage(cx); 226 } 227 228 Cursor canonical() @safe nothrow const { 229 return Cursor(clang_getCanonicalCursor(cx)); 230 } 231 232 /** 233 If this is the canonical cursor. Given forward declarations, there may 234 be several cursors for one entity. This returns true if this cursor 235 is the canonical one. 236 */ 237 bool isCanonical() @safe @nogc pure nothrow const { 238 return cast(bool) clang_equalCursors(cx, clang_getCanonicalCursor(cx)); 239 } 240 241 bool isDefinition() @safe @nogc pure nothrow const { 242 return cast(bool) clang_isCursorDefinition(cx); 243 } 244 245 bool isNull() @safe @nogc pure nothrow const { 246 return cast(bool) clang_Cursor_isNull(cx); 247 } 248 249 static Cursor nullCursor() @safe nothrow { 250 return Cursor(clang_getNullCursor()); 251 } 252 253 Cursor definition() @safe nothrow const { 254 return Cursor(clang_getCursorDefinition(cx)); 255 } 256 257 string toString() @safe pure nothrow const { 258 import std.conv: text; 259 try { 260 const returnTypeStr = kind == Kind.FunctionDecl 261 ? text(", ", returnType) 262 : ""; 263 264 return text("Cursor(", kind, `, "`, spelling, `", `, type, returnTypeStr, ")"); 265 } catch(Exception e) 266 assert(false, "Fatal error in Cursor.toString: " ~ e.msg); 267 } 268 269 bool isPredefined() @safe @nogc pure nothrow const { 270 // FIXME 271 return false; 272 } 273 274 Cursor semanticParent() @safe nothrow const { 275 return Cursor(clang_getCursorSemanticParent(cx)); 276 } 277 278 Cursor lexicalParent() @safe nothrow const { 279 return Cursor(clang_getCursorLexicalParent(cx)); 280 } 281 282 bool isInvalid() @safe @nogc pure nothrow const { 283 return cast(bool) clang_isInvalid(cx.kind); 284 } 285 286 auto hash() @safe @nogc pure nothrow const { 287 return clang_hashCursor(cx); 288 } 289 290 string mangling() @safe pure nothrow const { 291 return clang_Cursor_getMangling(cx).toString; 292 } 293 294 bool isAnonymous() @safe @nogc pure nothrow const { 295 return cast(bool) clang_Cursor_isAnonymous(cx); 296 } 297 298 bool isBitField() @safe @nogc pure nothrow const { 299 return cast(bool) clang_Cursor_isBitField(cx); 300 } 301 302 int bitWidth() @safe @nogc pure nothrow const { 303 return clang_getFieldDeclBitWidth(cx); 304 } 305 306 auto accessSpecifier() @safe @nogc pure nothrow const { 307 return cast(AccessSpecifier) clang_getCXXAccessSpecifier(cx); 308 } 309 310 StorageClass storageClass() @safe @nogc pure nothrow const { 311 return cast(StorageClass) clang_Cursor_getStorageClass(cx); 312 } 313 314 bool isConstCppMethod() @safe @nogc pure nothrow const { 315 return cast(bool) clang_CXXMethod_isConst(cx); 316 } 317 318 bool isMoveConstructor() @safe @nogc pure nothrow const { 319 return cast(bool) clang_CXXConstructor_isMoveConstructor(cx); 320 } 321 322 bool isCopyConstructor() @safe @nogc pure nothrow const { 323 return cast(bool) clang_CXXConstructor_isCopyConstructor(cx); 324 } 325 326 bool isMacroFunction() @safe @nogc pure nothrow const { 327 return cast(bool) clang_Cursor_isMacroFunctionLike(cx); 328 } 329 330 bool isMacroBuiltin() @safe @nogc pure nothrow const { 331 return cast(bool) clang_Cursor_isMacroBuiltin(cx); 332 } 333 334 Cursor specializedCursorTemplate() @safe pure nothrow const { 335 return Cursor(clang_getSpecializedCursorTemplate(cx)); 336 } 337 338 TranslationUnit translationUnit() @safe nothrow const { 339 return TranslationUnit(clang_Cursor_getTranslationUnit(cx)); 340 } 341 342 Token[] tokens() @safe nothrow const { 343 import std.algorithm: map; 344 import std.array: array; 345 346 CXToken* tokens; 347 uint numTokens; 348 349 () @trusted { clang_tokenize(translationUnit.cx, sourceRange.cx, &tokens, &numTokens); }(); 350 // I hope this only deallocates the array 351 scope(exit) clang_disposeTokens(translationUnit.cx, tokens, numTokens); 352 353 auto tokenSlice = () @trusted { return tokens[0 .. numTokens]; }(); 354 355 return tokenSlice.map!(a => Token(a, translationUnit)).array; 356 } 357 358 alias templateParams = templateParameters; 359 360 const(Cursor)[] templateParameters() @safe nothrow const { 361 import std.algorithm: filter; 362 import std.array: array; 363 364 const amTemplate = 365 kind == Cursor.Kind.ClassTemplate 366 || kind == Cursor.Kind.TypeAliasTemplateDecl 367 || kind == Cursor.Kind.FunctionTemplate 368 ; 369 const templateCursor = amTemplate ? this : specializedCursorTemplate; 370 371 auto range = templateCursor 372 .children 373 .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter || 374 a.kind == Cursor.Kind.NonTypeTemplateParameter); 375 376 // Why is this @system? Who knows. 377 return () @trusted { return range.array; }(); 378 } 379 380 /** 381 If declared at file scope. 382 */ 383 bool isFileScope() @safe nothrow const { 384 return lexicalParent.kind == Cursor.Kind.TranslationUnit; 385 } 386 387 int numTemplateArguments() @safe @nogc pure nothrow const { 388 return clang_Cursor_getNumTemplateArguments(cx); 389 } 390 391 TemplateArgumentKind templateArgumentKind(int i) @safe @nogc pure nothrow const { 392 return cast(TemplateArgumentKind) clang_Cursor_getTemplateArgumentKind(cx, i); 393 } 394 395 Type templateArgumentType(int i) @safe pure nothrow const { 396 return Type(clang_Cursor_getTemplateArgumentType(cx, i)); 397 } 398 399 long templateArgumentValue(int i) @safe @nogc pure nothrow const { 400 return clang_Cursor_getTemplateArgumentValue(cx, i); 401 } 402 403 bool isVirtual() @safe @nogc pure nothrow const { 404 return cast(bool) clang_CXXMethod_isVirtual(cx); 405 } 406 407 bool isPureVirtual() @safe @nogc pure nothrow const { 408 return cast(bool) clang_CXXMethod_isPureVirtual(cx); 409 } 410 411 string displayName() @safe pure nothrow const { 412 return clang_getCursorDisplayName(cx).toString; 413 } 414 415 /** 416 For e.g. TypeRef or TemplateRef 417 */ 418 Cursor referencedCursor() @safe nothrow const { 419 return Cursor(clang_getCursorReferenced(cx)); 420 } 421 422 Cursor[] overriddenCursors() @trusted /* @safe with DIP1000 */ const { 423 import std.algorithm: map; 424 import std.array: array; 425 426 uint length; 427 CXCursor* cursors; 428 429 clang_getOverriddenCursors(cx, &cursors, &length); 430 scope(exit) clang_disposeOverriddenCursors(cursors); 431 432 return cursors[0 .. length].map!(a => Cursor(a)).array; 433 } 434 435 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 436 return cast(bool) clang_equalCursors(cx, other.cx); 437 } 438 439 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 440 return cast(bool) clang_equalCursors(cx, other.cx); 441 } 442 443 void visitChildren(scope CursorVisitor visitor) @safe nothrow const { 444 scope clientData = ClientData(visitor); 445 // why isn't this @safe with dip10000??? 446 () @trusted { clang_visitChildren(cx, &cvisitor, &clientData); }(); 447 } 448 449 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 450 return opApplyN(block); 451 } 452 453 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 454 return opApplyN(block); 455 } 456 457 private int opApplyN(T)(scope T block) const { 458 import std.traits: Parameters; 459 460 int stop = 0; 461 462 enum numParams = Parameters!T.length; 463 464 visitChildren((cursor, parent) { 465 466 static if(numParams == 2) 467 stop = block(cursor, parent); 468 else static if(numParams == 1) 469 stop = block(cursor); 470 else 471 static assert(false); 472 473 return stop 474 ? ChildVisitResult.Break 475 : ChildVisitResult.Continue; 476 }); 477 478 return stop; 479 } 480 } 481 482 483 struct SourceRange { 484 485 CXSourceRange cx; 486 string path; 487 SourceLocation start; 488 SourceLocation end; 489 490 this(CXSourceRange cx) @safe pure nothrow { 491 this.cx = cx; 492 this.start = clang_getRangeStart(cx); 493 this.end = clang_getRangeEnd(cx); 494 this.path = start.path; 495 } 496 497 string toString() @safe pure const { 498 import std.conv: text; 499 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 500 } 501 } 502 503 struct SourceLocation { 504 CXSourceLocation cx; 505 string path; 506 uint line; 507 uint column; 508 uint offset; 509 510 this(CXSourceLocation cx) @safe pure nothrow { 511 this.cx = cx; 512 513 CXFile file; 514 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 515 this.path = clang_getFileName(file).toString; 516 517 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 518 } 519 520 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 521 if(path == other.path && line == other.line && column == other.column && 522 offset == other.offset) 523 return 0; 524 525 if(path < other.path) return -1; 526 if(path > other.path) return 1; 527 if(line < other.line) return -1; 528 if(line > other.line) return 1; 529 if(column < other.column) return -1; 530 if(column > other.column) return 1; 531 if(offset < other.offset) return -1; 532 if(offset > other.offset) return 1; 533 assert(false); 534 } 535 536 string toString() @safe pure nothrow const { 537 import std.conv: text; 538 return text(`"`, path, `" `, line, ":", column, ":", offset); 539 } 540 } 541 542 private struct ClientData { 543 /** 544 The D visitor delegate 545 */ 546 CursorVisitor dvisitor; 547 } 548 549 // This is the C function actually passed to libclang's clang_visitChildren 550 // The context (clientData) contains the D delegate that's then called on the 551 // (cursor, parent) pair 552 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 553 auto clientData = cast(ClientData*) clientData_; 554 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 555 } 556 557 558 struct Type { 559 560 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 561 562 CXType cx; 563 Kind kind; 564 string spelling; 565 566 this(CXType cx) @safe pure nothrow { 567 this.cx = cx; 568 this.kind = cast(Kind) cx.kind; 569 spelling = clang_getTypeSpelling(cx).toString; 570 } 571 572 this(in Type other) @trusted pure nothrow { 573 import std.algorithm: map; 574 import std.array: array; 575 576 this.cx.kind = other.cx.kind; 577 this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array; 578 579 this(this.cx); 580 } 581 582 this(in Kind kind) @safe @nogc pure nothrow { 583 this(kind, ""); 584 } 585 586 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 587 this.kind = kind; 588 this.spelling = spelling; 589 } 590 591 Type pointee() @safe pure nothrow const { 592 return Type(clang_getPointeeType(cx)); 593 } 594 595 Type unelaborate() @safe nothrow const { 596 return Type(clang_Type_getNamedType(cx)); 597 } 598 599 Type canonical() @safe pure nothrow const { 600 return Type(clang_getCanonicalType(cx)); 601 } 602 603 Type returnType() @safe pure const { 604 return Type(clang_getResultType(cx)); 605 } 606 607 // Returns a range of Type 608 auto paramTypes()() @safe pure const nothrow { 609 610 static struct Range { 611 const CXType cx; 612 const int numArgs; 613 int index = 0; 614 615 bool empty() { 616 return index < 0 || index >= numArgs; 617 } 618 619 void popFront() { 620 ++index; 621 } 622 623 Type front() { 624 return Type(clang_getArgType(cx, index)); 625 } 626 } 627 628 return Range(cx, clang_getNumArgTypes(cx)); 629 } 630 631 bool isVariadicFunction() @safe @nogc pure nothrow const { 632 return cast(bool) clang_isFunctionTypeVariadic(cx); 633 } 634 635 Type elementType() @safe pure nothrow const { 636 return Type(clang_getElementType(cx)); 637 } 638 639 long numElements() @safe @nogc pure nothrow const { 640 return clang_getNumElements(cx); 641 } 642 643 long arraySize() @safe @nogc pure nothrow const { 644 return clang_getArraySize(cx); 645 } 646 647 bool isConstQualified() @safe @nogc pure nothrow const { 648 return cast(bool) clang_isConstQualifiedType(cx); 649 } 650 651 bool isVolatileQualified() @safe @nogc pure nothrow const { 652 return cast(bool) clang_isVolatileQualifiedType(cx); 653 } 654 655 Cursor declaration() @safe pure nothrow const { 656 return Cursor(clang_getTypeDeclaration(cx)); 657 } 658 659 Type namedType() @safe pure nothrow const { 660 return Type(clang_Type_getNamedType(cx)); 661 } 662 663 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 664 return cast(bool) clang_equalTypes(cx, other.cx); 665 } 666 667 bool opEquals(in Type other) @safe @nogc pure nothrow const { 668 return cast(bool) clang_equalTypes(cx, other.cx); 669 } 670 671 bool isInvalid() @safe @nogc pure nothrow const { 672 return kind == Kind.Invalid; 673 } 674 675 long getSizeof() @safe @nogc pure nothrow const { 676 return clang_Type_getSizeOf(cx); 677 } 678 679 int numTemplateArguments() @safe @nogc pure nothrow const { 680 return clang_Type_getNumTemplateArguments(cx); 681 } 682 683 Type typeTemplateArgument(int i) @safe pure nothrow const { 684 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 685 } 686 687 string toString() @safe pure nothrow const { 688 import std.conv: text; 689 690 try { 691 return text("Type(", kind, `, "`, spelling, `")`); 692 } catch(Exception e) 693 assert(false, "Fatal error in Type.toString: " ~ e.msg); 694 } 695 } 696 697 698 struct Token { 699 700 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 701 702 Kind kind; 703 string spelling; 704 CXToken cx; 705 TranslationUnit translationUnit; 706 707 this(CXToken cx, TranslationUnit unit) @safe pure nothrow { 708 this.cx = cx; 709 this.translationUnit = unit; 710 this.kind = cast(Kind) clang_getTokenKind(cx); 711 this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx)); 712 } 713 714 this(Kind kind, string spelling) @safe @nogc pure nothrow { 715 this.kind = kind; 716 this.spelling = spelling; 717 } 718 719 string toString() @safe pure const { 720 import std.conv: text; 721 722 return text("Token(", kind, `, "`, spelling, `")`); 723 } 724 725 bool opEquals(in Token other) @safe pure nothrow const { 726 return kind == other.kind && spelling == other.spelling; 727 } 728 }