parser: support more recovery test cases
This commit is contained in:
@@ -205,6 +205,9 @@ pub const Tree = struct {
|
||||
token_tags[parse_error.token].symbol(),
|
||||
});
|
||||
},
|
||||
.expected_pub_item => {
|
||||
return stream.writeAll("expected function or variable declaration after pub");
|
||||
},
|
||||
.expected_return_type => {
|
||||
return stream.print("expected return type expression, found '{s}'", .{
|
||||
token_tags[parse_error.token].symbol(),
|
||||
@@ -265,6 +268,9 @@ pub const Tree = struct {
|
||||
.invalid_align => {
|
||||
return stream.writeAll("alignment not allowed on arrays");
|
||||
},
|
||||
.invalid_and => {
|
||||
return stream.writeAll("`&&` is invalid; note that `and` is boolean AND");
|
||||
},
|
||||
.invalid_bit_range => {
|
||||
return stream.writeAll("bit range not allowed on slices and arrays");
|
||||
},
|
||||
@@ -2316,6 +2322,7 @@ pub const Error = struct {
|
||||
expected_param_list,
|
||||
expected_prefix_expr,
|
||||
expected_primary_type_expr,
|
||||
expected_pub_item,
|
||||
expected_return_type,
|
||||
expected_semi_or_else,
|
||||
expected_semi_or_lbrace,
|
||||
@@ -2330,6 +2337,7 @@ pub const Error = struct {
|
||||
extra_const_qualifier,
|
||||
extra_volatile_qualifier,
|
||||
invalid_align,
|
||||
invalid_and,
|
||||
invalid_bit_range,
|
||||
invalid_token,
|
||||
same_line_doc_comment,
|
||||
|
||||
@@ -423,7 +423,7 @@ const Parser = struct {
|
||||
while (true) {
|
||||
const tok = p.nextToken();
|
||||
switch (p.token_tags[tok]) {
|
||||
// any of these can start a new top level declaration
|
||||
// Any of these can start a new top level declaration.
|
||||
.keyword_test,
|
||||
.keyword_comptime,
|
||||
.keyword_pub,
|
||||
@@ -436,13 +436,18 @@ const Parser = struct {
|
||||
.keyword_const,
|
||||
.keyword_var,
|
||||
.keyword_fn,
|
||||
.identifier,
|
||||
=> {
|
||||
if (level == 0) {
|
||||
p.tok_i -= 1;
|
||||
return;
|
||||
}
|
||||
},
|
||||
.identifier => {
|
||||
if (p.token_tags[tok + 1] == .comma and level == 0) {
|
||||
p.tok_i -= 1;
|
||||
return;
|
||||
}
|
||||
},
|
||||
.comma, .semicolon => {
|
||||
// this decl was likely meant to end here
|
||||
if (level == 0) {
|
||||
@@ -531,10 +536,13 @@ const Parser = struct {
|
||||
fn expectTopLevelDecl(p: *Parser) !Node.Index {
|
||||
const extern_export_inline_token = p.nextToken();
|
||||
var expect_fn: bool = false;
|
||||
var exported: bool = false;
|
||||
var expect_var_or_fn: bool = false;
|
||||
switch (p.token_tags[extern_export_inline_token]) {
|
||||
.keyword_extern => _ = p.eatToken(.string_literal),
|
||||
.keyword_export => exported = true,
|
||||
.keyword_extern => {
|
||||
_ = p.eatToken(.string_literal);
|
||||
expect_var_or_fn = true;
|
||||
},
|
||||
.keyword_export => expect_var_or_fn = true,
|
||||
.keyword_inline, .keyword_noinline => expect_fn = true,
|
||||
else => p.tok_i -= 1,
|
||||
}
|
||||
@@ -580,11 +588,12 @@ const Parser = struct {
|
||||
if (thread_local_token != null) {
|
||||
return p.fail(.expected_var_decl);
|
||||
}
|
||||
|
||||
if (exported) {
|
||||
if (expect_var_or_fn) {
|
||||
return p.fail(.expected_var_decl_or_fn);
|
||||
}
|
||||
|
||||
if (p.token_tags[p.tok_i] != .keyword_usingnamespace) {
|
||||
return p.fail(.expected_pub_item);
|
||||
}
|
||||
return p.expectUsingNamespace();
|
||||
}
|
||||
|
||||
@@ -599,7 +608,7 @@ const Parser = struct {
|
||||
}
|
||||
|
||||
fn expectUsingNamespace(p: *Parser) !Node.Index {
|
||||
const usingnamespace_token = try p.expectToken(.keyword_usingnamespace);
|
||||
const usingnamespace_token = p.assertToken(.keyword_usingnamespace);
|
||||
const expr = try p.expectExpr();
|
||||
const semicolon_token = try p.expectToken(.semicolon);
|
||||
return p.addNode(.{
|
||||
@@ -1346,6 +1355,11 @@ const Parser = struct {
|
||||
},
|
||||
});
|
||||
},
|
||||
.invalid_ampersands => {
|
||||
try p.warn(.invalid_and);
|
||||
p.tok_i += 1;
|
||||
return p.parseCompareExpr();
|
||||
},
|
||||
else => return res,
|
||||
}
|
||||
}
|
||||
@@ -2283,10 +2297,12 @@ const Parser = struct {
|
||||
if (node == 0) break;
|
||||
res = node;
|
||||
}
|
||||
const lparen = (try p.expectTokenRecoverable(.l_paren)) orelse {
|
||||
const lparen = p.nextToken();
|
||||
if (p.token_tags[lparen] != .l_paren) {
|
||||
p.tok_i -= 1;
|
||||
try p.warn(.expected_param_list);
|
||||
return res;
|
||||
};
|
||||
}
|
||||
if (p.eatToken(.r_paren)) |_| {
|
||||
return p.addNode(.{
|
||||
.tag = .async_call_one,
|
||||
@@ -3769,7 +3785,8 @@ const Parser = struct {
|
||||
/// ExprList <- (Expr COMMA)* Expr?
|
||||
fn parseBuiltinCall(p: *Parser) !Node.Index {
|
||||
const builtin_token = p.assertToken(.builtin);
|
||||
_ = (try p.expectTokenRecoverable(.l_paren)) orelse {
|
||||
if (p.token_tags[p.nextToken()] != .l_paren) {
|
||||
p.tok_i -= 1;
|
||||
try p.warn(.expected_param_list);
|
||||
// Pretend this was an identifier so we can continue parsing.
|
||||
return p.addNode(.{
|
||||
@@ -3780,7 +3797,7 @@ const Parser = struct {
|
||||
.rhs = undefined,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
if (p.eatToken(.r_paren)) |_| {
|
||||
return p.addNode(.{
|
||||
.tag = .builtin_call_two,
|
||||
@@ -4015,6 +4032,7 @@ const Parser = struct {
|
||||
fn expectToken(p: *Parser, tag: Token.Tag) Error!TokenIndex {
|
||||
const token = p.nextToken();
|
||||
if (p.token_tags[token] != tag) {
|
||||
p.tok_i -= 1; // Go back so that we can recover properly.
|
||||
return p.failMsg(.{
|
||||
.tag = .expected_token,
|
||||
.token = token,
|
||||
|
||||
@@ -3579,7 +3579,7 @@ test "zig fmt: file ends with struct field" {
|
||||
// \\
|
||||
// , &[_]Error{
|
||||
// .expected_expr,
|
||||
// .ExpectedVarDeclOrFn,
|
||||
// .expected_var_decl_or_fn,
|
||||
// });
|
||||
//}
|
||||
|
||||
@@ -4070,24 +4070,24 @@ test "recovery: block statements" {
|
||||
});
|
||||
}
|
||||
|
||||
//test "recovery: missing comma" {
|
||||
// try testError(
|
||||
// \\test "" {
|
||||
// \\ switch (foo) {
|
||||
// \\ 2 => {}
|
||||
// \\ 3 => {}
|
||||
// \\ else => {
|
||||
// \\ foo && bar +;
|
||||
// \\ }
|
||||
// \\ }
|
||||
// \\}
|
||||
// , &[_]Error{
|
||||
// .expected_token,
|
||||
// .expected_token,
|
||||
// .invalid_and,
|
||||
// .invalid_token,
|
||||
// });
|
||||
//}
|
||||
test "recovery: missing comma" {
|
||||
try testError(
|
||||
\\test "" {
|
||||
\\ switch (foo) {
|
||||
\\ 2 => {}
|
||||
\\ 3 => {}
|
||||
\\ else => {
|
||||
\\ foo && bar +;
|
||||
\\ }
|
||||
\\ }
|
||||
\\}
|
||||
, &[_]Error{
|
||||
.expected_token,
|
||||
.expected_token,
|
||||
.invalid_and,
|
||||
.invalid_token,
|
||||
});
|
||||
}
|
||||
|
||||
test "recovery: extra qualifier" {
|
||||
try testError(
|
||||
@@ -4099,94 +4099,93 @@ test "recovery: extra qualifier" {
|
||||
});
|
||||
}
|
||||
|
||||
//test "recovery: missing return type" {
|
||||
// try testError(
|
||||
// \\fn foo() {
|
||||
// \\ a && b;
|
||||
// \\}
|
||||
// \\test ""
|
||||
// , &[_]Error{
|
||||
// .ExpectedReturnType,
|
||||
// .invalid_and,
|
||||
// .expected_block,
|
||||
// });
|
||||
//}
|
||||
test "recovery: missing return type" {
|
||||
try testError(
|
||||
\\fn foo() {
|
||||
\\ a && b;
|
||||
\\}
|
||||
\\test ""
|
||||
, &[_]Error{
|
||||
.expected_return_type,
|
||||
.invalid_and,
|
||||
.expected_block,
|
||||
});
|
||||
}
|
||||
|
||||
//test "recovery: continue after invalid decl" {
|
||||
// try testError(
|
||||
// \\fn foo {
|
||||
// \\ inline;
|
||||
// \\}
|
||||
// \\pub test "" {
|
||||
// \\ async a && b;
|
||||
// \\}
|
||||
// , &[_]Error{
|
||||
// .expected_token,
|
||||
// .ExpectedPubItem,
|
||||
// .ExpectedParamList,
|
||||
// .invalid_and,
|
||||
// });
|
||||
// try testError(
|
||||
// \\threadlocal test "" {
|
||||
// \\ @a && b;
|
||||
// \\}
|
||||
// , &[_]Error{
|
||||
// .ExpectedVarDecl,
|
||||
// .ExpectedParamList,
|
||||
// .invalid_and,
|
||||
// });
|
||||
//}
|
||||
test "recovery: continue after invalid decl" {
|
||||
try testError(
|
||||
\\fn foo {
|
||||
\\ inline;
|
||||
\\}
|
||||
\\pub test "" {
|
||||
\\ async a && b;
|
||||
\\}
|
||||
, &[_]Error{
|
||||
.expected_token,
|
||||
.expected_pub_item,
|
||||
.expected_param_list,
|
||||
.invalid_and,
|
||||
});
|
||||
try testError(
|
||||
\\threadlocal test "" {
|
||||
\\ @a && b;
|
||||
\\}
|
||||
, &[_]Error{
|
||||
.expected_var_decl,
|
||||
.expected_param_list,
|
||||
.invalid_and,
|
||||
});
|
||||
}
|
||||
|
||||
//test "recovery: invalid extern/inline" {
|
||||
// try testError(
|
||||
// \\inline test "" { a && b; }
|
||||
// , &[_]Error{
|
||||
// .ExpectedFn,
|
||||
// .invalid_and,
|
||||
// });
|
||||
// try testError(
|
||||
// \\extern "" test "" { a && b; }
|
||||
// , &[_]Error{
|
||||
// .ExpectedVarDeclOrFn,
|
||||
// .invalid_and,
|
||||
// });
|
||||
//}
|
||||
test "recovery: invalid extern/inline" {
|
||||
try testError(
|
||||
\\inline test "" { a && b; }
|
||||
, &[_]Error{
|
||||
.expected_fn,
|
||||
.invalid_and,
|
||||
});
|
||||
try testError(
|
||||
\\extern "" test "" { a && b; }
|
||||
, &[_]Error{
|
||||
.expected_var_decl_or_fn,
|
||||
.invalid_and,
|
||||
});
|
||||
}
|
||||
|
||||
//test "recovery: missing semicolon" {
|
||||
// try testError(
|
||||
// \\test "" {
|
||||
// \\ comptime a && b
|
||||
// \\ c && d
|
||||
// \\ @foo
|
||||
// \\}
|
||||
// , &[_]Error{
|
||||
// .invalid_and,
|
||||
// .expected_token,
|
||||
// .invalid_and,
|
||||
// .expected_token,
|
||||
// .ExpectedParamList,
|
||||
// .expected_token,
|
||||
// });
|
||||
//}
|
||||
test "recovery: missing semicolon" {
|
||||
try testError(
|
||||
\\test "" {
|
||||
\\ comptime a && b
|
||||
\\ c && d
|
||||
\\ @foo
|
||||
\\}
|
||||
, &[_]Error{
|
||||
.invalid_and,
|
||||
.expected_token,
|
||||
.invalid_and,
|
||||
.expected_token,
|
||||
.expected_param_list,
|
||||
.expected_token,
|
||||
});
|
||||
}
|
||||
|
||||
//test "recovery: invalid container members" {
|
||||
// try testError(
|
||||
// \\usingnamespace;
|
||||
// \\foo+
|
||||
// \\bar@,
|
||||
// \\while (a == 2) { test "" {}}
|
||||
// \\test "" {
|
||||
// \\ a && b
|
||||
// \\}
|
||||
// , &[_]Error{
|
||||
// .expected_expr,
|
||||
// .expected_token,
|
||||
// .expected_token,
|
||||
// .expected_container_members,
|
||||
// .invalid_and,
|
||||
// .expected_token,
|
||||
// });
|
||||
//}
|
||||
test "recovery: invalid container members" {
|
||||
try testError(
|
||||
\\usingnamespace;
|
||||
\\foo+
|
||||
\\bar@,
|
||||
\\while (a == 2) { test "" {}}
|
||||
\\test "" {
|
||||
\\ a && b
|
||||
\\}
|
||||
, &[_]Error{
|
||||
.expected_expr,
|
||||
.expected_token,
|
||||
.expected_container_members,
|
||||
.invalid_and,
|
||||
.expected_token,
|
||||
});
|
||||
}
|
||||
|
||||
//test "recovery: invalid parameter" {
|
||||
// try testError(
|
||||
|
||||
Reference in New Issue
Block a user