Unexpected repeated alias expanding 类型别名错误地重复展开
How are you using the lua-language-server?
Visual Studio Code Extension (sumneko.lua)
Which OS are you using?
Windows
What is the issue affecting?
Hover
Expected Behaviour
hover.expandAlias为false时,如下标注
Set hover.expandAlias to false, and do annotations like this:
---while `hover.expandAlias` is `false`
---@alias T string | [T, T]
当鼠标置于[T, T]中的任意一个T时,应为(alias) T 展开为 string | [T, T]
When the mouse is placed on one T of [T, T], it should be expanded to (alias) T 展开为 string | [T, T]
Actual Behaviour
如图所示
As is seen:
对于任何一个出现了多于一次递归引用的别名,都会这样
For any alias that has more than one recursive reference, this will be the case:
单次递归引用,或者是简单得没有意义的别名,则不会发生
A single recursive reference, or a simply meaningless alias, will not be with glitches:
此外,若hover.expandAlias为true,展开的效果非常惊悚
In addition, if hover. extpandAlias is set to true, the effect is very frightening:
Reproduction steps
---while `hover.expandAlias` is `true`
---@alias S string | [S] | [S, S]
---while `hover.expandAlias` is `false`
---@alias T string | [T, T]
---while `hover.expandAlias` is `false`
---@alias U string | {msg: U} | {err: U}
---while `hover.expandAlias` is `false`
---@alias V string | V[]
---while `hover.expandAlias` is `false`
---@alias W {[string]: W} | W[]
Additional Notes
我查阅了一下代码,做出一种猜想:
在getInfer时的compileNode错误地使用了先前已缓存的节点(其并未标记这是一个不应该展开的别名),在_eraseAlias发挥效用前就展开了这个别名,并没有经过hover.expandAlias的判断,未在_drop集合中附上标注。这也是在单次使用递归时不会触发 bug 但两次使用递归就会触发的原因。
I looked up the code and made a guess:
When using getInfer, the compileNode mistakenly used a previously cached node which was not marked as an alias that should not be expanded. Without going through the judgment of hover.expandAlias, it expanded the alias before _eraseAlias takes into effect, which failed in marking in _drop set. This is the reason why it will not cause glitch when using recursion once, but only when using recursion twice.
作为本仓库的贡献者之一,我非常乐意协助这个 bug 的修复。但这个 bug 似乎太底层,有点超出我力所能及的范围了。故我提交本次 Issue,抛砖引玉一下。
As one of the contributors to this repository, I am more than happy to assist in fixing this glitch. But this glitch seems too fundamental for me to fix. So I am here to submit this issue for further discussion.
Log File
No response
從 document 看 @alias 的用法,感覺不是用來 support 遞歸結構定義的。。。
https://luals.github.io/wiki/annotations/#alias
以你第2個例子來看 ---@alias T string | [T, T]
這個像是1個 full binary tree,而 leaf node data type 是 string
那麼將 array 的部分轉用 @class 應該也可以達到相同效果?
---@alias T string|T.Node
---@class T.Node
---@field [1] T
---@field [2] T
---@type T
local t
local a = t --> T
local b = t[1] --> T
local c = t[1][1] --> T
local c = t[2][1] --> T
另外記插件作者說過,LuaLS 基本上已解決 90% 日常遇到的情況,剩下的 10% 若要解決的話,投入的時間成本和效益不成正比。#2366
我相信这个语言服务器已经解决了Lua编程中90%的问题,但是剩下的10%非常枯燥并且需要消耗大量的精力。
比較好奇你這種遞歸結構在日常使用中,是有什麼 use case 的呢? 😂
我尝试通过@alias定义一个msgpack:
---@alias msgpack boolean | number | string | msgpack[] | {[string]: msgpack}
把
Dictionary改成Key-Value Table(table<KEY_TYPE, VALUE_TYPE>)也无济于事。
再尝试通过@alias定义 JSON:
---@alias cjson.null lightuserdata
---@alias json boolean | number | string | cjson.null | json[] | {[string]: json}
这下倒好,因为过长直接不给我显示了。
顺带一提,本 Issue 提到的 Bug 仅限 Hover,对于如上的 JSON 定义,语法提示完全可以正常运作:
---@type json
local json
local str = json[1]['test'][2].test
str:match()
I was trying to define a msgpack by @alias:
---@alias msgpack boolean | number | string | msgpack[] | {[string]: msgpack}
Changing Dictionary to Key-Value Table (table<KEY_TYPE, VALUE_TYPE>) won't help either.
Another try of defining JSON by @alias:
---@alias cjson.null lightuserdata
---@alias json boolean | number | string | cjson.null | json[] | {[string]: json}
LOL, too long to show.
By the way, the glitch mentioned in this issue only happened to Hover. For the JSON definition mentioned above, the syntax prompts can function properly:
---@type json
local json
local str = json[1]['test'][2].test
str:match()
在
_eraseAlias发挥效用前就展开了这个别名
感覺跟 _eraseAlias 無關,主要還是當 alias 涉及遞歸時沒有即時停止 🤔
- 我在嘗試在
vm/infer.lua中mt:view裡加一些 print log https://github.com/LuaLS/lua-language-server/blob/26a7b690c7eeb1a209b1b600886b2ac6691c5d2e/script/vm/infer.lua#L402-L411- 增加1個
self._depth字段,defualt 當成是0,在執行self:_computeViews前+1,完成後就-1 - 而當執行
self:_computeViews前,if self._depth > 1就 print 一下
- 增加1個
self._depth = (self._depth or 0) + 1
if self._depth > 1 then
print("=====")
print(self, self._depth, self.node)
end
self:_computeViews(uri)
self._depth = self._depth - 1
- 然後用你的測試例子
---@alias S string | [S] | [S, S]
得出的 print log 如下
=====
table: 000001F0F68C5A10 2 table: 000001F0F68C5610
=====
table: 000001F0F68C5A10 2 table: 000001F0F68C5610
=====
table: 000001F0F68C6610 2 table: 000001F0F68C6290
=====
table: 000001F0F68C6FD0 2 table: 000001F0F68C6210
=> 明顯是 mt:view() 有重入調用了
我個人覺得如果要處理遞歸重入情況的話
- 最簡單的方式是在
self:_computeViews(uri)前,預先將self._lastView寫入自己的 type name- 這樣如果中途出現循環,就不再展開,單純用自己的 type name
- 在正常 (沒有遞歸的) 情況下,這個值應該不會有地方在 中途 讀取的
- 而
self:_computeViews()完結後就會寫入完整的self._lastView了,所以應該也不影響原有邏輯
- 而
function mt:view(uri, default)
if self._lastView
and self._lastViewUri == uri
and self._lastViewDefault == default then
return self._lastView
end
-- use self type name as temporary view to avoid recursion
self._lastView = self.node[1].name --<<< 新增這行,但我不確定 `node[1]` 是否永遠不為 nil
self._lastViewUri = uri
self._lastViewDefault = default
- 這樣當
hover.expandAlias = false時,應該就是你想要的效果了 =>(alias) S Expand to string|[S, S]|[S] - 但是
hover.expandAlias = true的話,好像還是會 expand 下去 😂 這個我暫時沒有想法 😕