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 Type[] paramTypes() @safe pure const { 553 const numArgs = clang_getNumArgTypes(cx); 554 auto types = new Type[numArgs]; 555 556 foreach(i; 0 .. numArgs) { 557 types[i] = Type(clang_getArgType(cx, i)); 558 } 559 560 return types; 561 } 562 563 bool isVariadicFunction() @safe @nogc pure nothrow const { 564 return cast(bool) clang_isFunctionTypeVariadic(cx); 565 } 566 567 Type elementType() @safe pure nothrow const { 568 return Type(clang_getElementType(cx)); 569 } 570 571 long numElements() @safe @nogc pure nothrow const { 572 return clang_getNumElements(cx); 573 } 574 575 long arraySize() @safe @nogc pure nothrow const { 576 return clang_getArraySize(cx); 577 } 578 579 bool isConstQualified() @safe @nogc pure nothrow const { 580 return cast(bool) clang_isConstQualifiedType(cx); 581 } 582 583 bool isVolatileQualified() @safe @nogc pure nothrow const { 584 return cast(bool) clang_isVolatileQualifiedType(cx); 585 } 586 587 Cursor declaration() @safe pure nothrow const { 588 return Cursor(clang_getTypeDeclaration(cx)); 589 } 590 591 Type namedType() @safe pure nothrow const { 592 return Type(clang_Type_getNamedType(cx)); 593 } 594 595 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 596 return cast(bool) clang_equalTypes(cx, other.cx); 597 } 598 599 bool opEquals(in Type other) @safe @nogc pure nothrow const { 600 return cast(bool) clang_equalTypes(cx, other.cx); 601 } 602 603 bool isInvalid() @safe @nogc pure nothrow const { 604 return kind == Kind.Invalid; 605 } 606 607 long getSizeof() @safe @nogc pure nothrow const { 608 return clang_Type_getSizeOf(cx); 609 } 610 611 int numTemplateArguments() @safe @nogc pure nothrow const { 612 return clang_Type_getNumTemplateArguments(cx); 613 } 614 615 Type typeTemplateArgument(int i) @safe pure nothrow const { 616 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 617 } 618 619 string toString() @safe pure nothrow const { 620 import std.conv: text; 621 try { 622 const pointeeText = pointee.isInvalid ? "" : text(", ", pointee); 623 return text("Type(", kind, `, "`, spelling, pointeeText, `")`); 624 } catch(Exception e) 625 assert(false, "Fatal error in Type.toString: " ~ e.msg); 626 } 627 } 628 629 630 struct Token { 631 632 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 633 634 Kind kind; 635 string spelling; 636 CXToken cx; 637 TranslationUnit translationUnit; 638 639 this(CXToken cx, TranslationUnit unit) @safe pure nothrow { 640 this.cx = cx; 641 this.translationUnit = unit; 642 this.kind = cast(Kind) clang_getTokenKind(cx); 643 this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx)); 644 } 645 646 this(Kind kind, string spelling) @safe @nogc pure nothrow { 647 this.kind = kind; 648 this.spelling = spelling; 649 } 650 651 string toString() @safe pure const { 652 import std.conv: text; 653 654 return text("Token(", kind, `, "`, spelling, `")`); 655 } 656 657 bool opEquals(in Token other) @safe pure nothrow const { 658 return kind == other.kind && spelling == other.spelling; 659 } 660 }