LuaLS/lua-language-server

Globals set with `rawset` are always parsed as a table

LJSonik opened this issue ยท 1 comments

How are you using the lua-language-server?

Visual Studio Code Extension (sumneko.lua)

Which OS are you using?

Windows

What is the issue affecting?

Annotations, Type Checking

Expected Behaviour

I expect x to be parsed as an integer:

rawset(_G, "x", 42)
print(x) -- x should be parsed as integer here

Actual Behaviour

x is parsed as integer|table instead.

Reproduction steps

rawset(_G, "x", 42)
print(x) -- x is parsed as integer|table here

Additional Notes

No response

Log File

No response

Root cause analysis

After some debugging, I found that the table infer type seems to be inserted by this code block ๐Ÿค”:

: case 'call'
: call(function (source)
local node = getReturn(source.node, 1, source.args)
if not node then
return
end
if not node:isTyped() then
node = vm.runOperator('call', source.node) or node
end
vm.setNode(source, node)
end)

  • When compiling a rawset() call statement, this code block will be executed.
  • I believe this logic is to use the return value of the call statement to set the left hand side variable if any (?),
    and the return value of rawset() is annotated as table type by built-in meta file:
    ---@param table table
    ---@param index any
    ---@param value any
    ---@return table
    function rawset(table, index, value) end
  • so in the following code snippet
    local a = rawset() -- a: table
  • But as rawset(_G, ...) is handled specially, the created global is treated as the (assignment) value of this call (?):
    local global = vm.declareGlobal('variable', name, uri)
    if source.node.special == 'rawset' then
    global:addSet(uri, source)
    source.value = source.args[3]
  • This explains why the injected type is always table (the return type of rawset())

Proposed solution

I just tried to skip the logic if it is a rawset statement, and it worked perfectly ๐ŸŽ‰

    : case 'call'
    : call(function (source)
        -- ignore rawset
        if source.node.special == 'rawset' then
            return
        end
  • I originally afraid that this will break the local a = rawset() case, but seems not ๐Ÿ˜„
    a can still be inferred as table
  • All existing tests passed, so no obvious side effects for this proposed change.

I believe this is the solution to this issue. I will try to open a PR for this, along with the proposed changes in #2862 (comment) to resolve these 2 issues together later ๐Ÿ˜„