Skip to content

fix: typed @field should not override other defined field #3224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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 @@ -2,6 +2,7 @@

## Unreleased
<!-- Add all new changes here. They will be moved under a version at release -->
* `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)

## 3.15.0
`2025-6-25`
Expand Down
91 changes: 53 additions & 38 deletions script/vm/compiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -434,44 +434,6 @@ function vm.getClassFields(suri, object, key, pushResult)
pushResult(field, true)
goto CONTINUE
end
if hasFounded[key] then
goto CONTINUE
end
local keyType = type(key)
if keyType == 'table' then
-- ---@field [integer] boolean -> class[integer]
local fieldNode = vm.compileNode(field.field)
if vm.isSubType(suri, key.name, fieldNode) then
local nkey = '|' .. key.name
if not searchedFields[nkey] then
pushResult(field, true)
hasFounded[nkey] = true
end
end
else
local keyObject
if keyType == 'number' then
if math.tointeger(key) then
keyObject = { type = 'integer', [1] = key }
else
keyObject = { type = 'number', [1] = key }
end
elseif keyType == 'boolean'
or keyType == 'string' then
keyObject = { type = keyType, [1] = key }
end
if keyObject and field.field.type ~= 'doc.field.name' then
-- ---@field [integer] boolean -> class[1]
local fieldNode = vm.compileNode(field.field)
if vm.isSubType(suri, keyObject, fieldNode) then
local nkey = '|' .. keyType
if not searchedFields[nkey] then
pushResult(field, true)
hasFounded[nkey] = true
end
end
end
end
::CONTINUE::
end
end
Expand Down Expand Up @@ -547,6 +509,59 @@ function vm.getClassFields(suri, object, key, pushResult)
end
copyToSearched()

-- search for typed @field, eg: ---@field [string] boolean
-- only if type for this field key is not found
if not searchedFields[key] and key ~= vm.ANY and key ~= vm.ANYDOC then
for _, set in ipairs(sets) do
if set.type == 'doc.class' then
for _, field in ipairs(set.fields) do
local fieldKey = guide.getKeyName(field)
if fieldKey then
-- already processed above
goto CONTINUE
end
local keyType = type(key)
if keyType == 'table' then
-- ---@field [integer] boolean -> class[integer]
local fieldNode = vm.compileNode(field.field)
if vm.isSubType(suri, key.name, fieldNode) then
local nkey = '|' .. key.name
if not searchedFields[nkey] then
pushResult(field, true)
hasFounded[nkey] = true
end
end
else
local keyObject
if keyType == 'number' then
if math.tointeger(key) then
keyObject = { type = 'integer', [1] = key }
else
keyObject = { type = 'number', [1] = key }
end
elseif keyType == 'boolean'
or keyType == 'string' then
keyObject = { type = keyType, [1] = key }
end
if keyObject and field.field.type ~= 'doc.field.name' then
-- ---@field [integer] boolean -> class[1]
local fieldNode = vm.compileNode(field.field)
if vm.isSubType(suri, keyObject, fieldNode) then
local nkey = '|' .. keyType
if not searchedFields[nkey] then
pushResult(field, true)
hasFounded[nkey] = true
end
end
end
end
::CONTINUE::
end
end
end
copyToSearched()
end

for _, set in ipairs(sets) do
if set.type == 'doc.class' then
-- look into extends(if field not found)
Expand Down
19 changes: 19 additions & 0 deletions test/type_inference/common.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3608,6 +3608,25 @@ local t
local <?x?> = t.n
]]

TEST 'function' [[
---@class A
---@field [string] boolean
local t
function t.f() end

local <?f?> = t.f
]]

TEST 'function' [[
---@class A
---@field [string] boolean
local t = {
f = function () end
}

local <?f?> = t.f
]]

TEST 'string' [[
---@class string
---@operator mod: string
Expand Down
Loading