Commit 0cc8435a83

mlugg <mlugg@mlugg.co.uk>
2024-04-29 03:25:54
std.zig: resolve syntactic ambiguity
The parse of `fn foo(a: switch (...) { ... })` was previously handled incorrectly; `a` was treated as both the parameter name and a label. The same issue exists for `for` and `while` expressions -- they should be fixed too, and the grammar amended appropriately. This commit does not do this: it only aims to avoid introducing regressions from labeled switch syntax.
1 parent 5e12ca9
Changed files (2)
lib
lib/std/zig/Ast.zig
@@ -1890,11 +1890,20 @@ pub fn taggedUnionEnumTag(tree: Ast, node: Node.Index) full.ContainerDecl {
 
 pub fn switchFull(tree: Ast, node: Node.Index) full.Switch {
     const data = &tree.nodes.items(.data)[node];
-    return tree.fullSwitchComponents(.{
-        .switch_token = tree.nodes.items(.main_token)[node],
-        .condition = data.lhs,
-        .sub_range = data.rhs,
-    });
+    const main_token = tree.nodes.items(.main_token)[node];
+    const switch_token: TokenIndex, const label_token: ?TokenIndex = switch (tree.tokens.items(.tag)[main_token]) {
+        .identifier => .{ main_token + 2, main_token },
+        .keyword_switch => .{ main_token, null },
+        else => unreachable,
+    };
+    return .{
+        .ast = .{
+            .switch_token = switch_token,
+            .condition = data.lhs,
+            .sub_range = data.rhs,
+        },
+        .label_token = label_token,
+    };
 }
 
 pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
@@ -3278,6 +3287,7 @@ pub const Node = struct {
         /// main_token is the `(`.
         async_call_comma,
         /// `switch(lhs) {}`. `SubRange[rhs]`.
+        /// `main_token` is the identifier of a preceding label, if any; otherwise `switch`.
         @"switch",
         /// Same as switch except there is known to be a trailing comma
         /// before the final rbrace
lib/std/zig/Parse.zig
@@ -1245,7 +1245,7 @@ fn parseLabeledStatement(p: *Parse) !Node.Index {
     const loop_stmt = try p.parseLoopStatement();
     if (loop_stmt != 0) return loop_stmt;
 
-    const switch_expr = try p.parseSwitchExpr();
+    const switch_expr = try p.parseSwitchExpr(label_token != 0);
     if (switch_expr != 0) return switch_expr;
 
     if (label_token != 0) {
@@ -2699,7 +2699,7 @@ fn parsePrimaryTypeExpr(p: *Parse) !Node.Index {
         .builtin => return p.parseBuiltinCall(),
         .keyword_fn => return p.parseFnProto(),
         .keyword_if => return p.parseIf(expectTypeExpr),
-        .keyword_switch => return p.expectSwitchExpr(),
+        .keyword_switch => return p.expectSwitchExpr(false),
 
         .keyword_extern,
         .keyword_packed,
@@ -2756,7 +2756,7 @@ fn parsePrimaryTypeExpr(p: *Parse) !Node.Index {
                 },
                 .keyword_switch => {
                     p.tok_i += 2;
-                    return p.expectSwitchExpr();
+                    return p.expectSwitchExpr(true);
                 },
                 .l_brace => {
                     p.tok_i += 2;
@@ -3034,17 +3034,17 @@ fn parseWhileTypeExpr(p: *Parse) !Node.Index {
 }
 
 /// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE
-fn parseSwitchExpr(p: *Parse) !Node.Index {
+fn parseSwitchExpr(p: *Parse, is_labeled: bool) !Node.Index {
     const switch_token = p.eatToken(.keyword_switch) orelse return null_node;
-    return p.expectSwitchSuffix(switch_token);
+    return p.expectSwitchSuffix(if (is_labeled) switch_token - 2 else switch_token);
 }
 
-fn expectSwitchExpr(p: *Parse) !Node.Index {
+fn expectSwitchExpr(p: *Parse, is_labeled: bool) !Node.Index {
     const switch_token = p.assertToken(.keyword_switch);
-    return p.expectSwitchSuffix(switch_token);
+    return p.expectSwitchSuffix(if (is_labeled) switch_token - 2 else switch_token);
 }
 
-fn expectSwitchSuffix(p: *Parse, switch_token: TokenIndex) !Node.Index {
+fn expectSwitchSuffix(p: *Parse, main_token: TokenIndex) !Node.Index {
     _ = try p.expectToken(.l_paren);
     const expr_node = try p.expectExpr();
     _ = try p.expectToken(.r_paren);
@@ -3055,7 +3055,7 @@ fn expectSwitchSuffix(p: *Parse, switch_token: TokenIndex) !Node.Index {
 
     return p.addNode(.{
         .tag = if (trailing_comma) .switch_comma else .@"switch",
-        .main_token = switch_token,
+        .main_token = main_token,
         .data = .{
             .lhs = expr_node,
             .rhs = try p.addExtra(Node.SubRange{