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