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