diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 3282497db4..5501352084 100644 --- a/lib/std/zig/Ast.zig +++ b/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, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 313b876278..46d2b0f49e 100644 --- a/lib/std/zig/parse.zig +++ b/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); diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index e5932ddfc3..9bf7ae8da9 100644 --- a/lib/std/zig/parser_test.zig +++ b/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; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 967ec1e9ec..650f54ae3f 100644 --- a/test/compile_errors.zig +++ b/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",