diff options
author | fesily <fesil@foxmail.com> | 2024-01-10 11:03:21 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-10 11:03:21 +0800 |
commit | 1e7bb72ad3ff2b75a1c55ee4bc53004cb7fe30f7 (patch) | |
tree | dd94cf09b9e3a675a73a9f1d41248d1538165997 /script/vm | |
parent | bb6e172d6166190bd4edd3bb56230a7d60ebcb93 (diff) | |
parent | 37779f9b2493e51e59e1e4366bf7dcb8350e69bd (diff) | |
download | lua-language-server-1e7bb72ad3ff2b75a1c55ee4bc53004cb7fe30f7.zip |
Merge branch 'LuaLS:master' into plugin-add-OnTransformAst
Diffstat (limited to 'script/vm')
-rw-r--r-- | script/vm/compiler.lua | 139 | ||||
-rw-r--r-- | script/vm/doc.lua | 21 | ||||
-rw-r--r-- | script/vm/function.lua | 10 | ||||
-rw-r--r-- | script/vm/global.lua | 67 | ||||
-rw-r--r-- | script/vm/infer.lua | 19 | ||||
-rw-r--r-- | script/vm/node.lua | 1 | ||||
-rw-r--r-- | script/vm/operator.lua | 8 | ||||
-rw-r--r-- | script/vm/sign.lua | 18 | ||||
-rw-r--r-- | script/vm/type.lua | 30 | ||||
-rw-r--r-- | script/vm/visible.lua | 28 |
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) |