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, in TranslationUnitFlags translUnitflags) 10 @safe 11 { 12 return parse(fileName, [], translUnitflags); 13 } 14 15 16 mixin EnumD!("ErrorCode", CXErrorCode, ""); 17 mixin EnumD!("DiagnosticSeverity", CXDiagnosticSeverity, "CXDiagnostic_"); 18 19 TranslationUnit parse(in string fileName, in string[] commandLineArgs, in TranslationUnitFlags translUnitflags) 20 @safe 21 { 22 23 import std.string: toStringz; 24 import std.algorithm: map; 25 import std.array: array, join; 26 import std.conv: text; 27 28 // faux booleans 29 const excludeDeclarationsFromPCH = 0; 30 const displayDiagnostics = 0; 31 auto index = clang_createIndex(excludeDeclarationsFromPCH, displayDiagnostics); 32 CXUnsavedFile[] unsavedFiles; 33 const commandLineArgz = commandLineArgs 34 .map!(a => a.toStringz) 35 .array; 36 37 CXTranslationUnit cx; 38 const err = () @trusted { 39 return cast(ErrorCode)clang_parseTranslationUnit2( 40 index, 41 fileName.toStringz, 42 commandLineArgz.ptr, // .ptr since the length can be 0 43 cast(int)commandLineArgz.length, 44 unsavedFiles.ptr, // .ptr since the length can be 0 45 cast(uint)unsavedFiles.length, 46 translUnitflags, 47 &cx, 48 ); 49 }(); 50 51 if(err != ErrorCode.success) { 52 throw new Exception(text("Could not parse ", fileName, ": ", err)); 53 } 54 55 string[] errorMessages; 56 // throw if there are error diagnostics 57 foreach(i; 0 .. clang_getNumDiagnostics(cx)) { 58 auto diagnostic = clang_getDiagnostic(cx, i); 59 scope(exit) clang_disposeDiagnostic(diagnostic); 60 const severity = cast(DiagnosticSeverity) clang_getDiagnosticSeverity(diagnostic); 61 if(severity == DiagnosticSeverity.Error || severity == DiagnosticSeverity.Fatal) 62 errorMessages ~= clang_formatDiagnostic(diagnostic, 0).toString; 63 } 64 65 if(errorMessages.length > 0) 66 throw new Exception(text("Error parsing '", fileName, "':\n", 67 errorMessages.join("\n"))); 68 69 70 return TranslationUnit(cx); 71 } 72 73 string[] systemPaths() @safe { 74 import std.process: execute; 75 import std.string: splitLines, stripLeft; 76 import std.algorithm: map, countUntil; 77 import std.array: array; 78 79 const res = execute(["gcc", "-v", "-xc", "/dev/null", "-fsyntax-only"]); 80 if(res.status != 0) throw new Exception("Failed to call gcc:\n" ~ res.output); 81 82 auto lines = res.output.splitLines; 83 84 const startIndex = lines.countUntil("#include <...> search starts here:") + 1; 85 assert(startIndex > 0); 86 const endIndex = lines.countUntil("End of search list."); 87 assert(endIndex > 0); 88 89 return lines[startIndex .. endIndex].map!stripLeft.array; 90 } 91 92 93 mixin EnumD!("ChildVisitResult", CXChildVisitResult, "CXChildVisit_"); 94 95 alias CursorVisitor = ChildVisitResult delegate(Cursor cursor, Cursor parent); 96 97 struct TranslationUnit { 98 99 CXTranslationUnit cx; 100 Cursor cursor; 101 102 this(CXTranslationUnit cx) @safe nothrow { 103 this.cx = cx; 104 this.cursor = Cursor(clang_getTranslationUnitCursor(cx)); 105 } 106 } 107 108 string toString(CXString cxString) @safe pure nothrow { 109 import std.conv: to; 110 auto cstr = clang_getCString(cxString); 111 scope(exit) clang_disposeString(cxString); 112 return () @trusted { return cstr.to!string; }(); 113 } 114 115 struct Cursor { 116 117 mixin EnumD!("Kind", CXCursorKind, "CXCursor_"); 118 119 CXCursor cx; 120 private Cursor[] _children; 121 Kind kind; 122 string spelling; 123 Type type; 124 Type returnType; // only if the cursor is a function 125 SourceRange sourceRange; 126 127 this(CXCursor cx) @safe nothrow { 128 this.cx = cx; 129 kind = cast(Kind) clang_getCursorKind(cx); 130 spelling = clang_getCursorSpelling(cx).toString; 131 type = Type(clang_getCursorType(cx)); 132 sourceRange = SourceRange(clang_getCursorExtent(cx)); 133 134 if(kind == Kind.FunctionDecl) 135 returnType = Type(clang_getCursorResultType(cx)); 136 } 137 138 private static extern(C) CXChildVisitResult ctorVisitor(CXCursor cursor, 139 CXCursor parent, 140 void* clientData_) 141 @safe nothrow 142 { 143 auto children = () @trusted { return cast(Cursor[]*) clientData_; }(); 144 *children ~= Cursor(cursor); 145 return CXChildVisit_Continue; 146 } 147 148 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 149 this(kind, spelling, Type()); 150 } 151 152 this(in Kind kind, in string spelling, Type type) @safe @nogc pure nothrow { 153 this.kind = kind; 154 this.spelling = spelling; 155 this.type = type; 156 } 157 158 inout(Cursor)[] children() @safe @property nothrow inout { 159 if(_children.length) return _children; 160 161 inout(Cursor)[] ret; 162 // calling Cursor.visitChildren here would cause infinite recursion 163 // because cvisitor constructs a Cursor out of the parent 164 () @trusted { clang_visitChildren(cx, &ctorVisitor, &ret); }(); 165 return ret; 166 } 167 168 void children(Cursor[] cursors) @safe @property pure nothrow { 169 _children = cursors; 170 } 171 172 /** 173 Constructs a function declaration cursor. 174 */ 175 static Cursor functionDecl(in string spelling, in string proto, Type returnType) 176 @safe pure nothrow 177 { 178 auto cursor = Cursor(Kind.FunctionDecl, 179 spelling, 180 Type(Type.Kind.FunctionProto, proto)); 181 cursor.returnType = returnType; 182 return cursor; 183 } 184 185 /** 186 For TypedefDecl cursors, return the underlying type 187 */ 188 Type underlyingType() @safe pure nothrow const { 189 assert(kind == Cursor.Kind.TypedefDecl, "Not a TypedefDecl cursor"); 190 return Type(clang_getTypedefDeclUnderlyingType(cx)); 191 } 192 193 /** 194 For EnumConstantDecl cursors, return the numeric value 195 */ 196 auto enumConstantValue() @safe @nogc pure nothrow const { 197 assert(kind == Cursor.Kind.EnumConstantDecl); 198 return clang_getEnumConstantDeclValue(cx); 199 } 200 201 Language language() @safe @nogc pure nothrow const { 202 return cast(Language) clang_getCursorLanguage(cx); 203 } 204 205 Cursor canonical() @safe nothrow const { 206 return Cursor(clang_getCanonicalCursor(cx)); 207 } 208 209 /** 210 If this is the canonical cursor. Given forward declarations, there may 211 be several cursors for one entity. This returns true if this cursor 212 is the canonical one. 213 */ 214 bool isCanonical() @safe @nogc pure nothrow const { 215 return cast(bool) clang_equalCursors(cx, clang_getCanonicalCursor(cx)); 216 } 217 218 bool isDefinition() @safe @nogc pure nothrow const { 219 return cast(bool) clang_isCursorDefinition(cx); 220 } 221 222 bool isNull() @safe @nogc pure nothrow const { 223 return cast(bool) clang_Cursor_isNull(cx); 224 } 225 226 static Cursor nullCursor() @safe nothrow { 227 return Cursor(clang_getNullCursor()); 228 } 229 230 Cursor definition() @safe nothrow const { 231 return Cursor(clang_getCursorDefinition(cx)); 232 } 233 234 string toString() @safe pure nothrow const { 235 import std.conv: text; 236 try { 237 const returnTypeStr = kind == Kind.FunctionDecl 238 ? text(", ", returnType) 239 : ""; 240 241 return text("Cursor(", kind, `, "`, spelling, `", `, type, returnTypeStr, ")"); 242 } catch(Exception e) 243 assert(false, "Fatal error in Cursor.toString: " ~ e.msg); 244 } 245 246 bool isPredefined() @safe @nogc pure nothrow const { 247 // FIXME 248 return false; 249 } 250 251 Cursor semanticParent() @safe nothrow const { 252 return Cursor(clang_getCursorSemanticParent(cx)); 253 } 254 255 Cursor lexicalParent() @safe nothrow const { 256 return Cursor(clang_getCursorLexicalParent(cx)); 257 } 258 259 bool isInvalid() @safe @nogc pure nothrow const { 260 return cast(bool) clang_isInvalid(cx.kind); 261 } 262 263 auto hash() @safe @nogc pure nothrow const { 264 return clang_hashCursor(cx); 265 } 266 267 string mangling() @safe pure nothrow const { 268 return clang_Cursor_getMangling(cx).toString; 269 } 270 271 bool isAnonymous() @safe @nogc pure nothrow const { 272 return cast(bool) clang_Cursor_isAnonymous(cx); 273 } 274 275 bool opEquals(ref const(Cursor) other) @safe @nogc pure nothrow const { 276 return cast(bool) clang_equalCursors(cx, other.cx); 277 } 278 279 bool opEquals(in Cursor other) @safe @nogc pure nothrow const { 280 return cast(bool) clang_equalCursors(cx, other.cx); 281 } 282 283 void visitChildren(CursorVisitor visitor) @safe nothrow const { 284 clang_visitChildren(cx, &cvisitor, new ClientData(visitor)); 285 } 286 287 int opApply(scope int delegate(Cursor cursor, Cursor parent) @safe block) @safe nothrow const { 288 return opApplyN(block); 289 } 290 291 int opApply(scope int delegate(Cursor cursor) @safe block) @safe nothrow const { 292 return opApplyN(block); 293 } 294 295 private int opApplyN(T...)(int delegate(T args) @safe block) const { 296 int stop = 0; 297 298 visitChildren((cursor, parent) { 299 300 static if(T.length == 2) 301 stop = block(cursor, parent); 302 else static if(T.length == 1) 303 stop = block(cursor); 304 else 305 static assert(false); 306 307 return stop 308 ? ChildVisitResult.Break 309 : ChildVisitResult.Continue; 310 }); 311 312 return stop; 313 } 314 } 315 316 317 struct SourceRange { 318 CXSourceRange cx; 319 string path; 320 SourceLocation start; 321 SourceLocation end; 322 323 this(CXSourceRange cx) @safe pure nothrow { 324 this.cx = cx; 325 this.start = clang_getRangeStart(cx); 326 this.end = clang_getRangeEnd(cx); 327 this.path = start.path; 328 } 329 330 string toString() @safe pure const { 331 import std.conv: text; 332 return text(`SourceRange("`, start.path, `", `, start.line, ":", start.column, ", ", end.line, ":", end.column, ")"); 333 } 334 } 335 336 struct SourceLocation { 337 CXSourceLocation cx; 338 string path; 339 uint line; 340 uint column; 341 uint offset; 342 343 this(CXSourceLocation cx) @safe pure nothrow { 344 this.cx = cx; 345 346 CXFile file; 347 () @trusted { clang_getExpansionLocation(cx, &file, null, null, null); }(); 348 this.path = clang_getFileName(file).toString; 349 350 () @trusted { clang_getSpellingLocation(cx, &file, &line, &column, &offset); }(); 351 } 352 353 int opCmp(ref const(SourceLocation) other) @safe @nogc pure nothrow const { 354 if(path == other.path && line == other.line && column == other.column && 355 offset == other.offset) 356 return 0; 357 358 if(path < other.path) return -1; 359 if(path > other.path) return 1; 360 if(line < other.line) return -1; 361 if(line > other.line) return 1; 362 if(column < other.column) return -1; 363 if(column > other.column) return 1; 364 if(offset < other.offset) return -1; 365 if(offset > other.offset) return 1; 366 assert(false); 367 } 368 369 string toString() @safe pure nothrow const { 370 import std.conv: text; 371 return text(`"`, path, `" `, line, ":", column, ":", offset); 372 } 373 } 374 375 private struct ClientData { 376 /** 377 The D visitor delegate 378 */ 379 CursorVisitor dvisitor; 380 } 381 382 // This is the C function actually passed to libclang's clang_visitChildren 383 // The context (clientData) contains the D delegate that's then called on the 384 // (cursor, parent) pair 385 private extern(C) CXChildVisitResult cvisitor(CXCursor cursor, CXCursor parent, void* clientData_) { 386 auto clientData = cast(ClientData*) clientData_; 387 return cast(CXChildVisitResult) clientData.dvisitor(Cursor(cursor), Cursor(parent)); 388 } 389 390 391 struct Type { 392 393 mixin EnumD!("Kind", CXTypeKind, "CXType_"); 394 395 CXType cx; 396 Kind kind; 397 string spelling; 398 Type* pointee; // only if pointer 399 400 this(CXType cx) @safe pure nothrow { 401 this.cx = cx; 402 this.kind = cast(Kind) cx.kind; 403 spelling = clang_getTypeSpelling(cx).toString; 404 405 if(this.kind == Kind.Pointer || this.kind == Kind.LValueReference) { 406 pointee = new Type(clang_getPointeeType(cx)); 407 } 408 } 409 410 this(in Kind kind) @safe @nogc pure nothrow { 411 this(kind, ""); 412 } 413 414 this(in Kind kind, in string spelling) @safe @nogc pure nothrow { 415 this.kind = kind; 416 this.spelling = spelling; 417 } 418 419 static Type* pointer(in string spelling, Type* pointee) @safe pure nothrow { 420 auto type = new Type(Kind.Pointer, spelling); 421 type.pointee = pointee; 422 return type; 423 } 424 425 Type unelaborate() @safe nothrow const { 426 return Type(clang_Type_getNamedType(cx)); 427 } 428 429 Type canonical() @safe pure nothrow const { 430 return Type(clang_getCanonicalType(cx)); 431 } 432 433 Type returnType() @safe pure const { 434 if(kind != Kind.FunctionProto) throw new Exception("Type not a function"); 435 return Type(clang_getResultType(cx)); 436 } 437 438 Type[] paramTypes() @safe pure const { 439 const numArgs = clang_getNumArgTypes(cx); 440 auto types = new Type[numArgs]; 441 442 foreach(i; 0 .. numArgs) { 443 types[i] = Type(clang_getArgType(cx, i)); 444 } 445 446 return types; 447 } 448 449 bool isVariadicFunction() @safe @nogc pure nothrow const { 450 return cast(bool) clang_isFunctionTypeVariadic(cx); 451 } 452 453 Type elementType() @safe pure nothrow const { 454 return Type(clang_getElementType(cx)); 455 } 456 457 long numElements() @safe @nogc pure nothrow const { 458 return clang_getNumElements(cx); 459 } 460 461 bool isConstQualified() @safe @nogc pure nothrow const { 462 return cast(bool)clang_isConstQualifiedType(cx); 463 } 464 465 bool isVolatileQualified() @safe @nogc pure nothrow const { 466 return cast(bool)clang_isVolatileQualifiedType(cx); 467 } 468 469 Cursor declaration() @safe nothrow const { 470 return Cursor(clang_getTypeDeclaration(cx)); 471 } 472 473 Type namedType() @safe pure nothrow const { 474 return Type(clang_Type_getNamedType(cx)); 475 } 476 477 string toString() @safe pure nothrow const { 478 import std.conv: text; 479 try { 480 const pointeeText = pointee is null ? "" : text(", ", *pointee); 481 return text("Type(", kind, `, "`, spelling, pointeeText, `")`); 482 } catch(Exception e) 483 assert(false, "Fatal error in Type.toString: " ~ e.msg); 484 } 485 }