Skip to content

Commit 9dbd786

Browse files
committed
fix unused-function
cannot handle recursion correctly
1 parent 2896a05 commit 9dbd786

File tree

4 files changed

+111
-63
lines changed

4 files changed

+111
-63
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# changelog
22

33
## 3.2.2
4+
* `FIX` diagnostic: `unused-function` cannot handle recursion correctly
45
* `FIX` [#1093](https://github.com/sumneko/lua-language-server/issues/1093)
56
* `FIX` runtime errors reported by telemetry, see [#1091](https://github.com/sumneko/lua-language-server/issues/1091)
67

script/core/diagnostics/unused-function.lua

Lines changed: 92 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -18,77 +18,106 @@ local function isToBeClosed(source)
1818
return false
1919
end
2020

21-
---@async
22-
return function (uri, callback)
23-
local ast = files.getState(uri)
24-
if not ast then
25-
return
21+
---@param source parser.object
22+
local function isValidFunction(source)
23+
if not source then
24+
return false
25+
end
26+
if source.type == 'main' then
27+
return false
28+
end
29+
local parent = source.parent
30+
if not parent then
31+
return false
32+
end
33+
if parent.type ~= 'local'
34+
and parent.type ~= 'setlocal' then
35+
return false
36+
end
37+
if isToBeClosed(parent) then
38+
return false
2639
end
40+
return true
41+
end
2742

28-
local cache = {}
29-
---@async
30-
local function checkFunction(source)
31-
if not source then
43+
local function collect(ast, white, roots, links)
44+
guide.eachSourceType(ast, 'function', function (src)
45+
if not isValidFunction(src) then
3246
return
3347
end
34-
if cache[source] ~= nil then
35-
return cache[source]
36-
end
37-
cache[source] = false
38-
local parent = source.parent
39-
if not parent then
40-
return false
41-
end
42-
if parent.type ~= 'local'
43-
and parent.type ~= 'setlocal' then
44-
return false
45-
end
46-
if isToBeClosed(parent) then
47-
return false
48-
end
49-
await.delay()
50-
if parent.type == 'setlocal' then
51-
parent = parent.node
48+
local loc = src.parent
49+
if loc.type == 'setlocal' then
50+
loc = loc.node
5251
end
53-
local refs = parent.ref
54-
local hasGet
55-
if refs then
56-
cache[source] = true
57-
for _, src in ipairs(refs) do
58-
if guide.isGet(src) then
59-
local func = guide.getParentFunction(src)
60-
if not checkFunction(func) then
61-
hasGet = true
62-
break
63-
end
52+
for _, ref in ipairs(loc.ref or {}) do
53+
if ref.type == 'getlocal' then
54+
local func = guide.getParentFunction(ref)
55+
if not isValidFunction(func) or roots[func] then
56+
roots[src] = true
57+
return
6458
end
59+
if not links[func] then
60+
links[func] = {}
61+
end
62+
links[func][#links[func]+1] = src
6563
end
66-
cache[source] = not hasGet
6764
end
68-
if not hasGet then
69-
if client.isVSCode() then
70-
callback {
71-
start = source.start,
72-
finish = source.finish,
73-
tags = { define.DiagnosticTag.Unnecessary },
74-
message = lang.script.DIAG_UNUSED_FUNCTION,
75-
}
76-
else
77-
callback {
78-
start = source.keyword[1],
79-
finish = source.keyword[2],
80-
tags = { define.DiagnosticTag.Unnecessary },
81-
message = lang.script.DIAG_UNUSED_FUNCTION,
82-
}
83-
end
84-
return true
85-
end
86-
return false
65+
white[src] = true
66+
end)
67+
68+
return white, roots, links
69+
end
70+
71+
local function turnBlack(source, black, white, links)
72+
if black[source] then
73+
return
74+
end
75+
black[source] = true
76+
white[source] = nil
77+
for _, link in ipairs(links[source] or {}) do
78+
turnBlack(link, black, white, links)
8779
end
80+
end
8881

89-
-- 只检查局部函数
90-
---@async
91-
guide.eachSourceType(ast.ast, 'function', function (src)
92-
checkFunction(src)
93-
end)
82+
---@async
83+
return function (uri, callback)
84+
local state = files.getState(uri)
85+
if not state then
86+
return
87+
end
88+
89+
if vm.isMetaFile(uri) then
90+
return
91+
end
92+
93+
local black = {}
94+
local white = {}
95+
local roots = {}
96+
local links = {}
97+
98+
-- collect
99+
collect(state.ast, white, roots, links)
100+
101+
-- turn black
102+
for source in pairs(roots) do
103+
turnBlack(source, black, white, links)
104+
end
105+
106+
for source in pairs(white) do
107+
if client.isVSCode() then
108+
callback {
109+
start = source.start,
110+
finish = source.finish,
111+
tags = { define.DiagnosticTag.Unnecessary },
112+
message = lang.script.DIAG_UNUSED_FUNCTION,
113+
}
114+
else
115+
callback {
116+
start = source.keyword[1],
117+
finish = source.keyword[2],
118+
tags = { define.DiagnosticTag.Unnecessary },
119+
message = lang.script.DIAG_UNUSED_FUNCTION,
120+
}
121+
end
122+
end
94123
end

script/core/diagnostics/unused-vararg.lua

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@ local files = require 'files'
22
local guide = require 'parser.guide'
33
local define = require 'proto.define'
44
local lang = require 'language'
5+
local vm = require 'vm'
56

67
return function (uri, callback)
78
local ast = files.getState(uri)
89
if not ast then
910
return
1011
end
1112

13+
if vm.isMetaFile(uri) then
14+
return
15+
end
16+
1217
guide.eachSourceType(ast.ast, 'function', function (source)
1318
local args = source.args
1419
if not args then

test/diagnostics/common.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,3 +1529,16 @@ local z = x and y
15291529
15301530
print(z.y)
15311531
]]
1532+
1533+
TEST [[
1534+
local x, y
1535+
function x()
1536+
y()
1537+
end
1538+
1539+
function y()
1540+
x()
1541+
end
1542+
1543+
x()
1544+
]]

0 commit comments

Comments
 (0)