recover from invalid global error set access

master
Vexu 2020-05-16 12:09:34 +03:00
parent b2f16d4484
commit 6ca0def499
No known key found for this signature in database
GPG Key ID: 59AEB8936E16A6AC
2 changed files with 48 additions and 35 deletions

View File

@ -232,6 +232,7 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree, top
switch (next) {
.Eof => break,
else => {
const index = it.index;
if (next == .RBrace) {
if (!top_level) break;
_ = nextToken(it);
@ -239,7 +240,6 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree, top
// this was likely not supposed to end yet,
// try to find the next declaration
const index = it.index;
findNextContainerMember(it);
try tree.errors.push(.{
.ExpectedContainerMembers = .{ .token = index },
@ -411,20 +411,16 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
fn_node.*.extern_export_inline_token = extern_export_inline_token;
fn_node.*.lib_name = lib_name;
if (eatToken(it, .Semicolon)) |_| return node;
if (parseBlock(arena, it, tree) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
if (try expectNodeRecoverable(arena, it, tree, parseBlock, .{
// since parseBlock only return error.ParseError on
// a missing '}' we can assume this function was
// supposed to end here.
error.ParseError => return node,
}) |body_node| {
fn_node.body_node = body_node;
return node;
}
try tree.errors.push(.{
.ExpectedSemiOrLBrace = .{ .token = it.index },
});
return error.ParseError;
})) |body_node| {
fn_node.body_node = body_node;
}
return node;
}
if (extern_export_inline_token) |token| {
@ -499,14 +495,11 @@ fn parseFnProto(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
const exclamation_token = eatToken(it, .Bang);
const return_type_expr = (try parseVarType(arena, it, tree)) orelse
(try parseTypeExpr(arena, it, tree)) orelse blk: {
try tree.errors.push(.{
.ExpectedReturnType = .{ .token = it.index },
});
try expectNodeRecoverable(arena, it, tree, parseTypeExpr, .{
// most likely the user forgot to specify the return type.
// Mark return type as invalid and try to continue.
break :blk null;
};
.ExpectedReturnType = .{ .token = it.index },
});
// TODO https://github.com/ziglang/zig/issues/3750
const R = Node.FnProto.ReturnType;
@ -716,12 +709,7 @@ fn parseStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*No
if (try parseLabeledStatement(arena, it, tree)) |node| return node;
if (try parseSwitchExpr(arena, it, tree)) |node| return node;
if (try parseAssignExpr(arena, it, tree)) |node| {
_ = eatToken(it, .Semicolon) orelse {
try tree.errors.push(.{
.ExpectedToken = .{ .token = it.index, .expected_id = .Semicolon },
});
// pretend we saw a semicolon and continue parsing
};
_ = try expectTokenRecoverable(it, tree, .Semicolon);
return node;
}
@ -965,12 +953,7 @@ fn parseWhileStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*No
fn parseBlockExprStatement(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
if (try parseBlockExpr(arena, it, tree)) |node| return node;
if (try parseAssignExpr(arena, it, tree)) |node| {
_ = eatToken(it, .Semicolon) orelse {
try tree.errors.push(.{
.ExpectedToken = .{ .token = it.index, .expected_id = .Semicolon },
});
// pretend we saw a semicolon and continue parsing
};
_ = try expectTokenRecoverable(it, tree, .Semicolon);
return node;
}
return null;
@ -1487,17 +1470,19 @@ fn parsePrimaryTypeExpr(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*N
return &node.base;
}
if (eatToken(it, .Keyword_error)) |token| {
const period = try expectToken(it, tree, .Period);
const identifier = try expectNode(arena, it, tree, parseIdentifier, .{
const period = try expectTokenRecoverable(it, tree, .Period);
const identifier = try expectNodeRecoverable(arena, it, tree, parseIdentifier, .{
.ExpectedIdentifier = .{ .token = it.index },
});
const global_error_set = try createLiteral(arena, Node.ErrorType, token);
if (period == null or identifier == null) return global_error_set;
const node = try arena.create(Node.InfixOp);
node.* = .{
.op_token = period,
.op_token = period.?,
.lhs = global_error_set,
.op = .Period,
.rhs = identifier,
.rhs = identifier.?,
};
return &node.base;
}
@ -3259,6 +3244,11 @@ fn eatAnnotatedToken(it: *TokenIterator, id: Token.Id) ?AnnotatedToken {
}
fn expectToken(it: *TokenIterator, tree: *Tree, id: Token.Id) Error!TokenIndex {
return (try expectTokenRecoverable(it, tree, id)) orelse
error.ParseError;
}
fn expectTokenRecoverable(it: *TokenIterator, tree: *Tree, id: Token.Id) !?TokenIndex {
const token = nextToken(it);
if (token.ptr.id != id) {
try tree.errors.push(.{
@ -3266,7 +3256,7 @@ fn expectToken(it: *TokenIterator, tree: *Tree, id: Token.Id) Error!TokenIndex {
});
// go back so that we can recover properly
putBackToken(it, token.index);
return error.ParseError;
return null;
}
return token.index;
}
@ -3306,9 +3296,20 @@ fn expectNode(
parseFn: NodeParseFn,
err: AstError, // if parsing fails
) Error!*Node {
return (try expectNodeRecoverable(arena, it, tree, parseFn, err)) orelse
return error.ParseError;
}
fn expectNodeRecoverable(
arena: *Allocator,
it: *TokenIterator,
tree: *Tree,
parseFn: NodeParseFn,
err: AstError, // if parsing fails
) !?*Node {
return (try parseFn(arena, it, tree)) orelse {
try tree.errors.push(err);
return error.ParseError;
return null;
};
}

View File

@ -172,6 +172,18 @@ test "recovery: mismatched bracket at top level" {
});
}
test "recovery: invalid global error set access" {
try testError(
\\test "" {
\\ error && foo;
\\}
, &[_]Error{
.ExpectedToken,
.ExpectedIdentifier,
.InvalidAnd,
});
}
test "zig fmt: top-level fields" {
try testCanonical(
\\a: did_you_know,