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 auto numOverloadedDecls() @safe @nogc pure nothrow const { 436 return clang_getNumOverloadedDecls(cx); 437 } 438 439 Cursor overloadedDecl(int i) @safe nothrow const { 440 return Cursor(clang_getOverloadedDecl(cx, cast(uint) i)); 441 } 442 443 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 444 return cast(bool) clang_equalCursors(cx, other.cx); 445 } 446 447 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 448 return cast(bool) clang_equalCursors(cx, other.cx); 449 } 450 451 void visitChildren(scope CursorVisitor visitor) @safe nothrow const { 452 scope clientData = ClientData(visitor); 453 // why isn't this @safe with dip10000??? 454 () @trusted { clang_visitChildren(cx, &cvisitor, &clientData); }(); 455 } 456 457 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 458 return opApplyN(block); 459 } 460 461 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 462 return opApplyN(block); 463 } 464 465 private int opApplyN(T)(scope T block) const { 466 import std.traits: Parameters; 467 468 int stop = 0; 469 470 enum numParams = Parameters!T.length; 471 472 visitChildren((cursor, parent) { 473 474 static if(numParams == 2) 475 stop = block(cursor, parent); 476 else static if(numParams == 1) 477 stop = block(cursor); 478 else 479 static assert(false); 480 481 return stop 482 ? ChildVisitResult.Break 483 : ChildVisitResult.Continue; 484 }); 485 486 return stop; 487 } 488 } 489 490 491 struct SourceRange { 492 493 CXSourceRange cx; 494 string path; 495 SourceLocation start; 496 SourceLocation end; 497 498 this(CXSourceRange cx) @safe pure nothrow { 499 this.cx = cx; 500 this.start = clang_getRangeStart(cx); 501 this.end = clang_getRangeEnd(cx); 502 this.path = start.path; 503 } 504 505 string toString() @safe pure const { 506 import std.conv: text; 507 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 508 } 509 } 510 511 struct SourceLocation { 512 CXSourceLocation cx; 513 string path; 514 uint line; 515 uint column; 516 uint offset; 517 518 this(CXSourceLocation cx) @safe pure nothrow { 519 this.cx = cx; 520 521 CXFile file; 522 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 523 this.path = clang_getFileName(file).toString; 524 525 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 526 } 527 528 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 529 if(path == other.path && line == other.line && column == other.column && 530 offset == other.offset) 531 return 0; 532 533 if(path < other.path) return -1; 534 if(path > other.path) return 1; 535 if(line < other.line) return -1; 536 if(line > other.line) return 1; 537 if(column < other.column) return -1; 538 if(column > other.column) return 1; 539 if(offset < other.offset) return -1; 540 if(offset > other.offset) return 1; 541 assert(false); 542 } 543 544 string toString() @safe pure nothrow const { 545 import std.conv: text; 546 return text(`"`, path, `" `, line, ":", column, ":", offset); 547 } 548 } 549 550 private struct ClientData { 551 /** 552 The D visitor delegate 553 */ 554 CursorVisitor dvisitor; 555 } 556 557 // This is the C function actually passed to libclang's clang_visitChildren 558 // The context (clientData) contains the D delegate that's then called on the 559 // (cursor, parent) pair 560 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 561 auto clientData = cast(ClientData*) clientData_; 562 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 563 } 564 565 566 struct Type { 567 568 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 569 570 CXType cx; 571 Kind kind; 572 string spelling; 573 574 this(CXType cx) @safe pure nothrow { 575 this.cx = cx; 576 this.kind = cast(Kind) cx.kind; 577 spelling = clang_getTypeSpelling(cx).toString; 578 } 579 580 this(in Type other) @trusted pure nothrow { 581 import std.algorithm: map; 582 import std.array: array; 583 584 this.cx.kind = other.cx.kind; 585 this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array; 586 587 this(this.cx); 588 } 589 590 this(in Kind kind) @safe @nogc pure nothrow { 591 this(kind, ""); 592 } 593 594 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 595 this.kind = kind; 596 this.spelling = spelling; 597 } 598 599 Type pointee() @safe pure nothrow const { 600 return Type(clang_getPointeeType(cx)); 601 } 602 603 Type unelaborate() @safe nothrow const { 604 return Type(clang_Type_getNamedType(cx)); 605 } 606 607 Type canonical() @safe pure nothrow const { 608 return Type(clang_getCanonicalType(cx)); 609 } 610 611 Type returnType() @safe pure const { 612 return Type(clang_getResultType(cx)); 613 } 614 615 // Returns a range of Type 616 auto paramTypes()() @safe pure const nothrow { 617 618 static struct Range { 619 const CXType cx; 620 const int numArgs; 621 int index = 0; 622 623 bool empty() { 624 return index < 0 || index >= numArgs; 625 } 626 627 void popFront() { 628 ++index; 629 } 630 631 Type front() { 632 return Type(clang_getArgType(cx, index)); 633 } 634 } 635 636 return Range(cx, clang_getNumArgTypes(cx)); 637 } 638 639 bool isVariadicFunction() @safe @nogc pure nothrow const { 640 return cast(bool) clang_isFunctionTypeVariadic(cx); 641 } 642 643 Type elementType() @safe pure nothrow const { 644 return Type(clang_getElementType(cx)); 645 } 646 647 long numElements() @safe @nogc pure nothrow const { 648 return clang_getNumElements(cx); 649 } 650 651 long arraySize() @safe @nogc pure nothrow const { 652 return clang_getArraySize(cx); 653 } 654 655 bool isConstQualified() @safe @nogc pure nothrow const { 656 return cast(bool) clang_isConstQualifiedType(cx); 657 } 658 659 bool isVolatileQualified() @safe @nogc pure nothrow const { 660 return cast(bool) clang_isVolatileQualifiedType(cx); 661 } 662 663 Cursor declaration() @safe pure nothrow const { 664 return Cursor(clang_getTypeDeclaration(cx)); 665 } 666 667 Type namedType() @safe pure nothrow const { 668 return Type(clang_Type_getNamedType(cx)); 669 } 670 671 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 672 return cast(bool) clang_equalTypes(cx, other.cx); 673 } 674 675 bool opEquals(in Type other) @safe @nogc pure nothrow const { 676 return cast(bool) clang_equalTypes(cx, other.cx); 677 } 678 679 bool isInvalid() @safe @nogc pure nothrow const { 680 return kind == Kind.Invalid; 681 } 682 683 long getSizeof() @safe @nogc pure nothrow const { 684 return clang_Type_getSizeOf(cx); 685 } 686 687 int numTemplateArguments() @safe @nogc pure nothrow const { 688 return clang_Type_getNumTemplateArguments(cx); 689 } 690 691 Type typeTemplateArgument(int i) @safe pure nothrow const { 692 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 693 } 694 695 string toString() @safe pure nothrow const { 696 import std.conv: text; 697 698 try { 699 return text("Type(", kind, `, "`, spelling, `")`); 700 } catch(Exception e) 701 assert(false, "Fatal error in Type.toString: " ~ e.msg); 702 } 703 } 704 705 706 struct Token { 707 708 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 709 710 Kind kind; 711 string spelling; 712 CXToken cx; 713 TranslationUnit translationUnit; 714 715 this(CXToken cx, TranslationUnit unit) @safe pure nothrow { 716 this.cx = cx; 717 this.translationUnit = unit; 718 this.kind = cast(Kind) clang_getTokenKind(cx); 719 this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx)); 720 } 721 722 this(Kind kind, string spelling) @safe @nogc pure nothrow { 723 this.kind = kind; 724 this.spelling = spelling; 725 } 726 727 string toString() @safe pure const { 728 import std.conv: text; 729 730 return text("Token(", kind, `, "`, spelling, `")`); 731 } 732 733 bool opEquals(in Token other) @safe pure nothrow const { 734 return kind == other.kind && spelling == other.spelling; 735 } 736 }