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