Path: Computer > Computer > Lua Related Stuff > LuaStrict


One of the two Lua “features” that really annoy me is the fact that globals don't have to be declared. Locals have, thank you, but globals spring into existence whenever you reference them: bonjour typos. (BTW, the other thing that's driving me nuts is that you can't assign nil to a variable without making it undefined. But that's a different story. Or is it?)

Not all is lost, though. There's a simple code example in the Lua distribution (see etc/strict.lua) that sort of solves this problem, at least for global variables in the main program. I have taken this example, extended it for use with modules and put the code itself into a module.

While doing that I threw in a few other things: there are now two modes (weak, similar to what strict.lua does, and strong which requires full declaration of all globals), there is a customisable error handler and more.

Have a closer look if you also fight against undeclared globals. Here's the code for the module (requires Lua 5.1 or later):

local g=_G
function handler(m,n,v)
&if m=='_G' then m=' in main program'
&else m=' in module '..m end
&if (v~=nil) then g.error('invalid assignment to '..n..m,3)
&else g.error(n..' not declared'..m,3) end
local function noaccess1() -- for __newindex
&local d=g.debug.getinfo(3,'S')
&local w=d and d.what or 'C'
&if strong then return w~='C' end
&return w~='main' and w~='C'
local function noaccess2() -- for __index
&local d=g.debug.getinfo(3,'S')
&local w=d and d.what or 'C'
&return w~='C'
function declareGlobal(n,v,m)
&m=m or g; -- no module means n will go into _G
&if not isDeclared(n,m) then -- if not there, go ahead
&&if v~=nil then g.rawset(m,n,v) end
function registerModule(m)
&-- based on Roberto's code in etc/strict.lua
&if g.type(m)~='table' then return end
&local mt=g.getmetatable(m)
&if mt==nil then
&if mt.__Idle_declared==nil then mt.__Idle_declared={}
&else return end -- already registered
&mt.__newindex=function (t,n,v)
&&if not mt.__Idle_declared[n] then
&&&if strict and noaccess1() then handler(t,n,v)
&&&else mt.__Idle_declared[n]=true end
&&-- the call to g.rawget() seems superfluous but if the call to handler()
&&-- already initialised n it'll not be overwritten here
&&if mt.__Idle_declared[n] and g.rawget(t,n)==nil then g.rawset(t,n,v) end
&mt.__index=function (t,n)
&&if strict and not mt.__Idle_declared[n] and noaccess2() then handler(t,n,nil)
&&else return g.rawget(t,n) end
function getModulename(m)
&for k,v in g.pairs(g.package.loaded) do
&&if m==v then return k end
&return '<UNKNOWN>'
function isDeclared(n,m)
&m=m or g;
&return g.getmetatable(m).__Idle_declared[n] or g.rawget(m,n)~=nil
-- Register loaded modules (may be too much hand-holding for some)
for k,v in g.pairs(g.package.loaded) do registerModule(v) end

There are also a few simple usage examples which will hopefully make up for the total lack of documentation.

You can download all files (the module and examples) in a tidy little archive.

$updated from: LuaStrict.htxt Thu 27 Apr 2017 10:06:49 thomasl (By Thomas Lauer)$