@@ -753,10 +753,43 @@ local function createLocal(obj, attrs)
753753 return obj
754754end
755755
756+ --- @param obj table
757+ local function createGlobal (obj , attrs )
758+ obj .type = ' global'
759+
760+ if attrs then
761+ obj .attrs = attrs
762+ attrs .parent = obj
763+ end
764+
765+ local chunk = Chunk [# Chunk ]
766+ if chunk then
767+ local globals = chunk .globals
768+ if not globals then
769+ globals = {}
770+ chunk .globals = globals
771+ end
772+ globals [# globals + 1 ] = obj
773+ end
774+ return obj
775+ end
776+
756777local function pushChunk (chunk )
757778 Chunk [# Chunk + 1 ] = chunk
758779end
759780
781+ local function hasAttr (attrs , attrName )
782+ if not attrs then
783+ return false
784+ end
785+ for i = 1 , # attrs do
786+ if attrs [i ][1 ] == attrName then
787+ return true
788+ end
789+ end
790+ return false
791+ end
792+
760793local function resolveLable (label , obj )
761794 if not label .ref then
762795 label .ref = {}
@@ -2102,23 +2135,77 @@ local function getLocal(name, pos)
21022135 end
21032136end
21042137
2138+ local function getVariable (name , pos )
2139+ for i = # Chunk , 1 , - 1 do
2140+ local chunk = Chunk [i ]
2141+ local resLocal
2142+ local resGlobal
2143+
2144+ -- Find most recent local in this chunk
2145+ local locals = chunk .locals
2146+ if locals then
2147+ for n = 1 , # locals do
2148+ local loc = locals [n ]
2149+ if loc .effect > pos then
2150+ break
2151+ end
2152+ if loc [1 ] == name then
2153+ if not resLocal or resLocal .effect < loc .effect then
2154+ resLocal = loc
2155+ end
2156+ end
2157+ end
2158+ end
2159+
2160+ -- Find global in this chunk (globals don't have effect time, just find the last one)
2161+ local globals = chunk .globals
2162+ if globals then
2163+ for n = # globals , 1 , - 1 do
2164+ local glob = globals [n ]
2165+ if glob [1 ] == name then
2166+ resGlobal = glob
2167+ break
2168+ end
2169+ end
2170+ end
2171+
2172+ -- Return the one declared later (compare by start position)
2173+ if resLocal and resGlobal then
2174+ if resLocal .start > resGlobal .start then
2175+ return resLocal
2176+ else
2177+ return resGlobal
2178+ end
2179+ end
2180+ if resLocal then
2181+ return resLocal
2182+ end
2183+ if resGlobal then
2184+ return resGlobal
2185+ end
2186+ end
2187+
2188+ return nil
2189+ end
2190+
21052191local function resolveName (node )
21062192 if not node then
21072193 return nil
21082194 end
2109- local loc = getLocal (node [1 ], node .start )
2110- if loc then
2195+ local var = getVariable (node [1 ], node .start )
2196+ if var and var . type == ' local ' then
21112197 node .type = ' getlocal'
2112- node .node = loc
2113- if not loc .ref then
2114- loc .ref = {}
2198+ node .node = var
2199+ if not var .ref then
2200+ var .ref = {}
21152201 end
2116- loc .ref [# loc .ref + 1 ] = node
2117- if loc .special then
2118- addSpecial (loc .special , node )
2202+ var .ref [# var .ref + 1 ] = node
2203+ if var .special then
2204+ addSpecial (var .special , node )
21192205 end
21202206 else
21212207 node .type = ' getglobal'
2208+ node .var = var
21222209 local env = getLocal (State .ENVMode , node .start )
21232210 if env then
21242211 node .node = env
@@ -2905,19 +2992,45 @@ local function bindValue(n, v, index, lastValue, isLocal, isSet)
29052992 end
29062993 end
29072994 if n .type == ' setglobal' then
2908- local gi = State .globalInfo
29092995 local nameKey = n [1 ]
2910- -- assignment / definition in strict global scope
2911- if gi .active then
2912- local info = gi .names [nameKey ]
2913- if not gi .hasAll and not info then
2996+ -- check if in strict global scope (any chunk has globals declared)
2997+ local hasGlobalDecl = false
2998+ local hasGlobalAll = false
2999+ local globalAllConst = false
3000+ local declaredGlobal = nil
3001+
3002+ for i = # Chunk , 1 , - 1 do
3003+ local chunk = Chunk [i ]
3004+ if chunk .globals then
3005+ hasGlobalDecl = true
3006+ for j = 1 , # chunk .globals do
3007+ if chunk .globals [j ][1 ] == nameKey then
3008+ declaredGlobal = chunk .globals [j ]
3009+ break
3010+ end
3011+ end
3012+ end
3013+ if chunk .hasGlobalAll then
3014+ hasGlobalAll = true
3015+ if chunk .globalAllConst then
3016+ globalAllConst = true
3017+ end
3018+ end
3019+ if declaredGlobal or hasGlobalAll then
3020+ break
3021+ end
3022+ end
3023+
3024+ -- assignment in strict global scope
3025+ if hasGlobalDecl or hasGlobalAll then
3026+ if not hasGlobalAll and not declaredGlobal then
29143027 pushError {
29153028 type = ' UNDEFINED_IN_GLOBAL_SCOPE' ,
29163029 start = n .start ,
29173030 finish = n .finish ,
29183031 }
29193032 end
2920- local isConst = (info and info . const ) or gi . allConst
3033+ local isConst = (declaredGlobal and declaredGlobal . attrs and hasAttr ( declaredGlobal . attrs , ' const' )) or globalAllConst
29213034 if isConst then
29223035 pushError {
29233036 type = ' ASSIGN_CONST_GLOBAL' ,
@@ -3205,10 +3318,7 @@ local function parseGlobal()
32053318 name .vstart = func .start
32063319 name .range = func .finish
32073320 func .parent = name
3208- local gi = State .globalInfo
3209- gi .active = true
3210- local nameKey = name [1 ]
3211- gi .names [nameKey ] = gi .names [nameKey ] or { declared = true , const = false }
3321+ createGlobal (name )
32123322 pushActionIntoCurrentChunk (name )
32133323 return name
32143324 else
@@ -3228,9 +3338,18 @@ local function parseGlobal()
32283338 finish = getPosition (Tokens [Index ], ' right' ),
32293339 attrs = attrs ,
32303340 }
3231- local gi = State .globalInfo
3232- gi .active = true
3233- gi .hasAll = true
3341+ local chunk = Chunk [# Chunk ]
3342+ if chunk then
3343+ chunk .hasGlobalAll = true
3344+ if attrs then
3345+ for i = 1 , # attrs do
3346+ if attrs [i ][1 ] == ' const' then
3347+ chunk .globalAllConst = true
3348+ break
3349+ end
3350+ end
3351+ end
3352+ end
32343353 if attrs then
32353354 attrs .parent = action
32363355 for i = 1 , # attrs do
@@ -3241,8 +3360,6 @@ local function parseGlobal()
32413360 start = a .start ,
32423361 finish = a .finish ,
32433362 }
3244- elseif a [1 ] == ' const' then
3245- gi .allConst = true
32463363 end
32473364 end
32483365 end
@@ -3259,14 +3376,10 @@ local function parseGlobal()
32593376 end
32603377
32613378 local glob = {
3262- type = ' setglobal' ,
32633379 start = name .start ,
32643380 finish = name .finish ,
32653381 [1 ] = name [1 ],
32663382 }
3267- local gi = State .globalInfo
3268- gi .active = true
3269- gi .names [glob [1 ]] = gi .names [glob [1 ]] or { declared = true , const = false }
32703383
32713384 if attrs then
32723385 glob .attrs = attrs
@@ -3279,8 +3392,6 @@ local function parseGlobal()
32793392 start = attrs [i ].start ,
32803393 finish = attrs [i ].finish ,
32813394 }
3282- elseif attrs [i ][1 ] == ' const' then
3283- gi .names [glob [1 ]].const = true
32843395 end
32853396 end
32863397 else
@@ -3297,8 +3408,6 @@ local function parseGlobal()
32973408 start = attrsAfter [i ].start ,
32983409 finish = attrsAfter [i ].finish ,
32993410 }
3300- elseif attrsAfter [i ][1 ] == ' const' then
3301- gi .names [glob [1 ]].const = true
33023411 end
33033412 end
33043413 if glob .attrs then
@@ -3312,6 +3421,7 @@ local function parseGlobal()
33123421 glob .finish = attrsAfter [# attrsAfter ].finish
33133422 end
33143423
3424+ createGlobal (glob , glob .attrs )
33153425 pushActionIntoCurrentChunk (glob )
33163426 skipSpace ()
33173427
@@ -3321,12 +3431,10 @@ local function parseGlobal()
33213431 local nameN = parseName (true )
33223432 if nameN then
33233433 local gN = {
3324- type = ' setglobal' ,
33253434 start = attrsN and attrsN .start or nameN .start ,
33263435 finish = nameN .finish ,
33273436 [1 ] = nameN [1 ],
33283437 }
3329- gi .names [gN [1 ]] = gi .names [gN [1 ]] or { declared = true , const = false }
33303438 if attrsN then
33313439 gN .attrs = attrsN
33323440 attrsN .parent = gN
@@ -3337,8 +3445,6 @@ local function parseGlobal()
33373445 start = attrsN [i ].start ,
33383446 finish = attrsN [i ].finish ,
33393447 }
3340- elseif attrsN [i ][1 ] == ' const' then
3341- gi .names [gN [1 ]].const = true
33423448 end
33433449 end
33443450 end
@@ -3351,8 +3457,6 @@ local function parseGlobal()
33513457 start = attrsNAfter [i ].start ,
33523458 finish = attrsNAfter [i ].finish ,
33533459 }
3354- elseif attrsNAfter [i ][1 ] == ' const' then
3355- gi .names [gN [1 ]].const = true
33563460 end
33573461 end
33583462 if gN .attrs then
@@ -3365,6 +3469,7 @@ local function parseGlobal()
33653469 end
33663470 gN .finish = attrsNAfter [# attrsNAfter ].finish
33673471 end
3472+ createGlobal (gN , gN .attrs )
33683473 return gN
33693474 end
33703475 return nil
@@ -4373,13 +4478,6 @@ local function initState(lua, version, options)
43734478 errs [# errs + 1 ] = err
43744479 return err
43754480 end
4376- -- Lua 5.5 global keyword semantic tracking (syntax-level errors)
4377- state .globalInfo = {
4378- names = {}, -- map name -> { declared=true, const=bool }
4379- hasAll = false , -- global * encountered
4380- allConst = false , -- global <const> * encountered
4381- active = false , -- any global declaration activates strict global scope
4382- }
43834481end
43844482
43854483return function (lua , mode , version , options )
0 commit comments