Commit 6dcd8f4f75

Techatrix <techatrix@mailbox.org>
2025-01-03 05:31:56
std.zig.Ast: add `blockStatements` and `builtinCallParams`
1 parent de9c889
Changed files (7)
lib/compiler/reduce/Walk.zig
@@ -230,20 +230,11 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
 
         .block_two,
         .block_two_semicolon,
-        => {
-            const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
-            if (datas[node].lhs == 0) {
-                return walkBlock(w, node, statements[0..0]);
-            } else if (datas[node].rhs == 0) {
-                return walkBlock(w, node, statements[0..1]);
-            } else {
-                return walkBlock(w, node, statements[0..2]);
-            }
-        },
         .block,
         .block_semicolon,
         => {
-            const statements = ast.extra_data[datas[node].lhs..datas[node].rhs];
+            var buf: [2]Ast.Node.Index = undefined;
+            const statements = ast.blockStatements(&buf, node).?;
             return walkBlock(w, node, statements);
         },
 
@@ -506,17 +497,13 @@ fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
             }
         },
 
-        .builtin_call_two, .builtin_call_two_comma => {
-            if (datas[node].lhs == 0) {
-                return walkBuiltinCall(w, node, &.{});
-            } else if (datas[node].rhs == 0) {
-                return walkBuiltinCall(w, node, &.{datas[node].lhs});
-            } else {
-                return walkBuiltinCall(w, node, &.{ datas[node].lhs, datas[node].rhs });
-            }
-        },
-        .builtin_call, .builtin_call_comma => {
-            const params = ast.extra_data[datas[node].lhs..datas[node].rhs];
+        .builtin_call_two,
+        .builtin_call_two_comma,
+        .builtin_call,
+        .builtin_call_comma,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const params = ast.builtinCallParams(&buf, node).?;
             return walkBuiltinCall(w, node, params);
         },
 
