Path: 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 Sat 18 Jan 2014 13:14:24 thomasl (By Thomas Lauer)$