authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-05-13 23:28:04+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-05-13 23:28:04+03:00
logad71d959d7d1b5f3a771dc2c1eaf37dbd7cd0852
tree6d039ebd3f9df24e0b2f9d7acc2b6bdd50e7859e
parent2296906e2ad54c387b4b19784148c47a26969cdc
signature Commit is signed but in an unrecognized format.

correctly recover from invalid top level declarations


2 files changed, 35 insertions(+), 7 deletions(-)

lib/std/zig/parse.zig+13-7
...@@ -130,7 +130,13 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) All...@@ -130,7 +130,13 @@ fn parseContainerMembers(arena: *Allocator, it: *TokenIterator, tree: *Tree) All
130130
131 const visib_token = eatToken(it, .Keyword_pub);131 const visib_token = eatToken(it, .Keyword_pub);
132132
133 if (try parseTopLevelDecl(arena, it, tree)) |node| {133 if (parseTopLevelDecl(arena, it, tree) catch |err| switch (err) {
134 error.OutOfMemory => return error.OutOfMemory,
135 error.ParseError => {
136 // try again
137 continue;
138 },
139 }) |node| {
134 if (field_state == .seen) {140 if (field_state == .seen) {
135 field_state = .{ .end = visib_token orelse node.firstToken() };141 field_state = .{ .end = visib_token orelse node.firstToken() };
136 }142 }
...@@ -315,7 +321,7 @@ fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*...@@ -315,7 +321,7 @@ fn parseTopLevelComptime(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*
315/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)321/// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
316/// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl322/// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl
317/// / KEYWORD_usingnamespace Expr SEMICOLON323/// / KEYWORD_usingnamespace Expr SEMICOLON
318fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocator.Error!?*Node {324fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Error!?*Node {
319 var lib_name: ?*Node = null;325 var lib_name: ?*Node = null;
320 const extern_export_inline_token = blk: {326 const extern_export_inline_token = blk: {
321 if (eatToken(it, .Keyword_export)) |token| break :blk token;327 if (eatToken(it, .Keyword_export)) |token| break :blk token;
...@@ -334,7 +340,7 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocat...@@ -334,7 +340,7 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocat
334 // this fn will likely have a body so we340 // this fn will likely have a body so we
335 // use findEndOfBlock instead of findToken.341 // use findEndOfBlock instead of findToken.
336 findEndOfBlock(it);342 findEndOfBlock(it);
337 return null;343 return error.ParseError;
338 },344 },
339 }) |node| {345 }) |node| {
340 const fn_node = node.cast(Node.FnProto).?;346 const fn_node = node.cast(Node.FnProto).?;
...@@ -373,7 +379,7 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocat...@@ -373,7 +379,7 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocat
373 error.ParseError => {379 error.ParseError => {
374 // try to skip to next decl380 // try to skip to next decl
375 findToken(it, .Semicolon);381 findToken(it, .Semicolon);
376 return null;382 return error.ParseError;
377 },383 },
378 }) |node| {384 }) |node| {
379 var var_decl = node.cast(Node.VarDecl).?;385 var var_decl = node.cast(Node.VarDecl).?;
...@@ -388,8 +394,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocat...@@ -388,8 +394,8 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocat
388 try tree.errors.push(.{394 try tree.errors.push(.{
389 .ExpectedVarDecl = .{ .token = it.index },395 .ExpectedVarDecl = .{ .token = it.index },
390 });396 });
391 // ignore this, try to find next decl by skipping the next block397 // ignore this and try again;
392 findEndOfBlock(it);398 return error.ParseError;
393 }399 }
394400
395 if (extern_export_inline_token) |token| {401 if (extern_export_inline_token) |token| {
...@@ -404,7 +410,7 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocat...@@ -404,7 +410,7 @@ fn parseTopLevelDecl(arena: *Allocator, it: *TokenIterator, tree: *Tree) Allocat
404 error.ParseError => {410 error.ParseError => {
405 // try to skip to next decl411 // try to skip to next decl
406 findToken(it, .Semicolon);412 findToken(it, .Semicolon);
407 return null;413 return error.ParseError;
408 },414 },
409 };415 };
410}416}
lib/std/zig/parser_test.zig+22
...@@ -62,6 +62,28 @@ test "recovery: missing return type" {...@@ -62,6 +62,28 @@ test "recovery: missing return type" {
62 });62 });
63}63}
6464
65test "recovery: continue after invalid decl" {
66 try testError(
67 \\fn foo {
68 \\ inline;
69 \\}
70 \\test "" {
71 \\ a && b;
72 \\}
73 , &[_]Error{
74 .ExpectedToken,
75 .InvalidAnd,
76 });
77 try testError(
78 \\threadlocal test "" {
79 \\ a && b;
80 \\}
81 , &[_]Error{
82 .ExpectedVarDecl,
83 .InvalidAnd,
84 });
85}
86
65test "zig fmt: top-level fields" {87test "zig fmt: top-level fields" {
66 try testCanonical(88 try testCanonical(
67 \\a: did_you_know,89 \\a: did_you_know,