What should a GLua linter lint for

I’m making a GLua linter, but I’m kind of short on ideas on what it should check for.

Currently it checks for:

  • Inconsistent use of C-style and Lua style syntax (if true && true and true)
  • Deprecated functions
  • Scope depth checking (when you have 5 nested scopes you’re doing something wrong)
  • Goto hating: using goto anywhere but a double nested loop makes you an idiot
  • Duplicate keys in a table ({a = 1, a = 2, b = 3})
  • self.Weapon in SWEPs/self.Entity in SENTs
  • not (a == b), not (a ~= b), not (a > b) etc.
  • Inconsistent " vs ’ usage
  • Variable/parameter/loop var/function name shadowing
  • Using “self” in a non-metafunction
  • Trailing whitespace
  • Lack of whitespace around operators, after a keyword or after a ‘)’
  • Inconsistent use of tabs and spaces for indentation (really basic, only looks for both "
    " and "
    " in whitespace)
  • Empty conditions/loops


  • Dead code checking/live variable analysis (very fucking difficult, I’ll leave this for when I have a decent linter)
  • Tail recursion, the Lua stack doesn’t like that, especially with big things.

**What do you think are common errors/bad practices that can be caught with a lexical or Abstract Syntax Tree (AST) based linter? **

(fyi, lexical means it only looks at the script like a list of tokens, when looking at the AST, it looks at the structure of the code)

One limitation: it’s very hard to reason about whitespace because that’s taken out before analysis starts. I’d have to recode half the thing to get whitespace in the analysis. Comments however are fine.
Update: The limitation is now somewhat weaker: I can reason about whitespace in on the lexical level, but on scope level is still too hard (e.g. reasoning about code indentation)

Maybe check for not closed stuff eg missing “end”, only ( or { and such

Syntax errors are caught by the parser, but syntax checking is something gluac already does. I don’t want my linter to conflict with it.
Currently the linter doesn’t output syntax errors.

Is this an in-game thing or an external tool?

Most likely it’s based on his GLua parser made in Haskell that he showed before.

“net.WriteEntity( LocalPlayer() )” in the same branch as “net.Start( … )”.

External, I mean to implement it in sublimeLinter.

Yes, very good.

[editline]15th August 2015[/editline]

Good one! That’s the kind of bullshit practice that I need. I don’t even think it needs to be in a net.Start thing. Seeing that anywhere is pretty much stupid.

LocalPlayer():ConCommand ?

> connect

RunConsoleCommand(“connect”, “”)
> connect “”[/lua]

Only way to connect to a server using Lua as far as I know.

self.Weapon in SWEPs

Also, checking if a module is loaded before using require(), I’m pretty sure require() already does that
if not module then

You could make it check if a non-precached model is being spawned, then prompt you on the model location so you can easily precache it?

I’m not even sure when to precache

This is an out-of-game tool; pretty impossible to check for that

How do I know what the module name is? Should I just throw a warning when I see an if with only one statement (require) and a simple condition?

[editline]16th August 2015[/editline]

It is done.

So are you going to give the programmer alternatives like “Use the ‘ply’ argument in the net.Recieve function instead” instead of yelling at the noob who doesn’t know any better?

Also, (why == true)

[lua]not (a == b)[/lua]

Obviously you’ll need a config file for turning these on/off and customising them (per project .rc style please, so it checks each successive parent folder starting from the file merging every found config), but here’s a collection of useful rules blatantly stolen from JSHint.

Inconsistent " vs ’ usage. (pick one and stick to it)
Not using the key parameter in a for loop (set it to _)
Writing to a global without using _G (e.g. forgotten local)
Reading from a global without it being visible in the file or declared in a comment (–[[ global foo ]]-- etc)
Inconsistent indentation (Everything in file must be X spaces or a tab)
Trailing whitespace
Using a global function before it’s defined
[lua]function two()

function one()
print(“I work but it’s evil!”);
Using common variable names (data, i, k, v whatever - let people customise this in their config file.)
Naming conventions (camelCase, with_underscores)
Empty blocks
[lua]local i;
function foo()
local i;
[lua]function bar(player)
Unused local variables
Unused local functions
Using self in a function that doesn’t define it via : or explicity
Using coroutines (unless they’re fixed?)
Using non-ascii characters
Using if chains instead of early return/continue
OR (people have opinions yo)
Using early return/continue instead of ifs
[lua]function blah()
local a = one();
if a then
local b = two();
if b then
[lua]function blah()
local a = one();
if not a then
local b = two();
if not b then
[lua]for _, item in pairs(tab) do
if item.foo then
– blah blah blah
[lua]for _, item in pairs(tab) do
if not item.foo then
– blah blah blah

I’ve been wanting a linter for Lua for a while but been far too lazy to do anything about it. Great job!

Coroutines work for anything that doesn’t call in to a 3rd-party binary module that uses Garry’s ILuaBase.

Holy shitballs that’s a lot of useful ideas.
I’m not sure about rc files, though. I’d rather have any configuration done through command line parameters.

The attribute grammar is going to be huge!

== true is useful for people who mix multiple data types on a single variable – which you should never do, but without type declaration, is done a lot by people who have never programmed outside of a functional language.