This commit is contained in:
agra
2026-02-11 01:43:30 +02:00
parent 25e1372731
commit 89fc6427c4
6 changed files with 236 additions and 27 deletions

View File

@@ -547,6 +547,23 @@ pub const Parser = struct {
return try params.toOwnedSlice(self.allocator);
}
/// Recursively find all generic type names ($T) in a type expression tree.
fn collectGenericNames(node: *Node, list: *std.ArrayList([]const u8), allocator: std.mem.Allocator) void {
switch (node.data) {
.type_expr => |te| {
if (te.is_generic) list.append(allocator, te.name) catch {};
},
.pointer_type_expr => |pte| collectGenericNames(pte.pointee_type, list, allocator),
.many_pointer_type_expr => |mpte| collectGenericNames(mpte.element_type, list, allocator),
.slice_type_expr => |ste| collectGenericNames(ste.element_type, list, allocator),
.array_type_expr => |ate| collectGenericNames(ate.element_type, list, allocator),
.parameterized_type_expr => |pte| {
for (pte.args) |arg| collectGenericNames(arg, list, allocator);
},
else => {},
}
}
/// Collect generic type params and comptime value params from parameter annotations.
fn collectTypeParams(self: *Parser, params: []const ast.Param) ![]const ast.StructTypeParam {
var type_params = std.ArrayList(ast.StructTypeParam).empty;
@@ -563,25 +580,20 @@ pub const Parser = struct {
try type_params.append(self.allocator, .{ .name = param.name, .constraint = param.type_expr });
}
} else {
// Check for generic type param: direct $T or nested inside []$T
const generic_type_expr: ?*Node = if (param.type_expr.data == .type_expr and param.type_expr.data.type_expr.is_generic)
param.type_expr
else if (param.type_expr.data == .slice_type_expr) blk: {
const elem = param.type_expr.data.slice_type_expr.element_type;
break :blk if (elem.data == .type_expr and elem.data.type_expr.is_generic) elem else null;
} else null;
if (generic_type_expr) |gte| {
// Collect all generic type params found anywhere in the type expression
var generic_names = std.ArrayList([]const u8).empty;
collectGenericNames(param.type_expr, &generic_names, self.allocator);
for (generic_names.items) |gen_name| {
var found = false;
for (type_params.items) |existing| {
if (std.mem.eql(u8, existing.name, gte.data.type_expr.name)) {
if (std.mem.eql(u8, existing.name, gen_name)) {
found = true;
break;
}
}
if (!found) {
const type_constraint = try self.createNode(param.type_expr.span.start, .{ .type_expr = .{ .name = "Type" } });
try type_params.append(self.allocator, .{ .name = gte.data.type_expr.name, .constraint = type_constraint });
const type_constraint = self.createNode(param.type_expr.span.start, .{ .type_expr = .{ .name = "Type" } }) catch continue;
type_params.append(self.allocator, .{ .name = gen_name, .constraint = type_constraint }) catch {};
}
}
}