Skip to content

Commit 14e6722

Browse files
committed
fix: resolve fields of constrained generic T: Class via its constraint
1 parent 66d6405 commit 14e6722

3 files changed

Lines changed: 31 additions & 1 deletion

File tree

‎changelog.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<!-- Add all new changes here. They will be moved under a version at release -->
55
* `FIX` A `.gitignore` line containing only whitespace no longer ignores the entire workspace [#3458](https://github.com/LuaLS/lua-language-server/issues/3458)
66
* `FIX` `need-check-nil` diagnostic is no longer reported on safe navigation access (e.g. `x?.field`, `f?.()`, `t?.[key]`), since the optional access itself already handles the nil check. Note that a non-safe access chained after a safe one (e.g. `x.upper()?.field`) still reports, because the safe access only protects its own result.
7+
* `FIX` Accessing fields on a value typed as a constrained generic (e.g. `---@generic T: SomeClass` with `---@param self T`) no longer reports `undefined-field`; fields are now looked up on the constraint
78

89
## 3.19.1
910
`2026-08-14`

‎script/vm/compiler.lua‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,8 @@ local function resolveGenericField(uri, classGlobal, field, signs)
355355
}
356356
end
357357

358-
local searchFieldSwitch = util.switch()
358+
local searchFieldSwitch
359+
searchFieldSwitch = util.switch()
359360
: case 'table'
360361
: call(function (_suri, source, key, pushResult)
361362
if type(key) == 'string'
@@ -515,6 +516,19 @@ local searchFieldSwitch = util.switch()
515516
vm.getClassFields(suri, node, key, pushResult)
516517
end
517518
end)
519+
: case 'doc.generic.name'
520+
: call(function (suri, source, key, pushResult)
521+
searchFieldByLocalID(source, key, pushResult)
522+
searchFieldByGlobalID(suri, source, key, pushResult)
523+
-- unresolved generic `T: SomeClass`: search fields of its constraint
524+
local constraint = source.generic and source.generic.extends
525+
if not constraint then
526+
return
527+
end
528+
for n in vm.compileNode(constraint):eachObject() do
529+
searchFieldSwitch(n.type, suri, n, key, pushResult)
530+
end
531+
end)
518532
: default(function (suri, source, key, pushResult)
519533
searchFieldByLocalID(source, key, pushResult)
520534
searchFieldByGlobalID(suri, source, key, pushResult)

‎test/diagnostics/undefined-field.lua‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,18 @@ X = {
156156
157157
print(X.<!C!>)
158158
]]
159+
160+
TEST [[
161+
---@class SomeClass
162+
---@field someExistingVar integer
163+
local SomeClass = {}
164+
165+
---@generic T: SomeClass
166+
---@param self T
167+
---@return T
168+
function SomeClass:xyz()
169+
print(self.someExistingVar)
170+
print(self.<!doesNotExist!>)
171+
return self
172+
end
173+
]]

0 commit comments

Comments
 (0)