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