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