• 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

96.07
/src/luacheck/main.lua
1
local argparse = require "argparse"
762✔
2
local cache = require "luacheck.cache"
762✔
3
local config = require "luacheck.config"
762✔
4
local luacheck = require "luacheck"
762✔
5
local multithreading = require "luacheck.multithreading"
762✔
6
local profiler = require "luacheck.profiler"
762✔
7
local runner = require "luacheck.runner"
762✔
8
local utils = require "luacheck.utils"
762✔
9
local version = require "luacheck.version"
762✔
10

11
local exit_codes = {
762✔
12
   ok = 0,
508✔
13
   warnings = 1,
508✔
14
   errors = 2,
508✔
15
   fatals = 3,
508✔
16
   critical = 4
508✔
17
}
18

19
local function critical(msg)
20
   io.stderr:write("Critical error: "..msg.."\n")
48✔
21
   os.exit(exit_codes.critical)
48✔
22
end
23

24
local function get_parser()
25
   local parser = argparse(
1,778✔
26
      "luacheck", "luacheck " .. luacheck._VERSION .. ", a linter and a static analyzer for Lua.", [[
762✔
27
Links:
28

29
   Luacheck on GitHub: https://github.com/lunarmodules/luacheck
30
   Luacheck documentation: https://luacheck.readthedocs.org]])
762✔
31
      :help_max_width(80)
762✔
32

33
   parser:argument("files", "List of files, directories and rockspecs to check. Pass '-' to check stdin.")
1,270✔
34
      :args "+"
762✔
35
      :argname "<file>"
762✔
36

37
   parser:group("Options for filtering warnings",
1,524✔
38
      parser:flag("-g --no-global", "Filter out warnings related to global variables. " ..
2,032✔
39
         "Equivalent to --ignore 1."):target("global"):action("store_false"),
2,032✔
40
      parser:flag("-u --no-unused", "Filter out warnings related to unused variables and values. " ..
2,032✔
41
         "Equivalent to --ignore [23]."):target("unused"):action("store_false"),
2,032✔
42
      parser:flag("-r --no-redefined", "Filter out warnings related to redefined variables. " ..
2,032✔
43
         "Equivalent to --ignore 4."):target("redefined"):action("store_false"),
2,032✔
44

45
      parser:flag("-a --no-unused-args", "Filter out warnings related to unused arguments and " ..
2,032✔
46
         "loop variables. Equivalent to --ignore 21[23]."):target("unused_args"):action("store_false"),
2,032✔
47
      parser:flag("-s --no-unused-secondaries", "Filter out warnings related to unused variables set " ..
2,032✔
48
         "together with used ones."):target("unused_secondaries"):action("store_false"),
2,032✔
49
      parser:flag("--no-self", "Filter out warnings related to implicit self argument.")
1,270✔
50
         :target("self"):action("store_false"),
1,270✔
51

52
      parser:option("--ignore -i", "Filter out warnings matching these patterns.\n" ..
2,794✔
53
         "If a pattern contains slash, part before slash matches warning code and part after it matches name of " ..
762✔
54
         "related variable. Otherwise, if the pattern contains letters or underscore, it matches name of " ..
1,143✔
55
         "related variable. Otherwise, the pattern matches warning code.")
762✔
56
         :args "+"
762✔
57
         :count "*"
762✔
58
         :argname "<patt>"
762✔
59
         :action "concat"
762✔
60
         :init(nil),
762✔
61
      parser:option("--enable -e", "Do not filter out warnings matching these patterns.")
2,032✔
62
         :args "+"
762✔
63
         :count "*"
762✔
64
         :argname "<patt>"
762✔
65
         :action "concat"
762✔
66
         :init(nil),
762✔
67
      parser:option("--only -o", "Filter out warnings not matching these patterns.")
2,032✔
68
         :args "+"
762✔
69
         :count "*"
762✔
70
         :argname "<patt>"
762✔
71
         :action "concat"
762✔
72
         :init(nil),
762✔
73
      parser:option("--operators", "Allow compound operators matching patterns")
2,032✔
74
         :args "+"
762✔
75
         :count "*"
762✔
76
         :argname "<patt>"
762✔
77
         :action "concat"
762✔
78
         :init(nil))
762✔
79

80
   parser:group("Options for configuring allowed globals",
1,524✔
81
      parser:option("--std", "Set standard globals, default is max. <std> can be one of:\n" ..
1,524✔
82
         "   max - union of globals of Lua 5.1, Lua 5.2, Lua 5.3 and LuaJIT 2.x;\n" ..
762✔
83
         "   min - intersection of globals of Lua 5.1, Lua 5.2, Lua 5.3 and LuaJIT 2.x;\n" ..
762✔
84
         "   lua51 - globals of Lua 5.1 without deprecated ones;\n" ..
762✔
85
         "   lua51c - globals of Lua 5.1;\n" ..
762✔
86
         "   lua52 - globals of Lua 5.2;\n" ..
762✔
87
         "   lua52c - globals of Lua 5.2 with LUA_COMPAT_ALL;\n" ..
762✔
88
         "   lua53 - globals of Lua 5.3;\n" ..
762✔
89
         "   lua53c - globals of Lua 5.3 with LUA_COMPAT_5_2;\n" ..
762✔
90
         "   lua54 - globals of Lua 5.4;\n" ..
762✔
91
         "   lua54c - globals of Lua 5.4 with LUA_COMPAT_5_3;\n" ..
762✔
92
         "   luajit - globals of LuaJIT 2.x;\n" ..
762✔
93
         "   ngx_lua - globals of Openresty lua-nginx-module 0.10.10, including standard LuaJIT 2.x globals;\n" ..
762✔
94
         "   love - globals added by LÖVE;\n" ..
762✔
95
         "   luanti - globals added by Luanti (formerly Minetest);\n" ..
762✔
96
         "   playdate - globals added by the Playdate SDK;\n" ..
762✔
97
         "   busted - globals added by Busted 2.0, by default added for files ending with _spec.lua within spec, " ..
762✔
98
         "test, and tests subdirectories;\n" ..
762✔
99
         "   rockspec - globals allowed in rockspecs, by default added for files ending with .rockspec;\n" ..
762✔
100
         "   luacheckrc - globals allowed in Luacheck configs, by default added for files ending with .luacheckrc;\n" ..
762✔
101
         "   none - no standard globals.\n\n" ..
762✔
102
         "Sets can be combined using '+'. Extra sets can be defined in config by " ..
1,143✔
103
         "adding to `stds` global in config."),
762✔
104
      parser:flag("-c --compat", "Equivalent to --std max."),
762✔
105

106
      parser:option("--globals", "Add custom global variables (e.g. foo) or fields (e.g. foo.bar) " ..
2,794✔
107
         "on top of standard ones.")
762✔
108
         :args "*"
762✔
109
         :count "*"
762✔
110
         :argname "<name>"
762✔
111
         :action "concat"
762✔
112
         :init(nil),
762✔
113
      parser:option("--read-globals", "Add read-only global variables or fields.")
2,032✔
114
         :args "*"
762✔
115
         :count "*"
762✔
116
         :argname "<name>"
762✔
117
         :action "concat"
762✔
118
         :init(nil),
762✔
119
      parser:option("--new-globals", "Set custom global variables or fields. Removes custom globals added previously.")
2,032✔
120
         :args "*"
762✔
121
         :count "*"
762✔
122
         :argname "<name>"
762✔
123
         :action "concat"
762✔
124
         :init(nil),
762✔
125
      parser:option("--new-read-globals", "Set read-only global variables or fields. " ..
2,794✔
126
         "Removes read-only globals added previously.")
762✔
127
         :args "*"
762✔
128
         :count "*"
762✔
129
         :argname "<name>"
762✔
130
         :action "concat"
762✔
131
         :init(nil),
762✔
132
      parser:option("--not-globals", "Remove custom and standard global variables or fields.")
2,032✔
133
         :args "*"
762✔
134
         :count "*"
762✔
135
         :argname "<name>"
762✔
136
         :action "concat"
762✔
137
         :init(nil),
762✔
138

139
      parser:flag("-d --allow-defined", "Allow defining globals implicitly by setting them."),
762✔
140
      parser:flag("-t --allow-defined-top",
1,524✔
141
         "Allow defining globals implicitly by setting them in the top level scope."),
762✔
142
      parser:flag("-m --module", "Limit visibility of implicitly defined globals to their files."))
762✔
143

144
   parser:group("Options for configuring line length limits",
1,524✔
145
      parser:option("--max-line-length", "Set maximum allowed line length (default: 120).")
1,270✔
146
         :argname "<length>"
762✔
147
         :convert(tonumber),
762✔
148
      parser:flag("--no-max-line-length", "Do not limit line length.")
1,270✔
149
         :action "store_false"
762✔
150
         :target "max_line_length",
762✔
151

152
      parser:option("--max-code-line-length", "Set maximum allowed length for lines ending with code (default: 120).")
1,270✔
153
         :argname "<length>"
762✔
154
         :convert(tonumber),
762✔
155
      parser:flag("--no-max-code-line-length", "Do not limit code line length.")
1,270✔
156
         :action "store_false"
762✔
157
         :target "max_code_line_length",
762✔
158

159
      parser:option("--max-string-line-length", "Set maximum allowed length for lines within a string (default: 120).")
1,270✔
160
         :argname "<length>"
762✔
161
         :convert(tonumber),
762✔
162
      parser:flag("--no-max-string-line-length", "Do not limit string line length.")
1,270✔
163
         :action "store_false"
762✔
164
         :target "max_string_line_length",
762✔
165

166
      parser:option("--max-comment-line-length", "Set maximum allowed length for comment lines (default: 120).")
1,270✔
167
         :argname "<length>"
762✔
168
         :convert(tonumber),
762✔
169
      parser:flag("--no-max-comment-line-length", "Do not limit comment line length.")
1,270✔
170
         :action "store_false"
762✔
171
         :target "max_comment_line_length")
762✔
172

173
   parser:option("--max-cyclomatic-complexity", "Set maximum cyclomatic complexity for functions.")
1,270✔
174
      :argname "<complexity>"
762✔
175
      :convert(tonumber)
762✔
176
   parser:flag("--no-max-cyclomatic-complexity", "Do not limit function cyclomatic complexity (default).")
1,270✔
177
      :action "store_false"
762✔
178
      :target "max_cyclomatic_complexity"
762✔
179

180
   local default_global_path = config.get_default_global_path()
762✔
181

182
   local config_opt = parser:option("--config", "Path to configuration file. (default: "..config.default_path..")")
762✔
183
   local no_config_opt = parser:flag("--no-config", "Do not look up configuration file.")
1,270✔
184
      :action "store_false"
762✔
185
      :target "config"
762✔
186

187
   parser:mutex(config_opt, no_config_opt)
762✔
188

189
   local default_config_opt = parser:option("--default-config", ("Path to configuration file to use if --[no-]config "..
1,905✔
190
      "is not used and project-specific %s is not found. (default: %s)"):format(
1,651✔
191
         config.default_path, default_global_path or "could not detect"))
762✔
192
   local no_default_config_opt = parser:flag("--no-default-config", "Do not use default configuration file.")
1,270✔
193
      :action "store_false"
762✔
194
      :target "default_config"
762✔
195

196
   parser:mutex(default_config_opt, no_default_config_opt)
762✔
197

198
   parser:group("Configuration file options",
1,524✔
199
      config_opt,
762✔
200
      no_config_opt,
762✔
201
      default_config_opt,
762✔
202
      no_default_config_opt)
762✔
203

204
   parser:group("File filtering options",
1,524✔
205
      parser:option("--exclude-files", "Do not check files matching these globbing patterns.")
2,032✔
206
         :args "+"
762✔
207
         :count "*"
762✔
208
         :argname "<glob>"
762✔
209
         :action "concat"
762✔
210
         :init(nil),
762✔
211
      parser:option("--include-files", "Do not check files not matching these globbing patterns.")
2,032✔
212
         :args "+"
762✔
213
         :count "*"
762✔
214
         :argname "<glob>"
762✔
215
         :action "concat"
762✔
216
         :init(nil))
762✔
217

218
   parser:option("--filename", "Use another filename in output and for selecting configuration overrides.")
762✔
219

220
   local cache_opt = parser:option("--cache", ("Path to cache directory. (default: %s)"):format(
1,778✔
221
         cache.get_default_dir()))
762✔
222
      :args "?"
762✔
223

224
   local no_cache_opt = parser:flag("--no-cache", "Do not use cache.")
1,270✔
225
      :action "store_false"
762✔
226
      :target "cache"
762✔
227

228
   parser:mutex(cache_opt, no_cache_opt)
762✔
229

230
   local lanes_notice = ""
762✔
231

232
   if not multithreading.has_lanes then
762✔
233
      lanes_notice = "\nWarning: LuaLanes not found, parallel checking disabled."
×
234
   end
235

236
   parser:group("Performance optimization options",
1,524✔
237
      cache_opt,
762✔
238
      no_cache_opt,
762✔
239
      parser:option(
1,778✔
240
         "-j --jobs", "Check <jobs> files in parallel (default: " ..
762✔
241
            tostring(multithreading.default_jobs) .. ")." .. lanes_notice):convert(tonumber))
1,524✔
242

243
   parser:group("Output formatting options",
1,524✔
244
      parser:option("--formatter" , "Use custom formatter. <formatter> must be a module name or one of:\n" ..
1,524✔
245
         "   TAP - Test Anything Protocol formatter;\n" ..
762✔
246
         "   JUnit - JUnit XML formatter;\n" ..
762✔
247
         "   visual_studio - MSBuild/Visual Studio aware formatter;\n" ..
762✔
248
         "   plain - simple warning-per-line formatter;\n" ..
1,143✔
249
         "   default - standard formatter."),
762✔
250
      parser:flag("-q --quiet", "Suppress output for files without warnings.\n" ..
1,778✔
251
         "-qq: Suppress output of warnings.\n" ..
1,143✔
252
         "-qqq: Only print total number of warnings and errors.")
762✔
253
         :count "0-3",
762✔
254
      parser:flag("--codes", "Show warning codes."),
762✔
255
      parser:flag("--ranges", "Show ranges of columns related to warnings."),
762✔
256
      parser:flag("--no-color", "Do not color output.")
1,270✔
257
         :action "store_false"
762✔
258
         :target "color")
762✔
259

260
   parser:flag("--profile", "Show performance statistics."):hidden(true)
1,016✔
261

262
   parser:flag("-v --version", "Show version info and exit.")
1,016✔
263
      :action(function() print(version.string) os.exit(exit_codes.ok) end)
768✔
264

265
   return parser
762✔
266
end
267

268
local function main()
269
   local parser = get_parser()
762✔
270
   local ok, args = parser:pparse()
762✔
271
   if not ok then
750✔
272
      io.stderr:write(("%s\n\nError: %s\n"):format(parser:get_usage(), args))
8✔
273
      os.exit(exit_codes.critical)
6✔
274
   end
275

276
   if args.profile then
744✔
277
      args.jobs = 1
6✔
278
      profiler.init()
6✔
279
   end
280

281
   if args.quiet == 0 then
744✔
282
      args.quiet = nil
666✔
283
   end
284

285
   if args.cache then
744✔
286
      args.cache = args.cache[1] or true
×
287
   end
288

289
   local checker, err, is_invalid_args_error = runner.new(args)
744✔
290

291
   if not checker then
744✔
292
      if is_invalid_args_error then
42✔
293
         io.stderr:write(("%s\n\nError: %s\n"):format(parser:get_usage(), err))
×
294
         os.exit(exit_codes.critical)
×
295
      else
296
         critical(err)
42✔
297
      end
298
   end
299

300
   local inputs = {}
702✔
301

302
   for _, file in ipairs(args.files) do
1,698✔
303
      local input = {filename = args.filename}
996✔
304

305
      if file == "-" then
996✔
306
         input.file = io.stdin
18✔
307
      elseif file:find("%.rockspec$") then
978✔
308
         input.rockspec_path = file
24✔
309
      else
310
         input.path = file
954✔
311
      end
312

313
      table.insert(inputs, input)
996✔
314
   end
315

316
   local report, check_err = checker:check(inputs)
702✔
317

318
   if not report then
696✔
319
      critical(check_err)
×
320
   end
321

322
   for _, file_report in ipairs(report) do
2,262✔
323
      if not file_report.filename then
1,566✔
324
         file_report.filename = "stdin"
12✔
325
      end
326
   end
327

328
   local output, format_err = checker:format(report)
696✔
329

330
   if not output then
696✔
331
      critical(format_err)
×
332
   end
333

334
   io.stdout:write(output)
696✔
335

336
   if args.profile then
696✔
337
      profiler.report()
6✔
338
   end
339

340
   if report.fatals > 0 then
696✔
341
      os.exit(exit_codes.fatals)
48✔
342
   elseif report.errors > 0 then
648✔
343
      os.exit(exit_codes.errors)
84✔
344
   elseif report.warnings > 0 then
564✔
345
      os.exit(exit_codes.warnings)
444✔
346
   else
347
      os.exit(exit_codes.ok)
120✔
348
   end
349
end
350

351
local _, error_wrapper = utils.try(main)
762✔
352
local err = error_wrapper.err
6✔
353
local traceback = error_wrapper.traceback
6✔
354

355
if utils.is_instance(err, utils.InvalidPatternError) then
8✔
356
   critical(("Invalid pattern '%s'"):format(err.pattern))
6✔
357
elseif type(err) == "string" and err:find("interrupted!$") then
×
358
   critical("Interrupted")
×
359
else
360
   local msg = ("Luacheck %s bug (please report at https://github.com/lunarmodules/luacheck/issues):\n%s\n%s"):format(
×
361
      luacheck._VERSION, err, traceback)
×
362
   critical(msg)
×
363
end
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