aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2020-01-13 13:38:31 -0500
committerGitHub <noreply@github.com>2020-01-13 13:38:31 -0500
commitc774c9376a20c2fde7db4fed94bdea5ae94e1494 (patch)
treead5ad8ee9f19a71667d79e9c2f65a0ad527cc9b1 /lib
parent2be12b24bcafd4b2785d61dcfbaa27f9e3cc705d (diff)
parent5880eb3a75b41d8ac26faa0185a83e001197a6e4 (diff)
downloadzig-c774c9376a20c2fde7db4fed94bdea5ae94e1494.tar.gz
zig-c774c9376a20c2fde7db4fed94bdea5ae94e1494.zip
Merge pull request #3957 from xackus/stage2_parser_3799
stage2 parser: fix segfault on extern block
Diffstat (limited to 'lib')
-rw-r--r--lib/std/zig/ast.zig3
-rw-r--r--lib/std/zig/parse.zig63
-rw-r--r--lib/std/zig/parser_test.zig15
-rw-r--r--lib/std/zig/render.zig2
4 files changed, 56 insertions, 27 deletions
diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig
index b921213f0c..dc9a6bc7ca 100644
--- a/lib/std/zig/ast.zig
+++ b/lib/std/zig/ast.zig
@@ -10,6 +10,7 @@ pub const TokenIndex = usize;
pub const Tree = struct {
source: []const u8,
tokens: TokenList,
+ /// undefined on parse error (errors not empty)
root_node: *Node.Root,
arena_allocator: std.heap.ArenaAllocator,
errors: ErrorList,
@@ -612,7 +613,7 @@ pub const Node = struct {
visib_token: ?TokenIndex,
thread_local_token: ?TokenIndex,
name_token: TokenIndex,
- eq_token: TokenIndex,
+ eq_token: ?TokenIndex,
mut_token: TokenIndex,
comptime_token: ?TokenIndex,
extern_export_token: ?TokenIndex,
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index c9c9956159..7cc9931d4e 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -13,7 +13,7 @@ pub const Error = error{ParseError} || Allocator.Error;
/// Result should be freed with tree.deinit() when there are
/// no more references to any of the tokens or nodes.
-pub fn parse(allocator: *Allocator, source: []const u8) !*Tree {
+pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree {
const tree = blk: {
// This block looks unnecessary, but is a "foot-shield" to prevent the SegmentedLists
// from being initialized with a pointer to this `arena`, which is created on
@@ -48,29 +48,32 @@ pub fn parse(allocator: *Allocator, source: []const u8) !*Tree {
while (it.peek().?.id == .LineComment) _ = it.next();
- tree.root_node = try parseRoot(arena, &it, tree);
+ tree.root_node = parseRoot(arena, &it, tree) catch |err| blk: {
+ switch (err) {
+ error.ParseError => {
+ assert(tree.errors.len != 0);
+ break :blk undefined;
+ },
+ error.OutOfMemory => {
+ return error.OutOfMemory;
+ },
+ }
+ };
+
return tree;
}
/// Root <- skip ContainerMembers eof
-fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!*Node.Root {
+fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!*Node.Root {
const node = try arena.create(Node.Root);
node.* = Node.Root{
- .decls = undefined,
- .eof_token = undefined,
- };
- node.decls = parseContainerMembers(arena, it, tree) catch |err| {
- // TODO: Switch on the error type
- // https://github.com/ziglang/zig/issues/2473
- if (err == error.ParseError) return node;
- assert(err == Allocator.Error.OutOfMemory);
- return Allocator.Error.OutOfMemory;
- };
- node.eof_token = eatToken(it, .Eof) orelse {
- try tree.errors.push(AstError{
- .ExpectedContainerMembers = AstError.ExpectedContainerMembers{ .token = it.index },
- });
- return node;
+ .decls = try parseContainerMembers(arena, it, tree),
+ .eof_token = eatToken(it, .Eof) orelse {
+ try tree.errors.push(AstError{
+ .ExpectedContainerMembers = .{ .token = it.index },
+ });
+ return error.ParseError;
+ },
};
return node;
}
@@ -303,7 +306,17 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const cc = parseFnCC(arena, it, tree);
const fn_token = eatToken(it, .Keyword_fn) orelse {
- if (cc == null) return null else return error.ParseError;
+ if (cc) |fnCC| {
+ if (fnCC == .Extern) {
+ putBackToken(it, fnCC.Extern); // 'extern' is also used in ContainerDecl
+ } else {
+ try tree.errors.push(AstError{
+ .ExpectedToken = .{ .token = it.index, .expected_id = .Keyword_fn },
+ });
+ return error.ParseError;
+ }
+ }
+ return null;
};
const name_token = eatToken(it, .Identifier);
const lparen = try expectToken(it, tree, .LParen);
@@ -390,7 +403,7 @@ fn parseVarDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
.visib_token = null,
.thread_local_token = null,
.name_token = name_token,
- .eq_token = eq_token orelse undefined,
+ .eq_token = eq_token,
.mut_token = mut_token,
.comptime_token = null,
.extern_export_token = null,
@@ -2177,7 +2190,7 @@ fn parsePrefixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
node.* = Node.PrefixOp{
.op_token = token.index,
.op = op,
- .rhs = undefined,
+ .rhs = undefined, // set by caller
};
return &node.base;
}
@@ -2819,8 +2832,8 @@ fn parseUse(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
.doc_comments = null,
.visib_token = null,
.use_token = token,
- .expr = undefined,
- .semicolon_token = undefined,
+ .expr = undefined, // set by caller
+ .semicolon_token = undefined, // set by caller
};
return &node.base;
}
@@ -2979,9 +2992,9 @@ fn createInfixOp(arena: *Allocator, index: TokenIndex, op: Node.InfixOp.Op) !*No
const node = try arena.create(Node.InfixOp);
node.* = Node.InfixOp{
.op_token = index,
- .lhs = undefined,
+ .lhs = undefined, // set by caller
.op = op,
- .rhs = undefined,
+ .rhs = undefined, // set by caller
};
return &node.base;
}
diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig
index 97df2dff15..c57540ade9 100644
--- a/lib/std/zig/parser_test.zig
+++ b/lib/std/zig/parser_test.zig
@@ -2714,6 +2714,13 @@ test "zig fmt: top level doc comments" {
);
}
+test "zig fmt: extern without container keyword returns error" {
+ try testError(
+ \\const container = extern {};
+ \\
+ );
+}
+
const std = @import("std");
const mem = std.mem;
const warn = std.debug.warn;
@@ -2820,3 +2827,11 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void {
fn testCanonical(source: []const u8) !void {
return testTransform(source, source);
}
+
+fn testError(source: []const u8) !void {
+ var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ const tree = try std.zig.parse(&fixed_allocator.allocator, source);
+ defer tree.deinit();
+
+ std.testing.expect(tree.errors.len != 0);
+}
diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig
index 2b1a739186..f2c64a0e4a 100644
--- a/lib/std/zig/render.zig
+++ b/lib/std/zig/render.zig
@@ -2020,7 +2020,7 @@ fn renderVarDecl(
if (var_decl.init_node) |init_node| {
const s = if (init_node.id == .MultilineStringLiteral) Space.None else Space.Space;
- try renderToken(tree, stream, var_decl.eq_token, indent, start_col, s); // =
+ try renderToken(tree, stream, var_decl.eq_token.?, indent, start_col, s); // =
try renderExpression(allocator, stream, tree, indent, start_col, init_node, Space.None);
}