Combined method and class generics does not resolve types properly #3438
Replies: 2 comments
|
I've found that specifying the unspecified ---@generic T
---@class MyClass<T>
local MyClass = {}
---@generic T, TAccum, TResult
---@param self MyClass<T> # This makes the trick
---@param seed TAccum
---@param accum fun(acc: TAccum, index: integer, value: T): TAccum
---@param result fun(accum: TAccum): TResult
function MyClass:my_func(seed, accum, result)
return result(accum)
end
---@type MyClass<string>
local myClass
local res = myClass:my_func("banana",
function(acc, index, value)
return acc .. value
end,
function(acc)
return acc
end
) |
|
I used claude code (with edit: PR created: #3439 TL;DRThe BugWhen a generic class method has a Minimal reproduction: ---@generic T
---@class MyClass<T>
local MyClass = {}
---@generic R
---@param val R
---@return R -- explicit @return is required
function MyClass:pass(val) return val end
---@type MyClass<string>
local myClass
local res = myClass:pass(42)
-- Expected: integer
-- Actual (without fix): unknown (R unresolved)Root Cause
FixScout before clone. Before cloning the return doc, scan it for +local onlyClassGenerics = true
+guide.eachSourceType(rtn, 'doc.generic.name', function(src)
+ if not genericMap[src[1]] then
+ onlyClassGenerics = false
+ end
+end)
+if onlyClassGenerics then
local newRtn = vm.cloneObject(rtn, genericMap)
...
+endNoteThis fix only addresses the overwrite bug — the receiver block undoing Round 1's correct resolution. You must still explicitly annotate your method with toggle to show the detailed report
Generic
|
| Case | Return doc references | Generic map | Scout result | Action |
|---|---|---|---|---|
Box<T> + @return T |
{T} |
{T=string} |
All in map ✅ | Clone & resolve normally |
MyClass<T> + @return R |
{R} |
{T=string} |
R NOT in map ❌ |
Skip clone, keep Round 1 result |
MyClass<T> + @return T, R |
{T, R} |
{T=string} |
R NOT in map ❌ |
Skip clone, keep Round 1 result |
MyClass<T> + @return integer |
{} |
{T=string} |
No generics found ✅ | Clone (no-op), same result |
Rationale
The receiver block's genericMap only contains class generics extracted from the receiver's concrete type. This is:
- Sufficient for class-generic-only returns (the
Tis in the map) - Insufficient when the return type references method-level generics (
Ris not in the map)
Checking upfront avoids unnecessary cloning and never produces a regression. Round 1's correct resolution is preserved by not running the receiver block when it cannot fully resolve the return type.
Files Changed
script/vm/compiler.lua— Scout-before-clone guard inbindReturnOfFunction()receiver block (around line 1748)
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Hello!
First of all, I want to thank you for your work. I recently discovered LuaCATS, and it’s a great system for working with Lua that offers typing and autocompletion.
I’m building my own framework with Lua using concepts like classes, interfaces, exceptions, inheritance, polymorphism… even a bit of IoC via DI. So far, I’ve been adding LuaCATS annotations, and everything has been working correctly. However, right now I’m creating a class that offers methods similar to those in C#’s LINQ, and I’ve hit a snag. Although the hierarchy is much larger, I’ve managed to boil it down to a minimal example that I’d like to share with you. The issue is that this LINQ-style class has a generic type T that defines the types of values it will iterate over. On the other hand, I have an ‘aggregate’ method that has its own generic types, TAccum and TResult. When I use it, I can’t figure out how to ensure everything resolves correctly for the method’s user: either the class generics resolve, or the method generics resolve. Here’s the minimal example:
In the code above, the method's generics are resolved correctly, and
accumis displayed with the correct type in the anonymous functions passed as parameters. However, although the class is correctly identified as aMyClass<string>, the anonymous function does not identify the type ofTin the parameter, and throws a warning when attempting to returnTinstead ofstring. I tried adding the method as a field like this:...and in that case, the type
Tof the value is recognized correctly, but the genericTAccumof the first parameter is identified as “unknown.” I haven't figured out how to get all of them to resolve correctly. I'd really appreciate it if you could help me out with this.Thank you in advance for your help!
All reactions