Commit 92f2767814

Veikka Tuominen <git@vexu.eu>
2022-02-17 13:22:34
parser: add error for missing colon before continue expr
If a '(' is found where the continue expression was expected and it is on the same line as the previous token issue an error about missing colon before the continue expression.
1 parent c9dde10
Changed files (4)
lib/std/zig/Ast.zig
@@ -300,6 +300,9 @@ pub fn renderError(tree: Ast, parse_error: Error, stream: anytype) !void {
         .varargs_nonfinal => {
             return stream.writeAll("function prototype has parameter after varargs");
         },
+        .expected_continue_expr => {
+            return stream.writeAll("expected ':' before while continue expression");
+        },
 
         .expected_semi_after_decl => {
             return stream.writeAll("expected ';' after declaration");
@@ -2467,6 +2470,7 @@ pub const full = struct {
 
 pub const Error = struct {
     tag: Tag,
+    /// True if `token` points to the token before the token causing an issue.
     token_is_prev: bool = false,
     token: TokenIndex,
     extra: union {
@@ -2513,8 +2517,7 @@ pub const Error = struct {
         same_line_doc_comment,
         unattached_doc_comment,
         varargs_nonfinal,
-
-        // these have `token` set to token after which a semicolon was expected
+        expected_continue_expr,
         expected_semi_after_decl,
         expected_semi_after_stmt,
         expected_comma_after_field,
lib/std/zig/parse.zig
@@ -2943,7 +2943,12 @@ const Parser = struct {
 
     /// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN
     fn parseWhileContinueExpr(p: *Parser) !Node.Index {
-        _ = p.eatToken(.colon) orelse return null_node;
+        _ = p.eatToken(.colon) orelse {
+            if (p.token_tags[p.tok_i] == .l_paren and
+                p.tokensOnSameLine(p.tok_i - 1, p.tok_i))
+                return p.fail(.expected_continue_expr);
+            return null_node;
+        };
         _ = try p.expectToken(.l_paren);
         const node = try p.parseAssignExpr();
         if (node == 0) return p.fail(.expected_expr_or_assignment);
lib/std/zig/parser_test.zig
@@ -5018,6 +5018,25 @@ test "zig fmt: make single-line if no trailing comma" {
     );
 }
 
+test "zig fmt: while continue expr" {
+    try testCanonical(
+        \\test {
+        \\    while (i > 0)
+        \\        (i * 2);
+        \\}
+        \\
+    );
+    try testError(
+        \\test {
+        \\    while (i > 0) (i -= 1) {
+        \\        print("test123", .{});
+        \\    }
+        \\}
+    , &[_]Error{
+        .expected_continue_expr,
+    });
+}
+
 test "zig fmt: error for invalid bit range" {
     try testError(
         \\var x: []align(0:0:0)u8 = bar;
test/compile_errors.zig
@@ -4869,11 +4869,11 @@ pub fn addCases(ctx: *TestContext) !void {
         \\export fn entry() void {
         \\    while(true) {}
         \\    var good = {};
-        \\    while(true) ({})
+        \\    while(true) 1
         \\    var bad = {};
         \\}
     , &[_][]const u8{
-        "tmp.zig:4:21: error: expected ';' or 'else' after statement",
+        "tmp.zig:4:18: error: expected ';' or 'else' after statement",
     });
 
     ctx.objErrStage1("implicit semicolon - while expression",