Bug fix: Prevent uninitialized parse nodes

If a parse node is reserved but never set the node remains
uninitialized and can crash tools doing a linear scan of the nodes
(like ZLS) when switching on the tag.
This commit is contained in:
Matt Chudleigh 2022-11-24 08:13:22 -08:00 committed by Veikka Tuominen
parent 30eb2a1753
commit f61c5f3f52

View File

@ -131,11 +131,23 @@ const Parser = struct {
return @intCast(Node.Index, i);
}
fn reserveNode(p: *Parser) !usize {
fn reserveNode(p: *Parser, tag: Ast.Node.Tag) !usize {
try p.nodes.resize(p.gpa, p.nodes.len + 1);
p.nodes.items(.tag)[p.nodes.len - 1] = tag;
return p.nodes.len - 1;
}
fn unreserveNode(p: *Parser, node_index: usize) void {
if (p.nodes.len == node_index) {
p.nodes.resize(p.gpa, p.nodes.len - 1) catch unreachable;
} else {
// There is zombie node left in the tree, let's make it as inoffensive as possible
// (sadly there's no no-op node)
p.nodes.items(.tag)[node_index] = .unreachable_literal;
p.nodes.items(.main_token)[node_index] = p.tok_i;
}
}
fn addExtra(p: *Parser, extra: anytype) Allocator.Error!Node.Index {
const fields = std.meta.fields(@TypeOf(extra));
try p.extra_data.ensureUnusedCapacity(p.gpa, fields.len);
@ -637,13 +649,15 @@ const Parser = struct {
return fn_proto;
},
.l_brace => {
const fn_decl_index = try p.reserveNode();
const body_block = try p.parseBlock();
assert(body_block != 0);
if (is_extern) {
try p.warnMsg(.{ .tag = .extern_fn_body, .token = extern_export_inline_token });
return null_node;
}
const fn_decl_index = try p.reserveNode(.fn_decl);
errdefer p.unreserveNode(fn_decl_index);
const body_block = try p.parseBlock();
assert(body_block != 0);
return p.setNode(fn_decl_index, .{
.tag = .fn_decl,
.main_token = p.nodes.items(.main_token)[fn_proto],
@ -724,7 +738,8 @@ const Parser = struct {
const fn_token = p.eatToken(.keyword_fn) orelse return null_node;
// We want the fn proto node to be before its children in the array.
const fn_proto_index = try p.reserveNode();
const fn_proto_index = try p.reserveNode(.fn_proto);
errdefer p.unreserveNode(fn_proto_index);
_ = p.eatToken(.identifier);
const params = try p.parseParamDeclList();