Skip to content

Commit 53b202f

Browse files
committed
fix: suppress undefined-doc-name warning for class generic parameters
When a generic class like Container<T> has methods that use T in their annotations (e.g., @return T[]), the diagnostic was incorrectly warning about T being undefined. Added isClassGenericParam() function that checks: 1. bindGroup for inline class/alias with matching generic signs 2. Direct class reference for doc.field, doc.overload, doc.operator 3. Method binding via vm.getDefinedClass for methods on generic classes Addresses maintainer feedback on PR #3330.
1 parent 3cd0a8d commit 53b202f

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

script/core/diagnostics/undefined-doc-name.lua

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,94 @@ local guide = require 'parser.guide'
33
local lang = require 'language'
44
local vm = require 'vm'
55

6+
--- Check if name is a generic parameter from a class context
7+
---@param source parser.object The doc.type.name source
8+
---@param name string The type name to check
9+
---@param uri uri The file URI
10+
---@return boolean
11+
local function isClassGenericParam(source, name, uri)
12+
-- Find containing doc node
13+
local doc = guide.getParentTypes(source, {
14+
['doc.return'] = true,
15+
['doc.param'] = true,
16+
['doc.type'] = true,
17+
['doc.field'] = true,
18+
['doc.overload'] = true,
19+
['doc.vararg'] = true,
20+
})
21+
if not doc then
22+
return false
23+
end
24+
25+
-- Walk up to find a doc node with bindGroup (intermediate doc.type nodes don't have it)
26+
while doc and not doc.bindGroup do
27+
doc = doc.parent
28+
end
29+
if not doc then
30+
return false
31+
end
32+
33+
-- Check bindGroup for class/alias with matching generic sign
34+
local bindGroup = doc.bindGroup
35+
if bindGroup then
36+
for _, other in ipairs(bindGroup) do
37+
if (other.type == 'doc.class' or other.type == 'doc.alias') and other.signs then
38+
for _, sign in ipairs(other.signs) do
39+
if sign[1] == name then
40+
return true
41+
end
42+
end
43+
end
44+
end
45+
end
46+
47+
-- Check direct class reference (for doc.field, doc.overload, doc.operator)
48+
if doc.class and doc.class.signs then
49+
for _, sign in ipairs(doc.class.signs) do
50+
if sign[1] == name then
51+
return true
52+
end
53+
end
54+
end
55+
56+
-- Check if bound to a method on a generic class
57+
if bindGroup then
58+
for _, other in ipairs(bindGroup) do
59+
local bindSource = other.bindSource
60+
if bindSource then
61+
-- Find the function: either bindSource is the function, or it's an arg inside a function
62+
local func = nil
63+
if bindSource.type == 'function' then
64+
func = bindSource
65+
elseif bindSource.parent and bindSource.parent.type == 'function' then
66+
func = bindSource.parent
67+
end
68+
69+
if func and func.parent then
70+
local parent = func.parent
71+
if parent.type == 'setmethod' or parent.type == 'setfield' or parent.type == 'setindex' then
72+
local classGlobal = vm.getDefinedClass(uri, parent.node)
73+
if classGlobal then
74+
for _, set in ipairs(classGlobal:getSets(uri)) do
75+
if set.type == 'doc.class' and set.signs then
76+
for _, sign in ipairs(set.signs) do
77+
if sign[1] == name then
78+
return true
79+
end
80+
end
81+
end
82+
end
83+
end
84+
end
85+
end
86+
break -- Only check first bindSource
87+
end
88+
end
89+
end
90+
91+
return false
92+
end
93+
694
return function (uri, callback)
795
local state = files.getState(uri)
896
if not state then
@@ -25,6 +113,9 @@ return function (uri, callback)
25113
if name == '...' or name == '_' or name == 'self' then
26114
return
27115
end
116+
if isClassGenericParam(source, name, uri) then
117+
return
118+
end
28119
if #vm.getDocSets(uri, name) > 0 then
29120
return
30121
end

test/diagnostics/undefined-doc-name.lua

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,42 @@ TEST [[
1717
TEST [[
1818
---@alias B <!AAA!>
1919
]]
20+
21+
-- Generic class methods should not warn about class generic params
22+
TEST [[
23+
---@class Container<T>
24+
local Container = {}
25+
26+
---@return T[]
27+
function Container:getAll()
28+
return {}
29+
end
30+
]]
31+
32+
-- Inline class fields with generics should not warn
33+
TEST [[
34+
---@class Box<T>
35+
---@field value T
36+
]]
37+
38+
-- Multiple generic params should all be recognized
39+
TEST [[
40+
---@class Map<K, V>
41+
local Map = {}
42+
43+
---@param key K
44+
---@return V
45+
function Map:get(key)
46+
end
47+
]]
48+
49+
-- Undefined types SHOULD still warn (control case)
50+
TEST [[
51+
---@class Container<T>
52+
local Container = {}
53+
54+
---@return <!UndefinedType!>
55+
function Container:getBad()
56+
return {}
57+
end
58+
]]

0 commit comments

Comments
 (0)