Page 1 of 2

Find globals with LuaJIT (Linux, probably Mac too)

Posted: Sat May 25, 2019 6:18 pm
by pgimeno
I made this for myself some time ago, but in light of recent threads, it appears that it's not a very widely known or used solution to the problem of finding globals in your program.

So here it is. Make a shell script like this:

Code: Select all

#!/bin/sh
luajit -b -l "$1" | egrep '^.{,12}\bG[GS]ET\b' | fgrep -v -f /path/to/luajit-globals.txt
It needs a file 'luajit.globals.txt' with these contents:

Code: Select all

"love"
"arg"
"ffi"
"lfs"
"socket"
"coroutine"
"assert"
"tostring"
"tonumber"
"io"
"rawget"
"xpcall"
"ipairs"
"print"
"pcall"
"gcinfo"
"module"
"setfenv"
"pairs"
"jit"
"bit"
"package"
"error"
"debug"
"loadfile"
"rawequal"
"loadstring"
"rawset"
"unpack"
"table"
"require"
"_VERSION"
"newproxy"
"collectgarbage"
"dofile"
"next"
"math"
"load"
"os"
"_G"
"select"
"string"
"type"
"getmetatable"
"getfenv"
"setmetatable"
BE VERY CAREFUL to not include an empty line in luajit-globals.txt, as that will make the script not report any results.

Run it on each Lua file you want to check, like this:

Code: Select all

check-globals myfile.lua
It will print the names of the globals that file is using, other than the LÖVE or the Lua library ones. Unfortunately it doesn't tell line numbers.

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Sat May 25, 2019 6:37 pm
by raidho36
Alternatively:

Code: Select all

for k, _ in pairs ( _G ) do print ( k ) end

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Sat May 25, 2019 7:04 pm
by pgimeno
Yeah but:
  • It doesn't tell you what files does it appear in.
  • It doesn't filter out the system globals.
  • It may miss some code paths.
The method I suggest doesn't have any of these caveats.

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Wed Jun 05, 2019 12:27 pm
by Rucikir
Do you know Luacheck ?
It’s a static analyzer and a linter for Lua, that is very mature and that has a lot of features. It is also well integrated with various editors.
For instance, a run of

Code: Select all

luacheck --std=love+luajit src/
is suitable for checking files using LÖVE.

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Wed Jun 05, 2019 6:16 pm
by pgimeno
Rucikir wrote: Wed Jun 05, 2019 12:27 pm Do you know Luacheck ?
Thanks, I heard about it, but I didn't realize it's available for Debian as the lua-check package.
Rucikir wrote: Wed Jun 05, 2019 12:27 pm For instance, a run of

Code: Select all

luacheck --std=love+luajit src/
is suitable for checking files using LÖVE.
That line doesn't work for me, it doesn't seem to recognize love. I can add my own globals to the command line though, so that's not a problem because LÖVE adds very few globals (just one in 11.x unless you use socket.ftp or mime).

My problem with this kind of static checkers is that they enforce a particular style. I don't like that. Of the 126 warnings it found in Gspot.lua, only one could be considered legitimate, namely a snippet with this form:

Code: Select all

  local var = 0
  var = fn()
which LuaJIT will probably optimize out anyway. The rest were just consequence of the style used. I'd have to disable lots of warnings before starting to use it. Having to replace unused identifiers with _ just to keep the linter happy, in particular, is a pet peeve of mine. It hides the meaning of the parameter or variable, and makes it one more place to change if I decide to use it later.

I tried it also on the Gspöt main.lua demo program, which (ab)uses globals, and it highlights another problem. The linter is not wise enough to know that love.load is executed right after executing the file, and reported every access to the globals that were defined in love.load as undeclared. That's another case where a particular style would be enforced just to keep the linter happy.

Maybe I spend some time analysing the list of warnings to see which ones to keep and which ones to ditch, but in its current state it's too unusable for me.

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Sat Jun 08, 2019 10:35 am
by Rucikir
but I didn't realize it's available for Debian as the lua-check package
I don’t know if the package is up to date on Debian, but if you have installed luarocks, you may prefer to install lua-check this way.

