Skip to content

Commit a8b4f6f

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 a8b4f6f

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

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

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,96 @@ 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+
-- First, find the function from any doc in the bindGroup
58+
local func = nil
59+
if bindGroup then
60+
for _, other in ipairs(bindGroup) do
61+
local bindSource = other.bindSource
62+
if bindSource then
63+
if bindSource.type == 'function' then
64+
func = bindSource
65+
break
66+
elseif bindSource.parent and bindSource.parent.type == 'function' then
67+
func = bindSource.parent
68+
break
69+
end
70+
end
71+
end
72+
end
73+
74+
-- If we found a function, check if it's a method on a generic class
75+
if func and func.parent then
76+
local parent = func.parent
77+
if parent.type == 'setmethod' or parent.type == 'setfield' or parent.type == 'setindex' then
78+
local classGlobal = vm.getDefinedClass(uri, parent.node)
79+
if classGlobal then
80+
for _, set in ipairs(classGlobal:getSets(uri)) do
81+
if set.type == 'doc.class' and set.signs then
82+
for _, sign in ipairs(set.signs) do
83+
if sign[1] == name then
84+
return true
85+
end
86+
end
87+
end
88+
end
89+
end
90+
end
91+
end
92+
93+
return false
94+
end
95+
696
return function (uri, callback)
797
local state = files.getState(uri)
898
if not state then
@@ -25,6 +115,9 @@ return function (uri, callback)
25115
if name == '...' or name == '_' or name == 'self' then
26116
return
27117
end
118+
if isClassGenericParam(source, name, uri) then
119+
return
120+
end
28121
if #vm.getDocSets(uri, name) > 0 then
29122
return
30123
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)