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