properly handle extra closing braces at top level

This commit is contained in:
Vexu
2020-05-15 14:30:49 +03:00
parent 440189a04a
commit a00fd6e254
2 changed files with 36 additions and 16 deletions

View File

@@ -57,16 +57,10 @@ pub fn parse(allocator: *Allocator, source: []const u8) Allocator.Error!*Tree {
fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!*Node.Root {
const node = try arena.create(Node.Root);
node.* = .{
.decls = try parseContainerMembers(arena, it, tree),
.eof_token = eatToken(it, .Eof) orelse blk: {
// parseContainerMembers will try to skip as much
// invalid tokens as it can so this can only be a '}'
const tok = eatToken(it, .RBrace).?;
try tree.errors.push(.{
.ExpectedContainerMembers = .{ .token = tok },
});
break :blk tok;
},
.decls = try parseContainerMembers(arena, it, tree, true),
// parseContainerMembers will try to skip as much
// invalid tokens as it can so this can only be the EOF
.eof_token = eatToken(it, .Eof).?,
};
return node;
}
@@ -78,7 +72,7 @@ fn parseRoot(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error
/// / KEYWORD_pub? ContainerField COMMA ContainerMembers
/// / KEYWORD_pub? ContainerField
/// /
fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !Node.Root.DeclList {
fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree, top_level: bool) !Node.Root.DeclList {
var list = Node.Root.DeclList.init(arena);
var field_state: union(enum) {
@@ -205,9 +199,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No
// try to continue parsing
const index = it.index;
findNextContainerMember(it);
switch (it.peek().?.id) {
.Eof, .RBrace => break,
const next = it.peek().?.id;
switch (next) {
.Eof => break,
else => {
if (next == .RBrace) {
if (!top_level) break;
_ = nextToken(it);
}
// add error and continue
try tree.errors.push(.{
.ExpectedToken = .{ .token = index, .expected_id = .Comma },
@@ -228,9 +228,15 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) !No
});
}
switch (it.peek().?.id) {
.Eof, .RBrace => break,
const next = it.peek().?.id;
switch (next) {
.Eof => break,
else => {
if (next == .RBrace) {
if (!top_level) break;
_ = nextToken(it);
}
// this was likely not supposed to end yet,
// try to find the next declaration
const index = it.index;
@@ -2778,7 +2784,7 @@ fn parsePtrTypeStart(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
fn parseContainerDeclAuto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const node = (try parseContainerDeclType(arena, it, tree)) orelse return null;
const lbrace = try expectToken(it, tree, .LBrace);
const members = try parseContainerMembers(arena, it, tree);
const members = try parseContainerMembers(arena, it, tree, false);
const rbrace = try expectToken(it, tree, .RBrace);
const decl_type = node.cast(Node.ContainerDecl).?;

View File

@@ -148,6 +148,20 @@ test "recovery: invalid parameter" {
});
}
test "recovery: extra '}' at top level" {
try testError(
\\}}}
\\test "" {
\\ a && b;
\\}
, &[_]Error{
.ExpectedContainerMembers,
.ExpectedContainerMembers,
.ExpectedContainerMembers,
.InvalidAnd,
});
}
test "zig fmt: top-level fields" {
try testCanonical(
\\a: did_you_know,