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