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 mixin EnumD!("Kind", CXCursorKind, "CXCursor_"); 133 mixin EnumD!("StorageClass", CX_StorageClass, "CX_SC_"); 134 135 CXCursor cx; 136 private Cursor[] _children; 137 Kind kind; 138 string spelling; 139 Type type; 140 SourceRange sourceRange; 141 142 this(CXCursor cx) @safe pure nothrow { 143 this.cx = cx; 144 kind = cast(Kind) clang_getCursorKind(cx); 145 spelling = clang_getCursorSpelling(cx).toString; 146 type = Type(clang_getCursorType(cx)); 147 sourceRange = SourceRange(clang_getCursorExtent(cx)); 148 } 149 150 private static extern(C) CXChildVisitResult ctorVisitor(CXCursor cursor, 151 CXCursor parent, 152 void* clientData_) 153 @safe nothrow 154 { 155 auto children = () @trusted { return cast(Cursor[]*) clientData_; }(); 156 *children ~= Cursor(cursor); 157 return CXChildVisit_Continue; 158 } 159 160 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 161 this(kind, spelling, Type()); 162 } 163 164 this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow { 165 this.kind = kind; 166 this.spelling = spelling; 167 this.type = type; 168 } 169 170 /// Lazily return the cursor's children 171 inout(Cursor)[] children() @safe @property nothrow inout { 172 if(_children.length) return _children; 173 174 inout(Cursor)[] ret; 175 // calling Cursor.visitChildren here would cause infinite recursion 176 // because cvisitor constructs a Cursor out of the parent 177 () @trusted { clang_visitChildren(cx, &ctorVisitor, &ret); }(); 178 return ret; 179 } 180 181 void children(Cursor[] cursors) @safe @property pure nothrow { 182 _children = cursors; 183 } 184 185 Type returnType() @safe pure nothrow const { 186 return Type(clang_getCursorResultType(cx)); 187 } 188 189 /** 190 For TypedefDecl cursors, return the underlying type 191 */ 192 Type underlyingType() @safe pure nothrow const { 193 assert(kind == Cursor.Kind.TypedefDecl || kind == Cursor.Kind.TypeAliasDecl, 194 "Not a TypedefDecl cursor"); 195 return Type(clang_getTypedefDeclUnderlyingType(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 Cursor specializedCursorTemplate() @safe nothrow const { 313 return Cursor(clang_getSpecializedCursorTemplate(cx)); 314 } 315 316 TranslationUnit translationUnit() @safe nothrow const { 317 return TranslationUnit(clang_Cursor_getTranslationUnit(cx)); 318 } 319 320 Token[] tokens() @safe nothrow const { 321 import std.algorithm: map; 322 import std.array: array; 323 324 CXToken* tokens; 325 uint numTokens; 326 327 () @trusted { clang_tokenize(translationUnit.cx, sourceRange.cx, &tokens, &numTokens); }(); 328 // I hope this only deallocates the array 329 scope(exit) clang_disposeTokens(translationUnit.cx, tokens, numTokens); 330 331 auto tokenSlice = () @trusted { return tokens[0 .. numTokens]; }(); 332 333 return tokenSlice.map!(a => Token(a, translationUnit)).array; 334 } 335 336 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 337 return cast(bool) clang_equalCursors(cx, other.cx); 338 } 339 340 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 341 return cast(bool) clang_equalCursors(cx, other.cx); 342 } 343 344 void visitChildren(CursorVisitor visitor) @safe nothrow const { 345 clang_visitChildren(cx, &cvisitor, new ClientData(visitor)); 346 } 347 348 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 349 return opApplyN(block); 350 } 351 352 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 353 return opApplyN(block); 354 } 355 356 private int opApplyN(T...)(int delegate(T args) @safe block) const { 357 int stop = 0; 358 359 visitChildren((cursor, parent) { 360 361 static if(T.length == 2) 362 stop = block(cursor, parent); 363 else static if(T.length == 1) 364 stop = block(cursor); 365 else 366 static assert(false); 367 368 return stop 369 ? ChildVisitResult.Break 370 : ChildVisitResult.Continue; 371 }); 372 373 return stop; 374 } 375 } 376 377 378 struct SourceRange { 379 380 CXSourceRange cx; 381 string path; 382 SourceLocation start; 383 SourceLocation end; 384 385 this(CXSourceRange cx) @safe pure nothrow { 386 this.cx = cx; 387 this.start = clang_getRangeStart(cx); 388 this.end = clang_getRangeEnd(cx); 389 this.path = start.path; 390 } 391 392 string toString() @safe pure const { 393 import std.conv: text; 394 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 395 } 396 } 397 398 struct SourceLocation { 399 CXSourceLocation cx; 400 string path; 401 uint line; 402 uint column; 403 uint offset; 404 405 this(CXSourceLocation cx) @safe pure nothrow { 406 this.cx = cx; 407 408 CXFile file; 409 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 410 this.path = clang_getFileName(file).toString; 411 412 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 413 } 414 415 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 416 if(path == other.path && line == other.line && column == other.column && 417 offset == other.offset) 418 return 0; 419 420 if(path < other.path) return -1; 421 if(path > other.path) return 1; 422 if(line < other.line) return -1; 423 if(line > other.line) return 1; 424 if(column < other.column) return -1; 425 if(column > other.column) return 1; 426 if(offset < other.offset) return -1; 427 if(offset > other.offset) return 1; 428 assert(false); 429 } 430 431 string toString() @safe pure nothrow const { 432 import std.conv: text; 433 return text(`"`, path, `" `, line, ":", column, ":", offset); 434 } 435 } 436 437 private struct ClientData { 438 /** 439 The D visitor delegate 440 */ 441 CursorVisitor dvisitor; 442 } 443 444 // This is the C function actually passed to libclang's clang_visitChildren 445 // The context (clientData) contains the D delegate that's then called on the 446 // (cursor, parent) pair 447 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 448 auto clientData = cast(ClientData*) clientData_; 449 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 450 } 451 452 453 struct Type { 454 455 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 456 457 CXType cx; 458 Kind kind; 459 string spelling; 460 461 this(CXType cx) @safe pure nothrow { 462 this.cx = cx; 463 this.kind = cast(Kind) cx.kind; 464 spelling = clang_getTypeSpelling(cx).toString; 465 } 466 467 this(in Type other) @trusted pure nothrow { 468 import std.algorithm: map; 469 import std.array: array; 470 471 this.cx.kind = other.cx.kind; 472 this.cx.data[] = other.cx.data[].map!(a => cast(void*) a).array; 473 474 this(this.cx); 475 } 476 477 this(in Kind kind) @safe @nogc pure nothrow { 478 this(kind, ""); 479 } 480 481 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 482 this.kind = kind; 483 this.spelling = spelling; 484 } 485 486 Type pointee() @safe pure nothrow const { 487 return Type(clang_getPointeeType(cx)); 488 } 489 490 Type unelaborate() @safe nothrow const { 491 return Type(clang_Type_getNamedType(cx)); 492 } 493 494 Type canonical() @safe pure nothrow const { 495 return Type(clang_getCanonicalType(cx)); 496 } 497 498 Type returnType() @safe pure const { 499 return Type(clang_getResultType(cx)); 500 } 501 502 Type[] paramTypes() @safe pure const { 503 const numArgs = clang_getNumArgTypes(cx); 504 auto types = new Type[numArgs]; 505 506 foreach(i; 0 .. numArgs) { 507 types[i] = Type(clang_getArgType(cx, i)); 508 } 509 510 return types; 511 } 512 513 bool isVariadicFunction() @safe @nogc pure nothrow const { 514 return cast(bool) clang_isFunctionTypeVariadic(cx); 515 } 516 517 Type elementType() @safe pure nothrow const { 518 return Type(clang_getElementType(cx)); 519 } 520 521 long numElements() @safe @nogc pure nothrow const { 522 return clang_getNumElements(cx); 523 } 524 525 long arraySize() @safe @nogc pure nothrow const { 526 return clang_getArraySize(cx); 527 } 528 529 bool isConstQualified() @safe @nogc pure nothrow const { 530 return cast(bool) clang_isConstQualifiedType(cx); 531 } 532 533 bool isVolatileQualified() @safe @nogc pure nothrow const { 534 return cast(bool) clang_isVolatileQualifiedType(cx); 535 } 536 537 Cursor declaration() @safe pure nothrow const { 538 return Cursor(clang_getTypeDeclaration(cx)); 539 } 540 541 Type namedType() @safe pure nothrow const { 542 return Type(clang_Type_getNamedType(cx)); 543 } 544 545 bool opEquals(ref const(Type) other) @safe @nogc pure nothrow const { 546 return cast(bool) clang_equalTypes(cx, other.cx); 547 } 548 549 bool opEquals(in Type other) @safe @nogc pure nothrow const { 550 return cast(bool) clang_equalTypes(cx, other.cx); 551 } 552 553 bool isInvalid() @safe @nogc pure nothrow const { 554 return kind == Kind.Invalid; 555 } 556 557 long getSizeof() @safe @nogc pure nothrow const { 558 return clang_Type_getSizeOf(cx); 559 } 560 561 int numTemplateArguments() @safe @nogc pure nothrow const { 562 return clang_Type_getNumTemplateArguments(cx); 563 } 564 565 Type typeTemplateArgument(int i) @safe pure nothrow const { 566 return Type(clang_Type_getTemplateArgumentAsType(cx, i)); 567 } 568 569 string toString() @safe pure nothrow const { 570 import std.conv: text; 571 try { 572 const pointeeText = pointee.isInvalid ? "" : text(", ", pointee); 573 return text("Type(", kind, `, "`, spelling, pointeeText, `")`); 574 } catch(Exception e) 575 assert(false, "Fatal error in Type.toString: " ~ e.msg); 576 } 577 } 578 579 580 struct Token { 581 582 mixin EnumD!("Kind", CXTokenKind, "CXToken_"); 583 584 Kind kind; 585 string spelling; 586 CXToken cx; 587 TranslationUnit translationUnit; 588 589 this(CXToken cx, TranslationUnit unit) @safe pure nothrow { 590 this.cx = cx; 591 this.translationUnit = unit; 592 this.kind = cast(Kind) clang_getTokenKind(cx); 593 this.spelling = .toString(clang_getTokenSpelling(translationUnit.cx, cx)); 594 } 595 596 this(Kind kind, string spelling) @safe @nogc pure nothrow { 597 this.kind = kind; 598 this.spelling = spelling; 599 } 600 601 string toString() @safe pure const { 602 import std.conv: text; 603 604 return text("Token(", kind, `, "`, spelling, `")`); 605 } 606 607 bool opEquals(in Token other) @safe pure nothrow const { 608 return kind == other.kind && spelling == other.spelling; 609 } 610 }