diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua index 7f3dffd22c..f9a4631b7e 100644 --- a/scripts/lintcommit.lua +++ b/scripts/lintcommit.lua @@ -88,7 +88,7 @@ local function validate_commit(commit_message) -- Check if type is correct local type = vim.split(before_colon, "(", {plain = true})[1] local allowed_types = {'build', 'ci', 'docs', 'feat', 'fix', 'perf', 'refactor', 'revert', 'test', 'vim-patch'} - if not vim.list_contains(allowed_types, type) then + if not vim.tbl_contains(allowed_types, type) then return string.format( [[Invalid commit type "%s". Allowed types are: %s. diff --git a/test/helpers.lua b/test/helpers.lua index 34c2036798..8f06311a3c 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -20,12 +20,16 @@ local module = { REMOVE_THIS = {}, } +--- @param p string +--- @return string local function relpath(p) p = vim.fs.normalize(p) local cwd = luv.cwd() return p:gsub("^" .. cwd) end +--- @param path string +--- @return boolean function module.isdir(path) if not path then return false @@ -37,6 +41,8 @@ function module.isdir(path) return stat.type == 'directory' end +--- @param path string +--- @return boolean function module.isfile(path) if not path then return false @@ -48,6 +54,7 @@ function module.isfile(path) return stat.type == 'file' end +--- @return string function module.argss_to_cmd(...) local cmd = '' for i = 1, select('#', ...) do @@ -462,6 +469,7 @@ function module.check_cores(app, force) -- luacheck: ignore end end +--- @return string? function module.repeated_read_cmd(...) for _ = 1, 10 do local stream = module.popen_r(...) @@ -561,6 +569,9 @@ function module.concat_tables(...) return ret end +--- @param str string +--- @param leave_indent? boolean +--- @return string function module.dedent(str, leave_indent) -- find minimum common indent across lines local indent = nil diff --git a/test/unit/formatc.lua b/test/unit/formatc.lua index 2fd37c599a..c94f0d88f7 100644 --- a/test/unit/formatc.lua +++ b/test/unit/formatc.lua @@ -154,6 +154,8 @@ local C_keywords = set { -- luacheck: ignore -- -- The first one will have a lot of false positives (the line '{' for -- example), the second one is more unique. +--- @param string +--- @return string local function formatc(str) local toks = TokeniseC(str) local result = {} diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 708929ad9f..10b7594a88 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -14,20 +14,15 @@ local map = global_helpers.tbl_map local eq = global_helpers.eq local trim = global_helpers.trim --- C constants. -local NULL = ffi.cast('void*', 0) - -local OK = 1 -local FAIL = 0 - -local cimport - -- add some standard header locations for _, p in ipairs(Paths.include_paths) do Preprocess.add_to_include_path(p) end -local child_pid = nil +local child_pid = nil --- @type integer +--- @generic F: function +--- @param func F +--- @return F local function only_separate(func) return function(...) if child_pid ~= 0 then @@ -36,9 +31,20 @@ local function only_separate(func) return func(...) end end -local child_calls_init = {} -local child_calls_mod = nil -local child_calls_mod_once = nil + +--- @class ChildCall +--- @field func function +--- @field args any[] + +--- @class ChildCallLog +--- @field func string +--- @field args any[] +--- @field ret any? + +local child_calls_init = {} --- @type ChildCall[] +local child_calls_mod = nil --- @type ChildCall[] +local child_calls_mod_once = nil --- @type ChildCall[]? + local function child_call(func, ret) return function(...) local child_calls = child_calls_mod or child_calls_init @@ -53,16 +59,16 @@ end -- Run some code at the start of the child process, before running the test -- itself. Is supposed to be run in `before_each`. +--- @param func function local function child_call_once(func, ...) if child_pid ~= 0 then - child_calls_mod_once[#child_calls_mod_once + 1] = { - func=func, args={...}} + child_calls_mod_once[#child_calls_mod_once + 1] = { func = func, args = {...} } else func(...) end end -local child_cleanups_mod_once = nil +local child_cleanups_mod_once = nil --- @type ChildCall[]? -- Run some code at the end of the child process, before exiting. Is supposed to -- be run in `before_each` because `after_each` is run after child has exited. @@ -125,8 +131,9 @@ local pragma_pack_id = 1 -- some things are just too complex for the LuaJIT C parser to digest. We -- usually don't need them anyway. +--- @param body string local function filter_complex_blocks(body) - local result = {} + local result = {} --- @type string[] for line in body:gmatch("[^\r\n]+") do if not (string.find(line, "(^)", 1, true) ~= nil @@ -153,18 +160,20 @@ typedef struct { char bytes[16]; } __attribute__((aligned(16))) __uint128_t; typedef struct { char bytes[16]; } __attribute__((aligned(16))) __float128; ]] -local preprocess_cache_init = {} +local preprocess_cache_init = {} --- @type table local previous_defines_mod = '' -local preprocess_cache_mod = nil +local preprocess_cache_mod = nil --- @type table local function is_child_cdefs() - return (os.getenv('NVIM_TEST_MAIN_CDEFS') ~= '1') + return os.getenv('NVIM_TEST_MAIN_CDEFS') ~= '1' end -- use this helper to import C files, you can pass multiple paths at once, -- this helper will return the C namespace of the nvim library. -cimport = function(...) - local previous_defines, preprocess_cache, cdefs +local function cimport(...) + local previous_defines --- @type string + local preprocess_cache --- @type table + local cdefs if is_child_cdefs() and preprocess_cache_mod then preprocess_cache = preprocess_cache_mod previous_defines = previous_defines_mod @@ -180,7 +189,7 @@ cimport = function(...) path = './' .. path end if not preprocess_cache[path] then - local body + local body --- @type string body, previous_defines = Preprocess.preprocess(previous_defines, path) -- format it (so that the lines are "unique" statements), also filter out -- Objective-C blocks @@ -202,6 +211,7 @@ cimport = function(...) -- (they are needed in the right order with the struct definitions, -- otherwise luajit has wrong memory layouts for the sturcts) if line:match("#pragma%s+pack") then + --- @type string line = line .. " // " .. pragma_pack_id pragma_pack_id = pragma_pack_id + 1 end @@ -229,20 +239,21 @@ cimport = function(...) return lib end -local cimport_immediate = function(...) +local function cimport_immediate(...) local saved_pid = child_pid child_pid = 0 local err, emsg = pcall(cimport, ...) child_pid = saved_pid if not err then - emsg = tostring(emsg) - io.stderr:write(emsg .. '\n') + io.stderr:write(tostring(emsg) .. '\n') assert(false) else return lib end end +--- @param preprocess_cache table +--- @param path string local function _cimportstr(preprocess_cache, path) if imported:contains(path) then return lib @@ -265,12 +276,14 @@ end local function alloc_log_new() local log = { - log={}, - lib=cimport('./src/nvim/memory.h'), - original_functions={}, + log={}, --- @type ChildCallLog[] + lib=cimport('./src/nvim/memory.h'), --- @type table + original_functions={}, --- @type table null={['\0:is_null']=true}, } + local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'} + function log:save_original_functions() for _, funcname in ipairs(allocator_functions) do if not self.original_functions[funcname] then @@ -278,13 +291,16 @@ local function alloc_log_new() end end end + log.save_original_functions = child_call(log.save_original_functions) + function log:set_mocks() for _, k in ipairs(allocator_functions) do do local kk = k self.lib['mem_' .. k] = function(...) - local log_entry = {func=kk, args={...}} + --- @type ChildCallLog + local log_entry = { func = kk, args = {...} } self.log[#self.log + 1] = log_entry if kk == 'free' then self.original_functions[kk](...) @@ -305,17 +321,21 @@ local function alloc_log_new() end end end + log.set_mocks = child_call(log.set_mocks) + function log:clear() self.log = {} end + function log:check(exp) eq(exp, self.log) self:clear() end + function log:clear_tmp_allocs(clear_null_frees) - local toremove = {} - local allocs = {} + local toremove = {} --- @type integer[] + local allocs = {} --- @type table for i, v in ipairs(self.log) do if v.func == 'malloc' or v.func == 'calloc' then allocs[tostring(v.ret)] = i @@ -338,26 +358,20 @@ local function alloc_log_new() table.remove(self.log, toremove[i]) end end - function log:restore_original_functions() - -- Do nothing: set mocks live in a separate process - return - --[[ - [ for k, v in pairs(self.original_functions) do - [ self.lib['mem_' .. k] = v - [ end - ]] - end + function log:setup() log:save_original_functions() log:set_mocks() end + function log:before_each() - return end + function log:after_each() - log:restore_original_functions() end + log:setup() + return log end @@ -374,98 +388,109 @@ local function to_cstr(string) end cimport_immediate('./test/unit/fixtures/posix.h') -local sc = { - fork = function() - return tonumber(ffi.C.fork()) - end, - pipe = function() - local ret = ffi.new('int[2]', {-1, -1}) - ffi.errno(0) - local res = ffi.C.pipe(ret) - if (res ~= 0) then - local err = ffi.errno(0) - assert(res == 0, ("pipe() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - assert(ret[0] ~= -1 and ret[1] ~= -1) - return ret[0], ret[1] - end, - read = function(rd, len) - local ret = ffi.new('char[?]', len, {0}) - local total_bytes_read = 0 - ffi.errno(0) - while total_bytes_read < len do - local bytes_read = tonumber(ffi.C.read( - rd, - ffi.cast('void*', ret + total_bytes_read), - len - total_bytes_read)) - if bytes_read == -1 then - local err = ffi.errno(0) - if err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("read() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - elseif bytes_read == 0 then - break - else - total_bytes_read = total_bytes_read + bytes_read - end - end - return ffi.string(ret, total_bytes_read) - end, - write = function(wr, s) - local wbuf = to_cstr(s) - local total_bytes_written = 0 - ffi.errno(0) - while total_bytes_written < #s do - local bytes_written = tonumber(ffi.C.write( - wr, - ffi.cast('void*', wbuf + total_bytes_written), - #s - total_bytes_written)) - if bytes_written == -1 then - local err = ffi.errno(0) - if err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("write() error: %u: %s ('%s')"):format( - err, ffi.string(ffi.C.strerror(err)), s)) - end - elseif bytes_written == 0 then - break - else - total_bytes_written = total_bytes_written + bytes_written - end - end - return total_bytes_written - end, - close = ffi.C.close, - wait = function(pid) - ffi.errno(0) - local stat_loc = ffi.new('int[1]', {0}) - while true do - local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED) - if r == -1 then - local err = ffi.errno(0) - if err == ffi.C.kPOSIXErrnoECHILD then - break - elseif err ~= ffi.C.kPOSIXErrnoEINTR then - assert(false, ("waitpid() error: %u: %s"):format( - err, ffi.string(ffi.C.strerror(err)))) - end - else - assert(r == pid) - end - end - return stat_loc[0] - end, - exit = ffi.C._exit, -} -local function format_list(lst) - local ret = '' - for _, v in ipairs(lst) do - if ret ~= '' then ret = ret .. ', ' end - ret = ret .. assert:format({v, n=1})[1] +local sc = {} + +function sc.fork() + return tonumber(ffi.C.fork()) +end + +function sc.pipe() + local ret = ffi.new('int[2]', {-1, -1}) + ffi.errno(0) + local res = ffi.C.pipe(ret) + if (res ~= 0) then + local err = ffi.errno(0) + assert(res == 0, ("pipe() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) end - return ret + assert(ret[0] ~= -1 and ret[1] ~= -1) + return ret[0], ret[1] +end + +--- @return string +function sc.read(rd, len) + local ret = ffi.new('char[?]', len, {0}) + local total_bytes_read = 0 + ffi.errno(0) + while total_bytes_read < len do + local bytes_read = tonumber(ffi.C.read( + rd, + ffi.cast('void*', ret + total_bytes_read), + len - total_bytes_read)) + if bytes_read == -1 then + local err = ffi.errno(0) + if err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("read() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) + end + elseif bytes_read == 0 then + break + else + total_bytes_read = total_bytes_read + bytes_read + end + end + return ffi.string(ret, total_bytes_read) +end + +function sc.write(wr, s) + local wbuf = to_cstr(s) + local total_bytes_written = 0 + ffi.errno(0) + while total_bytes_written < #s do + local bytes_written = tonumber(ffi.C.write( + wr, + ffi.cast('void*', wbuf + total_bytes_written), + #s - total_bytes_written)) + if bytes_written == -1 then + local err = ffi.errno(0) + if err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("write() error: %u: %s ('%s')"):format( + err, ffi.string(ffi.C.strerror(err)), s)) + end + elseif bytes_written == 0 then + break + else + total_bytes_written = total_bytes_written + bytes_written + end + end + return total_bytes_written +end + +sc.close = ffi.C.close + +--- @param pid integer +--- @return integer +function sc.wait(pid) + ffi.errno(0) + local stat_loc = ffi.new('int[1]', {0}) + while true do + local r = ffi.C.waitpid(pid, stat_loc, ffi.C.kPOSIXWaitWUNTRACED) + if r == -1 then + local err = ffi.errno(0) + if err == ffi.C.kPOSIXErrnoECHILD then + break + elseif err ~= ffi.C.kPOSIXErrnoEINTR then + assert(false, ("waitpid() error: %u: %s"):format( + err, ffi.string(ffi.C.strerror(err)))) + end + else + assert(r == pid) + end + end + return stat_loc[0] +end + +sc.exit = ffi.C._exit + +--- @param lst string[] +--- @return string +local function format_list(lst) + local ret = {} --- @type string[] + for _, v in ipairs(lst) do + ret[#ret+1] = assert:format({v, n=1})[1] + end + return table.concat(ret, ', ') end if os.getenv('NVIM_TEST_PRINT_SYSCALLS') == '1' then @@ -513,19 +538,26 @@ local tracehelp = dedent([[ ]]) local function child_sethook(wr) - local trace_level = os.getenv('NVIM_TEST_TRACE_LEVEL') - if not trace_level or trace_level == '' then - trace_level = 0 - else - trace_level = tonumber(trace_level) + local trace_level_str = os.getenv('NVIM_TEST_TRACE_LEVEL') + local trace_level = 0 + if trace_level_str and trace_level_str ~= '' then + --- @type number + trace_level = assert(tonumber(trace_level_str)) end + if trace_level <= 0 then return end + local trace_only_c = trace_level <= 1 + --- @type debuginfo?, string?, integer local prev_info, prev_reason, prev_lnum + + --- @param reason string + --- @param lnum integer + --- @param use_prev boolean local function hook(reason, lnum, use_prev) - local info = nil + local info = nil --- @type debuginfo? if use_prev then info = prev_info elseif reason ~= 'tail return' then -- tail return @@ -533,6 +565,7 @@ local function child_sethook(wr) end if trace_only_c and (not info or info.what ~= 'C') and not use_prev then + --- @cast info -nil if info.source:sub(-9) == '_spec.lua' then prev_info = info prev_reason = 'saved' @@ -573,12 +606,8 @@ local function child_sethook(wr) end -- assert(-1 <= lnum and lnum <= 99999) - local lnum_s - if lnum == -1 then - lnum_s = 'nknwn' - else - lnum_s = ('%u'):format(lnum) - end + local lnum_s = lnum == -1 and 'nknwn' or ('%u'):format(lnum) + --- @type string local msg = ( -- lua does not support %* '' .. msgchar @@ -600,6 +629,7 @@ end local trace_end_msg = ('E%s\n'):format((' '):rep(hook_msglen - 2)) +--- @type function local _debug_log local debug_log = only_separate(function(...) @@ -607,6 +637,7 @@ local debug_log = only_separate(function(...) end) local function itp_child(wr, func) + --- @param s string _debug_log = function(s) s = s:sub(1, hook_msglen - 2) sc.write(wr, '>' .. s .. (' '):rep(hook_msglen - 2 - #s) .. '\n') @@ -638,7 +669,7 @@ local function itp_child(wr, func) end local function check_child_err(rd) - local trace = {} + local trace = {} --- @type string[] local did_traceline = false local maxtrace = tonumber(os.getenv('NVIM_TEST_MAXTRACE')) or 1024 while true do @@ -668,11 +699,14 @@ local function check_child_err(rd) local len = tonumber(len_s) neq(0, len) if os.getenv('NVIM_TEST_TRACE_ON_ERROR') == '1' and #trace ~= 0 then + --- @type string err = '\nTest failed, trace:\n' .. tracehelp for _, traceline in ipairs(trace) do + --- @type string err = err .. traceline end end + --- @type string err = err .. sc.read(rd, len + 1) end local eres = sc.read(rd, 2) @@ -686,10 +720,12 @@ local function check_child_err(rd) end end if not did_traceline then + --- @type string err = err .. '\nNo end of trace occurred' end local cc_err, cc_emsg = pcall(check_cores, Paths.test_luajit_prg, true) if not cc_err then + --- @type string err = err .. '\ncheck_cores failed: ' .. cc_emsg end end @@ -822,9 +858,9 @@ local module = { lib = lib, cstr = cstr, to_cstr = to_cstr, - NULL = NULL, - OK = OK, - FAIL = FAIL, + NULL = ffi.cast('void*', 0), + OK = 1, + FAIL = 0, alloc_log_new = alloc_log_new, gen_itp = gen_itp, only_separate = only_separate, diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index 88f7a14d1f..c5fb62dce5 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -7,6 +7,9 @@ local global_helpers = require('test.helpers') local argss_to_cmd = global_helpers.argss_to_cmd local repeated_read_cmd = global_helpers.repeated_read_cmd +--- @alias Compiler {path: string[], type: string} + +--- @type Compiler[] local ccs = {} local env_cc = os.getenv("CC") @@ -27,6 +30,8 @@ table.insert(ccs, {path = {"/usr/bin/env", "clang"}, type = "clang"}) table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"}) -- parse Makefile format dependencies into a Lua table +--- @param deps string +--- @return string[] local function parse_make_deps(deps) -- remove line breaks and line concatenators deps = deps:gsub("\n", ""):gsub("\\", "") @@ -36,7 +41,7 @@ local function parse_make_deps(deps) deps = deps:gsub(" +", " ") -- split according to token (space in this case) - local headers = {} + local headers = {} --- @type string[] for token in deps:gmatch("[^%s]+") do -- headers[token] = true headers[#headers + 1] = token @@ -53,57 +58,58 @@ local function parse_make_deps(deps) return headers end --- will produce a string that represents a meta C header file that includes --- all the passed in headers. I.e.: --- --- headerize({"stdio.h", "math.h"}, true) --- produces: --- #include --- #include --- --- headerize({"vim.h", "memory.h"}, false) --- produces: --- #include "vim.h" --- #include "memory.h" +--- will produce a string that represents a meta C header file that includes +--- all the passed in headers. I.e.: +--- +--- headerize({"stdio.h", "math.h"}, true) +--- produces: +--- #include +--- #include +--- +--- headerize({"vim.h", "memory.h"}, false) +--- produces: +--- #include "vim.h" +--- #include "memory.h" +--- @param headers string[] +--- @param global? boolean +--- @return string local function headerize(headers, global) - local pre = '"' - local post = pre - if global then - pre = "<" - post = ">" - end - - local formatted = {} + local fmt = global and '#include <%s>' or '#include "%s"' + local formatted = {} --- @type string[] for _, hdr in ipairs(headers) do - formatted[#formatted + 1] = "#include " .. - tostring(pre) .. - tostring(hdr) .. - tostring(post) + formatted[#formatted + 1] = string.format(fmt, hdr) end return table.concat(formatted, "\n") end +--- @class Gcc +--- @field path string +--- @field preprocessor_extra_flags string[] +--- @field get_defines_extra_flags string[] +--- @field get_declarations_extra_flags string[] local Gcc = { preprocessor_extra_flags = {}, get_defines_extra_flags = {'-std=c99', '-dM', '-E'}, get_declarations_extra_flags = {'-std=c99', '-P', '-E'}, } +--- @param name string +--- @param args string[]? +--- @param val string? function Gcc:define(name, args, val) - local define = '-D' .. name - if args ~= nil then - define = define .. '(' .. table.concat(args, ',') .. ')' + local define = string.format('-D%s', name) + if args then + define = string.format('%s(%s)', define, table.concat(args, ',')) end - if val ~= nil then - define = define .. '=' .. val + if val then + define = string.format('%s=%s', define, val) end self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = define end function Gcc:undefine(name) - self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = ( - '-U' .. name) + self.preprocessor_extra_flags[#self.preprocessor_extra_flags + 1] = '-U' .. name end function Gcc:init_defines() @@ -128,6 +134,8 @@ function Gcc:init_defines() self:undefine('__BLOCKS__') end +--- @param obj? Compiler +--- @return Gcc function Gcc:new(obj) obj = obj or {} setmetatable(obj, self) @@ -136,6 +144,7 @@ function Gcc:new(obj) return obj end +--- @param ... string function Gcc:add_to_include_path(...) for i = 1, select('#', ...) do local path = select(i, ...) @@ -145,110 +154,115 @@ function Gcc:add_to_include_path(...) end -- returns a list of the headers files upon which this file relies +--- @param hdr string +--- @return string[]? function Gcc:dependencies(hdr) + --- @type string local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1' - local out = io.popen(cmd) + local out = assert(io.popen(cmd)) local deps = out:read("*a") out:close() if deps then return parse_make_deps(deps) - else - return nil end end +--- @param defines string +--- @return string function Gcc:filter_standard_defines(defines) if not self.standard_defines then local pseudoheader_fname = 'tmp_empty_pseudoheader.h' - local pseudoheader_file = io.open(pseudoheader_fname, 'w') + local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w')) pseudoheader_file:close() - local standard_defines = repeated_read_cmd(self.path, - self.preprocessor_extra_flags, - self.get_defines_extra_flags, - {pseudoheader_fname}) + local standard_defines = assert(repeated_read_cmd(self.path, + self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname})) os.remove(pseudoheader_fname) - self.standard_defines = {} + self.standard_defines = {} --- @type table for line in standard_defines:gmatch('[^\n]+') do self.standard_defines[line] = true end end - local ret = {} + + local ret = {} --- @type string[] for line in defines:gmatch('[^\n]+') do if not self.standard_defines[line] then ret[#ret + 1] = line end end + return table.concat(ret, "\n") end --- returns a stream representing a preprocessed form of the passed-in headers. --- Don't forget to close the stream by calling the close() method on it. +--- returns a stream representing a preprocessed form of the passed-in headers. +--- Don't forget to close the stream by calling the close() method on it. +--- @param previous_defines string +--- @param ... string +--- @return string, string function Gcc:preprocess(previous_defines, ...) -- create pseudo-header local pseudoheader = headerize({...}, false) local pseudoheader_fname = 'tmp_pseudoheader.h' - local pseudoheader_file = io.open(pseudoheader_fname, 'w') + local pseudoheader_file = assert(io.open(pseudoheader_fname, 'w')) pseudoheader_file:write(previous_defines) pseudoheader_file:write("\n") pseudoheader_file:write(pseudoheader) pseudoheader_file:flush() pseudoheader_file:close() - local defines = repeated_read_cmd(self.path, self.preprocessor_extra_flags, - self.get_defines_extra_flags, - {pseudoheader_fname}) + local defines = assert(repeated_read_cmd(self.path, self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname})) defines = self:filter_standard_defines(defines) - local declarations = repeated_read_cmd(self.path, - self.preprocessor_extra_flags, - self.get_declarations_extra_flags, - {pseudoheader_fname}) + local declarations = assert(repeated_read_cmd(self.path, + self.preprocessor_extra_flags, + self.get_declarations_extra_flags, + {pseudoheader_fname})) os.remove(pseudoheader_fname) - assert(declarations and defines) return declarations, defines end -local Clang = Gcc:new() -local Msvc = Gcc:new() - -local type_to_class = { - ["gcc"] = Gcc, - ["clang"] = Clang, - ["msvc"] = Msvc -} - -- find the best cc. If os.exec causes problems on windows (like popping up -- a console window) we might consider using something like this: -- http://scite-ru.googlecode.com/svn/trunk/pack/tools/LuaLib/shell.html#exec +--- @param compilers Compiler[] +--- @return Gcc? local function find_best_cc(compilers) for _, meta in pairs(compilers) do - local version = io.popen(tostring(meta.path) .. " -v 2>&1") + local version = assert(io.popen(tostring(meta.path) .. " -v 2>&1")) version:close() if version then - return type_to_class[meta.type]:new({path = meta.path}) + return Gcc:new({path = meta.path}) end end - return nil end -- find the best cc. If os.exec causes problems on windows (like popping up -- a console window) we might consider using something like this: -- http://scite-ru.googlecode.com/svn/trunk/pack/tools/LuaLib/shell.html#exec -local cc = nil -if cc == nil then - cc = find_best_cc(ccs) +local cc = assert(find_best_cc(ccs)) + +local M = {} + +--- @param hdr string +--- @return string[]? +function M.includes(hdr) + return cc:dependencies(hdr) end -return { - includes = function(hdr) - return cc:dependencies(hdr) - end, - preprocess = function(...) - return cc:preprocess(...) - end, - add_to_include_path = function(...) - return cc:add_to_include_path(...) - end -} +--- @param ... string +--- @return string, string +function M.preprocess(...) + return cc:preprocess(...) +end + +--- @param ... string +function M.add_to_include_path(...) + return cc:add_to_include_path(...) +end + +return M diff --git a/test/unit/set.lua b/test/unit/set.lua index f3d68c3042..91b7e2dd70 100644 --- a/test/unit/set.lua +++ b/test/unit/set.lua @@ -4,10 +4,15 @@ -- other: -- 1) index => item -- 2) item => index +--- @class Set +--- @field nelem integer +--- @field items string[] +--- @field tbl table local Set = {} +--- @param items? string[] function Set:new(items) - local obj = {} + local obj = {} --- @ type Set setmetatable(obj, self) self.__index = self @@ -26,8 +31,9 @@ function Set:new(items) return obj end +--- @return Set function Set:copy() - local obj = {} + local obj = {} --- @ type Set obj.nelem = self.nelem obj.tbl = {} obj.items = {} @@ -43,6 +49,7 @@ function Set:copy() end -- adds the argument Set to this Set +--- @param other Set function Set:union(other) for e in other:iterator() do self:add(e) @@ -57,6 +64,7 @@ function Set:union_table(t) end -- subtracts the argument Set from this Set +--- @param other Set function Set:diff(other) if other:size() > self:size() then -- this set is smaller than the other set @@ -75,6 +83,7 @@ function Set:diff(other) end end +--- @param it string function Set:add(it) if not self:contains(it) then local idx = #self.tbl + 1 @@ -84,6 +93,7 @@ function Set:add(it) end end +--- @param it string function Set:remove(it) if self:contains(it) then local idx = self.items[it] @@ -93,10 +103,13 @@ function Set:remove(it) end end +--- @param it string +--- @return boolean function Set:contains(it) return self.items[it] or false end +--- @return integer function Set:size() return self.nelem end @@ -113,29 +126,18 @@ function Set:iterator() return pairs(self.items) end +--- @return string[] function Set:to_table() -- there might be gaps in @tbl, so we have to be careful and sort first - local keys - do - local _accum_0 = { } - local _len_0 = 1 - for idx, _ in pairs(self.tbl) do - _accum_0[_len_0] = idx - _len_0 = _len_0 + 1 - end - keys = _accum_0 + local keys = {} --- @type string[] + for idx, _ in pairs(self.tbl) do + keys[#keys+1] = idx end + table.sort(keys) - local copy - do - local _accum_0 = { } - local _len_0 = 1 - for _index_0 = 1, #keys do - local idx = keys[_index_0] - _accum_0[_len_0] = self.tbl[idx] - _len_0 = _len_0 + 1 - end - copy = _accum_0 + local copy = {} --- @type string[] + for _, idx in ipairs(keys) do + copy[#copy+1] = self.tbl[idx] end return copy end