From 4b52ebf3492a8eff3fd7fdb5ad8d17d405c91b6d Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Wed, 6 Aug 2025 15:24:47 +0100 Subject: [PATCH 1/3] Prevent circular resolve Fixes: #3246 Co-authored-by: Tom Lau --- script/vm/sign.lua | 7 +++++++ test/type_inference/common.lua | 22 ++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/script/vm/sign.lua b/script/vm/sign.lua index 4f0fe1912..4a4b50010 100644 --- a/script/vm/sign.lua +++ b/script/vm/sign.lua @@ -30,10 +30,17 @@ function mt:resolve(uri, args) ---@type table local resolved = {} + ---@type table + local resolving = {} ---@param object vm.node|vm.node.object ---@param node vm.node local function resolve(object, node) + local resolveHash = ("%s|%s"):format(object, node) + if resolving[resolveHash] then + return -- prevent circular resolve calls + end + resolving[resolveHash] = true if object.type == 'vm.node' then for o in object:eachObject() do resolve(o, node) diff --git a/test/type_inference/common.lua b/test/type_inference/common.lua index 2404620c1..004478a27 100644 --- a/test/type_inference/common.lua +++ b/test/type_inference/common.lua @@ -4881,3 +4881,25 @@ end local a, b, , d = unpack(t) ]] + +-- Test for overflow in circular resolve, only pass requirement is no overflow +TEST 'Callback<>|fun():fun():fun():Success, string' [[ +--- @alias Success fun(): Success +--- @alias Callback fun(): Success, T + +--- @return Success +local function success() + return success +end + +--- @generic T +--- @param callback Callback +--- @return Callback +local function make_callback(callback) + return callback +end + +local = make_callback(function() + return success, "" +end) +]] From b96159d5b19a6e445a73ccc69f75b4f02e8dc848 Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Wed, 6 Aug 2025 15:38:29 +0100 Subject: [PATCH 2/3] Update changelog --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 4598cebd8..59d4dee24 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ * `FIX` adds the `|lambda|` operator to the `Lua.runtime.nonstandardSymbol` configuration template, which allows the use of that option. Previously, support for it existed in the parser, but we could not actually use the option because it is not recognised in the configuration. * `FIX` Typed `@field` (eg `---@field [string] boolean`) should not override other defined field [#2171](https://github.com/LuaLS/lua-language-server/issues/2171), [#2711](https://github.com/LuaLS/lua-language-server/issues/2711) * `FIX` don't return empty hover doc when luals failed to find definition +* `FIX` Prevent stack overflow when attempting to resolve function return values. [#3246](https://github.com/LuaLS/lua-language-server/issues/3246) ## 3.15.0 `2025-6-25` From cf6c60ea271816041efc3ad766f83bf8ae6899fa Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Thu, 7 Aug 2025 11:57:25 +0100 Subject: [PATCH 3/3] Rename `resolving` -> `visited` --- script/vm/sign.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/script/vm/sign.lua b/script/vm/sign.lua index 4a4b50010..3718391d1 100644 --- a/script/vm/sign.lua +++ b/script/vm/sign.lua @@ -31,16 +31,16 @@ function mt:resolve(uri, args) ---@type table local resolved = {} ---@type table - local resolving = {} + local visited = {} ---@param object vm.node|vm.node.object ---@param node vm.node local function resolve(object, node) - local resolveHash = ("%s|%s"):format(object, node) - if resolving[resolveHash] then - return -- prevent circular resolve calls + local visitedHash = ("%s|%s"):format(object, node) + if visited[visitedHash] then + return -- prevent circular resolve calls by only visiting each pair once end - resolving[resolveHash] = true + visited[visitedHash] = true if object.type == 'vm.node' then for o in object:eachObject() do resolve(o, node)