• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lunarmodules / luacheck / 24662353255

20 Apr 2026 10:48AM UTC coverage: 97.095% (+0.07%) from 97.027%
24662353255

push

github

web-flow
fix(ci): build LuaJIT using system malloc for lualanes tests (#144)

6318 of 6507 relevant lines covered (97.1%)

26928.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.19
/src/luacheck/utils.lua
1
-- luacheck: push compat
2
local unpack = table.unpack or unpack
930✔
3
local pack = table.pack or function(...) return {n = select("#", ...), ...} end
4,737✔
4
-- luacheck: pop
5

6
local utils = {}
930✔
7

8
utils.dir_sep = package.config:sub(1,1)
1,240✔
9
utils.is_windows = utils.dir_sep == "\\"
930✔
10

11
local bom = "\239\187\191"
930✔
12

13
-- Returns all contents of file (path or file handler) or nil + error message.
14
function utils.read_file(file)
930✔
15
   local handler
16

17
   if type(file) == "string" then
2,082✔
18
      local open_err
19
      handler, open_err = io.open(file, "rb")
2,064✔
20

21
      if not handler then
2,064✔
22
         open_err = utils.unprefix(open_err, file .. ": ")
112✔
23
         return nil, "couldn't read: " .. open_err
84✔
24
      end
25
   else
26
      handler = file
18✔
27
   end
28

29
   local res, read_err = handler:read("*a")
1,998✔
30
   handler:close()
1,998✔
31

32
   if not res then
1,998✔
33
      return nil, "couldn't read: " .. read_err
6✔
34
   end
35

36
   -- Use :len() instead of # operator because in some environments
37
   -- string library is patched to handle UTF.
38
   if res:sub(1, bom:len()) == bom then
3,320✔
39
      res = res:sub(bom:len() + 1)
10✔
40
   end
41

42
   return res
1,992✔
43
end
44

45
-- luacheck: push
46
-- luacheck: compat
47
if _VERSION:find "5.1" then
930✔
48
   -- Loads Lua source string in an environment, returns function or nil, error.
49
   function utils.load(src, env, chunkname)
465✔
50
      local func, err = loadstring(src, chunkname)
198✔
51

52
      if func then
198✔
53
         if env then
174✔
54
            setfenv(func, env)
168✔
55
         end
56

57
         return func
174✔
58
      else
59
         return nil, err
24✔
60
      end
61
   end
62
else
63
   -- Loads Lua source string in an environment, returns function or nil, error.
64
   function utils.load(src, env, chunkname)
465✔
65
      return load(src, chunkname, "t", env or _ENV)
198✔
66
   end
67
end
68
-- luacheck: pop
69

70
-- Loads config containing assignments to global variables from path.
71
-- Returns config table and return value of config or nil and error type
72
-- ("I/O" or "syntax" or "runtime") and error message.
73
function utils.load_config(path, env)
930✔
74
   env = env or {}
378✔
75
   local src, read_err = utils.read_file(path)
378✔
76

77
   if not src then
378✔
78
      return nil, "I/O", read_err
12✔
79
   end
80

81
   local func, load_err = utils.load(src, env, "chunk")
366✔
82

83
   if not func then
366✔
84
      return nil, "syntax", "line " .. utils.unprefix(load_err, "[string \"chunk\"]:")
56✔
85
   end
86

87
   local ok, res = pcall(func)
324✔
88

89
   if not ok then
324✔
90
      return nil, "runtime", "line " .. utils.unprefix(res, "[string \"chunk\"]:")
40✔
91
   end
92

93
   return env, res
294✔
94
end
95

96
function utils.array_to_set(array)
930✔
97
   local set = {}
60,900✔
98

99
   for index, value in ipairs(array) do
420,210✔
100
      set[value] = index
359,310✔
101
   end
102

103
   return set
60,900✔
104
end
105

106
function utils.concat_arrays(array)
930✔
107
   local res = {}
48,726✔
108

109
   for _, subarray in ipairs(array) do
146,190✔
110
      for _, item in ipairs(subarray) do
392,418✔
111
         table.insert(res, item)
294,954✔
112
      end
113
   end
114

115
   return res
48,726✔
116
end
117

118
function utils.update(t1, t2)
930✔
119
   for k, v in pairs(t2) do
84,738✔
120
      t1[k] = v
72,978✔
121
   end
122

123
   return t1
11,760✔
124
end
125

126
local class_metatable = {}
930✔
127

128
function class_metatable.__call(class, ...)
930✔
129
   local obj = setmetatable({}, class)
47,580✔
130

131
   if class.__init then
47,580✔
132
      local init_returns = pack(class.__init(obj, ...))
61,776✔
133

134
      if init_returns.n > 0 then
46,332✔
135
         return unpack(init_returns, 1, init_returns.n)
6✔
136
      end
137
   end
138

139
   return obj
47,574✔
140
end
141

142
function utils.class()
930✔
143
   local class = setmetatable({}, class_metatable)
12,888✔
144
   class.__index = class
12,888✔
145
   return class
12,888✔
146
end
147

148
function utils.is_instance(object, class)
930✔
149
   return rawequal(debug.getmetatable(object), class)
222✔
150
end
151

152
utils.Stack = utils.class()
1,240✔
153

154
function utils.Stack:__init()
1,860✔
155
   self.size = 0
9,048✔
156
end
157

158
function utils.Stack:push(value)
1,860✔
159
   self.size = self.size + 1
40,596✔
160
   self[self.size] = value
40,596✔
161
   self.top = value
40,596✔
162
end
163

164
function utils.Stack:pop()
1,860✔
165
   local value = self[self.size]
17,430✔
166
   self[self.size] = nil
17,430✔
167
   self.size = self.size - 1
17,430✔
168
   self.top = self[self.size]
17,430✔
169
   return value
17,430✔
170
end
171

172
local ErrorWrapper = utils.class()
930✔
173

174
function ErrorWrapper:__init(err, traceback)
930✔
175
   self.err = err
114✔
176
   self.traceback = traceback
114✔
177
end
178

179
function ErrorWrapper:__tostring()
930✔
180
   return tostring(self.err) .. "\n" .. self.traceback
×
181
end
182

183
local function error_handler(err)
184
   if utils.is_instance(err, ErrorWrapper) then
160✔
185
      return err
6✔
186
   else
187
      return ErrorWrapper(err, debug.traceback())
114✔
188
   end
189
end
190

191
-- Like pcall, but wraps errors in {err = err, traceback = traceback}
192
-- tables unless already wrapped.
193
function utils.try(f, ...)
930✔
194
   local args = {...}
1,608✔
195
   local num_args = select("#", ...)
1,608✔
196

197
   local function task()
198
      return f(unpack(args, 1, num_args))
1,608✔
199
   end
200

201
   return xpcall(task, error_handler)
1,608✔
202
end
203

204
local function ripairs_iterator(array, i)
205
   if i == 1 then
185,592✔
206
      return nil
28,734✔
207
   else
208
      i = i - 1
156,858✔
209
      return i, array[i]
156,858✔
210
   end
211
end
212

213
function utils.ripairs(array)
930✔
214
   return ripairs_iterator, array, #array + 1
48,984✔
215
end
216

217
function utils.sorted_pairs(t)
930✔
218
   local keys = {}
13,564✔
219

220
   for key in pairs(t) do
351,800✔
221
      table.insert(keys, key)
338,236✔
222
   end
223

224
   table.sort(keys)
13,564✔
225

226
   local index = 1
13,564✔
227

228
   return function()
229
      local key = keys[index]
349,850✔
230

231
      if key == nil then
349,850✔
232
         return
13,396✔
233
      end
234

235
      index = index + 1
336,454✔
236

237
      return key, t[key]
336,454✔
238
   end
239
end
240

241
function utils.unprefix(str, prefix)
930✔
242
   if str:sub(1, #prefix) == prefix then
1,224✔
243
      return str:sub(#prefix + 1)
918✔
244
   else
245
      return str
×
246
   end
247
end
248

249
function utils.after(str, pattern)
930✔
250
   local _, last_matched_index = str:find(pattern)
1,104✔
251

252
   if last_matched_index then
1,104✔
253
      return str:sub(last_matched_index + 1)
684✔
254
   end
255
end
256

257
function utils.strip(str)
930✔
258
   local _, last_start_space = str:find("^%s*")
9,394✔
259
   local first_end_space = str:find("%s*$")
9,394✔
260
   return str:sub(last_start_space + 1, first_end_space - 1)
9,394✔
261
end
262

263
-- `sep` must be nil or a single character. Behaves like python's `str.split`.
264
function utils.split(str, sep)
930✔
265
   local parts = {}
8,548✔
266
   local pattern
267

268
   if sep then
8,548✔
269
      pattern = sep .. "([^" .. sep .. "]*)"
8,032✔
270
      str = sep .. str
8,032✔
271
   else
272
      pattern = "%S+"
516✔
273
   end
274

275
   for part in str:gmatch(pattern) do
22,896✔
276
      table.insert(parts, part)
14,348✔
277
   end
278

279
   return parts
8,548✔
280
end
281

282
utils.InvalidPatternError = utils.class()
1,240✔
283

284
function utils.InvalidPatternError:__init(err, pattern)
1,860✔
285
   self.err = err
6✔
286
   self.pattern = pattern
6✔
287
end
288

289
function utils.InvalidPatternError:__tostring()
1,860✔
290
   return self.err
×
291
end
292

293
-- Behaves like string.match, except it normally returns boolean and
294
-- throws an instance of utils.InvalidPatternError on invalid pattern.
295
-- The error object turns into original error when tostring is used on it,
296
-- to ensure behaviour is predictable when luacheck is used as a module.
297
function utils.pmatch(str, pattern)
930✔
298
   assert(type(str) == "string")
95,346✔
299
   assert(type(pattern) == "string")
95,346✔
300

301
   local ok, res = pcall(string.match, str, pattern)
95,346✔
302

303
   if not ok then
95,346✔
304
      error(utils.InvalidPatternError(res, pattern), 0)
8✔
305
   else
306
      return not not res
95,340✔
307
   end
308
end
309

310
-- Maps func over array.
311
function utils.map(func, array)
930✔
312
   local res = {}
474✔
313

314
   for i, item in ipairs(array) do
948✔
315
      res[i] = func(item)
626✔
316
   end
317

318
   return res
474✔
319
end
320

321
-- Returns validator checking type.
322
function utils.has_type(type_)
930✔
323
   return function(x)
324
      if type(x) == type_ then
3,192✔
325
         return true
3,162✔
326
      else
327
         return false, ("%s expected, got %s"):format(type_, type(x))
30✔
328
      end
329
   end
330
end
331

332
-- Returns validator checking type and allowing false.
333
function utils.has_type_or_false(type_)
930✔
334
   return function(x)
335
      if type(x) == type_ then
1,056✔
336
         return true
432✔
337
      elseif type(x) == "boolean" then
624✔
338
         if x then
618✔
339
            return false, ("%s or false expected, got true"):format(type_)
6✔
340
         else
341
            return true
612✔
342
         end
343
      else
344
         return false, ("%s or false expected, got %s"):format(type_, type(x))
6✔
345
      end
346
   end
347
end
348

349
-- Returns validator checking two type possibilities.
350
function utils.has_either_type(type1, type2)
930✔
351
   return function(x)
352
      if type(x) == type1 or type(x) == type2 then
120✔
353
         return true
120✔
354
      else
355
         return false, ("%s or %s expected, got %s"):format(type1, type2, type(x))
×
356
      end
357
   end
358
end
359

360
-- Returns validator checking that value is an array with elements of type.
361
function utils.array_of(type_)
930✔
362
   return function(x)
363
      if type(x) ~= "table" then
930✔
364
         return false, ("array of %ss expected, got %s"):format(type_, type(x))
36✔
365
      end
366

367
      for index, item in ipairs(x) do
1,992✔
368
         if type(item) ~= type_ then
1,098✔
369
            return false, ("array of %ss expected, got %s at index [%d]"):format(type_, type(item), index)
×
370
         end
371
      end
372

373
      return true
894✔
374
   end
375
end
376

377
return utils
930✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc