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 SourceRange sourceRange; 145 146 this(CXCursor cx) @safe pure nothrow { 147 this.cx = cx; 148 kind = cast(Kind) clang_getCursorKind(cx); 149 spelling = clang_getCursorSpelling(cx).toString; 150 type = Type(clang_getCursorType(cx)); 151 sourceRange = SourceRange(clang_getCursorExtent(cx)); 152 } 153 154 private static extern(C) CXChildVisitResult ctorVisitor(CXCursor cursor, 155 CXCursor parent, 156 void* clientData_) 157 @safe nothrow 158 { 159 auto children = () @trusted { return cast(Cursor[]*) clientData_; }(); 160 *children ~= Cursor(cursor); 161 return CXChildVisit_Continue; 162 } 163 164 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 165 this(kind, spelling, Type()); 166 } 167 168 this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow { 169 this.kind = kind; 170 this.spelling = spelling; 171 this.type = type; 172 } 173 174 /// Lazily return the cursor's children 175 inout(Cursor)[] children() @safe @property nothrow inout { 176 if(_children.length) return _children; 177 178 inout(Cursor)[] ret; 179 // calling Cursor.visitChildren here would cause infinite recursion 180 // because cvisitor constructs a Cursor out of the parent 181 () @trusted { clang_visitChildren(cx, &ctorVisitor, &ret); }(); 182 return ret; 183 } 184 185 void children(Cursor[] cursors) @safe @property pure nothrow { 186 _children = cursors; 187 } 188 189 Type returnType() @safe pure nothrow const { 190 return Type(clang_getCursorResultType(cx)); 191 } 192 193 /** 194 For TypedefDecl cursors, return the underlying type 195 */ 196 Type underlyingType() @safe pure nothrow const { 197 assert(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl, 198 "Not a TypedefDecl cursor"); 199 return Type(clang_getTypedefDeclUnderlyingType(cx)); 200 } 201 202 /** 203 For EnumConstantDecl cursors, return the numeric value 204 */ 205 auto enumConstantValue() @safe @nogc pure nothrow const { 206 assert(kind == Cursor.Kind.EnumConstantDecl); 207 return clang_getEnumConstantDeclValue(cx); 208 } 209 210 Language language() @safe @nogc pure nothrow const { 211 return cast(Language) clang_getCursorLanguage(cx); 212 } 213 214 Cursor canonical() @safe nothrow const { 215 return Cursor(clang_getCanonicalCursor(cx)); 216 } 217 218 /** 219 If this is the canonical cursor. Given forward declarations, there may 220 be several cursors for one entity. This returns true if this cursor 221 is the canonical one. 222 */ 223 bool isCanonical() @safe @nogc pure nothrow const { 224 return cast(bool) clang_equalCursors(cx, clang_getCanonicalCursor(cx)); 225 } 226 227 bool isDefinition() @safe @nogc pure nothrow const { 228 return cast(bool) clang_isCursorDefinition(cx); 229 } 230 231 bool isNull() @safe @nogc pure nothrow const { 232 return cast(bool) clang_Cursor_isNull(cx); 233 } 234 235 static Cursor nullCursor() @safe nothrow { 236 return Cursor(clang_getNullCursor()); 237 } 238 239 Cursor definition() @safe nothrow const { 240 return Cursor(clang_getCursorDefinition(cx)); 241 } 242 243 string toString() @safe pure nothrow const { 244 import std.conv: text; 245 try { 246 const returnTypeStr = kind == Kind.FunctionDecl 247 ? text(", ", returnType) 248 : ""; 249 250 return text("Cursor(", kind, `, "`, spelling, `", `, type, returnTypeStr, ")"); 251 } catch(Exception e) 252 assert(false, "Fatal error in Cursor.toString: " ~ e.msg); 253 } 254 255 bool isPredefined() @safe @nogc pure nothrow const { 256 // FIXME 257 return false; 258 } 259 260 Cursor semanticParent() @safe nothrow const { 261 return Cursor(clang_getCursorSemanticParent(cx)); 262 } 263 264 Cursor lexicalParent() @safe nothrow const { 265 return Cursor(clang_getCursorLexicalParent(cx)); 266 } 267 268 bool isInvalid() @safe @nogc pure nothrow const { 269 return cast(bool) clang_isInvalid(cx.kind); 270 } 271 272 auto hash() @safe @nogc pure nothrow const { 273 return clang_hashCursor(cx); 274 } 275 276 string mangling() @safe pure nothrow const { 277 return clang_Cursor_getMangling(cx).toString; 278 } 279 280 bool isAnonymous() @safe @nogc pure nothrow const { 281 return cast(bool) clang_Cursor_isAnonymous(cx); 282 } 283 284 bool isBitField() @safe @nogc pure nothrow const { 285 return cast(bool) clang_Cursor_isBitField(cx); 286 } 287 288 int bitWidth() @safe @nogc pure nothrow const { 289 return clang_getFieldDeclBitWidth(cx); 290 } 291 292 auto accessSpecifier() @safe @nogc pure nothrow const { 293 return cast(AccessSpecifier) clang_getCXXAccessSpecifier(cx); 294 } 295 296 StorageClass storageClass() @safe @nogc pure nothrow const { 297 return cast(StorageClass) clang_Cursor_getStorageClass(cx); 298 } 299 300 bool isConstCppMethod() @safe @nogc pure nothrow const { 301 return cast(bool) clang_CXXMethod_isConst(cx); 302 } 303 304 bool isMoveConstructor() @safe @nogc pure nothrow const { 305 return cast(bool) clang_CXXConstructor_isMoveConstructor(cx); 306 } 307 308 bool isCopyConstructor() @safe @nogc pure nothrow const { 309 return cast(bool) clang_CXXConstructor_isCopyConstructor(cx); 310 } 311 312 bool isMacroFunction() @safe @nogc pure nothrow const { 313 return cast(bool) clang_Cursor_isMacroFunctionLike(cx); 314 } 315 316 Cursor specializedCursorTemplate() @safe 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 const(Cursor)[] templateParams() @safe nothrow const { 341 import std.algorithm: filter; 342 import std.array: array; 343 344 const templateCursor = kind == Cursor.Kind.ClassTemplate 345 ? this 346 : specializedCursorTemplate; 347 348 auto range = templateCursor 349 .children 350 .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter || a.kind == Cursor.Kind.NonTypeTemplateParameter); 351 352 // Why is this @system? Who knows. 353 return () @trusted { return range.array; }(); 354 } 355 356 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 357 return cast(bool) clang_equalCursors(cx, other.cx); 358 } 359 360 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 361 return cast(bool) clang_equalCursors(cx, other.cx); 362 } 363 364 void visitChildren(CursorVisitor visitor) @safe nothrow const { 365 clang_visitChildren(cx, &cvisitor, new ClientData(visitor)); 366 } 367 368 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 369 return opApplyN(block); 370 } 371 372 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 373 return opApplyN(block); 374 } 375 376 private int opApplyN(T...)(int delegate(T args) @safe block) const { 377 int stop = 0; 378 379 visitChildren((cursor, parent) { 380 381 static if(T.length == 2) 382 stop = block(cursor, parent); 383 else static if(T.length == 1) 384 stop = block(cursor); 385 else 386 static assert(false); 387 388 return stop 389 ? ChildVisitResult.Break 390 : ChildVisitResult.Continue; 391 }); 392 393 return stop; 394 } 395 } 396 397 398 struct SourceRange { 399 400 CXSourceRange cx; 401 string path; 402 SourceLocation start; 403 SourceLocation end; 404 405 this(CXSourceRange cx) @safe pure nothrow { 406 this.cx = cx; 407 this.start = clang_getRangeStart(cx); 408 this.end = clang_getRangeEnd(cx); 409 this.path = start.path; 410 } 411 412 string toString() @safe pure const { 413 import std.conv: text; 414 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 415 } 416 } 417 418 struct SourceLocation { 419 CXSourceLocation cx; 420 string path; 421 uint line; 422 uint column; 423 uint offset; 424 425 this(CXSourceLocation cx) @safe pure nothrow { 426 this.cx = cx; 427 428 CXFile file; 429 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 430 this.path = clang_getFileName(file).toString; 431 432 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 433 } 434 435 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 436 if(path == other.path && line == other.line && column == other.column && 437 offset == other.offset) 438 return 0; 439 440 if(path < other.path) return -1; 441 if(path > other.path) return 1; 442 if(line < other.line) return -1; 443 if(line > other.line) return 1; 444 if(column < other.column) return -1; 445 if(column > other.column) return 1; 446 if(offset < other.offset) return -1; 447 if(offset > other.offset) return 1; 448 assert(false); 449 } 450 451 string toString() @safe pure nothrow const { 452 import std.conv: text; 453 return text(`"`, path, `" `, line, ":", column, ":", offset); 454 } 455 } 456 457 private struct ClientData { 458 /** 459 The D visitor delegate 460 */ 461 CursorVisitor dvisitor; 462 } 463 464 // This is the C function actually passed to libclang's clang_visitChildren 465 // The context (clientData) contains the D delegate that's then called on the 466 // (cursor, parent) pair 467 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 468 auto clientData = cast(ClientData*) clientData_; 469 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 470 } 471 472 473 struct Type { 474 475 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 476 477 CXType cx; 478 Kind kind; 479 string spelling; 480 481 this(CXType cx) @safe pure nothrow { 482 this.cx = cx; 483 this.kind = cast(Kind) cx.kind; 484 spelling = clang_getTypeSpelling(cx).toString; 485 } 486 487 this(in Type other) @trusted pure nothrow { 488 import std.algorithm: map; 489 import std.array: array; 490 491 this.cx.kind = other.cx.kind; 492 this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array; 493 494 this(this.cx); 495 } 496 497 this(in Kind kind) @safe @nogc pure nothrow { 498 this(kind, ""); 499 } 500 501 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 502 this.kind = kind; 503 this.spelling = spelling; 504 } 505 506 Type pointee() @safe pure nothrow const { 507 return Type(clang_getPointeeType(cx)); 508 } 509 510 Type unelaborate() @safe nothrow const { 511 return Type(clang_Type_getNamedType(cx)); 512 } 513 514 Type canonical() @safe pure nothrow const { 515 return Type(clang_getCanonicalType(cx)); 516 } 517 518 Type returnType() @safe pure const { 519 return Type(clang_getResultType(cx)); 520 } 521 522 Type[] paramTypes() @safe pure const { 523 const numArgs = clang_getNumArgTypes(cx); 524 auto types = new Type[numArgs]; 525 526 foreach(i; 0 .. numArgs) { 527 types[i] = Type(clang_getArgType(cx, i)); 528 } 529 530 return types; 531 } 532 533 bool isVariadicFunction() @safe @nogc pure nothrow const { 534 return cast(bool) clang_isFunctionTypeVariadic(cx); 535 } 536 537 Type elementType() @safe pure nothrow const { 538 return Type(clang_getElementType(cx)); 539 } 540 541 long numElements() @safe @nogc pure nothrow const { 542 return clang_getNumElements(cx); 543 } 544 545 long arraySize() @safe @nogc pure nothrow const { 546 return clang_getArraySize(cx); 547 } 548 549 bool isConstQualified() @safe @nogc pure nothrow const { 550 return cast(bool) clang_isConstQualifiedType(cx); 551 } 552 553 bool isVolatileQualified() @safe @nogc pure nothrow const { 554 return cast(bool) clang_isVolatileQualifiedType(cx); 555 } 556 557 Cursor declaration() @safe pure nothrow const { 558 return Cursor(clang_getTypeDeclaration(cx)); 559 } 560 561 Type namedType() @safe pure nothrow const { 562 return Type(clang_Type_getNamedType(cx)); 563 } 564 565 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 566 return cast(bool) clang_equalTypes(cx, other.cx); 567 } 568 569 bool opEquals(in Type other) @safe @nogc pure nothrow const { 570 return cast(bool) clang_equalTypes(cx, other.cx); 571 } 572 573 bool isInvalid() @safe @nogc pure nothrow const { 574 return kind == Kind.Invalid; 575 } 576 577 long getSizeof() @safe @nogc pure nothrow const { 578 return clang_Type_getSizeOf(cx); 579 } 580 581 int numTemplateArguments() @safe @nogc pure nothrow const { 582 return clang_Type_getNumTemplateArguments(cx); 583 } 584 585 Type typeTemplateArgument(int i) @safe pure nothrow const { 586 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 587 } 588 589 string toString() @safe pure nothrow const { 590 import std.conv: text; 591 try { 592 const pointeeText = pointee.isInvalid ? "" : text(", ", pointee); 593 return text("Type(", kind, `, "`, spelling, pointeeText, `")`); 594 } catch(Exception e) 595 assert(false, "Fatal error in Type.toString: " ~ e.msg); 596 } 597 } 598 599 600 struct Token { 601 602 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 603 604 Kind kind; 605 string spelling; 606 CXToken cx; 607 TranslationUnit translationUnit; 608 609 this(CXToken cx, TranslationUnit unit) @safe pure nothrow { 610 this.cx = cx; 611 this.translationUnit = unit; 612 this.kind = cast(Kind) clang_getTokenKind(cx); 613 this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx)); 614 } 615 616 this(Kind kind, string spelling) @safe @nogc pure nothrow { 617 this.kind = kind; 618 this.spelling = spelling; 619 } 620 621 string toString() @safe pure const { 622 import std.conv: text; 623 624 return text("Token(", kind, `, "`, spelling, `")`); 625 } 626 627 bool opEquals(in Token other) @safe pure nothrow const { 628 return kind == other.kind && spelling == other.spelling; 629 } 630 }