Luacheck is a static analyzer and a linter for Lua. Luacheck detects various issues such as usage of undefined global variables, unused variables and values, accessing uninitialized variables, unreachable code and more.
The one real issue I have with Lua is its dynamic typing. Of all the bugs I fix in my own Lua code, I would say that the majority are due to typos (wrong variable name) or an unexpected type. So I was quite happy to come across and try out Luacheck. And fortunately, it's pretty straightforward to run.
I ran it over “Project: Sippy-Cup” and … wow.
The extensive regression test I have has already flushed out the typos and the unexpected type errors I tend to make.
But Luacheck found quite a few unused variables
(which is nice—it also found a bunch of unsused LPeg expressions)
and a ton of unintentional global variables (because I forgot to declare them with
The output is easy to read (here's a representative sample from some non-work related code I have):
Checking ptest-cr-select.lua Failure ptest-cr-select.lua:53:9: variable amount was previously defined as an argument on line 52 ptest-cr-select.lua:128:9: variable okay is never accessed ptest-cr-select.lua:193:40: unused argument event ptest-cr-select.lua:197:43: shadowing upvalue conn on line 194 ptest-cr-select.lua:213:21: shadowing upvalue argument event on line 193 ptest-cr-select.lua:215:15: unused variable rem ptest-cr-select.lua:215:15: shadowing upvalue rem on line 194 Total: 7 warnings / 0 errors in 1 file
About the only false positive it finds is this idiom:
function foo(param1,param2) local param1 = param1 or "default value" local param2 = param2 or 3 local a = ... -- ... end
where it will flag
param2 as shadowing an upvalue.
This idiom though,
is used to provide a default value if a parameter isn't given to a function.
It's easy enough to fix,
function foo(param1,param2) param1 = param1 or "default value" param2 = param2 or 3 local a = ... -- ... end
function foo(param1,param2) local param1 = param1 or "default value" -- luacheck: ignore local param2 = param2 or 3 -- luacheck: ignore local a = ... -- ... end
Overall, I'm glad I found this tool. It's been a real eye opener.