summaryrefslogtreecommitdiff
path: root/script/vm
diff options
context:
space:
mode:
authorfesily <fesil@foxmail.com>2024-01-10 11:03:21 +0800
committerGitHub <noreply@github.com>2024-01-10 11:03:21 +0800
commit1e7bb72ad3ff2b75a1c55ee4bc53004cb7fe30f7 (patch)
treedd94cf09b9e3a675a73a9f1d41248d1538165997 /script/vm
parentbb6e172d6166190bd4edd3bb56230a7d60ebcb93 (diff)
parent37779f9b2493e51e59e1e4366bf7dcb8350e69bd (diff)
downloadlua-language-server-1e7bb72ad3ff2b75a1c55ee4bc53004cb7fe30f7.zip
Merge branch 'LuaLS:master' into plugin-add-OnTransformAst
Diffstat (limited to 'script/vm')
-rw-r--r--script/vm/compiler.lua139
-rw-r--r--script/vm/doc.lua21
-rw-r--r--script/vm/function.lua10
-rw-r--r--script/vm/global.lua67
-rw-r--r--script/vm/infer.lua19
-rw-r--r--script/vm/node.lua1
-rw-r--r--script/vm/operator.lua8
-rw-r--r--script/vm/sign.lua18
-rw-r--r--script/vm/type.lua30
-rw-r--r--script/vm/visible.lua28
10 files changed, 271 insertions, 70 deletions
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
index 6b4636fc..8a1fa96a 100644
--- a/script/vm/compiler.lua
+++ b/script/vm/compiler.lua
@@ -7,10 +7,14 @@ local files = require 'files'
local vm = require 'vm.vm'
---@class parser.object
----@field _compiledNodes boolean
----@field _node vm.node
----@field cindex integer
----@field func parser.object
+---@field _compiledNodes boolean
+---@field _node vm.node
+---@field cindex integer
+---@field func parser.object
+---@field hideView boolean
+---@field package _returns? parser.object[]
+---@field package _callReturns? parser.object[]
+---@field package _asCache? parser.object[]
-- 该函数有副作用,会给source绑定node!
---@param source parser.object
@@ -28,6 +32,12 @@ function vm.bindDocs(source)
end
if doc.type == 'doc.class' then
vm.setNode(source, vm.compileNode(doc))
+ for j = i + 1, #docs do
+ local overload = docs[j]
+ if overload.type == 'doc.overload' then
+ overload.overload.hideView = true
+ end
+ end
return true
end
if doc.type == 'doc.param' then
@@ -55,6 +65,9 @@ function vm.bindDocs(source)
vm.setNode(source, vm.compileNode(ast))
return true
end
+ if doc.type == 'doc.overload' then
+ vm.setNode(source, vm.compileNode(doc))
+ end
end
return false
end
@@ -473,6 +486,7 @@ function vm.getReturnOfFunction(func, index)
func._returns = {}
end
if not func._returns[index] then
+ ---@diagnostic disable-next-line: missing-fields
func._returns[index] = {
type = 'function.return',
parent = func,
@@ -570,6 +584,7 @@ local function getReturn(func, index, args)
end
if not func._callReturns[index] then
local call = func.parent
+ ---@diagnostic disable-next-line: missing-fields
func._callReturns[index] = {
type = 'call.return',
parent = call,
@@ -756,6 +771,11 @@ function vm.selectNode(list, index)
if not exp then
return vm.createNode(vm.declareGlobal('type', 'nil')), nil
end
+
+ if vm.bindDocs(list) then
+ return vm.compileNode(list), exp
+ end
+
---@type vm.node?
local result
if exp.type == 'call' then
@@ -862,52 +882,69 @@ local function compileCallArgNode(arg, call, callNode, fixIndex, myIndex)
end
end
- for n in callNode:eachObject() do
- if n.type == 'function' then
- ---@cast n parser.object
- local sign = vm.getSign(n)
+ ---@param n parser.object
+ local function dealDocFunc(n)
+ local myEvent
+ if n.args[eventIndex] then
+ local argNode = vm.compileNode(n.args[eventIndex])
+ myEvent = argNode:get(1)
+ end
+ if not myEvent
+ or not eventMap
+ or myIndex <= eventIndex
+ or myEvent.type ~= 'doc.type.string'
+ or eventMap[myEvent[1]] then
local farg = getFuncArg(n, myIndex)
if farg then
for fn in vm.compileNode(farg):eachObject() do
if isValidCallArgNode(arg, fn) then
- if fn.type == 'doc.type.function' then
- ---@cast fn parser.object
- if sign then
- local generic = vm.createGeneric(fn, sign)
- local args = {}
- for i = fixIndex + 1, myIndex - 1 do
- args[#args+1] = call.args[i]
- end
- local resolvedNode = generic:resolve(guide.getUri(call), args)
- vm.setNode(arg, resolvedNode)
- goto CONTINUE
- end
- end
vm.setNode(arg, fn)
- ::CONTINUE::
end
end
end
end
- if n.type == 'doc.type.function' then
- ---@cast n parser.object
- local myEvent
- if n.args[eventIndex] then
- local argNode = vm.compileNode(n.args[eventIndex])
- myEvent = argNode:get(1)
- end
- if not myEvent
- or not eventMap
- or myIndex <= eventIndex
- or myEvent.type ~= 'doc.type.string'
- or eventMap[myEvent[1]] then
- local farg = getFuncArg(n, myIndex)
- if farg then
- for fn in vm.compileNode(farg):eachObject() do
- if isValidCallArgNode(arg, fn) then
- vm.setNode(arg, fn)
+ end
+
+ ---@param n parser.object
+ local function dealFunction(n)
+ local sign = vm.getSign(n)
+ local farg = getFuncArg(n, myIndex)
+ if farg then
+ for fn in vm.compileNode(farg):eachObject() do
+ if isValidCallArgNode(arg, fn) then
+ if fn.type == 'doc.type.function' then
+ ---@cast fn parser.object
+ if sign then
+ local generic = vm.createGeneric(fn, sign)
+ local args = {}
+ for i = fixIndex + 1, myIndex - 1 do
+ args[#args+1] = call.args[i]
+ end
+ local resolvedNode = generic:resolve(guide.getUri(call), args)
+ vm.setNode(arg, resolvedNode)
+ goto CONTINUE
end
end
+ vm.setNode(arg, fn)
+ ::CONTINUE::
+ end
+ end
+ end
+ end
+
+ for n in callNode:eachObject() do
+ if n.type == 'function' then
+ ---@cast n parser.object
+ dealFunction(n)
+ elseif n.type == 'doc.type.function' then
+ ---@cast n parser.object
+ dealDocFunc(n)
+ elseif n.type == 'global' and n.cate == 'type' then
+ ---@cast n vm.global
+ local overloads = vm.getOverloadsByTypeName(n.name, guide.getUri(arg))
+ if overloads then
+ for _, func in ipairs(overloads) do
+ dealDocFunc(func)
end
end
end
@@ -1020,6 +1057,7 @@ local function compileLocal(source)
vm.setNode(source, vm.compileNode(source.value))
end
end
+
-- function x.y(self, ...) --> function x:y(...)
if source[1] == 'self'
and not hasMarkDoc
@@ -1031,6 +1069,7 @@ local function compileLocal(source)
vm.setNode(source, vm.compileNode(setfield.node))
end
end
+
if source.parent.type == 'funcargs' and not hasMarkDoc and not hasMarkParam then
local func = source.parent.parent
-- local call ---@type fun(f: fun(x: number));call(function (x) end) --> x -> number
@@ -1055,6 +1094,7 @@ local function compileLocal(source)
vm.setNode(source, vm.declareGlobal('type', 'any'))
end
end
+
-- for x in ... do
if source.parent.type == 'in' then
compileForVars(source.parent, source)
@@ -1094,6 +1134,12 @@ local function compileLocal(source)
end
end
+ if source.value
+ and source.value.type == 'nil'
+ and not myNode:hasKnownType() then
+ vm.setNode(source, vm.compileNode(source.value))
+ end
+
myNode.hasDefined = hasMarkDoc or hasMarkParam or hasMarkValue
end
@@ -1140,6 +1186,9 @@ local compilerSwitch = util.switch()
end)
: case 'table'
: call(function (source)
+ if vm.bindAs(source) then
+ return
+ end
vm.setNode(source, source)
if source.parent.type == 'callargs' then
@@ -1147,6 +1196,16 @@ local compilerSwitch = util.switch()
vm.compileCallArg(source, call)
end
+ if source.parent.type == 'return' then
+ local myIndex = util.arrayIndexOf(source.parent, source)
+ ---@cast myIndex -?
+ local parentNode = vm.selectNode(source.parent, myIndex)
+ if not parentNode:isEmpty() then
+ vm.setNode(source, parentNode)
+ return
+ end
+ end
+
if source.parent.type == 'setglobal'
or source.parent.type == 'local'
or source.parent.type == 'setlocal'
@@ -1648,7 +1707,7 @@ local compilerSwitch = util.switch()
if state.type == 'doc.return'
or state.type == 'doc.param' then
local func = state.bindSource
- if func.type == 'function' then
+ if func and func.type == 'function' then
local node = guide.getFunctionSelfNode(func)
if node then
vm.setNode(source, vm.compileNode(node))
diff --git a/script/vm/doc.lua b/script/vm/doc.lua
index a6ea248f..6ac39910 100644
--- a/script/vm/doc.lua
+++ b/script/vm/doc.lua
@@ -5,7 +5,11 @@ local vm = require 'vm.vm'
local config = require 'config'
---@class parser.object
----@field package _castTargetHead parser.object | vm.global | false
+---@field package _castTargetHead? parser.object | vm.global | false
+---@field package _validVersions? table<string, boolean>
+---@field package _deprecated? parser.object | false
+---@field package _async? boolean
+---@field package _nodiscard? boolean
---获取class与alias
---@param suri uri
@@ -467,3 +471,18 @@ function vm.getCastTargetHead(doc)
end
return nil
end
+
+---@param doc parser.object
+---@param key string
+---@return boolean
+function vm.docHasAttr(doc, key)
+ if not doc.docAttr then
+ return false
+ end
+ for _, name in ipairs(doc.docAttr.names) do
+ if name[1] == key then
+ return true
+ end
+ end
+ return false
+end
diff --git a/script/vm/function.lua b/script/vm/function.lua
index bdd7e229..c6df6349 100644
--- a/script/vm/function.lua
+++ b/script/vm/function.lua
@@ -372,8 +372,14 @@ function vm.isVarargFunctionWithOverloads(func)
if not func.args then
return false
end
- if not func.args[1] or func.args[1].type ~= '...' then
- return false
+ if func.args[1] and func.args[1].type == 'self' then
+ if not func.args[2] or func.args[2].type ~= '...' then
+ return false
+ end
+ else
+ if not func.args[1] or func.args[1].type ~= '...' then
+ return false
+ end
end
if not func.bindDocs then
return false
diff --git a/script/vm/global.lua b/script/vm/global.lua
index c1b5f320..e830f6d8 100644
--- a/script/vm/global.lua
+++ b/script/vm/global.lua
@@ -1,6 +1,7 @@
local util = require 'utility'
local scope = require 'workspace.scope'
local guide = require 'parser.guide'
+local config = require 'config'
---@class vm
local vm = require 'vm.vm'
@@ -371,16 +372,36 @@ local compilerGlobalSwitch = util.switch()
return
end
source._enums = {}
- for _, field in ipairs(tbl) do
- if field.type == 'tablefield' then
- source._enums[#source._enums+1] = field
- local subType = vm.declareGlobal('type', name .. '.' .. field.field[1], uri)
- subType:addSet(uri, field)
- elseif field.type == 'tableindex' then
- source._enums[#source._enums+1] = field
- if field.index.type == 'string' then
- local subType = vm.declareGlobal('type', name .. '.' .. field.index[1], uri)
+ if vm.docHasAttr(source, 'key') then
+ for _, field in ipairs(tbl) do
+ if field.type == 'tablefield' then
+ source._enums[#source._enums+1] = {
+ type = 'doc.type.string',
+ start = field.field.start,
+ finish = field.field.finish,
+ [1] = field.field[1],
+ }
+ elseif field.type == 'tableindex' then
+ source._enums[#source._enums+1] = {
+ type = 'doc.type.string',
+ start = field.index.start,
+ finish = field.index.finish,
+ [1] = field.index[1],
+ }
+ end
+ end
+ else
+ for _, field in ipairs(tbl) do
+ if field.type == 'tablefield' then
+ source._enums[#source._enums+1] = field
+ local subType = vm.declareGlobal('type', name .. '.' .. field.field[1], uri)
subType:addSet(uri, field)
+ elseif field.type == 'tableindex' then
+ source._enums[#source._enums+1] = field
+ if field.index.type == 'string' then
+ local subType = vm.declareGlobal('type', name .. '.' .. field.index[1], uri)
+ subType:addSet(uri, field)
+ end
end
end
end
@@ -518,6 +539,33 @@ function vm.hasGlobalSets(suri, cate, name)
return true
end
+---@param src parser.object
+local function checkIsUndefinedGlobal(src)
+ local key = src[1]
+
+ local uri = guide.getUri(src)
+ local dglobals = util.arrayToHash(config.get(uri, 'Lua.diagnostics.globals'))
+ local rspecial = config.get(uri, 'Lua.runtime.special')
+
+ local node = src.node
+ return src.type == 'getglobal' and key and not (
+ dglobals[key] or
+ rspecial[key] or
+ node.tag ~= '_ENV' or
+ vm.hasGlobalSets(uri, 'variable', key)
+ )
+end
+
+---@param src parser.object
+---@return boolean
+function vm.isUndefinedGlobal(src)
+ local node = vm.compileNode(src)
+ if node.undefinedGlobal == nil then
+ node.undefinedGlobal = checkIsUndefinedGlobal(src)
+ end
+ return node.undefinedGlobal
+end
+
---@param source parser.object
function compileObject(source)
if source._globalNode ~= nil then
@@ -593,6 +641,7 @@ function vm.getGlobalBase(source)
end
local name = global:asKeyName()
if not root._globalBaseMap[name] then
+ ---@diagnostic disable-next-line: missing-fields
root._globalBaseMap[name] = {
type = 'globalbase',
parent = root,
diff --git a/script/vm/infer.lua b/script/vm/infer.lua
index 94fdfd88..f2673ed3 100644
--- a/script/vm/infer.lua
+++ b/script/vm/infer.lua
@@ -386,9 +386,11 @@ function mt:_computeViews(uri)
self.views = {}
for n in self.node:eachObject() do
- local view = viewNodeSwitch(n.type, n, self, uri)
- if view then
- self.views[view] = true
+ if not n.hideView then
+ local view = viewNodeSwitch(n.type, n, self, uri)
+ if view then
+ self.views[view] = true
+ end
end
end
@@ -565,11 +567,12 @@ function vm.viewKey(source, uri)
return vm.viewKey(source.types[1], uri)
else
local key = vm.getInfer(source):view(uri)
- return '[' .. key .. ']'
+ return '[' .. key .. ']', key
end
end
if source.type == 'tableindex'
- or source.type == 'setindex' then
+ or source.type == 'setindex'
+ or source.type == 'getindex' then
local index = source.index
local name = vm.getInfer(index):viewLiterals()
if not name then
@@ -587,7 +590,11 @@ function vm.viewKey(source, uri)
return vm.viewKey(source.name, uri)
end
if source.type == 'doc.type.name' then
- return '[' .. source[1] .. ']'
+ return '[' .. source[1] .. ']', source[1]
+ end
+ if source.type == 'doc.type.string' then
+ local name = util.viewString(source[1], source[2])
+ return ('[%s]'):format(name), name
end
local key = vm.getKeyName(source)
if key == nil then
diff --git a/script/vm/node.lua b/script/vm/node.lua
index 0ffd8c70..bc1dfcb1 100644
--- a/script/vm/node.lua
+++ b/script/vm/node.lua
@@ -15,6 +15,7 @@ vm.nodeCache = setmetatable({}, util.MODE_K)
---@field [integer] vm.node.object
---@field [vm.node.object] true
---@field fields? table<vm.node|string, vm.node>
+---@field undefinedGlobal boolean?
local mt = {}
mt.__index = mt
mt.id = 0
diff --git a/script/vm/operator.lua b/script/vm/operator.lua
index bc8703c6..7ce2b30d 100644
--- a/script/vm/operator.lua
+++ b/script/vm/operator.lua
@@ -126,6 +126,7 @@ vm.unarySwich = util.switch()
if result == nil then
vm.setNode(source, vm.declareGlobal('type', 'boolean'))
else
+ ---@diagnostic disable-next-line: missing-fields
vm.setNode(source, {
type = 'boolean',
start = source.start,
@@ -155,6 +156,7 @@ vm.unarySwich = util.switch()
vm.setNode(source, node or vm.declareGlobal('type', 'number'))
end
else
+ ---@diagnostic disable-next-line: missing-fields
vm.setNode(source, {
type = 'number',
start = source.start,
@@ -171,6 +173,7 @@ vm.unarySwich = util.switch()
local node = vm.runOperator('bnot', source[1])
vm.setNode(source, node or vm.declareGlobal('type', 'integer'))
else
+ ---@diagnostic disable-next-line: missing-fields
vm.setNode(source, {
type = 'integer',
start = source.start,
@@ -223,6 +226,7 @@ vm.binarySwitch = util.switch()
if source.op.type == '~=' then
result = not result
end
+ ---@diagnostic disable-next-line: missing-fields
vm.setNode(source, {
type = 'boolean',
start = source.start,
@@ -247,6 +251,7 @@ vm.binarySwitch = util.switch()
or op == '&' and a & b
or op == '|' and a | b
or op == '~' and a ~ b
+ ---@diagnostic disable-next-line: missing-fields
vm.setNode(source, {
type = 'integer',
start = source.start,
@@ -285,6 +290,7 @@ vm.binarySwitch = util.switch()
or op == '%' and a % b
or op == '//' and a // b
or op == '^' and a ^ b
+ ---@diagnostic disable-next-line: missing-fields
vm.setNode(source, {
type = (op == '//' or math.type(result) == 'integer') and 'integer' or 'number',
start = source.start,
@@ -364,6 +370,7 @@ vm.binarySwitch = util.switch()
end
end
end
+ ---@diagnostic disable-next-line: missing-fields
vm.setNode(source, {
type = 'string',
start = source.start,
@@ -407,6 +414,7 @@ vm.binarySwitch = util.switch()
or op == '<' and a < b
or op == '>=' and a >= b
or op == '<=' and a <= b
+ ---@diagnostic disable-next-line: missing-fields
vm.setNode(source, {
type = 'boolean',
start = source.start,
diff --git a/script/vm/sign.lua b/script/vm/sign.lua
index 1f434475..38cb2242 100644
--- a/script/vm/sign.lua
+++ b/script/vm/sign.lua
@@ -142,13 +142,15 @@ function mt:resolve(uri, args)
end
if object.type == 'doc.type.function' then
for i, arg in ipairs(object.args) do
- for n in node:eachObject() do
- if n.type == 'function'
- or n.type == 'doc.type.function' then
- ---@cast n parser.object
- local farg = n.args and n.args[i]
- if farg then
- resolve(arg.extends, vm.compileNode(farg))
+ if arg.extends then
+ for n in node:eachObject() do
+ if n.type == 'function'
+ or n.type == 'doc.type.function' then
+ ---@cast n parser.object
+ local farg = n.args and n.args[i]
+ if farg then
+ resolve(arg.extends, vm.compileNode(farg))
+ end
end
end
end
@@ -254,7 +256,7 @@ function mt:resolve(uri, args)
local argNode = vm.compileNode(arg)
local knownTypes, genericNames = getSignInfo(sign)
if not isAllResolved(genericNames) then
- local newArgNode = buildArgNode(argNode,sign, knownTypes)
+ local newArgNode = buildArgNode(argNode, sign, knownTypes)
resolve(sign, newArgNode)
end
end
diff --git a/script/vm/type.lua b/script/vm/type.lua
index 8382eb86..545d2de5 100644
--- a/script/vm/type.lua
+++ b/script/vm/type.lua
@@ -143,6 +143,9 @@ end
---@param errs? typecheck.err[]
---@return boolean?
local function checkChildEnum(childName, parent , uri, mark, errs)
+ if mark[childName] then
+ return
+ end
local childClass = vm.getGlobal('type', childName)
if not childClass then
return nil
@@ -157,11 +160,14 @@ local function checkChildEnum(childName, parent , uri, mark, errs)
if not enums then
return nil
end
+ mark[childName] = true
for _, enum in ipairs(enums) do
if not vm.isSubType(uri, vm.compileNode(enum), parent, mark ,errs) then
+ mark[childName] = nil
return false
end
end
+ mark[childName] = nil
return true
end
@@ -752,7 +758,7 @@ function vm.viewTypeErrorMessage(uri, errs)
lines[#lines+1] = '- ' .. line
end
end
- util.revertTable(lines)
+ util.revertArray(lines)
if #lines > 15 then
lines[13] = ('...(+%d)'):format(#lines - 15)
table.move(lines, #lines - 2, #lines, 14)
@@ -761,3 +767,25 @@ function vm.viewTypeErrorMessage(uri, errs)
return table.concat(lines, '\n')
end
end
+
+---@param name string
+---@param uri uri
+---@return parser.object[]?
+function vm.getOverloadsByTypeName(name, uri)
+ local global = vm.getGlobal('type', name)
+ if not global then
+ return nil
+ end
+ local results
+ for _, set in ipairs(global:getSets(uri)) do
+ for _, doc in ipairs(set.bindGroup) do
+ if doc.type == 'doc.overload' then
+ if not results then
+ results = {}
+ end
+ results[#results+1] = doc.overload
+ end
+ end
+ end
+ return results
+end
diff --git a/script/vm/visible.lua b/script/vm/visible.lua
index e550280f..d13ecf1f 100644
--- a/script/vm/visible.lua
+++ b/script/vm/visible.lua
@@ -7,9 +7,10 @@ local glob = require 'glob'
---@class parser.object
---@field package _visibleType? parser.visibleType
----@param source parser.object
----@return parser.visibleType
-function vm.getVisibleType(source)
+local function getVisibleType(source)
+ if guide.isLiteral(source) then
+ return 'public'
+ end
if source._visibleType then
return source._visibleType
end
@@ -55,6 +56,27 @@ function vm.getVisibleType(source)
return 'public'
end
+---@class vm.node
+---@field package _visibleType parser.visibleType
+
+---@param source parser.object
+---@return parser.visibleType
+function vm.getVisibleType(source)
+ local node = vm.compileNode(source)
+ if node._visibleType then
+ return node._visibleType
+ end
+ for _, def in ipairs(vm.getDefs(source)) do
+ local visible = getVisibleType(def)
+ if visible ~= 'public' then
+ node._visibleType = visible
+ return visible
+ end
+ end
+ node._visibleType = 'public'
+ return 'public'
+end
+
---@param source parser.object
---@return vm.global?
function vm.getParentClass(source)