lua-language-server icon indicating copy to clipboard operation
lua-language-server copied to clipboard

fix: improve function type narrow by checking params' literal identical

Open tomlau10 opened this issue 1 year ago • 7 comments

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 ... 🤦‍♂️ )

tomlau10 avatar Aug 24 '24 06:08 tomlau10