Catching variable name errors: LUA typos

Discussion in 'C-Bus Automation Controllers' started by ssaunders, Oct 29, 2023.

  1. ssaunders

    ssaunders

    Joined:
    Dec 17, 2008
    Messages:
    242
    Likes Received:
    35
    Location:
    Melbourne
    I've written a few big scripts, and while doing so discovered much late that I had subtle bugs that would silently ruin things unexpectedly. (How many noticed I used "late" instead of "later"?) So I thought I'd share an LUA technique that I've recently adopted for my AC code, which has uncovered plenty of lurking nasties.

    Consider this:

    Code:
    hot = true
    if hot = nil then dream = 'waking' else dreams = 'walking' end
    
    print(dream) -- and you will get nil, not 'walking' even though "hot" is true
    The subtle plural on "dream". Or it could be a case like "nothaving ==" vs. "notHaving ==". Same result. A silent nasty.

    The code below fixes this. As long as every single variable used is declared as "local var" or "local var = ..." this metatable will catch typos in variables.

    The way it works is to flag undeclared global variable use as a warning. (To actually use a global, use "declare(var, init)".) So if you've a typo in a variable name it'll get spotted quickly before much debug logging ensues.

    Cheers.

    Code:
    -- Runtime global variable checking. Globals must be explicitly declared, which will catch variable name typos
    local declaredNames = {['vprint'] = true, ['vprinthex'] = true, ['maxgroup'] = true, ['mosquitto'] = true, ['ssl'] = true, } -- Reserved declarations that should be ignored
    local function declare(name, initval) rawset(_G, name, initval) declaredNames[name] = true end
    local exclude = {['ngx'] = true, } -- Reserved variable reads that should be ignored
    setmetatable(_G, {
      __newindex = function (t, n, v) if not declaredNames[n] then log('Warning: Write to undeclared global variable "'..n..'"') end rawset(t, n, v) end,
      __index = function (_, n) if not exclude[n] and not declaredNames[n] then log('Warning: Read undeclared global variable "'..n..'"') end return nil end,
    })
    
     
    ssaunders, Oct 29, 2023
    #1
    Robert likes this.
Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.