diff --git a/polymer/eduke32/source/lunatic/doc/lunacon.txt b/polymer/eduke32/source/lunatic/doc/lunacon.txt index 2e335a1a0..feda13f01 100644 --- a/polymer/eduke32/source/lunatic/doc/lunacon.txt +++ b/polymer/eduke32/source/lunatic/doc/lunacon.txt @@ -143,6 +143,14 @@ for a 32-bit integer, but inside that of an unsigned 32-bit integer. In this case, 2^32^ is subtracted from the number, producing a negative value without changing the bit representation. +`-Wnever-used-gamevar` (default: off):: +After translation, issues a warning for every CON-side user gamevar definition +that was never referenced, that is, neither read nor written in the CON code. + +`-Wnever-read-gamevar` (default: off):: +After translation, issues a warning for every CON-side user gamevar definition +that was assigned to but never read in the CON code. + `-Wsystem-gamevar` (default: on):: Warns whenever the initial value of a system gamevar was overridden (by issuing `gamevar` at file scope), but the provided gamevar flags did not match those of diff --git a/polymer/eduke32/source/lunatic/lunacon.lua b/polymer/eduke32/source/lunatic/lunacon.lua index 6fc9eb3a0..ef296475f 100644 --- a/polymer/eduke32/source/lunatic/lunacon.lua +++ b/polymer/eduke32/source/lunatic/lunacon.lua @@ -119,7 +119,8 @@ local g_defaultDir = nil -- -Wno-bad-identifier for disabling the "bad identifier" warning. local g_warn = { ["not-redefined"]=true, ["bad-identifier"]=false, ["number-conversion"]=true, ["system-gamevar"]=true, - ["error-bad-getactorvar"]=false, ["chained-loadactor"]=true, } + ["error-bad-getactorvar"]=false, ["chained-loadactor"]=true, + ["never-used-gamevar"]=false, ["never-read-gamevar"]=false, } -- Code generation and output options. local g_cgopt = { ["no"]=false, ["debug-lineinfo"]=false, ["gendir"]=nil, @@ -341,7 +342,8 @@ local function new_initial_gvartab() local function GamevarCreationFunc(addflags) return function(varname) - return { name=varname, flags=GVFLAG.SYSTEM+addflags } + -- 'used' is a bitmask: 1 is 'was read', 2 is 'was written to' + return { name=varname, flags=GVFLAG.SYSTEM+addflags, used=3 } end end @@ -1553,12 +1555,12 @@ function Cmd.gamevar(identifier, initval, flags) -- Declare new session gamevar. g_gamevar[identifier] = { name=format("_gv._sessionVar[%d]", g_numSessionVars), - flags=flags, loc=getLocation() } + flags=flags, loc=getLocation(), used=0 } g_numSessionVars = g_numSessionVars+1 return end - local gv = { name=mangle_name(identifier, "V"), flags=flags, loc=getLocation() } + local gv = { name=mangle_name(identifier, "V"), flags=flags, loc=getLocation(), used=0 } g_gamevar[identifier] = gv if (storeWithSavegames) then @@ -1624,6 +1626,8 @@ function lookup.gamevar(identifier, aorpvar, writable) return "_READONLYGV" end + gv.used = bit.bor(gv.used, writable and 2 or 1) + if (bit.band(gv.flags, GVFLAG.PERACTOR)~=0) then return format("%s[%s]", gv.name, aorpvar) elseif (bit.band(gv.flags, GVFLAG.CON_PERPLAYER)~=0 and g_cgopt["playervar"]) then @@ -1998,6 +2002,10 @@ function lookup.array_expr(writep, structname, index, membertab) return "_INVALIDAV" end + if (gv) then + gv.used = bit.bor(gv.used, writep and 2 or 1) + end + assert(#membertab == 1) return lookup.gamevar(membertab[1], index, writep) end @@ -2108,6 +2116,10 @@ local function GetOrSetPerxvarCmd(Setp, Actorp) idx = thisactor_to_pli(idx) end + if (gv) then + gv.used = bit.bor(gv.used, Setp and 2 or 1) + end + if (Setp) then return format("%s=%s", lookup.gamevar(perxvarname, idx, true), var) else @@ -2252,6 +2264,8 @@ local handle = -- even if we're saving it. local code = lookup.gamevar(identifier, index, true) + gv.used = bit.bor(gv.used, not dosave and 2 or 1) + if (dosave) then return format("_con._savegamevar(%q,%s)", identifier, code) else @@ -3730,6 +3744,51 @@ function parse(contents) -- local end end + -- Check read/written status of all user gamevars. + if (idx == #contents+1 and g_recurslevel==0) then + local gvs = {} + for identifier, gv in pairs(g_gamevar) do + if (gv.used ~= 3) then + -- NOTE: read but not written to gamevar (gv.used == 1) has its + -- use in C-CON + if (gv.used == 0 and g_warn["never-used-gamevar"] or + gv.used == 2 and g_warn["never-read-gamevar"]) then + gv.id = identifier + gvs[#gvs+1] = gv + end + end + end + + function compare_gv(gva, gvb) + if (gva.loc[1] ~= gvb.loc[1]) then + return gva.loc[1] < gvb.loc[1] + end + return (gva.loc[2] < gvb.loc[2]) + end + + table.sort(gvs, compare_gv) + + for i=1,#gvs do + local gv = gvs[i] + + local loc = gv.loc + local locstr = loc and format("%s %d:%d: ", loc[1], loc[2], loc[3]) or "" + + local perActor = (bit.band(gv.flags, GVFLAG.PERACTOR) ~= 0) + local perPlayer = (bit.band(gv.flags, GVFLAG.PERPLAYER) ~= 0) + local kindstr = perActor and "per-actor " or (perPlayer and "per-player " or "") + + if (gv.used == 0) then + printf("%sWarning: never used %sgamevar `%s'", locstr, + kindstr, gv.id) + else + printf("%sWarning: never %s %sgamevar `%s'", locstr, + gv.used == 1 and "written to" or "read", + kindstr, gv.id) + end + end + end + addcodef("-- END %s", g_filename) g_recurslevel = g_recurslevel-1