diff options
Diffstat (limited to 'server-beta/src')
-rw-r--r-- | server-beta/src/core/diagnostics/global-in-nil-env.lua | 57 | ||||
-rw-r--r-- | server-beta/src/core/diagnostics/undefined-global.lua | 10 | ||||
-rw-r--r-- | server-beta/src/parser/guide.lua | 84 | ||||
-rw-r--r-- | server-beta/src/searcher/eachRef.lua | 39 |
4 files changed, 176 insertions, 14 deletions
diff --git a/server-beta/src/core/diagnostics/global-in-nil-env.lua b/server-beta/src/core/diagnostics/global-in-nil-env.lua index b3d19c21..a19a341f 100644 --- a/server-beta/src/core/diagnostics/global-in-nil-env.lua +++ b/server-beta/src/core/diagnostics/global-in-nil-env.lua @@ -1,3 +1,56 @@ -return function () - +local files = require 'files' +local guide = require 'parser.guide' +local searcher = require 'searcher' +local lang = require 'language' + +-- TODO: 检查路径是否可达 +local function mayRun(path) + return true +end + +return function (uri, callback) + local ast = files.getAst(uri) + if not ast then + return + end + local root = guide.getRoot(ast.ast) + local env = guide.getENV(root) + + local nilDefs = {} + if not env.ref then + return + end + for _, ref in ipairs(env.ref) do + if ref.type == 'setlocal' then + if ref.value and ref.value.type == 'nil' then + nilDefs[#nilDefs+1] = ref + end + end + end + + if #nilDefs == 0 then + return + end + + local function check(source) + local node = source.node + if node.tag == '_ENV' then + local ok + for _, nilDef in ipairs(nilDefs) do + local mode, pathA = guide.getPath(nilDef, source) + if mode == 'before' + and mayRun(pathA) then + callback { + start = source.start, + finish = source.finish, + uri = uri, + message = lang.script.DIAG_GLOBAL_IN_NIL_ENV, + } + end + end + end + end + + guide.eachSourceType(ast.ast, 'getglobal', check) + guide.eachSourceType(ast.ast, 'setglobal', check) end diff --git a/server-beta/src/core/diagnostics/undefined-global.lua b/server-beta/src/core/diagnostics/undefined-global.lua index ec796086..71980c8b 100644 --- a/server-beta/src/core/diagnostics/undefined-global.lua +++ b/server-beta/src/core/diagnostics/undefined-global.lua @@ -64,6 +64,16 @@ return function (uri, callback) -- 再遍历一次 getglobal ,找出 _ENV 被重载的情况 guide.eachSourceType(ast.ast, 'getglobal', function (source) if hasSet[source] == nil then + -- 单独验证自己是否在重载过的 _ENV 中有定义 + local setInENV + searcher.eachRef(source, function (info) + if info.mode == 'set' then + setInENV = true + end + end) + if setInENV then + return + end local key = source[1] callback { start = source.start, diff --git a/server-beta/src/parser/guide.lua b/server-beta/src/parser/guide.lua index 02284b1e..92dd79bc 100644 --- a/server-beta/src/parser/guide.lua +++ b/server-beta/src/parser/guide.lua @@ -469,4 +469,88 @@ function m.getENV(ast) return ast.locals[1] end +--- 测试 a 到 b 的路径(不经过函数,不考虑 goto), +--- 每个路径是一个 block 。 +--- +--- 如果 a 在 b 的前面,返回 `"before"` 加上 2个`list<block>` +--- +--- 如果 a 在 b 的后面,返回 `"after"` 加上 2个`list<block>` +--- +--- 否则返回 `false` +--- +--- 返回的2个 `list` 分别为基准block到达 a 与 b 的路径。 +---@param a table +---@param b table +---@return string|boolean mode +---@return table|nil pathA +---@return table|nil pathB +function m.getPath(a, b) + --- 首先测试双方在同一个函数内 + if m.getParentFunction(a) ~= m.getParentFunction(b) then + return false + end + local mode + local objA + local objB + if a.start < b.start then + mode = 'before' + objA = a + objB = b + else + mode = 'after' + objA = b + objB = a + end + local pathA = {} + local pathB = {} + for _ = 1, 1000 do + objA = m.getParentBlock(objA) + pathA[#pathA+1] = objA + if objA.type == 'function' or objA.type == 'main' then + break + end + end + for _ = 1, 1000 do + objB = m.getParentBlock(objB) + pathB[#pathB+1] = objB + if objB.type == 'function' or objB.type == 'main' then + break + end + end + -- pathA: {1, 2, 3, 4, 5} + -- pathB: {5, 6, 2, 3} + local top = #pathB + local start + for i = #pathA, 1, -1 do + local currentBlock = pathA[i] + if currentBlock == pathB[top] then + start = i + break + end + end + -- pathA: { 1, 2, 3} + -- pathB: {5, 6, 2, 3} + local extra = 0 + local align = top - start + for i = start, 1, -1 do + local currentA = pathA[i] + local currentB = pathB[i+align] + if currentA ~= currentB then + extra = i + break + end + end + -- pathA: {1} + local resultA = {} + for i = extra, 1, -1 do + resultA[#resultA+1] = pathA[i] + end + -- pathB: {5, 6} + local resultB = {} + for i = extra + align, 1, -1 do + resultB[#resultB+1] = pathB[i] + end + return mode, resultA, resultB +end + return m diff --git a/server-beta/src/searcher/eachRef.lua b/server-beta/src/searcher/eachRef.lua index 8f6b5c2f..3ae020cd 100644 --- a/server-beta/src/searcher/eachRef.lua +++ b/server-beta/src/searcher/eachRef.lua @@ -227,20 +227,35 @@ local function ofLocal(loc, callback) end local function ofGlobal(source, callback) - local key = guide.getKeyName(source) - for uri in files.eachFile() do - local globals = files.getGlobals(uri) - local ast = files.getAst(uri) - if ast and globals and globals[key] then - searcher.eachGlobal(ast.ast, function (info) - if key == info.key then - callback(info) - if info.value then - ofValue(info.value, callback) + local key = guide.getKeyName(source) + local node = source.node + if node.tag == '_ENV' then + for uri in files.eachFile() do + local globals = files.getGlobals(uri) + local ast = files.getAst(uri) + if ast and globals and globals[key] then + searcher.eachGlobal(ast.ast, function (info) + if key == info.key then + callback(info) + if info.value then + ofValue(info.value, callback) + end end - end - end) + end) + end end + else + searcher.eachField(node, function (info) + if key == info.key then + callback { + source = info.source, + mode = info.mode, + } + if info.value then + ofValue(info.value, callback) + end + end + end) end end |