Skip to content

Commit 255e83b

Browse files
ChouUnclaude
andcommitted
feat: support type inference for @field and @type function declarations in method overrides
When a child class overrides a parent class method that was declared using @field or @type annotations (instead of function declarations), the parameter types are now correctly inferred from the parent class. This extends the existing method override type inference (PR #2859) to support field-style function declarations. Example: ```lua ---@Class Buff local mt = {} ---@type (fun(self: Buff, target: Buff): boolean)? mt.on_cover = nil ---@Class Buff.CommandAura : Buff local tpl = {} function tpl:on_cover(target) -- target type is now correctly inferred as Buff (was any before) return self.level > target.level end ``` Changes: - Modified compileFunctionParam in script/vm/compiler.lua to extract function type from doc.field nodes by accessing their extends property - Added support for doc.type.function in parent class field lookup - Added comprehensive test cases for @field and @type method overrides Fixes #3367 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d965edc commit 255e83b

3 files changed

Lines changed: 88 additions & 5 deletions

File tree

script/vm/compiler.lua

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,10 +1353,16 @@ local function compileFunctionParam(func, source)
13531353
]]
13541354
local found = false
13551355
for n in funcNode:eachObject() do
1356-
if (n.type == 'doc.type.function' or n.type == 'function')
1357-
and n.args[aindex] and n.args[aindex] ~= source
1356+
-- Extract actual function type node
1357+
local funcType = n
1358+
if n.type == 'doc.field' and n.extends then
1359+
funcType = n.extends
1360+
end
1361+
1362+
if (funcType.type == 'doc.type.function' or funcType.type == 'function')
1363+
and funcType.args and funcType.args[aindex] and funcType.args[aindex] ~= source
13581364
then
1359-
local argNode = vm.compileNode(n.args[aindex])
1365+
local argNode = vm.compileNode(funcType.args[aindex])
13601366
for an in argNode:eachObject() do
13611367
if an.type ~= 'doc.generic.name' then
13621368
vm.setNode(source, an)
@@ -1460,8 +1466,16 @@ local function compileFunctionParam(func, source)
14601466
end
14611467
vm.getClassFields(suri, extClass, key, function (field, _isMark)
14621468
for n in vm.compileNode(field):eachObject() do
1463-
if n.type == 'function' and n.args[aindex] then
1464-
local argNode = vm.compileNode(n.args[aindex])
1469+
-- Extract actual function type node
1470+
local funcType = n
1471+
if n.type == 'doc.field' and n.extends then
1472+
funcType = n.extends
1473+
end
1474+
1475+
if (funcType.type == 'function' or funcType.type == 'doc.type.function')
1476+
and funcType.args and funcType.args[aindex]
1477+
then
1478+
local argNode = vm.compileNode(funcType.args[aindex])
14651479
for an in argNode:eachObject() do
14661480
if an.type ~= 'doc.generic.name' then
14671481
vm.setNode(source, an)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
2+
-- Test @type field declaration with method override
3+
TEST 'Buff' [[
4+
---@class Buff
5+
local mt = {}
6+
---@type (fun(self: Buff, target: Buff): boolean)?
7+
mt.on_cover = nil
8+
9+
---@class Buff.CommandAura : Buff
10+
local tpl = {}
11+
function tpl:on_cover(<?target?>)
12+
return true
13+
end
14+
]]
15+
16+
-- Test @field declaration with method override
17+
TEST 'Animal' [[
18+
---@class Animal
19+
---@field can_eat (fun(self: Animal, other: Animal): boolean)?
20+
local base = {}
21+
22+
---@class Dog : Animal
23+
local dog = {}
24+
function dog:can_eat(<?other?>)
25+
return true
26+
end
27+
]]
28+
29+
-- Test optional method with @type
30+
TEST 'string' [[
31+
---@class Base
32+
local base = {}
33+
---@type (fun(self: Base, x: string): number)?
34+
base.callback = nil
35+
36+
---@class Child : Base
37+
local child = {}
38+
function child:callback(<?x?>)
39+
return 1
40+
end
41+
]]
42+
43+
-- Test non-optional @field
44+
TEST 'number' [[
45+
---@class Handler
46+
---@field process fun(self: Handler, value: number): string
47+
local handler = {}
48+
49+
---@class CustomHandler : Handler
50+
local custom = {}
51+
function custom:process(<?value?>)
52+
return tostring(value)
53+
end
54+
]]
55+
56+
-- Test multiple parameters with @type
57+
TEST 'string' [[
58+
---@class Processor
59+
local proc = {}
60+
---@type fun(self: Processor, a: number, b: string): boolean
61+
proc.handle = nil
62+
63+
---@class MyProcessor : Processor
64+
local my = {}
65+
function my:handle(a, <?b?>)
66+
return true
67+
end
68+
]]

test/type_inference/init.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,4 @@ end
4646

4747
require 'type_inference.common'
4848
require 'type_inference.param_match'
49+
require 'type_inference.field_override'

0 commit comments

Comments
 (0)