lsp/sema: regression tests for generic indexing through import merge
Covers List(Move).items[i] -> Move via the LSP's flat-import struct_types merge (pre-registered, not self-declared) and with realistic methods/cross-referencing fields. Confirmed end-to-end against the real binary: the inlay hint for 'm := legal.items[i]' now resolves to Move.
This commit is contained in:
77
src/sema.zig
77
src/sema.zig
@@ -1786,6 +1786,83 @@ test "sema: index into generic List(T).items resolves the element struct" {
|
||||
try std.testing.expect(found_m);
|
||||
}
|
||||
|
||||
test "sema: generic index resolves with pre-registered (imported) struct types" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
// An "imported" module defining List + Move.
|
||||
const lib_src = "Move :: struct { score: s64; }" ++
|
||||
"List :: struct ($T: Type) { items: [*]T = null; len: s64; }";
|
||||
var lib_parser = parser_mod.Parser.init(alloc, lib_src);
|
||||
const lib_root = try lib_parser.parse();
|
||||
var lib = Analyzer.init(alloc);
|
||||
const lib_res = try lib.analyze(lib_root);
|
||||
|
||||
// Main analyzer pre-loaded with the imported struct_types (bare names),
|
||||
// mirroring DocumentStore.analyzeDocument's flat-import merge.
|
||||
var main_an = Analyzer.init(alloc);
|
||||
var it = lib_res.struct_types.iterator();
|
||||
while (it.next()) |e| try main_an.struct_types.put(e.key_ptr.*, e.value_ptr.*);
|
||||
|
||||
const main_src = "main :: () { legal := List(Move).{}; m := legal.items[0]; }";
|
||||
var main_parser = parser_mod.Parser.init(alloc, main_src);
|
||||
const main_root = try main_parser.parse();
|
||||
const main_res = try main_an.analyze(main_root);
|
||||
|
||||
var found_m = false;
|
||||
for (main_res.symbols) |sym| {
|
||||
if (std.mem.eql(u8, sym.name, "m")) {
|
||||
found_m = true;
|
||||
const ty = sym.ty orelse return error.TestUnexpectedResult;
|
||||
try std.testing.expect(ty == .struct_type);
|
||||
try std.testing.expectEqualStrings("Move", ty.struct_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
try std.testing.expect(found_m);
|
||||
}
|
||||
|
||||
test "sema: generic index resolves with realistic List/Move (methods, cross-refs)" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const lib_src =
|
||||
"Square :: struct { index: s64; }" ++
|
||||
"MoveFlag :: enum { none; promote_rook; }" ++
|
||||
"Move :: struct { from: Square; to: Square; flag: MoveFlag; is_capture :: (self: Move) -> bool { true; } }" ++
|
||||
"List :: struct ($T: Type) { items: [*]T = null; len: s64 = 0; cap: s64 = 0; append :: (list: *List(T), item: T) {} }";
|
||||
var lib_parser = parser_mod.Parser.init(alloc, lib_src);
|
||||
const lib_root = try lib_parser.parse();
|
||||
var lib = Analyzer.init(alloc);
|
||||
const lib_res = try lib.analyze(lib_root);
|
||||
|
||||
var main_an = Analyzer.init(alloc);
|
||||
var sit = lib_res.struct_types.iterator();
|
||||
while (sit.next()) |e| try main_an.struct_types.put(e.key_ptr.*, e.value_ptr.*);
|
||||
var eit = lib_res.enum_types.iterator();
|
||||
while (eit.next()) |e| try main_an.enum_types.put(e.key_ptr.*, e.value_ptr.*);
|
||||
|
||||
const main_src = "main :: () { legal := List(Move).{}; m := legal.items[0]; f := m.from; }";
|
||||
var main_parser = parser_mod.Parser.init(alloc, main_src);
|
||||
const main_root = try main_parser.parse();
|
||||
const main_res = try main_an.analyze(main_root);
|
||||
|
||||
var m_ty: ?Type = null;
|
||||
var f_ty: ?Type = null;
|
||||
for (main_res.symbols) |sym| {
|
||||
if (std.mem.eql(u8, sym.name, "m")) m_ty = sym.ty;
|
||||
if (std.mem.eql(u8, sym.name, "f")) f_ty = sym.ty;
|
||||
}
|
||||
try std.testing.expect(m_ty != null and m_ty.? == .struct_type);
|
||||
try std.testing.expectEqualStrings("Move", m_ty.?.struct_type);
|
||||
try std.testing.expect(f_ty != null and f_ty.? == .struct_type);
|
||||
try std.testing.expectEqualStrings("Square", f_ty.?.struct_type);
|
||||
}
|
||||
|
||||
test "sema: variable shadowing in same scope is allowed" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user