Code: Select all

luarocks install luacheck
they enforce a particular style
I tend to like conforming myself to a linter or a formatter. Anyway, they're just tools. They have their limitations.
Having to replace unused identifiers with _ just to keep the linter happy, in particular, is a pet peeve of mine. It hides the meaning of the parameter or variable, and makes it one more place to change if I decide to use it later.
I disagree on this point. It’s just a convention to name a variable `_`, but it does clearly tell the reader that the variable is unused. If I see an unused variable, I want it removed. It’s already a burden to keep track of all the other used variables ;-)

Of course, it’s painful to convert existing code to what Luacheck enjoys, but if you start developing right away with it, it can be rewarding. With dynamically-typed languages (especially Lua), when I misspell a variable I’m actually defining a new global. I like that Luacheck is able to catch that early. I’like even more static typing.
reported every access to the globals that were defined in love.load as undeclared
If I have to declare globals I do it the ugly way with `_G`. I also do declarations of "globals to the file chunk".

Code: Select all

local x, y, z
function love.load()
   x = some_resource()
   -- ...
end

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Sun Jun 09, 2019 11:34 am
by pgimeno
Rucikir wrote: Sat Jun 08, 2019 10:35 am I disagree on this point. It’s just a convention to name a variable `_`, but it does clearly tell the reader that the variable is unused. If I see an unused variable, I want it removed. It’s already a burden to keep track of all the other used variables ;-)
Not always can you remove it. Like when you want e.g. the second result of a function, or the third parameter, or you need to use ipairs but you don't need the loop variable. I think that this:

Code: Select all

function love.keypressed(key, scancode, is_repeat)
  if scancode == "w" then
    -- stuff
  end
  -- more stuff
end
is clearer than this:

Code: Select all

function love.keypressed(_, scancode, _)
  if scancode == "w" then
    -- stuff
  end
  -- more stuff
end
and if you have reasons to use is_repeat or key, you don't need to look up where they must go.

Rucikir wrote: Sat Jun 08, 2019 10:35 am With dynamically-typed languages (especially Lua), when I misspell a variable I’m actually defining a new global. I like that Luacheck is able to catch that early. I’like even more static typing.
That's what the OP is about :) Misspelling a variable is usually caught early in debugging, though, but forgetting 'local' isn't. And I agree about static typing.

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Tue Jun 11, 2019 5:09 am
by Karai17
I definitely like the _ idiom. If I am calling a function that has optional variables, I want it to be very obvious if I am not using them. A note about your example, in Lua you can just leave out trailing variables if you do not need to use them.

Code: Select all

function love.keypressed(_, scancode, _)
could be written as

Code: Select all

function love.keypressed(_, scancode)
This makes it very clear that scancode is the only arg being used. Now, sometimes I can get a little ticked off about luacheck poking me about colon syntax in functions that don't use self. I want my API to be consistant so I want all functions to be called with colons, not making people remember which ones have colons and which ones do not. But I end up agreeing that writing the code "correctly" is actually better since it is clear that the function only interacts with local state and does not affect the state of anything else while still making my API consistant.

Code: Select all

function Object:method(a, b, c) end

local o = Object.new()
o:method(a, b, c)
becomes

Code: Select all

function Object.method(_, a, b, c) end -- static function locally accessable to the object

local o = Object.new()
o:method(a, b, c) -- o:method() instead of Object.method()

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Tue Jun 11, 2019 10:05 am
by raidho36
Unused variables is something that Lua does a lot in general, so the linter should be able to handle it gracefully rather than force users into particular way of writing code - which is against Lua's principles.

My idea of this is that it should be able to pick up metacomments about it and issue warning if declared and factual data don't align, the same way as it does now except the default "all variables declared as used" can be overridden.

Code: Select all

function ( a, b, c, d )
--luacheck-unused: b, d
end

Re: Find globals with LuaJIT (Linux, probably Mac too)

Posted: Tue Jun 11, 2019 4:07 pm
by Karai17
the underscore method is idiomatic Lua so i think it's fine~