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