diff options
-rw-r--r-- | changelog.md | 1 | ||||
-rw-r--r-- | script/core/diagnostics/unused-function.lua | 155 | ||||
-rw-r--r-- | script/core/diagnostics/unused-vararg.lua | 5 | ||||
-rw-r--r-- | test/diagnostics/common.lua | 13 |
4 files changed, 111 insertions, 63 deletions
diff --git a/changelog.md b/changelog.md index 080bf34e..0b8fd84c 100644 --- a/changelog.md +++ b/changelog.md @@ -1,6 +1,7 @@ # changelog ## 3.2.2 +* `FIX` diagnostic: `unused-function` cannot handle recursion correctly * `FIX` [#1093](https://github.com/sumneko/lua-language-server/issues/1093) * `FIX` runtime errors reported by telemetry, see [#1091](https://github.com/sumneko/lua-language-server/issues/1091) diff --git a/script/core/diagnostics/unused-function.lua b/script/core/diagnostics/unused-function.lua index 5b1a1f70..81c7e457 100644 --- a/script/core/diagnostics/unused-function.lua +++ b/script/core/diagnostics/unused-function.lua @@ -18,77 +18,106 @@ local function isToBeClosed(source) return false end ----@async -return function (uri, callback) - local ast = files.getState(uri) - if not ast then - return +---@param source parser.object +local function isValidFunction(source) + if not source then + return false + end + if source.type == 'main' then + return false + end + local parent = source.parent + if not parent then + return false + end + if parent.type ~= 'local' + and parent.type ~= 'setlocal' then + return false + end + if isToBeClosed(parent) then + return false end + return true +end - local cache = {} - ---@async - local function checkFunction(source) - if not source then +local function collect(ast, white, roots, links) + guide.eachSourceType(ast, 'function', function (src) + if not isValidFunction(src) then return end - if cache[source] ~= nil then - return cache[source] - end - cache[source] = false - local parent = source.parent - if not parent then - return false - end - if parent.type ~= 'local' - and parent.type ~= 'setlocal' then - return false - end - if isToBeClosed(parent) then - return false - end - await.delay() - if parent.type == 'setlocal' then - parent = parent.node + local loc = src.parent + if loc.type == 'setlocal' then + loc = loc.node end - local refs = parent.ref - local hasGet - if refs then - cache[source] = true - for _, src in ipairs(refs) do - if guide.isGet(src) then - local func = guide.getParentFunction(src) - if not checkFunction(func) then - hasGet = true - break - end + for _, ref in ipairs(loc.ref or {}) do + if ref.type == 'getlocal' then + local func = guide.getParentFunction(ref) + if not isValidFunction(func) or roots[func] then + roots[src] = true + return end + if not links[func] then + links[func] = {} + end + links[func][#links[func]+1] = src end - cache[source] = not hasGet end - if not hasGet then - if client.isVSCode() then - callback { - start = source.start, - finish = source.finish, - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script.DIAG_UNUSED_FUNCTION, - } - else - callback { - start = source.keyword[1], - finish = source.keyword[2], - tags = { define.DiagnosticTag.Unnecessary }, - message = lang.script.DIAG_UNUSED_FUNCTION, - } - end - return true - end - return false + white[src] = true + end) + + return white, roots, links +end + +local function turnBlack(source, black, white, links) + if black[source] then + return + end + black[source] = true + white[source] = nil + for _, link in ipairs(links[source] or {}) do + turnBlack(link, black, white, links) end +end - -- 只检查局部函数 - ---@async - guide.eachSourceType(ast.ast, 'function', function (src) - checkFunction(src) - end) +---@async +return function (uri, callback) + local state = files.getState(uri) + if not state then + return + end + + if vm.isMetaFile(uri) then + return + end + + local black = {} + local white = {} + local roots = {} + local links = {} + + -- collect + collect(state.ast, white, roots, links) + + -- turn black + for source in pairs(roots) do + turnBlack(source, black, white, links) + end + + for source in pairs(white) do + if client.isVSCode() then + callback { + start = source.start, + finish = source.finish, + tags = { define.DiagnosticTag.Unnecessary }, + message = lang.script.DIAG_UNUSED_FUNCTION, + } + else + callback { + start = source.keyword[1], + finish = source.keyword[2], + tags = { define.DiagnosticTag.Unnecessary }, + message = lang.script.DIAG_UNUSED_FUNCTION, + } + end + end end diff --git a/script/core/diagnostics/unused-vararg.lua b/script/core/diagnostics/unused-vararg.lua index 2e07e1ee..ce033cf3 100644 --- a/script/core/diagnostics/unused-vararg.lua +++ b/script/core/diagnostics/unused-vararg.lua @@ -2,6 +2,7 @@ local files = require 'files' local guide = require 'parser.guide' local define = require 'proto.define' local lang = require 'language' +local vm = require 'vm' return function (uri, callback) local ast = files.getState(uri) @@ -9,6 +10,10 @@ return function (uri, callback) return end + if vm.isMetaFile(uri) then + return + end + guide.eachSourceType(ast.ast, 'function', function (source) local args = source.args if not args then diff --git a/test/diagnostics/common.lua b/test/diagnostics/common.lua index 53c7b975..5e0d6579 100644 --- a/test/diagnostics/common.lua +++ b/test/diagnostics/common.lua @@ -1529,3 +1529,16 @@ local z = x and y print(z.y) ]] + +TEST [[ +local x, y +function x() + y() +end + +function y() + x() +end + +x() +]] |