@@ -972,24 +959,13 @@ fn walkParamList(w: *Walk, params: []const Ast.Node.Index) Error!void {
 fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool {
     // skip over discards
     const node_tags = ast.nodes.items(.tag);
-    const datas = ast.nodes.items(.data);
     var statements_buf: [2]Ast.Node.Index = undefined;
     const statements = switch (node_tags[body_node]) {
         .block_two,
         .block_two_semicolon,
-        => blk: {
-            statements_buf[0..2].* = .{ datas[body_node].lhs, datas[body_node].rhs };
-            break :blk if (datas[body_node].lhs == 0)
-                statements_buf[0..0]
-            else if (datas[body_node].rhs == 0)
-                statements_buf[0..1]
-            else
-                statements_buf[0..2];
-        },
-
         .block,
         .block_semicolon,
-        => ast.extra_data[datas[body_node].lhs..datas[body_node].rhs],
+        => ast.blockStatements(&statements_buf, body_node).?,
 
         else => return false,
     };
@@ -1016,17 +992,13 @@ fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory {
     const datas = ast.nodes.items(.data);
     const main_tokens = ast.nodes.items(.main_token);
     switch (node_tags[stmt]) {
-        .builtin_call_two, .builtin_call_two_comma => {
-            if (datas[stmt].lhs == 0) {
-                return categorizeBuiltinCall(ast, main_tokens[stmt], &.{});
-            } else if (datas[stmt].rhs == 0) {
-                return categorizeBuiltinCall(ast, main_tokens[stmt], &.{datas[stmt].lhs});
-            } else {
-                return categorizeBuiltinCall(ast, main_tokens[stmt], &.{ datas[stmt].lhs, datas[stmt].rhs });
-            }
-        },
-        .builtin_call, .builtin_call_comma => {
-            const params = ast.extra_data[datas[stmt].lhs..datas[stmt].rhs];
+        .builtin_call_two,
+        .builtin_call_two_comma,
+        .builtin_call,
+        .builtin_call_comma,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const params = ast.builtinCallParams(&buf, stmt).?;
             return categorizeBuiltinCall(ast, main_tokens[stmt], params);
         },
         .assign => {
lib/docs/wasm/Walk.zig
@@ -232,20 +232,13 @@ pub const File = struct {
                     return .{ .global_const = node };
                 },
 
-                .builtin_call_two, .builtin_call_two_comma => {
-                    if (node_datas[node].lhs == 0) {
-                        const params = [_]Ast.Node.Index{};
-                        return categorize_builtin_call(file_index, node, &params);
-                    } else if (node_datas[node].rhs == 0) {
-                        const params = [_]Ast.Node.Index{node_datas[node].lhs};
-                        return categorize_builtin_call(file_index, node, &params);
-                    } else {
-                        const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
-                        return categorize_builtin_call(file_index, node, &params);
-                    }
-                },
-                .builtin_call, .builtin_call_comma => {
-                    const params = ast.extra_data[node_datas[node].lhs..node_datas[node].rhs];
+                .builtin_call_two,
+                .builtin_call_two_comma,
+                .builtin_call,
+                .builtin_call_comma,
+                => {
+                    var buf: [2]Ast.Node.Index = undefined;
+                    const params = ast.builtinCallParams(&buf, node).?;
                     return categorize_builtin_call(file_index, node, params);
                 },
 
@@ -818,20 +811,13 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
             try expr(w, scope, parent_decl, full.ast.template);
         },
 
-        .builtin_call_two, .builtin_call_two_comma => {
-            if (node_datas[node].lhs == 0) {
-                const params = [_]Ast.Node.Index{};
-                return builtin_call(w, scope, parent_decl, node, &params);
-            } else if (node_datas[node].rhs == 0) {
-                const params = [_]Ast.Node.Index{node_datas[node].lhs};
-                return builtin_call(w, scope, parent_decl, node, &params);
-            } else {
-                const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
-                return builtin_call(w, scope, parent_decl, node, &params);
-            }
-        },
-        .builtin_call, .builtin_call_comma => {
-            const params = ast.extra_data[node_datas[node].lhs..node_datas[node].rhs];
+        .builtin_call_two,
+        .builtin_call_two_comma,
+        .builtin_call,
+        .builtin_call_comma,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const params = ast.builtinCallParams(&buf, node).?;
             return builtin_call(w, scope, parent_decl, node, params);
         },
 
@@ -886,18 +872,13 @@ fn expr(w: *Walk, scope: *Scope, parent_decl: Decl.Index, node: Ast.Node.Index)
         .slice_open => return slice(w, scope, parent_decl, ast.sliceOpen(node)),
         .slice_sentinel => return slice(w, scope, parent_decl, ast.sliceSentinel(node)),
 
-        .block_two, .block_two_semicolon => {
-            const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
-            if (node_datas[node].lhs == 0) {
-                return block(w, scope, parent_decl, statements[0..0]);
-            } else if (node_datas[node].rhs == 0) {
-                return block(w, scope, parent_decl, statements[0..1]);
-            } else {
-                return block(w, scope, parent_decl, statements[0..2]);
-            }
-        },
-        .block, .block_semicolon => {
-            const statements = ast.extra_data[node_datas[node].lhs..node_datas[node].rhs];
+        .block_two,
+        .block_two_semicolon,
+        .block,
+        .block_semicolon,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const statements = ast.blockStatements(&buf, node).?;
             return block(w, scope, parent_decl, statements);
         },
 
lib/std/zig/Ast.zig
@@ -2432,6 +2432,42 @@ pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.C
     };
 }
 
+pub fn builtinCallParams(tree: Ast, buffer: *[2]Ast.Node.Index, node: Ast.Node.Index) ?[]const Node.Index {
+    const data = tree.nodes.items(.data)[node];
+    return switch (tree.nodes.items(.tag)[node]) {
+        .builtin_call_two, .builtin_call_two_comma => {
+            buffer.* = .{ data.lhs, data.rhs };
+            if (data.rhs != 0) {
+                return buffer[0..2];
+            } else if (data.lhs != 0) {
+                return buffer[0..1];
+            } else {
+                return buffer[0..0];
+            }
+        },
+        .builtin_call, .builtin_call_comma => tree.extra_data[data.lhs..data.rhs],
+        else => null,
+    };
+}
+
+pub fn blockStatements(tree: Ast, buffer: *[2]Ast.Node.Index, node: Ast.Node.Index) ?[]const Node.Index {
+    const data = tree.nodes.items(.data)[node];
+    return switch (tree.nodes.items(.tag)[node]) {
+        .block_two, .block_two_semicolon => {
+            buffer.* = .{ data.lhs, data.rhs };
+            if (data.rhs != 0) {
+                return buffer[0..2];
+            } else if (data.lhs != 0) {
+                return buffer[0..1];
+            } else {
+                return buffer[0..0];
+            }
+        },
+        .block, .block_semicolon => tree.extra_data[data.lhs..data.rhs],
+        else => null,
+    };
+}
+
 /// Fully assembled AST node information.
 pub const full = struct {
     pub const VarDecl = struct {
lib/std/zig/AstGen.zig
@@ -824,20 +824,13 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
         .number_literal => return numberLiteral(gz, ri, node, node, .positive),
         // zig fmt: on
 
-        .builtin_call_two, .builtin_call_two_comma => {
-            if (node_datas[node].lhs == 0) {
-                const params = [_]Ast.Node.Index{};
-                return builtinCall(gz, scope, ri, node, &params, false);
-            } else if (node_datas[node].rhs == 0) {
-                const params = [_]Ast.Node.Index{node_datas[node].lhs};
-                return builtinCall(gz, scope, ri, node, &params, false);
-            } else {
-                const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
-                return builtinCall(gz, scope, ri, node, &params, false);
-            }
-        },
-        .builtin_call, .builtin_call_comma => {
-            const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
+        .builtin_call_two,
+        .builtin_call_two_comma,
+        .builtin_call,
+        .builtin_call_comma,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const params = tree.builtinCallParams(&buf, node).?;
             return builtinCall(gz, scope, ri, node, params, false);
         },
 
@@ -991,18 +984,13 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
                 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node);
             },
         },
-        .block_two, .block_two_semicolon => {
-            const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
-            if (node_datas[node].lhs == 0) {
-                return blockExpr(gz, scope, ri, node, statements[0..0], .normal);
-            } else if (node_datas[node].rhs == 0) {
-                return blockExpr(gz, scope, ri, node, statements[0..1], .normal);
-            } else {
-                return blockExpr(gz, scope, ri, node, statements[0..2], .normal);
-            }
-        },
-        .block, .block_semicolon => {
-            const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
+        .block_two,
+        .block_two_semicolon,
+        .block,
+        .block_semicolon,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const statements = tree.blockStatements(&buf, node).?;
             return blockExpr(gz, scope, ri, node, statements, .normal);
         },
         .enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| {
@@ -2080,28 +2068,11 @@ fn comptimeExpr2(
             if (token_tags[lbrace - 1] == .colon and
                 token_tags[lbrace - 2] == .identifier)
             {
-                const node_datas = tree.nodes.items(.data);
-                switch (node_tags[node]) {
-                    .block_two, .block_two_semicolon => {
-                        const stmts: [2]Ast.Node.Index = .{ node_datas[node].lhs, node_datas[node].rhs };
-                        const stmt_slice = if (stmts[0] == 0)
-                            stmts[0..0]
-                        else if (stmts[1] == 0)
-                            stmts[0..1]
-                        else
-                            stmts[0..2];
-
-                        const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true, .normal);
-                        return rvalue(gz, ri, block_ref, node);
-                    },
-                    .block, .block_semicolon => {
-                        const stmts = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
-                        // Replace result location and copy back later - see above.
-                        const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal);
-                        return rvalue(gz, ri, block_ref, node);
-                    },
-                    else => unreachable,
-                }
+                var buf: [2]Ast.Node.Index = undefined;
+                const stmts = tree.blockStatements(&buf, node).?;
+                // Replace result location and copy back later - see above.
+                const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal);
+                return rvalue(gz, ri, block_ref, node);
             }
         },
 
