ljclang.lua (31082B)
1 -- LuaJIT-based binding to libclang, modelled after 2 -- https://github.com/mkottman/luaclang-parser 3 -- 4 -- See COPYRIGHT.TXT for the Copyright Notice of LJClang. 5 -- LICENSE_LLVM.TXT is the license for libclang. 6 7 local assert = assert 8 local error = error 9 local print = print 10 local require = require 11 local select = select 12 local setmetatable = setmetatable 13 local table = table 14 local tonumber = tonumber 15 local tostring = tostring 16 local type = type 17 local unpack = unpack 18 19 local ffi = require("ffi") 20 local C = ffi.C 21 22 local bit = require("bit") 23 local io = require("io") 24 25 local function lib(basename) 26 return (ffi.os=="Windows" and "lib" or "")..basename 27 end 28 29 local clang = ffi.load(lib"clang") 30 require("ljclang_Index_h") 31 local support = ffi.load(lib"ljclang_support") 32 local g_CursorKindName = require("ljclang_cursor_kind").name 33 34 ------------------------------------------------------------------------- 35 36 -- The table of externally exposed elements, returned at the end. 37 local api = {} 38 39 --[[ 40 local function debugf(fmt, ...) 41 print(string.format("ljclang: "..fmt, ...)) 42 end 43 --[=[]] 44 local function debugf() end 45 --]=] 46 47 -- Wrap 'error' in assert-like call to write type checks in one line instead of 48 -- three. 49 local function check(pred, msg, level) 50 if (not pred) then 51 error(msg, level+1) 52 end 53 end 54 55 -- CXIndex is a pointer type, wrap it to be able to define a metatable. 56 local Index_t = ffi.typeof "struct { CXIndex _idx; }" 57 local TranslationUnit_t_ = ffi.typeof "struct { CXTranslationUnit _tu; }" 58 -- NOTE: CXCursor is a struct type by itself, but we wrap it to e.g. provide a 59 -- kind() *method* (CXCursor contains a member of the same name). 60 local Cursor_t = ffi.typeof "struct { CXCursor _cur; }" 61 local Type_t = ffi.typeof "struct { CXType _typ; }" 62 63 -- CompilationDatabase types 64 local CompilationDatabase_t = ffi.typeof "struct { CXCompilationDatabase _ptr; }" 65 local CompileCommands_t = ffi.typeof "struct { CXCompileCommands _ptr; const double numCommands; }" 66 local CompileCommand_t = ffi.typeof[[ 67 struct { 68 CXCompileCommand _ptr; 69 const double numArgs, numSources; 70 } 71 ]] 72 73 -- [<address of CXTranslationUnit as string>] = count 74 local TUCount = {} 75 76 local function getCXTUaddr(cxtu) 77 return tostring(cxtu):gsub(".*: 0x", "") 78 end 79 80 local function TranslationUnit_t(cxtu) 81 local addr = getCXTUaddr(cxtu) 82 TUCount[addr] = (TUCount[addr] or 0) + 1 83 return TranslationUnit_t_(cxtu) 84 end 85 86 -- Our wrapping type Cursor_t is seen as raw CXCursor on the C side. 87 assert(ffi.sizeof("CXCursor") == ffi.sizeof(Cursor_t)) 88 89 ffi.cdef([[ 90 typedef enum CXChildVisitResult (*LJCX_CursorVisitor)( 91 $ *cursor, $ *parent, CXClientData client_data); 92 ]], Cursor_t, Cursor_t) 93 94 ffi.cdef[[ 95 int ljclang_regCursorVisitor(LJCX_CursorVisitor visitor, enum CXCursorKind *kinds, int numkinds); 96 int ljclang_visitChildren(CXCursor parent, int visitoridx); 97 ]] 98 99 ------------------------------------------------------------------------- 100 -------------------------------- CXString ------------------------------- 101 ------------------------------------------------------------------------- 102 103 local CXString = ffi.typeof("CXString") 104 105 -- Convert from a libclang's encapsulated CXString to a plain Lua string and 106 -- dispose of the CXString afterwards. 107 local function getString(cxstr) 108 assert(ffi.istype(CXString, cxstr)) 109 local cstr = clang.clang_getCString(cxstr) 110 assert(cstr ~= nil) 111 local str = ffi.string(cstr) 112 clang.clang_disposeString(cxstr) 113 return str 114 end 115 116 ------------------------------------------------------------------------- 117 -------------------------- CompilationDatabase -------------------------- 118 ------------------------------------------------------------------------- 119 120 -- newargs = api.stripArgs(args, pattern, num) 121 function api.stripArgs(args, pattern, num) 122 assert(args[0] == nil) 123 local numArgs = #args 124 125 for i=1,numArgs do 126 if (args[i] and args[i]:find(pattern)) then 127 for j=0,num-1 do 128 args[i+j] = nil 129 end 130 end 131 end 132 133 local newargs = {} 134 for i=1,numArgs do 135 newargs[#newargs+1] = args[i] 136 end 137 return newargs 138 end 139 140 local CompDB_Error_Ar = ffi.typeof[[ 141 CXCompilationDatabase_Error [1] 142 ]] 143 144 local CompileCommand_mt = { 145 __index = { 146 -- args = cmd:getArgs([alsoCompilerExe]) 147 -- args: sequence table. If <alsoCompilerExe> is true, args[0] is the 148 -- compiler executable. 149 getArgs = function(self, alsoCompilerExe) 150 local args = {} 151 for i = (alsoCompilerExe and 0 or 1), self.numArgs-1 do 152 local cxstr = clang.clang_CompileCommand_getArg(self._ptr, i) 153 args[i] = getString(cxstr) 154 end 155 156 return args 157 end, 158 159 -- dir = cmd:getDirectory() 160 getDirectory = function(self) 161 local cxstr = clang.clang_CompileCommand_getDirectory(self._ptr) 162 return getString(cxstr) 163 end, 164 165 -- paths = cmd:getSourcePaths() 166 -- paths: sequence table. 167 getSourcePaths = function(self) 168 -- XXX: This is a workaround implementation due to a missing 169 -- clang_CompileCommand_getMappedSourcePath symbol in libclang.so, 170 -- see commented out code below. 171 local args = self:getArgs() 172 for i=1,#args do 173 if (args[i] == '-c' and args[i+1]) then 174 local sourceFile = args[i+1] 175 if (sourceFile:sub(1,1) ~= "/") then -- XXX: Windows 176 sourceFile = self:getDirectory() .. "/" .. sourceFile 177 end 178 return { sourceFile } 179 end 180 end 181 182 print(table.concat(args, ' ')) 183 check(false, "Did not find -c option (workaround for missing ".. 184 "clang_CompileCommand_getMappedSourcePath symbol)") 185 --[[ 186 local paths = {} 187 for i=0,self.numSources-1 do 188 -- XXX: for me, the symbol is missing in libclang.so: 189 local cxstr = clang.clang_CompileCommand_getMappedSourcePath(self._ptr, i) 190 paths[i] = getString(cxstr) 191 end 192 return paths 193 --]] 194 end, 195 }, 196 } 197 198 local CompileCommands_mt = { 199 -- #commands: number of commands 200 __len = function(self) 201 return self.numCommands 202 end, 203 204 -- commands[i]: get the i'th CompileCommand object (i is 1-based) 205 __index = function(self, i) 206 check(type(i) == "number", "<i> must be a number", 2) 207 check(i >= 1 and i <= self.numCommands, "<i> must be in [1, numCommands]", 2) 208 local cmdPtr = clang.clang_CompileCommands_getCommand(self._ptr, i-1) 209 local numArgs = clang.clang_CompileCommand_getNumArgs(cmdPtr) 210 local numSources = 0 --clang.clang_CompileCommand_getNumMappedSources(cmdPtr) 211 return CompileCommand_t(cmdPtr, numArgs, numSources) 212 end, 213 214 __gc = function(self) 215 clang.clang_CompileCommands_dispose(self._ptr) 216 end, 217 } 218 219 local CompilationDatabase_mt = { 220 __index = { 221 getCompileCommands = function(self, completeFileName) 222 check(type(completeFileName) == "string", "<completeFileName> must be a string", 2) 223 local cmdsPtr = clang.clang_CompilationDatabase_getCompileCommands( 224 self._ptr, completeFileName) 225 local numCommands = clang.clang_CompileCommands_getSize(cmdsPtr) 226 return CompileCommands_t(cmdsPtr, numCommands) 227 end, 228 229 getAllCompileCommands = function(self) 230 local cmdsPtr = clang.clang_CompilationDatabase_getAllCompileCommands(self._ptr) 231 local numCommands = clang.clang_CompileCommands_getSize(cmdsPtr) 232 return CompileCommands_t(cmdsPtr, numCommands) 233 end, 234 }, 235 236 __gc = function(self) 237 clang.clang_CompilationDatabase_dispose(self._ptr) 238 end, 239 } 240 241 -- compDB = api.CompilationDatabase(buildDir) 242 function api.CompilationDatabase(buildDir) 243 check(type(buildDir) == "string", "<buildDir> must be a string", 2) 244 245 local errAr = CompDB_Error_Ar() 246 local ptr = clang.clang_CompilationDatabase_fromDirectory(buildDir, errAr) 247 assert(ptr ~= nil or errAr[0] ~= 'CXCompilationDatabase_NoError') 248 249 return ptr ~= nil and CompilationDatabase_t(ptr) or nil 250 end 251 252 ------------------------------------------------------------------------- 253 --------------------------------- Index --------------------------------- 254 ------------------------------------------------------------------------- 255 256 -- Metatable for our Index_t. 257 local Index_mt = { 258 __index = {}, 259 260 __gc = function(self) 261 -- "The index must not be destroyed until all of the translation units created 262 -- within that index have been destroyed." 263 for i=1,#self._tus do 264 self._tus[i]:_cleanup() 265 end 266 clang.clang_disposeIndex(self._idx) 267 end, 268 } 269 270 local function NewIndex(cxidx) 271 assert(ffi.istype("CXIndex", cxidx)) 272 -- _tus is a list of the Index_t's TranslationUnit_t objects. 273 local index = { _idx=cxidx, _tus={} } 274 return setmetatable(index, Index_mt) 275 end 276 277 ------------------------------------------------------------------------- 278 ---------------------------- TranslationUnit ---------------------------- 279 ------------------------------------------------------------------------- 280 281 local function check_tu_valid(self) 282 if (self._tu == nil) then 283 error("Attempt to access freed TranslationUnit", 3) 284 end 285 end 286 287 -- Construct a Cursor_t from a libclang's CXCursor <cxcur>. If <cxcur> is the 288 -- NULL cursor, return nil. 289 local function getCursor(cxcur) 290 return (clang.clang_Cursor_isNull(cxcur) == 0) and Cursor_t(cxcur) or nil 291 end 292 293 -- Metatable for our TranslationUnit_t. 294 local TranslationUnit_mt = { 295 __index = { 296 _cleanup = function(self) 297 if (self._tu ~= nil) then 298 local addr = getCXTUaddr(self._tu) 299 TUCount[addr] = TUCount[addr]-1 300 if (TUCount[addr] == 0) then 301 clang.clang_disposeTranslationUnit(self._tu) 302 TUCount[addr] = nil 303 end 304 self._tu = nil 305 end 306 end, 307 308 cursor = function(self) 309 check_tu_valid(self) 310 local cxcur = clang.clang_getTranslationUnitCursor(self._tu) 311 return getCursor(cxcur) 312 end, 313 314 file = function(self, filename) 315 check_tu_valid(self) 316 if (type(filename) ~= "string") then 317 error("<filename> must be a string", 2) 318 end 319 local cxfile = clang.clang_getFile(self._tu, filename) 320 return getString(clang.clang_getFileName(cxfile)) -- NYI: modification time 321 end, 322 323 diagnostics = function(self) 324 check_tu_valid(self) 325 326 local numdiags = clang.clang_getNumDiagnostics(self._tu) 327 local tab = {} 328 329 for i=0,numdiags-1 do 330 local diag = clang.clang_getDiagnostic(self._tu, i) 331 tab[i+1] = { 332 category = getString(clang.clang_getDiagnosticCategoryText(diag)), 333 text = getString(clang.clang_formatDiagnostic( 334 diag, clang.clang_defaultDiagnosticDisplayOptions())), 335 severity = clang.clang_getDiagnosticSeverity(diag), 336 } 337 clang.clang_disposeDiagnostic(diag) 338 end 339 340 return tab 341 end, 342 }, 343 } 344 345 TranslationUnit_mt.__gc = TranslationUnit_mt.__index._cleanup 346 347 ------------------------------------------------------------------------- 348 --------------------------------- Cursor -------------------------------- 349 ------------------------------------------------------------------------- 350 351 local function getType(cxtyp) 352 return cxtyp.kind ~= 'CXType_Invalid' and Type_t(cxtyp) or nil 353 end 354 355 local CXFileAr = ffi.typeof("CXFile [1]") 356 357 local LineCol = ffi.typeof[[ 358 union { 359 struct { unsigned line, col; }; 360 unsigned ar[2]; 361 } 362 ]] 363 364 local LineColOfs = ffi.typeof[[ 365 union { 366 struct { unsigned line, col, offset; }; 367 unsigned ar[3]; 368 } 369 ]] 370 371 -- Get line number, column number and offset for a given CXSourceRange 372 local function getLineColOfs(cxsrcrange, cxfile, clang_rangefunc) 373 local cxsrcloc = clang[clang_rangefunc](cxsrcrange) 374 local lco = LineColOfs() 375 clang.clang_getSpellingLocation(cxsrcloc, cxfile, lco.ar, lco.ar+1, lco.ar+2) 376 return lco 377 end 378 379 -- Returns a LineColOfs for the beginning and end of a range. Also, the file name. 380 local function getBegEndFilename(cxsrcrange) 381 local cxfilear = CXFileAr() 382 local Beg = getLineColOfs(cxsrcrange, cxfilear, "clang_getRangeStart") 383 local filename = getString(clang.clang_getFileName(cxfilear[0])) 384 local End = getLineColOfs(cxsrcrange, cxfilear, "clang_getRangeEnd") 385 return Beg, End, filename 386 end 387 388 local function getPresumedLineCol(cxsrcrange, clang_rangefunc) 389 local cxsrcloc = clang[clang_rangefunc](cxsrcrange) 390 local linecol = LineCol() 391 local file = CXString() 392 clang.clang_getPresumedLocation(cxsrcloc, file, linecol.ar, linecol.ar+1) 393 return linecol, getString(file) 394 end 395 396 local function getSourceRange(cxcur) 397 local cxsrcrange = clang.clang_getCursorExtent(cxcur) 398 return (clang.clang_Range_isNull(cxsrcrange) == 0) and cxsrcrange or nil 399 end 400 401 -- Metatable for our Cursor_t. 402 local Cursor_mt = { 403 __eq = function(cur1, cur2) 404 if (ffi.istype(Cursor_t, cur1) and ffi.istype(Cursor_t, cur2)) then 405 return (clang.clang_equalCursors(cur1._cur, cur2._cur) ~= 0) 406 else 407 return false 408 end 409 end, 410 411 __index = { 412 parent = function(self) 413 return getCursor(clang.clang_getCursorSemanticParent(self._cur)) 414 end, 415 416 name = function(self) 417 return getString(clang.clang_getCursorSpelling(self._cur)) 418 end, 419 420 displayName = function(self) 421 return getString(clang.clang_getCursorDisplayName(self._cur)) 422 end, 423 424 kind = function(self) 425 local kindnum = tonumber(self:kindnum()) 426 local kindstr = g_CursorKindName[kindnum] 427 return kindstr or "Unknown" 428 end, 429 430 templateKind = function(self) 431 local kindnum = tonumber(clang.clang_getTemplateCursorKind(self._cur)) 432 local kindstr = g_CursorKindName[kindnum] 433 return kindstr or "Unknown" 434 end, 435 436 arguments = function(self) 437 local tab = {} 438 local numargs = clang.clang_Cursor_getNumArguments(self._cur) 439 for i=1,numargs do 440 tab[i] = getCursor(clang.clang_Cursor_getArgument(self._cur, i-1)) 441 end 442 return tab 443 end, 444 445 overloadedDecls = function(self) 446 local tab = {} 447 local numdecls = clang.clang_getNumOverloadedDecls(self._cur) 448 for i=1,numdecls do 449 tab[i] = getCursor(clang.clang_getOverloadedDecl(self._cur, i-1)) 450 end 451 return tab 452 end, 453 454 location = function(self, linesfirst) 455 local cxsrcrange = getSourceRange(self._cur) 456 if (cxsrcrange == nil) then 457 return nil 458 end 459 460 local Beg, End, filename = getBegEndFilename(cxsrcrange) 461 462 if (linesfirst == 'offset') then 463 return filename, Beg.offset, End.offset 464 elseif (linesfirst) then 465 -- LJClang order -- IMO you're usually more interested in the 466 -- line number 467 return filename, Beg.line, End.line, Beg.col, End.col, Beg.offset, End.offset 468 else 469 -- luaclang-parser order (offset: XXX) 470 return filename, Beg.line, Beg.col, End.line, End.col, Beg.offset, End.offset 471 end 472 end, 473 474 presumedLocation = function(self, linesfirst) 475 local cxsrcrange = getSourceRange(self._cur) 476 if (cxsrcrange == nil) then 477 return nil 478 end 479 480 local Beg, filename = getPresumedLineCol(cxsrcrange, "clang_getRangeStart") 481 local End = getPresumedLineCol(cxsrcrange, "clang_getRangeEnd") 482 483 if (linesfirst) then 484 return filename, Beg.line, End.line, Beg.col, End.col 485 else 486 return filename, Beg.line, Beg.col, End.line, End.col 487 end 488 end, 489 490 referenced = function(self) 491 return getCursor(clang.clang_getCursorReferenced(self._cur)) 492 end, 493 494 definition = function(self) 495 return getCursor(clang.clang_getCursorDefinition(self._cur)) 496 end, 497 498 isDeleted = function(self) 499 return clang.clang_CXX_isDeleted(self._cur) ~= 0 500 end, 501 502 isMutable = function(self) 503 return clang.clang_CXXField_isMutable(self._cur) ~= 0 504 end, 505 506 isDefaulted = function(self) 507 return clang.clang_CXXMethod_isDefaulted(self._cur) ~= 0 508 end, 509 510 isPureVirtual = function(self) 511 return clang.clang_CXXMethod_isPureVirtual(self._cur) ~= 0 512 end, 513 514 isVirtual = function(self) 515 return clang.clang_CXXMethod_isVirtual(self._cur) ~= 0 516 end, 517 518 isOverride = function(self) 519 return clang.clang_CXXMethod_isOverride(self._cur) ~= 0 520 end, 521 522 isStatic = function(self) 523 return clang.clang_CXXMethod_isStatic(self._cur) ~= 0 524 end, 525 526 isConst = function(self) 527 return clang.clang_CXXMethod_isConst(self._cur) ~= 0 528 end, 529 530 type = function(self) 531 return getType(clang.clang_getCursorType(self._cur)) 532 end, 533 534 resultType = function(self) 535 return getType(clang.clang_getCursorResultType(self._cur)) 536 end, 537 538 access = function(self) 539 local spec = clang.clang_getCXXAccessSpecifier(self._cur); 540 541 if (spec == 'CX_CXXPublic') then 542 return "public" 543 elseif (spec == 'CX_CXXProtected') then 544 return "protected" 545 elseif (spec == 'CX_CXXPrivate') then 546 return "private" 547 else 548 assert(spec == 'CX_CXXInvalidAccessSpecifier') 549 return nil 550 end 551 end, 552 553 --== LJClang-specific ==-- 554 555 translationUnit = function(self) 556 return TranslationUnit_t(clang.clang_Cursor_getTranslationUnit(self._cur)) 557 end, 558 559 -- XXX: Should be a TranslationUnit_t method instead. 560 -- 561 -- NOTE: *Sometimes* returns one token too much, see 562 -- http://clang-developers.42468.n3.nabble.com/querying-information-about-preprocessing-directives-in-libclang-td2740612.html 563 -- Related bug report: 564 -- http://llvm.org/bugs/show_bug.cgi?id=9069 565 -- Also, see TOKENIZE_WORKAROUND in extractdecls.lua 566 _tokens = function(self) 567 local tu = self:translationUnit() 568 local cxtu = tu._tu 569 570 local _, b, e = self:location('offset') 571 local cxsrcrange = getSourceRange(self._cur) 572 if (cxsrcrange == nil) then 573 return nil 574 end 575 576 local ntoksar = ffi.new("unsigned [1]") 577 local tokensar = ffi.new("CXToken *[1]") 578 clang.clang_tokenize(cxtu, cxsrcrange, tokensar, ntoksar) 579 local numtoks = ntoksar[0] 580 local tokens = tokensar[0] 581 582 local tab = {} 583 local tabextra = {} 584 585 local kinds = { 586 [tonumber(C.CXToken_Punctuation)] = 'Punctuation', 587 [tonumber(C.CXToken_Keyword)] = 'Keyword', 588 [tonumber(C.CXToken_Identifier)] = 'Identifier', 589 [tonumber(C.CXToken_Literal)] = 'Literal', 590 [tonumber(C.CXToken_Comment)] = 'Comment', 591 } 592 593 for i=0,numtoks-1 do 594 if (clang.clang_getTokenKind(tokens[i]) ~= 'CXToken_Comment') then 595 local sourcerange = clang.clang_getTokenExtent(cxtu, tokens[i]) 596 local Beg, End, filename = getBegEndFilename(sourcerange) 597 local tb, te = Beg.offset, End.offset 598 599 if (tb >= b and te <= e) then 600 local kind = clang.clang_getTokenKind(tokens[i]) 601 local extent = getString(clang.clang_getTokenSpelling(cxtu, tokens[i])) 602 tab[#tab+1] = extent 603 tabextra[#tabextra+1] = { 604 extent = extent, 605 kind = kinds[tonumber(kind)], 606 b = b, e = e, 607 tb = tb, te = te 608 } 609 end 610 end 611 end 612 613 clang.clang_disposeTokens(cxtu, tokens, numtoks) 614 615 return tab, tabextra 616 end, 617 618 lexicalParent = function(self) 619 return getCursor(clang.clang_getCursorLexicalParent(self._cur)) 620 end, 621 622 baseTemplate = function(self) 623 return getCursor(clang.clang_getSpecializedCursorTemplate(self._cur)) 624 end, 625 626 -- Returns an enumeration constant, which in LuaJIT can be compared 627 -- against a *string*, too. 628 -- XXX: Should we split into 'kindenum' (giving the enum) and 'kindnum'? 629 kindnum = function(self) 630 return clang.clang_getCursorKind(self._cur) 631 end, 632 633 haskind = function(self, kind) 634 if (type(kind) == "string") then 635 return self:kindnum() == "CXCursor_"..kind 636 else 637 return self:kindnum() == kind 638 end 639 end, 640 641 enumValue = function(self, unsigned) 642 if (not self:haskind("EnumConstantDecl")) then 643 error("cursor must have kind EnumConstantDecl", 2) 644 end 645 646 if (unsigned) then 647 return clang.clang_getEnumConstantDeclUnsignedValue(self._cur) 648 else 649 return clang.clang_getEnumConstantDeclValue(self._cur) 650 end 651 end, 652 653 enumval = function(self, unsigned) 654 return tonumber(self:enumValue(unsigned)) 655 end, 656 657 isDefinition = function(self) 658 return (clang.clang_isCursorDefinition(self._cur) ~= 0) 659 end, 660 --[=[ 661 --| tab = cur:argtypes([alsoret]) 662 argtypes = function(self, alsoret) 663 if (clang.clang_Cursor_getNumArguments(self._cur) == -1) then 664 return nil 665 end 666 667 local tab = self:arguments() 668 669 if (alsoret) then 670 tab[0] = self:resultType() 671 end 672 673 for i=1,#tab do 674 tab[i] = tab[i]:type() 675 end 676 end, 677 --]=] 678 typedefType = function(self) 679 return getType(clang.clang_getTypedefDeclUnderlyingType(self._cur)) 680 end, 681 }, 682 } 683 684 Cursor_mt.__tostring = Cursor_mt.__index.name 685 686 ------------------------------------------------------------------------- 687 ---------------------------------- Type --------------------------------- 688 ------------------------------------------------------------------------- 689 690 -- Metatable for our Type_t. 691 local Type_mt = { 692 __eq = function(typ1, typ2) 693 if (ffi.istype(Type_t, typ1) and ffi.istype(Type_t, typ2)) then 694 return (clang.clang_equalTypes(typ1._typ, typ2._typ) ~= 0) 695 else 696 return false 697 end 698 end, 699 700 __index = { 701 name = function(self) 702 return getString(clang.clang_getTypeSpelling(self._typ)) 703 end, 704 705 canonical = function(self) 706 -- NOTE: no dispatching to getPointeeType() for pointer types like 707 -- luaclang-parser. 708 return getType(clang.clang_getCanonicalType(self._typ)) 709 end, 710 711 pointee = function(self) 712 return getType(clang.clang_getPointeeType(self._typ)) 713 end, 714 715 resultType = function(self) 716 return getType(clang.clang_getResultType(self._typ)) 717 end, 718 719 arrayElementType = function(self) 720 return getType(clang.clang_getArrayElementType(self._typ)) 721 end, 722 723 arraySize = function(self) 724 return tonumber(clang.clang_getArraySize(self._typ)) 725 end, 726 727 isConst = function(self) 728 return (clang.clang_isConstQualifiedType(self._typ) ~= 0); 729 end, 730 731 isPod = function(self) 732 return (clang.clang_isPODType(self._typ) ~= 0); 733 end, 734 735 isFinal = function(self) 736 return (clang.clang_isFinalType(self._typ) ~= 0); 737 end, 738 739 isAbstract = function(self) 740 return (clang.clang_isAbstractType(self._typ) ~= 0); 741 end, 742 743 isNoexcept = function(self) 744 return (clang.clang_isNoexcept(self._typ) ~= 0); 745 end, 746 747 declaration = function(self) 748 return getCursor(clang.clang_getTypeDeclaration(self._typ)) 749 end, 750 751 --== LJClang-specific ==-- 752 -- Returns an enumeration constant. 753 kindnum = function(self) 754 return self._typ.kind 755 end, 756 757 haskind = function(self, kind) 758 if (type(kind) == "string") then 759 return self:kindnum() == "CXType_"..kind 760 else 761 return self:kindnum() == kind 762 end 763 end, 764 765 templateArguments = function(self) 766 local tab = {} 767 local numargs = clang.clang_Type_getNumTemplateArguments(self._typ) 768 for i=1,numargs do 769 tab[i] = getType(clang.clang_Type_getTemplateArgumentAsType(self._typ, i-1)) 770 end 771 return tab 772 end, 773 }, 774 } 775 776 -- Aliases 777 local Type__index = Type_mt.__index 778 Type__index.isConstQualified = Type__index.isConst 779 780 Type_mt.__tostring = Type__index.name 781 782 ------------------------------------------------------------------------- 783 784 function api.clangVersion() 785 return getString(clang.clang_getClangVersion()) 786 end 787 788 --| index = clang.createIndex([excludeDeclarationsFromPCH [, displayDiagnostics]]) 789 function api.createIndex(excludeDeclarationsFromPCH, displayDiagnostics) 790 local cxidx = clang.clang_createIndex(excludeDeclarationsFromPCH or false, 791 displayDiagnostics or false) 792 if (cxidx == nil) then 793 return nil 794 end 795 796 return NewIndex(cxidx) 797 end 798 799 -- argstab = clang.splitAtWhitespace(args) 800 function api.splitAtWhitespace(args) 801 assert(type(args) == "string") 802 local argstab = {} 803 -- Split delimited by whitespace. 804 for str in args:gmatch("[^%s]+") do 805 argstab[#argstab+1] = str 806 end 807 return argstab 808 end 809 810 -- Is <tab> a sequence of strings? 811 local function iscellstr(tab) 812 for i=1,#tab do 813 if (type(tab[i]) ~= "string") then 814 return false 815 end 816 end 817 818 -- We require this because in ARGS_FROM_TAB below, an index 0 would be 819 -- interpreted as the starting index. 820 return (tab[0] == nil) 821 end 822 823 local function check_iftab_iscellstr(tab, name) 824 if (type(tab)=="table") then 825 if (not iscellstr(tab)) then 826 error(name.." must be a string sequence when a table with no element at [0]", 3) 827 end 828 end 829 end 830 831 --| tunit = index:parse(srcfile, args [, opts]) 832 --| 833 --| <args>: string or sequence of strings 834 --| <opts>: number or sequence of strings (CXTranslationUnit_* enum members, 835 --| without the prefix) 836 function Index_mt.__index.parse(self, srcfile, args, opts) 837 check(type(srcfile)=="string", "<srcfile> must be a string", 2) 838 check(type(args)=="string" or type(args)=="table", "<args> must be a string or table", 2) 839 check_iftab_iscellstr(args, "<args>") 840 841 if (srcfile == "") then 842 srcfile = nil 843 end 844 845 if (opts == nil) then 846 opts = 0 847 else 848 check(type(opts)=="number" or type(opts)=="table", 2) 849 check_iftab_iscellstr(args, "<opts>") 850 end 851 852 -- Input argument handling. 853 854 if (type(args)=="string") then 855 args = api.splitAtWhitespace(args) 856 end 857 858 if (type(opts)=="table") then 859 local optflags = {} 860 for i=1,#opts do 861 optflags[i] = clang["CXTranslationUnit_"..opts[i]] -- look up the enum 862 end 863 opts = bit.bor(unpack(optflags)) 864 end 865 866 local argsptrs = ffi.new("const char * [?]", #args, args) -- ARGS_FROM_TAB 867 868 -- Create the CXTranslationUnit. 869 local tunitptr = clang.clang_parseTranslationUnit( 870 self._idx, srcfile, argsptrs, #args, nil, 0, opts) 871 872 if (tunitptr == nil) then 873 return nil 874 end 875 876 -- Wrap it in a TranslationUnit_t. 877 local tunit = TranslationUnit_t(tunitptr) 878 879 -- Add this TranslationUnit_t to the list of its Index_t's TUs. 880 self._tus[#self._tus+1] = tunit 881 882 return tunit 883 end 884 885 886 -- enum CXChildVisitResult constants 887 api.ChildVisitResult = ffi.new[[struct{ 888 static const int Break = 0; 889 static const int Continue = 1; 890 static const int Recurse = 2; 891 }]] 892 893 function api.regCursorVisitor(visitorfunc) 894 check(type(visitorfunc)=="function", "<visitorfunc> must be a Lua function", 2) 895 896 local ret = support.ljclang_regCursorVisitor(visitorfunc, nil, 0) 897 if (ret < 0) then 898 error("failed registering visitor function, code "..ret, 2) 899 end 900 901 return ret 902 end 903 904 local Cursor_ptr_t = ffi.typeof("$ *", Cursor_t) 905 906 function api.Cursor(cur) 907 check(ffi.istype(Cursor_ptr_t, cur), "<cur> must be a cursor as passed to the visitor callback", 2) 908 return Cursor_t(cur[0]) 909 end 910 911 -- Support for legacy luaclang-parser API collecting direct descendants of a 912 -- cursor: this will be the table where they go. 913 local collectTab 914 915 local function collectDirectChildren(cur) 916 debugf("collectDirectChildren: %s, child cursor kind: %s", tostring(collectTab), cur:kind()) 917 collectTab[#collectTab+1] = Cursor_t(cur[0]) 918 return 1 -- Continue 919 end 920 921 local cdc_visitoridx = api.regCursorVisitor(collectDirectChildren) 922 923 function Cursor_mt.__index.children(self, visitoridx) 924 if (visitoridx ~= nil) then 925 -- LJClang way of visiting 926 local ret = support.ljclang_visitChildren(self._cur, visitoridx) 927 return (ret ~= 0) 928 else 929 -- luaclang-parser way 930 if (collectTab ~= nil) then 931 error("children() must not be called while another invocation is active", 2) 932 collectTab = nil 933 end 934 935 collectTab = {} 936 -- XXX: We'll be blocked if the visitor callback errors. 937 support.ljclang_visitChildren(self._cur, cdc_visitoridx) 938 local tab = collectTab 939 collectTab = nil 940 return tab 941 end 942 end 943 944 945 -- Register the metatables for the custom ctypes. 946 ffi.metatype(Index_t, Index_mt) 947 ffi.metatype(TranslationUnit_t_, TranslationUnit_mt) 948 ffi.metatype(Cursor_t, Cursor_mt) 949 ffi.metatype(Type_t, Type_mt) 950 951 ffi.metatype(CompileCommand_t, CompileCommand_mt) 952 ffi.metatype(CompileCommands_t, CompileCommands_mt) 953 ffi.metatype(CompilationDatabase_t, CompilationDatabase_mt) 954 955 api.Index_t = Index_t 956 api.TranslationUnit_t = TranslationUnit_t_ 957 api.Cursor_t = Cursor_t 958 api.Type_t = Type_t 959 960 -- Done! 961 return api