lsp/sema: resolve pointer-typed params and field access through pointers
Two gaps made 'piece := board.squares[move.from.index]' (board: *Board) <unresolved>: analyzeParams typed params with fromTypeExpr (bare-name only), so *Board / []T / *List params became null; and field_access only handled a struct value, not a *Struct. Params now resolve via fieldType, and field_access auto-derefs a pointer object (p.field on *T resolves on T). Regression test added.
This commit is contained in:
45
src/sema.zig
45
src/sema.zig
@@ -559,7 +559,11 @@ pub const Analyzer = struct {
|
||||
return self.inferExprType(unop.operand);
|
||||
},
|
||||
.field_access => |fa| {
|
||||
const obj_ty = self.inferExprType(fa.object);
|
||||
var obj_ty = self.inferExprType(fa.object);
|
||||
// `p.field` where `p` is `*T` resolves on the pointee `T`.
|
||||
if (obj_ty.isPointer()) {
|
||||
obj_ty = self.resolveTypeNameStr(obj_ty.pointer_type.pointee_name);
|
||||
}
|
||||
// `.len` / `.ptr` on the built-in containers (string, slice, array).
|
||||
if (std.mem.eql(u8, fa.field, "len")) {
|
||||
if (obj_ty == .string_type or obj_ty.isSlice() or obj_ty.isArray()) return Type.s(64);
|
||||
@@ -718,19 +722,9 @@ pub const Analyzer = struct {
|
||||
fn analyzeParams(self: *Analyzer, params: []const ast.Param) !void {
|
||||
for (params) |param| {
|
||||
self.resolveTypeRef(param.type_expr);
|
||||
const param_type = Type.fromTypeExpr(param.type_expr) orelse blk: {
|
||||
if (param.type_expr.data == .type_expr) {
|
||||
const name = param.type_expr.data.type_expr.name;
|
||||
const resolved = self.type_aliases.get(name) orelse name;
|
||||
if (self.symbol_index.get(resolved)) |indices| {
|
||||
for (indices.items) |idx| {
|
||||
if (self.symbols.items[idx].ty) |ty| break :blk ty;
|
||||
}
|
||||
}
|
||||
}
|
||||
break :blk null;
|
||||
};
|
||||
try self.addSymbol(param.name, .param, param_type, param.name_span);
|
||||
// `fieldType` (not `fromTypeExpr`) so pointer/slice/array param types
|
||||
// like `*Board` / `[]Event` resolve instead of becoming null.
|
||||
try self.addSymbol(param.name, .param, self.fieldType(param.type_expr), param.name_span);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1924,6 +1918,29 @@ test "sema: method-return slice + .ptr index + tagged-enum element" {
|
||||
try std.testing.expectEqualStrings("Event", e_ty.?.toName().?);
|
||||
}
|
||||
|
||||
test "sema: field access + index through a *Struct param" {
|
||||
const parser_mod = @import("parser.zig");
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
|
||||
const source =
|
||||
"Cell :: struct { v: s64; }" ++
|
||||
"Grid :: struct { cells: [4]Cell; }" ++
|
||||
"look :: (g: *Grid) { c := g.cells[0]; }";
|
||||
var parser = parser_mod.Parser.init(alloc, source);
|
||||
const root = try parser.parse();
|
||||
var an = Analyzer.init(alloc);
|
||||
const res = try an.analyze(root);
|
||||
|
||||
var c_ty: ?Type = null;
|
||||
for (res.symbols) |sym| {
|
||||
if (std.mem.eql(u8, sym.name, "c")) c_ty = sym.ty;
|
||||
}
|
||||
try std.testing.expect(c_ty != null and c_ty.? == .struct_type);
|
||||
try std.testing.expectEqualStrings("Cell", c_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