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