diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 8dec2de15c..dd9abe61ac 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -165,6 +165,7 @@ pub const Error = union(enum) { ExpectedDerefOrUnwrap: ExpectedDerefOrUnwrap, ExpectedSuffixOp: ExpectedSuffixOp, DeclBetweenFields: DeclBetweenFields, + MissingComma: MissingComma, pub fn render(self: *const Error, tokens: *Tree.TokenList, stream: var) !void { switch (self.*) { @@ -213,6 +214,7 @@ pub const Error = union(enum) { .ExpectedDerefOrUnwrap => |*x| return x.render(tokens, stream), .ExpectedSuffixOp => |*x| return x.render(tokens, stream), .DeclBetweenFields => |*x| return x.render(tokens, stream), + .MissingComma => |*x| return x.render(tokens, stream), } } @@ -263,6 +265,7 @@ pub const Error = union(enum) { .ExpectedDerefOrUnwrap => |x| return x.token, .ExpectedSuffixOp => |x| return x.token, .DeclBetweenFields => |x| return x.token, + .MissingComma => |x| return x.token, } } @@ -308,6 +311,7 @@ pub const Error = union(enum) { pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier"); pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier"); pub const DeclBetweenFields = SimpleError("Declarations are not allowed between container fields"); + pub const MissingComma = SimpleError("Expected comma between items"); pub const ExpectedCall = struct { node: *Node, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 586af02194..ad4f5e1242 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1083,7 +1083,14 @@ fn parseBlock(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { var statements = Node.Block.StatementList.init(arena); while (true) { - const statement = (try parseStatement(arena, it, tree)) orelse break; + const statement = (parseStatement(arena, it, tree) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ParseError => { + // try to skip to the next statement + findToken(it, .Semicolon); + continue; + }, + }) orelse break; try statements.push(statement); } @@ -2816,7 +2823,24 @@ fn ListParseFn(comptime L: type, comptime nodeParseFn: var) ParseFn(L) { var list = L.init(arena); while (try nodeParseFn(arena, it, tree)) |node| { try list.push(node); - if (eatToken(it, .Comma) == null) break; + + const token = nextToken(it); + switch (token.ptr.id) { + .Comma => {}, + // all possible delimiters + .Colon, .RParen, .RBrace, .RBracket => { + putBackToken(it, token.index); + break; + }, + else => { + // this is likely just a missing comma, + // continue parsing this list and give an error + try tree.errors.push(.{ + .MissingComma = .{ .token = token.index }, + }); + putBackToken(it, token.index); + }, + } } return list; } diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index a925aacc0b..2c8b53f7f0 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -6,6 +6,30 @@ test "zig fmt: fault tolerant parsing" { .ExpectedInlinable, .ExpectedInlinable, }); + try testError( + \\test "" { + \\ foo + +; + \\ inline; + \\} + , &[_]Error{ + .InvalidToken, + .ExpectedInlinable, + }); + try testError( + \\test "" { + \\ switch (foo) { + \\ 2 => {} + \\ 3 => {} + \\ else => { + \\ inline; + \\ } + \\ } + \\} + , &[_]Error{ + .MissingComma, + .MissingComma, + .ExpectedInlinable, + }); } test "zig fmt: top-level fields" {