Commit 34be5784a3

Veikka Tuominen <git@vexu.eu>
2022-11-26 21:40:43
parser: disallow defer and variable declaration as else branch
Closes #13658
1 parent 1829b6e
Changed files (2)
lib/std/zig/parse.zig
@@ -950,13 +950,15 @@ const Parser = struct {
     ///      / LabeledStatement
     ///      / SwitchExpr
     ///      / AssignExpr SEMICOLON
-    fn parseStatement(p: *Parser) Error!Node.Index {
+    fn parseStatement(p: *Parser, allow_defer_var: bool) Error!Node.Index {
         const comptime_token = p.eatToken(.keyword_comptime);
 
-        const var_decl = try p.parseVarDecl();
-        if (var_decl != 0) {
-            try p.expectSemicolon(.expected_semi_after_decl, true);
-            return var_decl;
+        if (allow_defer_var) {
+            const var_decl = try p.parseVarDecl();
+            if (var_decl != 0) {
+                try p.expectSemicolon(.expected_semi_after_decl, true);
+                return var_decl;
+            }
         }
 
         if (comptime_token) |token| {
@@ -993,7 +995,7 @@ const Parser = struct {
                     },
                 });
             },
-            .keyword_defer => return p.addNode(.{
+            .keyword_defer => if (allow_defer_var) return p.addNode(.{
                 .tag = .@"defer",
                 .main_token = p.nextToken(),
                 .data = .{
@@ -1001,7 +1003,7 @@ const Parser = struct {
                     .rhs = try p.expectBlockExprStatement(),
                 },
             }),
-            .keyword_errdefer => return p.addNode(.{
+            .keyword_errdefer => if (allow_defer_var) return p.addNode(.{
                 .tag = .@"errdefer",
                 .main_token = p.nextToken(),
                 .data = .{
@@ -1040,8 +1042,8 @@ const Parser = struct {
         return null_node;
     }
 
-    fn expectStatement(p: *Parser) !Node.Index {
-        const statement = try p.parseStatement();
+    fn expectStatement(p: *Parser, allow_defer_var: bool) !Node.Index {
+        const statement = try p.parseStatement(allow_defer_var);
         if (statement == 0) {
             return p.fail(.expected_statement);
         }
@@ -1053,7 +1055,7 @@ const Parser = struct {
     /// statement, returns 0.
     fn expectStatementRecoverable(p: *Parser) Error!Node.Index {
         while (true) {
-            return p.expectStatement() catch |err| switch (err) {
+            return p.expectStatement(true) catch |err| switch (err) {
                 error.OutOfMemory => return error.OutOfMemory,
                 error.ParseError => {
                     p.findNextStmt(); // Try to skip to the next statement.
@@ -1114,7 +1116,7 @@ const Parser = struct {
             });
         };
         _ = try p.parsePayload();
-        const else_expr = try p.expectStatement();
+        const else_expr = try p.expectStatement(false);
         return p.addNode(.{
             .tag = .@"if",
             .main_token = if_token,
@@ -1226,7 +1228,7 @@ const Parser = struct {
                 .lhs = array_expr,
                 .rhs = try p.addExtra(Node.If{
                     .then_expr = then_expr,
-                    .else_expr = try p.expectStatement(),
+                    .else_expr = try p.expectStatement(false),
                 }),
             },
         });
@@ -1309,7 +1311,7 @@ const Parser = struct {
             }
         };
         _ = try p.parsePayload();
-        const else_expr = try p.expectStatement();
+        const else_expr = try p.expectStatement(false);
         return p.addNode(.{
             .tag = .@"while",
             .main_token = while_token,
lib/std/zig/parser_test.zig
@@ -4233,6 +4233,30 @@ test "zig fmt: remove newlines surrounding doc comment within container decl" {
     );
 }
 
+test "zig fmt: invalid else branch statement" {
+    try testError(
+        \\comptime {
+        \\    if (true) {} else var a = 0;
+        \\    if (true) {} else defer {}
+        \\}
+        \\comptime {
+        \\    while (true) {} else var a = 0;
+        \\    while (true) {} else defer {}
+        \\}
+        \\comptime {
+        \\    for ("") |_| {} else var a = 0;
+        \\    for ("") |_| {} else defer {}
+        \\}
+    , &[_]Error{
+        .expected_statement,
+        .expected_statement,
+        .expected_statement,
+        .expected_statement,
+        .expected_statement,
+        .expected_statement,
+    });
+}
+
 test "zig fmt: anytype struct field" {
     try testError(
         \\pub const Pointer = struct {