LUACHECK(1)
π LUACHECK(1) β the luacheck manual, bestie edition π
okay so hi π you opened the manual page for luacheck, and ngl this is the whole documentation, all of it, in one long beautiful scroll. no gatekeeping here. we're gonna read every single section together and i'm gonna be right next to you the whole time π«Ά
this is part one of eleven, so don't panic when it justβ¦ starts. it's giving "table of contents energy" and that's on purpose. strap in β¨
π NAME π
luacheck β luacheck Documentation
Contents:
that's the whole vibe of this first bit, fr fr β the name, the docs, and a Contents situation that's about to unfold into everything π¦
no skips, no "tl;dr", no summarising the boring parts away β we're reading the whole thing because every flag and every line matters, iykyk π
bestie heads up: this is only part 1 of 11, so nothing is ending here. keep scrolling, we're just getting started π₯
okay bestie, this is the part where we go through every single warning code Luacheck can throw at you π like, all of them, no skipping, we're doing this together π«Ά
β¨ how the codes work β¨
So warnings that Luacheck produces are sorted into categories using three-digit warning codes. Those codes can be shown in your CLI output if you turn on the --codes CLI option, or if you set the codes config option. iykyk.
Errors also have codes starting with zero β but here's the tea: unlike warnings, they can not be ignored. Nope. Not happening. Periodt. π
π the codes, one by one π
011β A syntax error. (yeah, this one hurts π)021β An invalid inline option.022β An unpaired inline push directive.023β An unpaired inline pop directive.033β Invalid use of a compound operator. (Lua doesn't support compound operator by default; if using an extension that does, please set theoperatorsoption.)111β Setting an undefined global variable.112β Mutating an undefined global variable.113β Accessing an undefined global variable.121β Setting a read-only global variable.122β Setting a read-only field of a global variable.131β Unused implicitly defined global variable.142β Setting an undefined field of a global variable.143β Accessing an undefined field of a global variable.211β Unused local variable.212β Unused argument.213β Unused loop variable.214β Used variable.221β Local variable is accessed but never set.231β Local variable is set but never accessed.232β An argument is set but never accessed.233β Loop variable is set but never accessed.241β Local variable is mutated but never accessed.311β Value assigned to a local variable is unused.312β Value of an argument is unused.313β Value of a loop variable is unused.314β Value of a field in a table literal is unused.321β Accessing uninitialized local variable.331β Value assigned to a local variable is mutated but never accessed.341β Mutating uninitialized local variable.411β Redefining a local variable.412β Redefining an argument.413β Redefining a loop variable.421β Shadowing a local variable.422β Shadowing an argument.423β Shadowing a loop variable.431β Shadowing an upvalue.432β Shadowing an upvalue argument.433β Shadowing an upvalue loop variable.511β Unreachable code.512β Loop can be executed at most once.521β Unused label.531β Left-hand side of an assignment is too short.532β Left-hand side of an assignment is too long.541β An emptydo endblock.542β An emptyifbranch.551β An empty statement.561β Cyclomatic complexity of a function is too high.571β A numericforloop goes from#(expr)down to 1 or less without negative step.
and yes bestie, that list is a whole mood π β we literally went through every single one, ate and left no crumbs π
581β Negation of a relational operator; the operator can be flipped.582β Error prone negation: negation has a higher priority than equality.611β A line consists of nothing but whitespace.612β A line contains trailing whitespace.613β Trailing whitespace in a string.614β Trailing whitespace in a comment.621β Inconsistent indentation (SPACE followed by TAB).631β Line is too long.
β¨ Global variables (1xx) β¨
okay so for every single file, Luacheck builds up a list of the globals and fields that are allowed to show up in there. By default the only globals it knows about are the ones from the Lua standard library π
. Wanna add your own? Use the --globals CLI option or the globals config option. Wanna pick which version of the standard library it's vibing with? That's the --std CLI option or the std config option, bestie. And the moment an undefined global or field gets set, mutated, or accessed β boom, Luacheck hits you with a warning. π«Ά
π Read-only globals π
By default, most standard globals and fields are marked as read-only, which means the second you try to set one you get a warning. Not me being overprotective, it's just how it is π. If you want your own read-only globals and fields, add them with the --read-globals CLI option or the read_globals config option, or just roll a custom set of globals. See Custom sets of globals.
Globals and fields that are NOT read-only by default:
_G_ENV(treated as a global by Luacheck)package.pathpackage.cpathpackage.loadedpackage.preloadpackage.loaderspackage.searchers
πΈ Implicitly defined globals πΈ
Luacheck can be told to consider globals that get assigned under certain conditions as being defined implicitly, iykyk. Two flavors:
- With the
-d/--allow_definedCLI option or theallow_definedconfig option, all assignments to globals define them. - With the
-t/--allow_defined_topCLI option or theallow_defined_topconfig option, assignments to globals in the top level function scope (aka the main chunk, bestie) define them.
And if an implicitly defined global never gets accessed anywhere? Warning. Periodt. β¨
π¦ Modules π¦
You can mark files as modules with the -m/--module CLI option or the module config option, which simulates the semantics of the deprecated module function. π Globals that get implicitly defined inside a module count as part of its interface β they're not visible outside and they don't get reported as unused. Assignments to other globals though? Not allowed. Not even to defined ones. π«
π Unused variables (2xx) and values (3xx) π
Luacheck drops warnings for every unused local variableβ¦ except one that's literally named _. That one gets a pass, always. π
It also catches variables that are set but never accessed, and variables that are accessed but never set.
β "Unused hint" (214) β
If a function argument starts with an underscore _, it receives an "unused hint" β meaning it's meant to be left unused, no drama. But if you actually use it? That's a 214 warning, bestie. π
π Unused values and uninitialized variables π
For every value that gets assigned to a local variable, Luacheck computes the set of expressions where that value could be used. Then:
- unused values β warning when a value can't be used anywhere
- uninitialized variables β warning when no values can reach an expression
Like, look at this snippet: the value assigned to foo on line 1 is unused, and variable bar is uninitialized on line 9. π
local foo = expr1()
local bar
if condition() then
foo = expr2()
bar = expr3()
else
foo = expr4()
print(bar)
end
return foo, bar
π Secondary values and variables π
An unused value assigned to a local variable is secondary if its origin is the last item on the RHS of an assignment, and another value from that same item is used. Secondary values usually pop up when a function call's result gets dumped into locals and only some of them are used later.
In this example, the value assigned to b is secondary, the value assigned to c is used, and the value assigned to a is justβ¦ unused, plain and simple. π
local a, b, c = f(), g()
return c
A variable is secondary if all values assigned to it are secondary. So in that snippet above, b is a secondary variable.
Wanna yeet warnings about unused secondary values and variables? Use the -s/--no-unused-secondaries CLI option or the unused_secondaries config option. π«Ά
π Shadowing declarations (4xx) π
Luacheck catches declarations of local variables that shadow previous declarations β unless the variable is named _, because _ is untouchable and iconic. If the previous declaration lives in the same scope as the new one, that's called redefining.
Also, real talk: you do NOT need to declare a whole new local variable just to overwrite an argument. Look:
local function f(x)
local x = x or "default" -- bad
end
local function f(x)
x = x or "default" -- good
end
See the difference? One's a crime, one's a slay. π
π₯ Control flow and data flow issues (5xx) π₯
π Unreachable code
Luacheck detects unreachable code. It also detects it when the end of a loop block is unreachable β which basically means the loop can only ever run once. Not me exposing your loop like that:
for i = 1, 100 do
-- Break statement is outside the `if` block,
-- so that the loop always stops after the first iteration.
if cond(i) then f() end break
end
π― Unused labels
Labels that no goto statement ever uses? Reported as unused. Simple as that. β¨
βοΈ Unbalanced assignments
If an assignment has a left side and a right side with different lengths, it's unbalanced and Luacheck will say something about it.
The one exception: initializing several local variables in a single statement while leaving some uninitialized. Like this:
local a, b, c = nil -- Effectively sets `a`, `b`, and `c` to nil, no warning.
No warning there, bestie. You're good. π«Ά
π«§ Empty blocks
Luacheck warns about empty do end blocks and empty if branches (then else, then elseif, and then end).
π Empty statements
In Lua 5.2+ semicolons count as statements and can show up even when they're not following normal statements. Those semicolons get flagged, because they are completely useless. Like, why are you even here? π
π Cyclomatic complexity
If a limit is set with the --max-cyclomatic-complexity CLI option or the corresponding config or inline options, Luacheck warns about functions whose cyclomatic complexity is too high. π
π Reversed numeric for loops
Iterating a table in reverse with a numeric for loop going from #t to 1 requires a negative loop step. So Luacheck warns about loops going from #(some expression) to 1 (or a smaller constant) when the loop step isn't negative:
-- Warning for this loop:
-- numeric for loop goes from #(expr) down to 1 but loop step is not negative
for i = #t, 1 do
print(t[i])
end
-- This loop is okay.
for i = #t, 1, -1 do
print(t[i])
end
π¨ Error-prone and Unnecessary Negations
Negation has a higher priority than relational operators, so (not x == 3) gets read as (not x) == 3, rather than not (x == 3). Sneaky, right? π
And negating the output of a relational operator is just unnecessary β every single one has another operator you can use directly:
not (x == y) => x ~= y
not (x ~= y) => x == y
not (x > y) => x <= y
not (x >= y) => x < y
not (x < y) => x >= y
not (x <= y) => x > y
Heads up bestie: these replacements work for all numbers, but they can fail with metatables or NaN's. β οΈ
π Formatting issues (6xx) π
πΈ Whitespace issues
Luacheck warns about trailing whitespace and inconsistent indentation (SPACE followed by TAB).
Some examples of trailing whitespace Luacheck finds:
-- Whitespace example.
print("Hello")
print("World")
Here:
- Any tabs or spaces after either
)would be considered trailing. - Any tabs or spaces after the
.in the comment would be considered trailing - Any tabs or spaces on the empty line between the two
printstatements would also be considered a form of trailing whitespace.
Trailing whitespace in any of these forms is useless, can be a nuisance to developers navigating around a file, and is forbidden in many formatting styles. π«β¨
π Line length limits
Luacheck warns about lines that are longer than some limit. The default limit is 120 characters. You can change it with the --max-line-length CLI option, or disable the check completely with --no-max-line-length; there are similar config and inline options too. π
Also, you can set separate limits for three different types of lines:
- "String" lines have their line ending inside a string, typically a long string using
[[...]]syntax. - "Comment" lines have their line ending inside a long comment (
--[[...]]), or end with a short comment using normal--...syntax. - "Code" lines are all other lines.
These line types are limited using CLI options named --[no-]max-string-line-length, --[no-]max-comment-line-length, and --[no-]max-code-line-length, with similar config and inline options. π
π COMMAND LINE INTERFACE π
The luacheck program accepts files, directories and rockspecs as arguments, bestie. And you can filter them with the --include-files and --exclude-files options β more on those below. π
- Given a file,
luacheckwill check it. β - Given
-,luacheckwill check stdin. π«Ά - Given a directory,
luacheckwill check all files within it, selecting only files with.luaextension unless the--include-filesoption is used.
psst β that directory feature requires LuaFileSystem (installed automatically if LuaRocks was used to install Luacheck). π¦
- And listen, if you feed it a rockspec (a file with a
.rockspecextension, periodt π ), luacheck will check all files with.luaextension mentioned in the rockspec inbuild.install.lua,build.install.binandbuild.modulestables. Iconic.
The output is separate reports for each checked file, and it ends with a lil summary, like this:
$ luacheck src
Checking src/bad_code.lua 5 warnings
src/bad_code.lua:3:16: unused variable helper
src/bad_code.lua:3:23: unused variable length argument
src/bad_code.lua:7:10: setting non-standard global variable embrace
src/bad_code.lua:8:10: variable opt was previously defined as an argument on line 7
src/bad_code.lua:9:11: accessing undefined variable hepler
Checking src/good_code.lua OK
Checking src/python_code.lua 1 error
src/python_code.lua:1:6: expected '=' near '__future__'
Checking src/unused_code.lua 9 warnings
src/unused_code.lua:3:18: unused argument baz
src/unused_code.lua:4:8: unused loop variable i
src/unused_code.lua:5:13: unused variable q
src/unused_code.lua:7:11: unused loop variable a
src/unused_code.lua:7:14: unused loop variable b
src/unused_code.lua:7:17: unused loop variable c
src/unused_code.lua:13:7: value assigned to variable x is unused
src/unused_code.lua:14:1: value assigned to variable x is unused
src/unused_code.lua:22:1: value assigned to variable z is unused
Total: 14 warnings / 1 error in 4 files
Look at that glow-up. β¨ OK files get called out, the messy ones get receipts, and everything gets a total at the bottom.
Now, how does luacheck pick its exit code? Bestie, it's a whole little grading scale:
- Exit code is
0if no warnings or errors occurred. flawless, no notes π«Ά - Exit code is
1if some warnings occurred but there were no syntax errors or invalid inline options. - Exit code is
2if there were some syntax errors or invalid inline options. - Exit code is
3if some files couldn't be checked, typically due to an incorrect file name. - Exit code is
4if there was a critical error (invalid CLI arguments, config, or cache file). the whole vibe is off, run π
π Command line options π
Okay so short options that don't take an argument? You can absolutely stack them like friendship bracelets β -qqu is literally equivalent to -q -q -u. Slay. And for long options, both --option value or --option=value work, so do whatever feels right.
Options that take several arguments can also be used several times, because why not β --ignore foo --ignore bar is equivalent to --ignore foo bar. β¨
β οΈ Real talk though: options that may take several arguments, like--globals, should not be used immediately before positional arguments. Because if you write--globals foo bar file.lua, luacheck will treat all offoo,barandfile.luaas globals and then panic as there are no file names left. Not the serve you wanted. π
-g,--no-globalβ filter out warnings related to global variables.-u,--no-unusedβ filter out warnings related to unused variables and values.-r,--no-redefinedβ filter out warnings related to redefined variables.-a,--no-unused-argsβ filter out warnings related to unused arguments and loop variables.-s,--no-unused-secondariesβ filter out warnings related to unused variables set together with used ones.- See Secondary values and variables.
--no-selfβ filter out warnings related to implicitselfargument.--std<std>β set standard globals, default ismax.<std>can be one of:maxβ union of globals of Lua 5.1 - 5.4 and LuaJIT 2.x;minβ intersection of globals of Lua 5.1 - 5.4 and LuaJIT 2.x;lua51β globals of Lua 5.1 without deprecated ones;lua51cβ globals of Lua 5.1;lua52β globals of Lua 5.2;lua52cβ globals of Lua 5.2 compiled withLUA_COMPAT_ALL;lua53β globals of Lua 5.3;lua53cβ globals of Lua 5.3 compiled withLUA_COMPAT_5_2;lua54β globals of Lua 5.4;lua54cβ globals of Lua 5.4 compiled withLUA_COMPAT_5_3;luajitβ globals of LuaJIT 2.x;ngx_luaβ globals of Openresty lua-nginx-module 0.10.10, including standard LuaJIT 2.x globals;loveβ globals added by LΓVE;bustedβ globals added by Busted 2.0, by default added for files ending with_spec.luawithinspec,test, andtestssubdirectories;rockspecβ globals allowed in rockspecs, by default added for files ending with.rockspec;luacheckrcβ globals allowed in Luacheck configs, by default added for files ending with.luacheckrc;ldocβ globals allowed in LDoc config, by default added for files namedconfig.ld;sileβ globals allowed in The SILE Typesetter and its package ecosystem;noneβ no standard globals.
See Sets of standard globals
--globals [<name>] ...β Add custom global variables or fields on top of the standard ones. Peep Sets of standard globals and Defining extra globals and fields for the full deets.--read-globals [<name>] ...β Add read-only global variables or fields.--new-globals [<name>] ...β Set custom global variables or fields. Heads up: this removes custom globals you added previously.--new-read-globals [<name>] ...β Set read-only global variables or fields. Also removes read-only globals added previously.--not-globals [<name>] ...β Remove custom <em>and</em> standard global variables or fields. Bye bestie π-c,--compatβ Literally just the same as--std max. No cap.-d,--allow-definedβ Allow defining globals implicitly by setting them. See Implicitly defined globals for the tea.-t,--allow-defined-topβ Allow defining globals implicitly by setting them in the top level scope. See Implicitly defined globals.-m,--moduleβ Limit the visibility of implicitly defined globals to their files. See Modules.--max-line-length <length>β Set the maximum allowed line length. Default is120.--no-max-line-lengthβ Do not limit line length. Live your truth, long lines and all π--max-code-line-length <length>β Set the maximum allowed length for lines ending with code. Default is120.--no-max-code-line-lengthβ Do not limit code line length. Code can be as long as it wants β¨--max-string-line-length <length>β Set the maximum allowed length for lines within a string. Default is120.--no-max-string-line-lengthβ Do not limit string line length. String lines can go off π--max-comment-line-length <length>β Set the maximum allowed length for comment lines. Default is120.--no-max-comment-line-lengthβ Do not limit comment line length. Comment away, bestie π--max-cyclomatic-complexity <limit>β Set the maximum cyclomatic complexity for functions. Keep it cute and simple, y'know?--no-max-cyclomatic-complexityβ Do not limit function cyclomatic complexity. This is the default.--ignore,-i <patt> [<patt>] ...β Filter out warnings matching the patterns. Like a block list for warnings π«--enable,-e <patt> [<patt>] ...β Do <em>not</em> filter out warnings matching the patterns. It's giving unblocked π--only,-o <patt> [<patt>] ...β Filter out warnings <em>not</em> matching the patterns. Only the chosen ones get through π--operators <patt> [<patt>] ...β Allow compound operators matching the patterns. Note: multiple assignment is not supported, because this is specifically for the Playdate SDK.--config <config>β Path to a custom configuration file. Default is.luacheckrc.--no-configβ Do not look up a custom configuration file. No config, no drama π--default-config <config>β Default path to a custom configuration file. This gets used if--[no-]configis not used and.luacheckrcis not found. The default global location is:%LOCALAPPDATA%\Luacheck\.luacheckrcon Windows;~/Library/Application Support/Luacheck/.luacheckrcon OS X/macOS;
$XDG_CONFIG_HOME/luacheck/.luacheckrcor~/.config/luacheck/.luacheckrcon other systems.
--no-default-configβ pls don't fall back to a default configuration file. we're not doing that today. π«--filename <filename>β use a different name for the file in the output, bestie. this is what gets used for picking config overrides and for file filtering. lowkey important.--exclude-files <glob> [<glob>] ...β do not check files that match these globbing patterns. recursive globs like**/*.luaare fully supported, no cap. β¨--include-files <glob> [<glob>] ...β do not check files that don't match these globbing patterns. (yes it's the mirror twin of the one above π )--cache [<cache>]β path to the cache file. (default:.luacheckcache). head to Caching for the tea.--no-cacheβ no cache. zero. none. bye. π-j | --jobsβ check<jobs>files in parallel. requires LuaLanes. default number of jobs = however many processing units you've got. multiprocess slay. π₯--formatter <formatter>β use a custom formatter.<formatter>has to be a module name, or one of these icons:TAPβ Test Anything Protocol formatter;JUnitβ JUnit XML formatter;visual_studioβ MSBuild/Visual Studio aware formatter;plainβ simple warning-per-line formatter;defaultβ standard formatter.
-q | --quietβ suppress report output for files with no warnings. and if you stack it:-qqβ suppress output of warnings.-qqqβ only output summary. that's it, that's the whole vibe.
--codesβ show warning codes.--rangesβ show ranges of columns related to warnings.--no-colorβ do not colorize output. (tragic, but okay π)-v | --versionβ show version of Luacheck and its dependencies, then exit.-h | --helpβ show help and exit.
π Patterns
okay so this is the good part. the CLI options --ignore, --enable and --only (plus their corresponding config options) let you filter warnings using pattern matching on warning codes, variable names, or both. here's the ruleset, bestie:
if a pattern contains a slash, the part before the slash matches the warning code and the part after matches the variable name. otherwise, if a pattern contains a letter or an underscore, it matches the variable name. otherwise, it matches the warning code. iykyk. for example:
4.2β shadowing declarations of arguments or redefining them..*_β warnings related to variables with_suffix.4.2/.*_β shadowing declarations of arguments with_suffix or redefining them.
unless they're already anchored, patterns matching variable names get anchored at both sides, and patterns matching warning codes get anchored at their beginnings. this is the little trick that lets you filter warnings by category β like, --only 1 focuses luacheck on global-related warnings. π
πΈ Defining extra globals and fields
the CLI options --globals, --new-globals, --read-globals, --new-read-globals, and their corresponding config options, add new allowed globals or fields. for example, --read-globals foo --globals foo.bar lets you access the foo global and mutate its bar field. --not-globals also operates on globals and fields, and it removes definitions of both standard and custom globals. gone. π
π¦ Sets of standard globals
the CLI option --stds lets you combine built-in sets described above using +. for example, --std max is equivalent to:
--std=lua51c+lua52c+lua53c+luajit
a leading plus sign adds new sets to the current one instead of replacing it. for instance, --std +love is perfect for checking files using the LΓVE framework. π and if you want your own custom sets of globals, you define them by mutating the global variable stds in config. see Custom sets of globals.
β¨ Formatters
the CLI option --formatter lets you select a custom formatter for luacheck output. a custom formatter is a Lua module that returns a function with three arguments:
- report as returned by luacheck module (see Report format),
- array of file names,
- table of options.
those options contain the values assigned to the quiet, color, limit, codes, ranges and formatter options in CLI or config. the formatter function must return a string. that's the deal. π«Ά
π Caching
if LuaFileSystem is available, Luacheck can cache the results of checking files. on subsequent checks, only the files that have changed since the last check get rechecked, which improves run time significantly. iconic behaviour honestly.
heads up though: changing options (like defining additional globals) does not invalidate the cache. π
you can turn caching on with the --cache <cache> option or the cache config option. using --cache with no argument, or setting the cache config option to true, sets .luacheckcache as the cache file. and note this: --cache must be used every time luacheck is run, not just on the first run only. don't forget it, bestie.
π Stable interface for editor plugins and tools
real talk: the command-line interface of Luacheck can change between minor releases. but starting from version 0.11.0, the following interface is guaranteed at least till version 1.0.0, and it should be used by tools that consume Luacheck output β e.g. editor plugins. π
- Luacheck should be started from the directory containing the checked file.
- The file can be passed through stdin using
-as argument, or using a temporary file. the real filename should be passed using the--filenameoption. - The plain formatter should be used. it outputs one issue (warning or error) per line.
- Wanna know exactly where the problem is? bestie, use the
--rangesoption π . Every line kicks off with the real filename (that's the one you passed in with--filename), and then right after it comes:<line>:<start_column>-<end_column>:β where<line>is the line number where the issue popped off, and<start_column>-<end_column>is the inclusive range of columns of the token that the issue is about. Counting starts from 1, no zero-index shenanigans here β¨. And if you don't use--ranges? Then the end column and the dash just don't get printed. Periodt. - Want the codes too? That's
--codesπ. On each line, the little substring hanging out between parentheses holds a three digit issue code, prefixed withEfor errors andWfor warnings. And if there's no such substring at all? That's a fatal error (think I/O error energy π), not a normal warning. - The rest of the line after all that? That's just the warning message, bestie. Simple as π.
If you're out here wanting compatibility with an older Luacheck version, you can peek at the output of luacheck --help to grab its version π. If it contains a string like 0.<minor>.<patch>, where <minor> is at least 11 and <patch> is any number, then the interface we just described is the one you should be using. Iykyk π.
π CONFIGURATION FILE π
Okay so here's the tea β: luacheck tries to load its config from a .luacheckrc file sitting in the current directory. Not there? Then it goes looking in the parent directory, and then that parent, and so on, climbing up and up until it hits the file system root π¦. It's giving main character determination fr fr.
Wanna point it somewhere specific? Use the --config option, and in that case it'll be used during recursive loading too. Important vibe: paths within the config are interpreted relatively to the directory the config was loaded from.
You can also just shut the whole thing down with the --no-config flag π.
Now β if you use none of --config, --no-config, and --no-default-config, then luacheck will try to load config from the value of the --default-config option. And if you didn't set that either, it falls back to these defaults per system πΈ:
%LOCALAPPDATA%\Luacheck\.luacheckrcon Windows~/Library/Application Support/Luacheck/.luacheckrcon OS X/macOS$XDG_CONFIG_HOME/luacheck/.luacheckrcor~/.config/luacheck/.luacheckrcon other systems
Paths within the default config are interpreted relatively to the current directory. (Yes, different from the --config case, don't @ me π)
So what is a config, actually? It's simply a Lua script that luacheck executes π§. It can set all kinds of options by assigning to globals, or by returning a table that has option names as keys. Either way slay β¨.
And here's the priority hierarchy, listen up: options loaded from config have the lowest priority π₯. So you can totally overwrite them with CLI options or inline options. Config is the understudy, not the star π.
π Config options π
okay bestie, gather round, because this is the full menu of config options β¨ β every single one, the type it's expecting, and the value it quietly falls back to when you don't say anything. no skipping, we're doing all 33, it's giving completionist. π
quietβ wants an Integer in range0..3. ghost it and it defaults to0.colorβ Boolean. defaults totrue. πcodesβ Boolean. defaults tofalse.rangesβ Boolean. defaults tofalse.formatterβ String or function. defaults to"default".cacheβ Boolean or string. defaults tofalse.jobsβ Positive integer. defaults to1.exclude_filesβ Array of strings. defaults to{}.include_filesβ Array of strings. default is (Include all files).globalβ Boolean. defaults totrue.unusedβ Boolean. defaults totrue.redefinedβ Boolean. defaults totrue.unused_argsβ Boolean. defaults totrue.unused_secondariesβ Boolean. defaults totrue.selfβ Boolean. defaults totrue.stdβ String or set of standard globals. defaults to"max".globalsβ Array of strings or field definition map. defaults to{}.new_globalsβ Array of strings or field definition map. default is (Do not overwrite).read_globalsβ Array of strings or field definition map. defaults to{}.new_read_globalsβ Array of strings or field definition map. default is (Do not overwrite).not_globalsβ Array of strings. defaults to{}.operatorsβ Array of strings. defaults to{}.compatβ Boolean. defaults tofalse.allow_definedβ Boolean. defaults tofalse.allow_defined_topβ Boolean. defaults tofalse.moduleβ Boolean. defaults tofalse.max_line_lengthβ Number orfalse. defaults to120.max_code_line_lengthβ Number orfalse. defaults to120.max_string_line_lengthβ Number orfalse. defaults to120.max_comment_line_lengthβ Number orfalse. defaults to120.max_cyclomatic_complexityβ Number orfalse. defaults tofalse.ignoreβ Array of patterns (see Patterns π). defaults to{}.enableβ Array of patterns. defaults to{}.onlyβ Array of patterns. default is (Do not filter).
so here's a lil example of a config that makes luacheck ensure that only globals from the portable intersection of Lua 5.1, Lua 5.2, Lua 5.3 and LuaJIT 2.0 are used, and also disables detection of unused arguments. iconic combo tbh π«Ά
std = "min"
ignore = {"212"}
π¦ Custom sets of globals π¦
the std option lets you set a custom standard set of globals using a table. this table can have two fields: globals and read_globals. both of them should contain a field definition map defining some globals. and the simplest way to define globals? just list their names, periodt:
std = {
globals = {"foo", "bar"}, -- these globals can be set and accessed.
read_globals = {"baz", "quux"} -- these globals can only be accessed.
}
see the difference bestie? globals get to be set and accessed, read_globals can only be accessed. that's the whole vibe check right there π
For globals defined like this, Luacheck is also gonna lowkey count any fields inside them as defined. But bestie, if you want a global with a restricted set of fields? You use the global name as the key and a table as the value. Inside that table, the fields subtable holds the fields in that exact same format:
std = {
read_globals = {
foo = { -- Defining read-only global `foo`...
fields = {
field1 = { -- `foo.field1` is now defined...
fields = {
nested_field = {} -- `foo.field1.nested_field` is now defined...
}
},
field2 = {} -- `foo.field2` is defined.
}
}
}
}
Globals and fields can be marked read-only (or not π
) using the read_only property with a boolean value. And the other_fields property controls whether the global or field can also contain other unspecified fields:
std = {
read_globals = {
foo = { -- `foo` and its fields are read-only by default (because they are within `read_globals` table).
fields = {
bar = {
read_only = false, -- `foo.bar` is not read-only, can be set.
other_fields = true, -- `foo.bar[anything]` is defined and can be set or mutated (inherited from `foo.bar`).
fields = {
baz = {read_only = true}, -- `foo.bar.baz` is read-only as an exception.
}
}
}
}
}
}
Custom sets can get names too, fr fr β you do it by mutating the global stds variable, so then they're usable in the --std CLI option and the std inline and config option:
stds.some_lib = {...}
std = "min+some_lib"
And in config, globals, new_globals, read_globals, and new_read_globals can also hold definitions in that same format:
read_globals = {
server = {
fields = {
-- Allow mutating `server.sessions` with any keys...
sessions = {read_only = false, other_fields = true},
-- other fields...
}
},
--- other globals...
}
πΈ Per-file and per-path overrides πΈ
Okay so the environment where Luacheck loads the config has a special global called files. When it's checking a file <path>, Luacheck will override options from the main config with entries from files[<glob>] if <glob> matches <path> β and it applies entries for the more general globs first. β¨
Example time: this config re-enables detection of unused arguments only for files in src/dir, but not for files ending with _special.lua:
std = "min"
ignore = {"212"}
files["src/dir"] = {enable = {"212"}}
files["src/dir/**/*_special.lua"] = {ignore = {"212"}}
Heads up bestie: the files table supports autovivification, so these two are literally equivalent:
files["src/dir"].enable = {"212"}
files["src/dir"] = {enable = {"212"}}
The configs get processed in order of increasing specificity, k? globals and read_globals will add to the set of allowed globals. not_globals is your girl for removing previously allowed globals. And new_globals and new_read_globals override the whole set of globals β wiping all previously allowed globals or read_globals respectively, and replacing them with the new entries. Slay. π
π¦ Default per-path std overrides π¦
Luacheck ships with a set of default per-path overrides, no cap:
files["**/spec/**/*_spec.lua"].std = "+busted"
files["**/test/**/*_spec.lua"].std = "+busted"
files["**/tests/**/*_spec.lua"].std = "+busted"
files["**/*.rockspec"].std = "+rockspec"
files["**/*.luacheckrc"].std = "+luacheckrc"
files["**/config.ld"].std = "+ldoc"
These get added to the global std specified in the config file. Each of these can be overridden by setting a different std value for the corresponding key in files. And setting std on the commandline removes these default overrides entirely. β¨
β¨ INLINE OPTIONS β¨
okay bestie so here's the tea: Luacheck lets you set some options directly inside the files it's checking, using inline configuration comments. And these inline options are the highest priority β they overwrite both config options AND CLI options. like, they run the whole group chat, fr fr. π
An inline configuration comment is a short comment that starts with the luacheck: label, possibly after some whitespace. The body of that comment should have comma separated options, where each option invocation is its name plus space separated arguments. It can also contain notes wrapped in balanced parentheses</>, and those just get ignored (they're giving "no thoughts, head empty" energy). π«Ά
Here's every option that's supported, plus how many arguments each one takes:
globalβ 0 argumentsunusedβ 0 argumentsredefinedβ 0 argumentsunused argsβ 0 argumentsunused secondariesβ 0 argumentsselfβ 0 argumentscompatβ 0 argumentsmoduleβ 0 argumentsallow definedβ 0 argumentsallow defined topβ 0 argumentsmax line lengthβ 1 argument (withnoand no arguments, it disables line length checks)max code line lengthβ 1 argument (withnoand no arguments, it disables code line length checks)max string line lengthβ 1 argument (withnoand no arguments, it disables string line length checks)max comment line lengthβ 1 argument (withnoand no arguments, it disables comment line length checks)stdβ 1 argumentglobalsβ 0 or more argumentsnew globalsβ 0 or more argumentsread globalsβ 0 or more argumentsnew read globalsβ 0 or more argumentsnot globalsβ 0 or more argumentsignoreβ 0 or more arguments (without arguments, everything is ignored)enableβ 1 or more argumentsonlyβ 1 or more arguments
Options that take no arguments can be prefixed with no to invert their meaning. E.g. --luacheck: no unused args disables unused argument warnings. iconic, honestly. π
Now, which part of the file gets affected by an inline option depends on where you put it. If there's any code on the same line as the option, only that line is affected. Otherwise, everything up until the end of the current closure is. And in particular, inline options at the very top of the file affect all of it. main character placement energy. π
-- luacheck: globals g1 g2, ignore foo
local foo = g1(g2) -- No warnings emitted.
-- The following unused function is not reported.
local function f() -- luacheck: ignore
-- luacheck: globals g3
g3() -- No warning.
end
g3() -- Warning is emitted as the inline option defining g3 only affected function f.
For fine-grained control over inline option visibility, use the luacheck: push and luacheck: pop directives:
-- luacheck: push ignore foo
foo() -- No warning.
-- luacheck: pop
foo() -- Warning is emitted.
it's giving save-state-and-restore, and honestly? we're obsessed. π
β¨ LUACHECK MODULE β¨
okay bestie, plot twist β luacheck isn't just a command you yell at in your terminal, it's also a whole Lua module you can pull into your own code. Just hit it with:
local luacheck = require "luacheck"
and boom, you've got these functions to play with:
luacheck.get_report(source)β hand it a source string, it hands you back the analysis data (a table, obviously, this is Lua π ).luacheck.process_reports(reports, options)β takes an array of analysis reports and applies options to them. So like,reports[i]gets checked againstoptions,options[i],options[i][1],options[i][2],...as its options, and each one overrides the last in that order. The options table has fields just like the config options do β go peep Config options for the deets. Any analysis report with afatalfield gets ignored entirely, no cap. Returns the final report, and yes, Report format is right below if you're lost.luacheck.check_strings(sources, options)β checks an array of sources usingoptionsand returns the final report. Tables in thesourcesarray that have afatalfield get skipped, sorry bestie π«Ά.luacheck.check_files(files, options)β checks an array of files withoptions, returns the final report. Fun fact: you can pass open file handles instead of filenames! They get read till EOF and then closed. Very demure, very mindful β¨.luacheck.get_message(issue)β give it an issue, it gives you back a string message. See Report format.
and also, lowkey important:
luacheck._VERSIONβ contains the Luacheck version as a string inMAJOR.MINOR.PATCHformat π
one more thing: using luacheck as a function is literally equivalent to calling luacheck.check_files. same energy, shorter name.
πΈ Report format πΈ
A final report is an array of file reports, plus three extra fields chilling on it: warnings, errors, and fatals β holding the total number of warnings, errors, and fatal errors respectively. slay.
A file report is an array of issues (those are your warnings and errors). But if a fatal error happened while checking that file, its report will instead have a fatal field holding the error type and a msg field holding the error message. π
An issue is a table with a code field telling you its type (go see List of warnings), plus line, column, and end_column fields pointing at exactly where in the source the warning lives. There might also be a name field with the name of the related variable. And some issue types come with extra bonus fields:
011β themsgfield contains the syntax error message.111β themodulefield indicates that the assignment is to a non-module global variable.122,142,143β theindirectfield indicates that the global field was accessed using a local alias.122,142,143β thefieldfield contains the string representation of the related global field.211β thefuncfield indicates that the unused variable is a function.211β therecursivefield indicates that the unused function is recursive.211β themutually_recursivefield is set for unused mutually recursive functions.314β thefieldfield contains the string representation of the ununsed field or index. (yes bestie, that spelling is straight from the source, I don't make the rules π)011β theprev_line,prev_column, andprev_end_columnfields may point to an extra relevant location, like the opening unpaired bracket. so helpful fr fr.4..β theprev_line,prev_column, andprev_end_columnfields contain the location of the overwritten definition.521β thelabelfield contains the label name.631β theline_endingfield contains"comment"or"string"if the line ending is within a comment or a string.631β themax_lengthfield contains the maximum allowed line length.
Other fields may be present for internal reasons. mysterious. iconic. we don't ask questions. π
This is documentation for version 1.2.0 of Luacheck, a linter for Lua. π¦
π AUTHOR π
Peter Melnichenko
π COPYRIGHT π
2024 - 2018, Peter Melnichenko
and that's a wrap on the whole page, bestie!! π«Ά this was version 1.2.0 of Luacheck, dated Jun 13, 2024 β go forth and lint absolutely everything, you absolute main character πβ¨
π¦ where this page comes from
- debian trixie: lua-check 1.2.0-1 Β· MIT
- ubuntu questing: lua-check 1.2.0-1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms