From a00fd6e25469a9929defea95425a1c312a68cf0e Mon Sep 17 00:00:00 2001 From: Vexu Date: Fri, 15 May 2020 14:30:49 +0300 Subject: [PATCH] properly handle extra closing braces at top level --- lib/std/zig/parse.zig | 38 +++++++++++++++++++++---------------- lib/std/zig/parser_test.zig | 14 ++++++++++++++ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index a269dc616c..6e41771ecc 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -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).?; diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 6adc44a5b7..616c1a5ea4 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -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,