Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* `FIX` Deduplicate documentation bindings for parameters
* `FIX` Correct `math.type` meta return annotation to use `nil` instead of the string literal `'nil'`
* `FIX` Fix initial `nameStyle.config` not getting loaded in the appropriate workspace.
* `FIX` Fix method-level generic return type being overwritten by receiver class generic resolution [#3438](https://github.com/LuaLS/lua-language-server/discussions/3438)

## 3.18.2
* `CHG` `duplicate-set-field` diagnostic now supports linked suppression: when any occurrence of a duplicate field is suppressed with `---@diagnostic disable` or `---@diagnostic disable-next-line`, all warnings for that field name will be suppressed
Expand Down
26 changes: 19 additions & 7 deletions script/vm/compiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1752,13 +1752,25 @@ local function bindReturnOfFunction(source, mfunc, index, args)
if doc.type == 'doc.return' then
for _, rtn in ipairs(doc.returns) do
if rtn.returnIndex == index then
local newRtn = vm.cloneObject(rtn, genericMap)
if newRtn then
returnNode = vm.compileNode(newRtn)
for rnode in returnNode:eachObject() do
if rnode.type == 'generic' then
returnNode = rnode:resolve(guide.getUri(source), args)
break
-- Only clone if the return type only references class-level generics
-- (i.e. generics that exist in the genericMap from the receiver).
-- Method-level generics (e.g. V not in {T=string}) are
-- already resolved correctly in the first round above.
local onlyClassGenerics = true
guide.eachSourceType(rtn, 'doc.generic.name', function(src)
if not genericMap[src[1]] then
onlyClassGenerics = false
end
end)
if onlyClassGenerics then
local newRtn = vm.cloneObject(rtn, genericMap)
if newRtn then
returnNode = vm.compileNode(newRtn)
for rnode in returnNode:eachObject() do
if rnode.type == 'generic' then
returnNode = rnode:resolve(guide.getUri(source), args)
break
end
end
end
end
Expand Down
18 changes: 18 additions & 0 deletions test/type_inference/common.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5215,3 +5215,21 @@ local mylist

local <?result?> = mylist:identity()
]]

-- Discussion #3438: Method-level generic return type not clobbered by receiver block
-- When a generic class method has @return R where R is method-only (not in class genericMap),
-- the receiver block should not overwrite Round 1's correct resolution.
TEST 'integer' [[
---@class MyClass<T>
local MyClass = {}

---@generic R
---@param val R
---@return R
function MyClass:pass(val) return val end

---@type MyClass<string>
local myClass

local <?result?> = myClass:pass(42)
]]
Loading