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);
|
return self.inferExprType(unop.operand);
|
||||||
},
|
},
|
||||||
.field_access => |fa| {
|
.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).
|
// `.len` / `.ptr` on the built-in containers (string, slice, array).
|
||||||
if (std.mem.eql(u8, fa.field, "len")) {
|
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);
|
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 {
|
fn analyzeParams(self: *Analyzer, params: []const ast.Param) !void {
|
||||||
for (params) |param| {
|
for (params) |param| {
|
||||||
self.resolveTypeRef(param.type_expr);
|
self.resolveTypeRef(param.type_expr);
|
||||||
const param_type = Type.fromTypeExpr(param.type_expr) orelse blk: {
|
// `fieldType` (not `fromTypeExpr`) so pointer/slice/array param types
|
||||||
if (param.type_expr.data == .type_expr) {
|
// like `*Board` / `[]Event` resolve instead of becoming null.
|
||||||
const name = param.type_expr.data.type_expr.name;
|
try self.addSymbol(param.name, .param, self.fieldType(param.type_expr), param.name_span);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1924,6 +1918,29 @@ test "sema: method-return slice + .ptr index + tagged-enum element" {
|
|||||||
try std.testing.expectEqualStrings("Event", e_ty.?.toName().?);
|
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" {
|
test "sema: variable shadowing in same scope is allowed" {
|
||||||
const parser_mod = @import("parser.zig");
|
const parser_mod = @import("parser.zig");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user