This commit is contained in:
agra
2026-02-14 19:33:33 +02:00
parent d61c6488f3
commit 0e777e9d2e
7 changed files with 957 additions and 72 deletions

View File

@@ -148,7 +148,7 @@ pub const Analyzer = struct {
});
},
.const_decl => |cd| {
const ty = resolveTypeAnnotation(cd.type_annotation) orelse inferValueType(cd.value);
const ty = self.resolveTypeAnnotation(cd.type_annotation) orelse inferValueType(cd.value);
const kind = classifyConstDecl(cd);
try self.addSymbol(cd.name, kind, ty, node.span);
// Populate type_aliases registry
@@ -175,7 +175,7 @@ pub const Analyzer = struct {
}
},
.var_decl => |vd| {
const ty = resolveTypeAnnotation(vd.type_annotation);
const ty = self.resolveTypeAnnotation(vd.type_annotation);
try self.addSymbol(vd.name, .variable, ty, node.span);
},
.enum_decl => |ed| {
@@ -581,7 +581,7 @@ pub const Analyzer = struct {
.const_decl => |cd| {
// Analyze value first (so it can't reference itself)
try self.analyzeNode(cd.value);
const ty = resolveTypeAnnotation(cd.type_annotation) orelse inferValueType(cd.value);
const ty = self.resolveTypeAnnotation(cd.type_annotation) orelse inferValueType(cd.value);
const kind = classifyConstDecl(cd);
try self.addSymbol(cd.name, kind, ty, node.span);
},
@@ -589,7 +589,7 @@ pub const Analyzer = struct {
if (vd.value) |val| {
try self.analyzeNode(val);
}
const ty = resolveTypeAnnotation(vd.type_annotation) orelse
const ty = self.resolveTypeAnnotation(vd.type_annotation) orelse
if (vd.value) |val| self.inferExprType(val) else null;
try self.addSymbol(vd.name, .variable, ty, node.span);
},
@@ -637,7 +637,12 @@ pub const Analyzer = struct {
.match_expr => |me| {
try self.analyzeNode(me.subject);
for (me.arms) |arm| {
try self.pushScope();
if (arm.capture) |cap_name| {
try self.addSymbol(cap_name, .variable, null, arm.body.span);
}
try self.analyzeNode(arm.body);
self.popScope();
}
},
.while_expr => |we| {
@@ -773,9 +778,19 @@ pub const Analyzer = struct {
return null;
}
fn resolveTypeAnnotation(type_node: ?*Node) ?Type {
fn resolveTypeAnnotation(self: *Analyzer, type_node: ?*Node) ?Type {
if (type_node) |tn| {
return Type.fromTypeExpr(tn);
if (Type.fromTypeExpr(tn)) |t| return t;
// Check registered types (structs, enums, tagged enums)
if (tn.data == .type_expr) {
const name = tn.data.type_expr.name;
// Check type aliases first
const resolved = self.type_aliases.get(name) orelse name;
for (self.symbols.items) |sym| {
if (!std.mem.eql(u8, sym.name, resolved)) continue;
if (sym.ty) |ty| return ty;
}
}
}
return null;
}
@@ -986,6 +1001,71 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
return node;
}
/// Find the nearest match_expr ancestor that contains the given offset.
/// Returns the match subject node if found, null otherwise.
pub fn findEnclosingMatchSubject(node: *Node, offset: u32) ?*Node {
if (offset < node.span.start or offset >= node.span.end) return null;
switch (node.data) {
.match_expr => |me| {
// First recurse into arm bodies — there might be a nested match
for (me.arms) |arm| {
if (findEnclosingMatchSubject(arm.body, offset)) |inner| return inner;
}
// If offset is inside this match_expr (but not in the subject itself),
// it's in an arm pattern, between arms, or in a partially-typed arm
if (me.subject.span.start <= offset and offset < me.subject.span.end) {
// Cursor is on the subject itself, not in an arm
} else {
return me.subject;
}
},
.root => |r| {
for (r.decls) |decl| {
if (findEnclosingMatchSubject(decl, offset)) |found| return found;
}
},
.fn_decl => |fd| {
if (findEnclosingMatchSubject(fd.body, offset)) |found| return found;
},
.block => |blk| {
for (blk.stmts) |stmt| {
if (findEnclosingMatchSubject(stmt, offset)) |found| return found;
}
},
.if_expr => |ie| {
if (findEnclosingMatchSubject(ie.then_branch, offset)) |found| return found;
if (ie.else_branch) |eb| {
if (findEnclosingMatchSubject(eb, offset)) |found| return found;
}
},
.while_expr => |we| {
if (findEnclosingMatchSubject(we.body, offset)) |found| return found;
},
.for_expr => |fe| {
if (findEnclosingMatchSubject(fe.body, offset)) |found| return found;
},
.const_decl => |cd| {
if (findEnclosingMatchSubject(cd.value, offset)) |found| return found;
},
.var_decl => |vd| {
if (vd.value) |val| {
if (findEnclosingMatchSubject(val, offset)) |found| return found;
}
},
.lambda => |lam| {
if (findEnclosingMatchSubject(lam.body, offset)) |found| return found;
},
.namespace_decl => |ns| {
for (ns.decls) |decl| {
if (findEnclosingMatchSubject(decl, offset)) |found| return found;
}
},
else => {},
}
return null;
}
test "sema: collect top-level declarations" {
const parser_mod = @import("parser.zig");