@@ -2402,25 +2373,10 @@ fn fullBodyExpr(
     block_kind: BlockKind,
 ) InnerError!Zir.Inst.Ref {
     const tree = gz.astgen.tree;
-    const node_tags = tree.nodes.items(.tag);
-    const node_datas = tree.nodes.items(.data);
     const main_tokens = tree.nodes.items(.main_token);
     const token_tags = tree.tokens.items(.tag);
     var stmt_buf: [2]Ast.Node.Index = undefined;
-    const statements: []const Ast.Node.Index = switch (node_tags[node]) {
-        else => return expr(gz, scope, ri, node),
-        .block_two, .block_two_semicolon => if (node_datas[node].lhs == 0) s: {
-            break :s &.{};
-        } else if (node_datas[node].rhs == 0) s: {
-            stmt_buf[0] = node_datas[node].lhs;
-            break :s stmt_buf[0..1];
-        } else s: {
-            stmt_buf[0] = node_datas[node].lhs;
-            stmt_buf[1] = node_datas[node].rhs;
-            break :s stmt_buf[0..2];
-        },
-        .block, .block_semicolon => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
-    };
+    const statements = tree.blockStatements(&stmt_buf, node).?;
 
     const lbrace = main_tokens[node];
     if (token_tags[lbrace - 1] == .colon and
@@ -2671,33 +2627,23 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
 
                 .for_simple,
                 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
+                // zig fmt: on
 
                 // These cases are here to allow branch hints.
-                .builtin_call_two, .builtin_call_two_comma => {
-                    try emitDbgNode(gz, inner_node);
-                    const ri: ResultInfo = .{ .rl = .none };
-                    const result = if (node_data[inner_node].lhs == 0) r: {
-                        break :r try builtinCall(gz, scope, ri, inner_node, &.{}, allow_branch_hint);
-                    } else if (node_data[inner_node].rhs == 0) r: {
-                        break :r try builtinCall(gz, scope, ri, inner_node, &.{node_data[inner_node].lhs}, allow_branch_hint);
-                    } else r: {
-                        break :r try builtinCall(gz, scope, ri, inner_node, &.{
-                            node_data[inner_node].lhs,
-                            node_data[inner_node].rhs,
-                        }, allow_branch_hint);
-                    };
-                    noreturn_src_node = try addEnsureResult(gz, result, inner_node);
-                },
-                .builtin_call, .builtin_call_comma => {
+                .builtin_call_two,
+                .builtin_call_two_comma,
+                .builtin_call,
+                .builtin_call_comma,
+                => {
+                    var buf: [2]Ast.Node.Index = undefined;
+                    const params = tree.builtinCallParams(&buf, inner_node).?;
+
                     try emitDbgNode(gz, inner_node);
-                    const ri: ResultInfo = .{ .rl = .none };
-                    const params = tree.extra_data[node_data[inner_node].lhs..node_data[inner_node].rhs];
-                    const result = try builtinCall(gz, scope, ri, inner_node, params, allow_branch_hint);
+                    const result = try builtinCall(gz, scope, .{ .rl = .none }, inner_node, params, allow_branch_hint);
                     noreturn_src_node = try addEnsureResult(gz, result, inner_node);
                 },
 
                 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
-                // zig fmt: on
             }
             break;
         }
@@ -9194,13 +9140,14 @@ fn ptrCast(
             else => break,
         }
 
-        if (node_datas[node].lhs == 0) break; // 0 args
+        var buf: [2]Ast.Node.Index = undefined;
+        const args = tree.builtinCallParams(&buf, node).?;
+        std.debug.assert(args.len <= 2);
 
         const builtin_token = main_tokens[node];
         const builtin_name = tree.tokenSlice(builtin_token);
         const info = BuiltinFn.list.get(builtin_name) orelse break;
-        if (node_datas[node].rhs == 0) {
-            // 1 arg
+        if (args.len == 1) {
             if (info.param_count != 1) break;
 
             switch (info.tag) {
@@ -9218,9 +9165,9 @@ fn ptrCast(
                 },
             }
 
-            node = node_datas[node].lhs;
+            node = args[0];
         } else {
-            // 2 args
+            std.debug.assert(args.len == 2);
             if (info.param_count != 2) break;
 
             switch (info.tag) {
@@ -9231,8 +9178,8 @@ fn ptrCast(
                     const flags_int: FlagsInt = @bitCast(flags);
                     const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
                     const parent_ptr_type = try ri.rl.resultTypeForCast(gz, root_node, "@alignCast");
-                    const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, node_datas[node].lhs, .field_name);
-                    const field_ptr = try expr(gz, scope, .{ .rl = .none }, node_datas[node].rhs);
+                    const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, args[0], .field_name);
+                    const field_ptr = try expr(gz, scope, .{ .rl = .none }, args[1]);
                     try emitDbgStmt(gz, cursor);
                     const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, flags_int, Zir.Inst.FieldParentPtr{
                         .src_node = gz.nodeIndexToRelative(node),
lib/std/zig/AstRlAnnotate.zig
@@ -313,17 +313,13 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
         .error_set_decl,
         => return false,
 
-        .builtin_call_two, .builtin_call_two_comma => {
-            if (node_datas[node].lhs == 0) {
-                return astrl.builtinCall(block, ri, node, &.{});
-            } else if (node_datas[node].rhs == 0) {
-                return astrl.builtinCall(block, ri, node, &.{node_datas[node].lhs});
-            } else {
-                return astrl.builtinCall(block, ri, node, &.{ node_datas[node].lhs, node_datas[node].rhs });
-            }
-        },
-        .builtin_call, .builtin_call_comma => {
-            const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
+        .builtin_call_two,
+        .builtin_call_two_comma,
+        .builtin_call,
+        .builtin_call_comma,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const params = tree.builtinCallParams(&buf, node).?;
             return astrl.builtinCall(block, ri, node, params);
         },
 
@@ -499,17 +495,13 @@ fn expr(astrl: *AstRlAnnotate, node: Ast.Node.Index, block: ?*Block, ri: ResultI
         .unwrap_optional,
         => return astrl.expr(node_datas[node].lhs, block, ri),
 
-        .block_two, .block_two_semicolon => {
-            if (node_datas[node].lhs == 0) {
-                return astrl.blockExpr(block, ri, node, &.{});
-            } else if (node_datas[node].rhs == 0) {
-                return astrl.blockExpr(block, ri, node, &.{node_datas[node].lhs});
-            } else {
-                return astrl.blockExpr(block, ri, node, &.{ node_datas[node].lhs, node_datas[node].rhs });
-            }
-        },
-        .block, .block_semicolon => {
-            const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
+        .block_two,
+        .block_two_semicolon,
+        .block,
+        .block_semicolon,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const statements = tree.blockStatements(&buf, node).?;
             return astrl.blockExpr(block, ri, node, statements);
         },
         .anyframe_type => {
lib/std/zig/render.zig
@@ -141,7 +141,6 @@ fn renderMember(
 ) Error!void {
     const tree = r.tree;
     const ais = r.ais;
-    const node_tags = tree.nodes.items(.tag);
     const token_tags = tree.tokens.items(.tag);
     const main_tokens = tree.nodes.items(.main_token);
     const datas = tree.nodes.items(.data);
@@ -223,25 +222,7 @@ fn renderMember(
                     }
                 }
                 var statements_buf: [2]Ast.Node.Index = undefined;
-                const statements = switch (node_tags[body_node]) {
-                    .block_two,
-                    .block_two_semicolon,
-                    => b: {
-                        statements_buf = .{ datas[body_node].lhs, datas[body_node].rhs };
-                        if (datas[body_node].lhs == 0) {
-                            break :b statements_buf[0..0];
-                        } else if (datas[body_node].rhs == 0) {
-                            break :b statements_buf[0..1];
-                        } else {
-                            break :b statements_buf[0..2];
-                        }
-                    },
-                    .block,
-                    .block_semicolon,
-                    => tree.extra_data[datas[body_node].lhs..datas[body_node].rhs],
-
-                    else => unreachable,
-                };
+                const statements = tree.blockStatements(&statements_buf, body_node).?;
                 return finishRenderBlock(r, body_node, statements, space);
             } else {
                 return renderExpression(r, body_node, space);
@@ -394,20 +375,11 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
 
         .block_two,
         .block_two_semicolon,
-        => {
-            const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
-            if (datas[node].lhs == 0) {
-                return renderBlock(r, node, statements[0..0], space);
-            } else if (datas[node].rhs == 0) {
-                return renderBlock(r, node, statements[0..1], space);
-            } else {
-                return renderBlock(r, node, statements[0..2], space);
-            }
-        },
         .block,
         .block_semicolon,
         => {
-            const statements = tree.extra_data[datas[node].lhs..datas[node].rhs];
+            var buf: [2]Ast.Node.Index = undefined;
+            const statements = tree.blockStatements(&buf, node).?;
             return renderBlock(r, node, statements, space);
         },
 
@@ -813,17 +785,13 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
             }
         },
 
-        .builtin_call_two, .builtin_call_two_comma => {
-            if (datas[node].lhs == 0) {
-                return renderBuiltinCall(r, main_tokens[node], &.{}, space);
-            } else if (datas[node].rhs == 0) {
-                return renderBuiltinCall(r, main_tokens[node], &.{datas[node].lhs}, space);
-            } else {
-                return renderBuiltinCall(r, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space);
-            }
-        },
-        .builtin_call, .builtin_call_comma => {
-            const params = tree.extra_data[datas[node].lhs..datas[node].rhs];
+        .builtin_call_two,
+        .builtin_call_two_comma,
+        .builtin_call,
+        .builtin_call_comma,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            const params = tree.builtinCallParams(&buf, node).?;
             return renderBuiltinCall(r, main_tokens[node], params, space);
         },
 
src/Zcu.zig
@@ -1162,19 +1162,10 @@ pub const SrcLoc = struct {
             },
             .node_offset_builtin_call_arg => |builtin_arg| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_datas = tree.nodes.items(.data);
-                const node_tags = tree.nodes.items(.tag);
                 const node = src_loc.relativeToNodeIndex(builtin_arg.builtin_call_node);
-                const param = switch (node_tags[node]) {
-                    .builtin_call_two, .builtin_call_two_comma => switch (builtin_arg.arg_index) {
-                        0 => node_datas[node].lhs,
-                        1 => node_datas[node].rhs,
-                        else => unreachable,
-                    },
-                    .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + builtin_arg.arg_index],
-                    else => unreachable,
-                };
-                return tree.nodeToSpan(param);
+                var buf: [2]Ast.Node.Index = undefined;
+                const params = tree.builtinCallParams(&buf, node).?;
+                return tree.nodeToSpan(params[builtin_arg.arg_index]);
             },
             .node_offset_ptrcast_operand => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
@@ -1855,14 +1846,10 @@ pub const SrcLoc = struct {
                     else => unreachable,
                 };
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_datas = tree.nodes.items(.data);
-                const node_tags = tree.nodes.items(.tag);
                 const node = src_loc.relativeToNodeIndex(builtin_call_node);
-                const arg_node = switch (node_tags[node]) {
-                    .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs,
-                    .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1],
-                    else => unreachable,
-                };
+                var builtin_buf: [2]Ast.Node.Index = undefined;
+                const args = tree.builtinCallParams(&builtin_buf, node).?;
+                const arg_node = args[1];
                 var buf: [2]Ast.Node.Index = undefined;
                 const full = tree.fullStructInit(&buf, arg_node) orelse
                     return tree.nodeToSpan(arg_node);