lua-language-server
lua-language-server copied to clipboard
fix: improve function type narrow by checking params' literal identical
The following related issues are not fixed by this PR, but I tested and they already work as of v3.10.5: ~#2509~, #1343, #1146
edit: I originally thought #2509 is working, but NO. Just that the reproduction code is not complete, and I didn't reproduce it and think it is solved. I confirmed that it is still unsolved with a more complete example code.
The Problem
Originated from an example provided by @carsakiller: https://github.com/LuaLS/lua-language-server/issues/1456#issuecomment-2303627852, where the base signature contains a param type that is a superset of the other overload, type narrow feature will always includes the baseline one and cannot perform a more accurate type narrow.
A minimal example:
---@overload fun(a: string): string
---@overload fun(a: 'test'): integer
local function test(...) end
local a = test('') -- string, good
local b = test('test') -- string|integer, bad
local s = 'test'
local c = test(s) -- string|integer, bad
Proposed Solution
I introduced a match score when trying to do a further type narrow. After the 1st pass checking isAllParamMatched in the existing logic, I tried to check if their arguments' literal have exact match:
- If call args are literals, they will have a literal value like
123,"test" - If the overload params also specify a literal value, then they will have a literals map, we can then check if it contains the arg's literal value
- When a function's params contain literal values of call args, I add bonus scores to it
- Finally only keep functions that have the highest score
This is just an extra logic and should be no worse than current logic. Because when there are no identical view param, all functions just have the same score 0 => all will be kept as in the existing logic.
After the fix:
---@overload fun(a: string): string
---@overload fun(a: 'test'): integer
local function test(...) end
local a = test('') -- string, good
local b = test('test') -- integer, good
local s = 'test'
local c = test(s) -- integer, good
中文版
長話短說,我嘗試在 vm.getExactMatchedFunctions 引入1個 計分制
在滿足原有的 isAllParamMatched 前題下
- 如果 function param 的 literals map 包含 call arg 的 literal value => +1 分 這樣能夠在 literal type 的 param exact match 時有較高分數
- 最後只保留最高分數 (有可能多個) 的 functions 達致更準確的 type narrow
- 而由於這個 logic 是額外的,所以最差情況就只是跟現狀一樣
=> 所有 function 都是
0分=> 全部保留
我不確定有沒有更好改法 這個需要 @sumneko 來 review (希望我沒有像上次一樣,fix 了1個問題卻又引起新 bug ... 🤦♂️ )