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 templateCursor = kind == Cursor.Kind.ClassTemplate 347 ? this 348 : specializedCursorTemplate; 349 350 auto range = templateCursor 351 .children 352 .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter || a.kind == Cursor.Kind.NonTypeTemplateParameter); 353 354 // Why is this @system? Who knows. 355 return () @trusted { return range.array; }(); 356 } 357 358 /** 359 If declared at file scope. 360 */ 361 bool isFileScope() @safe nothrow const { 362 return lexicalParent.kind == Cursor.Kind.TranslationUnit; 363 } 364 365 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 366 return cast(bool) clang_equalCursors(cx, other.cx); 367 } 368 369 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 370 return cast(bool) clang_equalCursors(cx, other.cx); 371 } 372 373 void visitChildren(CursorVisitor visitor) @safe nothrow const { 374 clang_visitChildren(cx, &cvisitor, new ClientData(visitor)); 375 } 376 377 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 378 return opApplyN(block); 379 } 380 381 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 382 return opApplyN(block); 383 } 384 385 private int opApplyN(T...)(int delegate(T args) @safe block) const { 386 int stop = 0; 387 388 visitChildren((cursor, parent) { 389 390 static if(T.length == 2) 391 stop = block(cursor, parent); 392 else static if(T.length == 1) 393 stop = block(cursor); 394 else 395 static assert(false); 396 397 return stop 398 ? ChildVisitResult.Break 399 : ChildVisitResult.Continue; 400 }); 401 402 return stop; 403 } 404 } 405 406 407 struct SourceRange { 408 409 CXSourceRange cx; 410 string path; 411 SourceLocation start; 412 SourceLocation end; 413 414 this(CXSourceRange cx) @safe pure nothrow { 415 this.cx = cx; 416 this.start = clang_getRangeStart(cx); 417 this.end = clang_getRangeEnd(cx); 418 this.path = start.path; 419 } 420 421 string toString() @safe pure const { 422 import std.conv: text; 423 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 424 } 425 } 426 427 struct SourceLocation { 428 CXSourceLocation cx; 429 string path; 430 uint line; 431 uint column; 432 uint offset; 433 434 this(CXSourceLocation cx) @safe pure nothrow { 435 this.cx = cx; 436 437 CXFile file; 438 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 439 this.path = clang_getFileName(file).toString; 440 441 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 442 } 443 444 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 445 if(path == other.path && line == other.line && column == other.column && 446 offset == other.offset) 447 return 0; 448 449 if(path < other.path) return -1; 450 if(path > other.path) return 1; 451 if(line < other.line) return -1; 452 if(line > other.line) return 1; 453 if(column < other.column) return -1; 454 if(column > other.column) return 1; 455 if(offset < other.offset) return -1; 456 if(offset > other.offset) return 1; 457 assert(false); 458 } 459 460 string toString() @safe pure nothrow const { 461 import std.conv: text; 462 return text(`"`, path, `" `, line, ":", column, ":", offset); 463 } 464 } 465 466 private struct ClientData { 467 /** 468 The D visitor delegate 469 */ 470 CursorVisitor dvisitor; 471 } 472 473 // This is the C function actually passed to libclang's clang_visitChildren 474 // The context (clientData) contains the D delegate that's then called on the 475 // (cursor, parent) pair 476 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 477 auto clientData = cast(ClientData*) clientData_; 478 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 479 } 480 481 482 struct Type { 483 484 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 485 486 CXType cx; 487 Kind kind; 488 string spelling; 489 490 this(CXType cx) @safe pure nothrow { 491 this.cx = cx; 492 this.kind = cast(Kind) cx.kind; 493 spelling = clang_getTypeSpelling(cx).toString; 494 } 495 496 this(in Type other) @trusted pure nothrow { 497 import std.algorithm: map; 498 import std.array: array; 499 500 this.cx.kind = other.cx.kind; 501 this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array; 502 503 this(this.cx); 504 } 505 506 this(in Kind kind) @safe @nogc pure nothrow { 507 this(kind, ""); 508 } 509 510 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 511 this.kind = kind; 512 this.spelling = spelling; 513 } 514 515 Type pointee() @safe pure nothrow const { 516 return Type(clang_getPointeeType(cx)); 517 } 518 519 Type unelaborate() @safe nothrow const { 520 return Type(clang_Type_getNamedType(cx)); 521 } 522 523 Type canonical() @safe pure nothrow const { 524 return Type(clang_getCanonicalType(cx)); 525 } 526 527 Type returnType() @safe pure const { 528 return Type(clang_getResultType(cx)); 529 } 530 531 Type[] paramTypes() @safe pure const { 532 const numArgs = clang_getNumArgTypes(cx); 533 auto types = new Type[numArgs]; 534 535 foreach(i; 0 .. numArgs) { 536 types[i] = Type(clang_getArgType(cx, i)); 537 } 538 539 return types; 540 } 541 542 bool isVariadicFunction() @safe @nogc pure nothrow const { 543 return cast(bool) clang_isFunctionTypeVariadic(cx); 544 } 545 546 Type elementType() @safe pure nothrow const { 547 return Type(clang_getElementType(cx)); 548 } 549 550 long numElements() @safe @nogc pure nothrow const { 551 return clang_getNumElements(cx); 552 } 553 554 long arraySize() @safe @nogc pure nothrow const { 555 return clang_getArraySize(cx); 556 } 557 558 bool isConstQualified() @safe @nogc pure nothrow const { 559 return cast(bool) clang_isConstQualifiedType(cx); 560 } 561 562 bool isVolatileQualified() @safe @nogc pure nothrow const { 563 return cast(bool) clang_isVolatileQualifiedType(cx); 564 } 565 566 Cursor declaration() @safe pure nothrow const { 567 return Cursor(clang_getTypeDeclaration(cx)); 568 } 569 570 Type namedType() @safe pure nothrow const { 571 return Type(clang_Type_getNamedType(cx)); 572 } 573 574 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 575 return cast(bool) clang_equalTypes(cx, other.cx); 576 } 577 578 bool opEquals(in Type other) @safe @nogc pure nothrow const { 579 return cast(bool) clang_equalTypes(cx, other.cx); 580 } 581 582 bool isInvalid() @safe @nogc pure nothrow const { 583 return kind == Kind.Invalid; 584 } 585 586 long getSizeof() @safe @nogc pure nothrow const { 587 return clang_Type_getSizeOf(cx); 588 } 589 590 int numTemplateArguments() @safe @nogc pure nothrow const { 591 return clang_Type_getNumTemplateArguments(cx); 592 } 593 594 Type typeTemplateArgument(int i) @safe pure nothrow const { 595 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 596 } 597 598 string toString() @safe pure nothrow const { 599 import std.conv: text; 600 try { 601 const pointeeText = pointee.isInvalid ? "" : text(", ", pointee); 602 return text("Type(", kind, `, "`, spelling, pointeeText, `")`); 603 } catch(Exception e) 604 assert(false, "Fatal error in Type.toString: " ~ e.msg); 605 } 606 } 607 608 609 struct Token { 610 611 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 612 613 Kind kind; 614 string spelling; 615 CXToken cx; 616 TranslationUnit translationUnit; 617 618 this(CXToken cx, TranslationUnit unit) @safe pure nothrow { 619 this.cx = cx; 620 this.translationUnit = unit; 621 this.kind = cast(Kind) clang_getTokenKind(cx); 622 this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx)); 623 } 624 625 this(Kind kind, string spelling) @safe @nogc pure nothrow { 626 this.kind = kind; 627 this.spelling = spelling; 628 } 629 630 string toString() @safe pure const { 631 import std.conv: text; 632 633 return text("Token(", kind, `, "`, spelling, `")`); 634 } 635 636 bool opEquals(in Token other) @safe pure nothrow const { 637 return kind == other.kind && spelling == other.spelling; 638 } 639 }