Commit 1f8f79cd53

Techatrix <19954306+Techatrix@users.noreply.github.com>
2023-01-09 15:59:19
std: add helper functions to std.zig.Ast for extracting data out of nodes
1 parent fcee1bf
Changed files (5)
lib/std/zig/Ast.zig
@@ -1243,7 +1243,7 @@ pub fn globalVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
     assert(tree.nodes.items(.tag)[node] == .global_var_decl);
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.lhs, Node.GlobalVarDecl);
-    return tree.fullVarDecl(.{
+    return tree.fullVarDeclComponents(.{
         .type_node = extra.type_node,
         .align_node = extra.align_node,
         .addrspace_node = extra.addrspace_node,
@@ -1257,7 +1257,7 @@ pub fn localVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
     assert(tree.nodes.items(.tag)[node] == .local_var_decl);
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.lhs, Node.LocalVarDecl);
-    return tree.fullVarDecl(.{
+    return tree.fullVarDeclComponents(.{
         .type_node = extra.type_node,
         .align_node = extra.align_node,
         .addrspace_node = 0,
@@ -1270,7 +1270,7 @@ pub fn localVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
 pub fn simpleVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
     assert(tree.nodes.items(.tag)[node] == .simple_var_decl);
     const data = tree.nodes.items(.data)[node];
-    return tree.fullVarDecl(.{
+    return tree.fullVarDeclComponents(.{
         .type_node = data.lhs,
         .align_node = 0,
         .addrspace_node = 0,
@@ -1283,7 +1283,7 @@ pub fn simpleVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
 pub fn alignedVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
     assert(tree.nodes.items(.tag)[node] == .aligned_var_decl);
     const data = tree.nodes.items(.data)[node];
-    return tree.fullVarDecl(.{
+    return tree.fullVarDeclComponents(.{
         .type_node = 0,
         .align_node = data.lhs,
         .addrspace_node = 0,
@@ -1296,7 +1296,7 @@ pub fn alignedVarDecl(tree: Ast, node: Node.Index) full.VarDecl {
 pub fn ifSimple(tree: Ast, node: Node.Index) full.If {
     assert(tree.nodes.items(.tag)[node] == .if_simple);
     const data = tree.nodes.items(.data)[node];
-    return tree.fullIf(.{
+    return tree.fullIfComponents(.{
         .cond_expr = data.lhs,
         .then_expr = data.rhs,
         .else_expr = 0,
@@ -1308,7 +1308,7 @@ pub fn ifFull(tree: Ast, node: Node.Index) full.If {
     assert(tree.nodes.items(.tag)[node] == .@"if");
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.rhs, Node.If);
-    return tree.fullIf(.{
+    return tree.fullIfComponents(.{
         .cond_expr = data.lhs,
         .then_expr = extra.then_expr,
         .else_expr = extra.else_expr,
@@ -1321,7 +1321,7 @@ pub fn containerField(tree: Ast, node: Node.Index) full.ContainerField {
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.rhs, Node.ContainerField);
     const main_token = tree.nodes.items(.main_token)[node];
-    return tree.fullContainerField(.{
+    return tree.fullContainerFieldComponents(.{
         .main_token = main_token,
         .type_expr = data.lhs,
         .value_expr = extra.value_expr,
@@ -1334,7 +1334,7 @@ pub fn containerFieldInit(tree: Ast, node: Node.Index) full.ContainerField {
     assert(tree.nodes.items(.tag)[node] == .container_field_init);
     const data = tree.nodes.items(.data)[node];
     const main_token = tree.nodes.items(.main_token)[node];
-    return tree.fullContainerField(.{
+    return tree.fullContainerFieldComponents(.{
         .main_token = main_token,
         .type_expr = data.lhs,
         .value_expr = data.rhs,
@@ -1347,7 +1347,7 @@ pub fn containerFieldAlign(tree: Ast, node: Node.Index) full.ContainerField {
     assert(tree.nodes.items(.tag)[node] == .container_field_align);
     const data = tree.nodes.items(.data)[node];
     const main_token = tree.nodes.items(.main_token)[node];
-    return tree.fullContainerField(.{
+    return tree.fullContainerFieldComponents(.{
         .main_token = main_token,
         .type_expr = data.lhs,
         .value_expr = 0,
@@ -1361,7 +1361,7 @@ pub fn fnProtoSimple(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.F
     const data = tree.nodes.items(.data)[node];
     buffer[0] = data.lhs;
     const params = if (data.lhs == 0) buffer[0..0] else buffer[0..1];
-    return tree.fullFnProto(.{
+    return tree.fullFnProtoComponents(.{
         .proto_node = node,
         .fn_token = tree.nodes.items(.main_token)[node],
         .return_type = data.rhs,
@@ -1378,7 +1378,7 @@ pub fn fnProtoMulti(tree: Ast, node: Node.Index) full.FnProto {
     const data = tree.nodes.items(.data)[node];
     const params_range = tree.extraData(data.lhs, Node.SubRange);
     const params = tree.extra_data[params_range.start..params_range.end];
-    return tree.fullFnProto(.{
+    return tree.fullFnProtoComponents(.{
         .proto_node = node,
         .fn_token = tree.nodes.items(.main_token)[node],
         .return_type = data.rhs,
@@ -1396,7 +1396,7 @@ pub fn fnProtoOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.FnPr
     const extra = tree.extraData(data.lhs, Node.FnProtoOne);
     buffer[0] = extra.param;
     const params = if (extra.param == 0) buffer[0..0] else buffer[0..1];
-    return tree.fullFnProto(.{
+    return tree.fullFnProtoComponents(.{
         .proto_node = node,
         .fn_token = tree.nodes.items(.main_token)[node],
         .return_type = data.rhs,
@@ -1413,7 +1413,7 @@ pub fn fnProto(tree: Ast, node: Node.Index) full.FnProto {
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.lhs, Node.FnProto);
     const params = tree.extra_data[extra.params_start..extra.params_end];
-    return tree.fullFnProto(.{
+    return tree.fullFnProtoComponents(.{
         .proto_node = node,
         .fn_token = tree.nodes.items(.main_token)[node],
         .return_type = data.rhs,
@@ -1431,11 +1431,13 @@ pub fn structInitOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.S
     const data = tree.nodes.items(.data)[node];
     buffer[0] = data.rhs;
     const fields = if (data.rhs == 0) buffer[0..0] else buffer[0..1];
-    return tree.fullStructInit(.{
-        .lbrace = tree.nodes.items(.main_token)[node],
-        .fields = fields,
-        .type_expr = data.lhs,
-    });
+    return .{
+        .ast = .{
+            .lbrace = tree.nodes.items(.main_token)[node],
+            .fields = fields,
+            .type_expr = data.lhs,
+        },
+    };
 }
 
 pub fn structInitDotTwo(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) full.StructInit {
@@ -1449,22 +1451,26 @@ pub fn structInitDotTwo(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) ful
         buffer[0..1]
     else
         buffer[0..0];
-    return tree.fullStructInit(.{
-        .lbrace = tree.nodes.items(.main_token)[node],
-        .fields = fields,
-        .type_expr = 0,
-    });
+    return .{
+        .ast = .{
+            .lbrace = tree.nodes.items(.main_token)[node],
+            .fields = fields,
+            .type_expr = 0,
+        },
+    };
 }
 
 pub fn structInitDot(tree: Ast, node: Node.Index) full.StructInit {
     assert(tree.nodes.items(.tag)[node] == .struct_init_dot or
         tree.nodes.items(.tag)[node] == .struct_init_dot_comma);
     const data = tree.nodes.items(.data)[node];
-    return tree.fullStructInit(.{
-        .lbrace = tree.nodes.items(.main_token)[node],
-        .fields = tree.extra_data[data.lhs..data.rhs],
-        .type_expr = 0,
-    });
+    return .{
+        .ast = .{
+            .lbrace = tree.nodes.items(.main_token)[node],
+            .fields = tree.extra_data[data.lhs..data.rhs],
+            .type_expr = 0,
+        },
+    };
 }
 
 pub fn structInit(tree: Ast, node: Node.Index) full.StructInit {
@@ -1472,11 +1478,13 @@ pub fn structInit(tree: Ast, node: Node.Index) full.StructInit {
         tree.nodes.items(.tag)[node] == .struct_init_comma);
     const data = tree.nodes.items(.data)[node];
     const fields_range = tree.extraData(data.rhs, Node.SubRange);
-    return tree.fullStructInit(.{
-        .lbrace = tree.nodes.items(.main_token)[node],
-        .fields = tree.extra_data[fields_range.start..fields_range.end],
-        .type_expr = data.lhs,
-    });
+    return .{
+        .ast = .{
+            .lbrace = tree.nodes.items(.main_token)[node],
+            .fields = tree.extra_data[fields_range.start..fields_range.end],
+            .type_expr = data.lhs,
+        },
+    };
 }
 
 pub fn arrayInitOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.ArrayInit {
@@ -1572,7 +1580,7 @@ pub fn arrayTypeSentinel(tree: Ast, node: Node.Index) full.ArrayType {
 pub fn ptrTypeAligned(tree: Ast, node: Node.Index) full.PtrType {
     assert(tree.nodes.items(.tag)[node] == .ptr_type_aligned);
     const data = tree.nodes.items(.data)[node];
-    return tree.fullPtrType(.{
+    return tree.fullPtrTypeComponents(.{
         .main_token = tree.nodes.items(.main_token)[node],
         .align_node = data.lhs,
         .addrspace_node = 0,
@@ -1586,7 +1594,7 @@ pub fn ptrTypeAligned(tree: Ast, node: Node.Index) full.PtrType {
 pub fn ptrTypeSentinel(tree: Ast, node: Node.Index) full.PtrType {
     assert(tree.nodes.items(.tag)[node] == .ptr_type_sentinel);
     const data = tree.nodes.items(.data)[node];
-    return tree.fullPtrType(.{
+    return tree.fullPtrTypeComponents(.{
         .main_token = tree.nodes.items(.main_token)[node],
         .align_node = 0,
         .addrspace_node = 0,
@@ -1601,7 +1609,7 @@ pub fn ptrType(tree: Ast, node: Node.Index) full.PtrType {
     assert(tree.nodes.items(.tag)[node] == .ptr_type);
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.lhs, Node.PtrType);
-    return tree.fullPtrType(.{
+    return tree.fullPtrTypeComponents(.{
         .main_token = tree.nodes.items(.main_token)[node],
         .align_node = extra.align_node,
         .addrspace_node = extra.addrspace_node,
@@ -1616,7 +1624,7 @@ pub fn ptrTypeBitRange(tree: Ast, node: Node.Index) full.PtrType {
     assert(tree.nodes.items(.tag)[node] == .ptr_type_bit_range);
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.lhs, Node.PtrTypeBitRange);
-    return tree.fullPtrType(.{
+    return tree.fullPtrTypeComponents(.{
         .main_token = tree.nodes.items(.main_token)[node],
         .align_node = extra.align_node,
         .addrspace_node = extra.addrspace_node,
@@ -1682,7 +1690,7 @@ pub fn containerDeclTwo(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) ful
         buffer[0..1]
     else
         buffer[0..0];
-    return tree.fullContainerDecl(.{
+    return tree.fullContainerDeclComponents(.{
         .main_token = tree.nodes.items(.main_token)[node],
         .enum_token = null,
         .members = members,
@@ -1694,7 +1702,7 @@ pub fn containerDecl(tree: Ast, node: Node.Index) full.ContainerDecl {
     assert(tree.nodes.items(.tag)[node] == .container_decl or
         tree.nodes.items(.tag)[node] == .container_decl_trailing);
     const data = tree.nodes.items(.data)[node];
-    return tree.fullContainerDecl(.{
+    return tree.fullContainerDeclComponents(.{
         .main_token = tree.nodes.items(.main_token)[node],
         .enum_token = null,
         .members = tree.extra_data[data.lhs..data.rhs],
@@ -1707,7 +1715,7 @@ pub fn containerDeclArg(tree: Ast, node: Node.Index) full.ContainerDecl {
         tree.nodes.items(.tag)[node] == .container_decl_arg_trailing);
     const data = tree.nodes.items(.data)[node];
     const members_range = tree.extraData(data.rhs, Node.SubRange);
-    return tree.fullContainerDecl(.{
+    return tree.fullContainerDeclComponents(.{
         .main_token = tree.nodes.items(.main_token)[node],
         .enum_token = null,
         .members = tree.extra_data[members_range.start..members_range.end],
@@ -1739,7 +1747,7 @@ pub fn taggedUnionTwo(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) full.
     else
         buffer[0..0];
     const main_token = tree.nodes.items(.main_token)[node];
-    return tree.fullContainerDecl(.{
+    return tree.fullContainerDeclComponents(.{
         .main_token = main_token,
         .enum_token = main_token + 2, // union lparen enum
         .members = members,
@@ -1752,7 +1760,7 @@ pub fn taggedUnion(tree: Ast, node: Node.Index) full.ContainerDecl {
         tree.nodes.items(.tag)[node] == .tagged_union_trailing);
     const data = tree.nodes.items(.data)[node];
     const main_token = tree.nodes.items(.main_token)[node];
-    return tree.fullContainerDecl(.{
+    return tree.fullContainerDeclComponents(.{
         .main_token = main_token,
         .enum_token = main_token + 2, // union lparen enum
         .members = tree.extra_data[data.lhs..data.rhs],
@@ -1766,7 +1774,7 @@ pub fn taggedUnionEnumTag(tree: Ast, node: Node.Index) full.ContainerDecl {
     const data = tree.nodes.items(.data)[node];
     const members_range = tree.extraData(data.rhs, Node.SubRange);
     const main_token = tree.nodes.items(.main_token)[node];
-    return tree.fullContainerDecl(.{
+    return tree.fullContainerDeclComponents(.{
         .main_token = main_token,
         .enum_token = main_token + 2, // union lparen enum
         .members = tree.extra_data[members_range.start..members_range.end],
@@ -1777,7 +1785,7 @@ pub fn taggedUnionEnumTag(tree: Ast, node: Node.Index) full.ContainerDecl {
 pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
     const data = &tree.nodes.items(.data)[node];
     const values: *[1]Node.Index = &data.lhs;
-    return tree.fullSwitchCase(.{
+    return tree.fullSwitchCaseComponents(.{
         .values = if (data.lhs == 0) values[0..0] else values[0..1],
         .arrow_token = tree.nodes.items(.main_token)[node],
         .target_expr = data.rhs,
@@ -1787,7 +1795,7 @@ pub fn switchCaseOne(tree: Ast, node: Node.Index) full.SwitchCase {
 pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.lhs, Node.SubRange);
-    return tree.fullSwitchCase(.{
+    return tree.fullSwitchCaseComponents(.{
         .values = tree.extra_data[extra.start..extra.end],
         .arrow_token = tree.nodes.items(.main_token)[node],
         .target_expr = data.rhs,
@@ -1796,7 +1804,7 @@ pub fn switchCase(tree: Ast, node: Node.Index) full.SwitchCase {
 
 pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {
     const data = tree.nodes.items(.data)[node];
-    return tree.fullAsm(.{
+    return tree.fullAsmComponents(.{
         .asm_token = tree.nodes.items(.main_token)[node],
         .template = data.lhs,
         .items = &.{},
@@ -1807,7 +1815,7 @@ pub fn asmSimple(tree: Ast, node: Node.Index) full.Asm {
 pub fn asmFull(tree: Ast, node: Node.Index) full.Asm {
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.rhs, Node.Asm);
-    return tree.fullAsm(.{
+    return tree.fullAsmComponents(.{
         .asm_token = tree.nodes.items(.main_token)[node],
         .template = data.lhs,
         .items = tree.extra_data[extra.items_start..extra.items_end],
@@ -1817,7 +1825,7 @@ pub fn asmFull(tree: Ast, node: Node.Index) full.Asm {
 
 pub fn whileSimple(tree: Ast, node: Node.Index) full.While {
     const data = tree.nodes.items(.data)[node];
-    return tree.fullWhile(.{
+    return tree.fullWhileComponents(.{
         .while_token = tree.nodes.items(.main_token)[node],
         .cond_expr = data.lhs,
         .cont_expr = 0,
@@ -1829,7 +1837,7 @@ pub fn whileSimple(tree: Ast, node: Node.Index) full.While {
 pub fn whileCont(tree: Ast, node: Node.Index) full.While {
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.rhs, Node.WhileCont);
-    return tree.fullWhile(.{
+    return tree.fullWhileComponents(.{
         .while_token = tree.nodes.items(.main_token)[node],
         .cond_expr = data.lhs,
         .cont_expr = extra.cont_expr,
@@ -1841,7 +1849,7 @@ pub fn whileCont(tree: Ast, node: Node.Index) full.While {
 pub fn whileFull(tree: Ast, node: Node.Index) full.While {
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.rhs, Node.While);
-    return tree.fullWhile(.{
+    return tree.fullWhileComponents(.{
         .while_token = tree.nodes.items(.main_token)[node],
         .cond_expr = data.lhs,
         .cont_expr = extra.cont_expr,
@@ -1852,7 +1860,7 @@ pub fn whileFull(tree: Ast, node: Node.Index) full.While {
 
 pub fn forSimple(tree: Ast, node: Node.Index) full.While {
     const data = tree.nodes.items(.data)[node];
-    return tree.fullWhile(.{
+    return tree.fullWhileComponents(.{
         .while_token = tree.nodes.items(.main_token)[node],
         .cond_expr = data.lhs,
         .cont_expr = 0,
@@ -1864,7 +1872,7 @@ pub fn forSimple(tree: Ast, node: Node.Index) full.While {
 pub fn forFull(tree: Ast, node: Node.Index) full.While {
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.rhs, Node.If);
-    return tree.fullWhile(.{
+    return tree.fullWhileComponents(.{
         .while_token = tree.nodes.items(.main_token)[node],
         .cond_expr = data.lhs,
         .cont_expr = 0,
@@ -1877,7 +1885,7 @@ pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call {
     const data = tree.nodes.items(.data)[node];
     buffer.* = .{data.rhs};
     const params = if (data.rhs != 0) buffer[0..1] else buffer[0..0];
-    return tree.fullCall(.{
+    return tree.fullCallComponents(.{
         .lparen = tree.nodes.items(.main_token)[node],
         .fn_expr = data.lhs,
         .params = params,
@@ -1887,14 +1895,14 @@ pub fn callOne(tree: Ast, buffer: *[1]Node.Index, node: Node.Index) full.Call {
 pub fn callFull(tree: Ast, node: Node.Index) full.Call {
     const data = tree.nodes.items(.data)[node];
     const extra = tree.extraData(data.rhs, Node.SubRange);
-    return tree.fullCall(.{
+    return tree.fullCallComponents(.{
         .lparen = tree.nodes.items(.main_token)[node],
         .fn_expr = data.lhs,
         .params = tree.extra_data[extra.start..extra.end],
     });
 }
 
-fn fullVarDecl(tree: Ast, info: full.VarDecl.Components) full.VarDecl {
+fn fullVarDeclComponents(tree: Ast, info: full.VarDecl.Components) full.VarDecl {
     const token_tags = tree.tokens.items(.tag);
     var result: full.VarDecl = .{
         .ast = info,
@@ -1919,7 +1927,7 @@ fn fullVarDecl(tree: Ast, info: full.VarDecl.Components) full.VarDecl {
     return result;
 }
 
-fn fullIf(tree: Ast, info: full.If.Components) full.If {
+fn fullIfComponents(tree: Ast, info: full.If.Components) full.If {
     const token_tags = tree.tokens.items(.tag);
     var result: full.If = .{
         .ast = info,
@@ -1944,7 +1952,7 @@ fn fullIf(tree: Ast, info: full.If.Components) full.If {
     return result;
 }
 
-fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.ContainerField {
+fn fullContainerFieldComponents(tree: Ast, info: full.ContainerField.Components) full.ContainerField {
     const token_tags = tree.tokens.items(.tag);
     var result: full.ContainerField = .{
         .ast = info,
@@ -1962,7 +1970,7 @@ fn fullContainerField(tree: Ast, info: full.ContainerField.Components) full.Cont
     return result;
 }
 
-fn fullFnProto(tree: Ast, info: full.FnProto.Components) full.FnProto {
+fn fullFnProtoComponents(tree: Ast, info: full.FnProto.Components) full.FnProto {
     const token_tags = tree.tokens.items(.tag);
     var result: full.FnProto = .{
         .ast = info,
@@ -1998,15 +2006,7 @@ fn fullFnProto(tree: Ast, info: full.FnProto.Components) full.FnProto {
     return result;
 }
 
-fn fullStructInit(tree: Ast, info: full.StructInit.Components) full.StructInit {
-    _ = tree;
-    var result: full.StructInit = .{
-        .ast = info,
-    };
-    return result;
-}
-
-fn fullPtrType(tree: Ast, info: full.PtrType.Components) full.PtrType {
+fn fullPtrTypeComponents(tree: Ast, info: full.PtrType.Components) full.PtrType {
     const token_tags = tree.tokens.items(.tag);
     const Size = std.builtin.Type.Pointer.Size;
     const size: Size = switch (token_tags[info.main_token]) {
@@ -2053,7 +2053,7 @@ fn fullPtrType(tree: Ast, info: full.PtrType.Components) full.PtrType {
     return result;
 }
 
-fn fullContainerDecl(tree: Ast, info: full.ContainerDecl.Components) full.ContainerDecl {
+fn fullContainerDeclComponents(tree: Ast, info: full.ContainerDecl.Components) full.ContainerDecl {
     const token_tags = tree.tokens.items(.tag);
     var result: full.ContainerDecl = .{
         .ast = info,
@@ -2069,7 +2069,7 @@ fn fullContainerDecl(tree: Ast, info: full.ContainerDecl.Components) full.Contai
     return result;
 }
 
-fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase {
+fn fullSwitchCaseComponents(tree: Ast, info: full.SwitchCase.Components, node: Node.Index) full.SwitchCase {
     const token_tags = tree.tokens.items(.tag);
     const node_tags = tree.nodes.items(.tag);
     var result: full.SwitchCase = .{
@@ -2087,7 +2087,7 @@ fn fullSwitchCase(tree: Ast, info: full.SwitchCase.Components, node: Node.Index)
     return result;
 }
 
-fn fullAsm(tree: Ast, info: full.Asm.Components) full.Asm {
+fn fullAsmComponents(tree: Ast, info: full.Asm.Components) full.Asm {
     const token_tags = tree.tokens.items(.tag);
     const node_tags = tree.nodes.items(.tag);
     var result: full.Asm = .{
@@ -2150,7 +2150,7 @@ fn fullAsm(tree: Ast, info: full.Asm.Components) full.Asm {
     return result;
 }
 
-fn fullWhile(tree: Ast, info: full.While.Components) full.While {
+fn fullWhileComponents(tree: Ast, info: full.While.Components) full.While {
     const token_tags = tree.tokens.items(.tag);
     var result: full.While = .{
         .ast = info,
@@ -2185,7 +2185,7 @@ fn fullWhile(tree: Ast, info: full.While.Components) full.While {
     return result;
 }
 
-fn fullCall(tree: Ast, info: full.Call.Components) full.Call {
+fn fullCallComponents(tree: Ast, info: full.Call.Components) full.Call {
     const token_tags = tree.tokens.items(.tag);
     var result: full.Call = .{
         .ast = info,
@@ -2198,6 +2198,139 @@ fn fullCall(tree: Ast, info: full.Call.Components) full.Call {
     return result;
 }
 
+pub fn fullVarDecl(tree: Ast, node: Node.Index) ?full.VarDecl {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .global_var_decl => tree.globalVarDecl(node),
+        .local_var_decl => tree.localVarDecl(node),
+        .aligned_var_decl => tree.alignedVarDecl(node),
+        .simple_var_decl => tree.simpleVarDecl(node),
+        else => null,
+    };
+}
+
+pub fn fullIf(tree: Ast, node: Node.Index) ?full.If {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .if_simple => tree.ifSimple(node),
+        .@"if" => tree.ifFull(node),
+        else => null,
+    };
+}
+
+pub fn fullWhile(tree: Ast, node: Node.Index) ?full.While {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .while_simple => tree.whileSimple(node),
+        .while_cont => tree.whileCont(node),
+        .@"while" => tree.whileFull(node),
+        .for_simple => tree.forSimple(node),
+        .@"for" => tree.forFull(node),
+        else => null,
+    };
+}
+
+pub fn fullContainerField(tree: Ast, node: Node.Index) ?full.ContainerField {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .container_field_init => tree.containerFieldInit(node),
+        .container_field_align => tree.containerFieldAlign(node),
+        .container_field => tree.containerField(node),
+        else => null,
+    };
+}
+
+pub fn fullFnProto(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.FnProto {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .fn_proto => tree.fnProto(node),
+        .fn_proto_multi => tree.fnProtoMulti(node),
+        .fn_proto_one => tree.fnProtoOne(buffer, node),
+        .fn_proto_simple => tree.fnProtoSimple(buffer, node),
+        .fn_decl => tree.fullFnProto(buffer, tree.nodes.items(.data)[node].lhs),
+        else => null,
+    };
+}
+
+pub fn fullStructInit(tree: Ast, buffer: *[2]Ast.Node.Index, node: Node.Index) ?full.StructInit {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .struct_init_one, .struct_init_one_comma => tree.structInitOne(buffer[0..1], node),
+        .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(buffer, node),
+        .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node),
+        .struct_init, .struct_init_comma => tree.structInit(node),
+        else => null,
+    };
+}
+
+pub fn fullArrayInit(tree: Ast, buffer: *[2]Node.Index, node: Node.Index) ?full.ArrayInit {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .array_init_one, .array_init_one_comma => tree.arrayInitOne(buffer[0..1], node),
+        .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(buffer, node),
+        .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node),
+        .array_init, .array_init_comma => tree.arrayInit(node),
+        else => null,
+    };
+}
+
+pub fn fullArrayType(tree: Ast, node: Node.Index) ?full.ArrayType {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .array_type => tree.arrayType(node),
+        .array_type_sentinel => tree.arrayTypeSentinel(node),
+        else => null,
+    };
+}
+
+pub fn fullPtrType(tree: Ast, node: Node.Index) ?full.PtrType {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .ptr_type_aligned => tree.ptrTypeAligned(node),
+        .ptr_type_sentinel => tree.ptrTypeSentinel(node),
+        .ptr_type => tree.ptrType(node),
+        .ptr_type_bit_range => tree.ptrTypeBitRange(node),
+        else => null,
+    };
+}
+
+pub fn fullSlice(tree: Ast, node: Node.Index) ?full.Slice {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .slice_open => tree.sliceOpen(node),
+        .slice => tree.slice(node),
+        .slice_sentinel => tree.sliceSentinel(node),
+        else => null,
+    };
+}
+
+pub fn fullContainerDecl(tree: Ast, buffer: *[2]Ast.Node.Index, node: Node.Index) ?full.ContainerDecl {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .root => tree.containerDeclRoot(),
+        .container_decl, .container_decl_trailing => tree.containerDecl(node),
+        .container_decl_arg, .container_decl_arg_trailing => tree.containerDeclArg(node),
+        .container_decl_two, .container_decl_two_trailing => tree.containerDeclTwo(buffer, node),
+        .tagged_union, .tagged_union_trailing => tree.taggedUnion(node),
+        .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => tree.taggedUnionEnumTag(node),
+        .tagged_union_two, .tagged_union_two_trailing => tree.taggedUnionTwo(buffer, node),
+        else => null,
+    };
+}
+
+pub fn fullSwitchCase(tree: Ast, node: Node.Index) ?full.SwitchCase {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(node),
+        .switch_case, .switch_case_inline => tree.switchCase(node),
+        else => null,
+    };
+}
+
+pub fn fullAsm(tree: Ast, node: Node.Index) ?full.Asm {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .asm_simple => tree.asmSimple(node),
+        .@"asm" => tree.asmFull(node),
+        else => null,
+    };
+}
+
+pub fn fullCall(tree: Ast, buffer: *[1]Ast.Node.Index, node: Node.Index) ?full.Call {
+    return switch (tree.nodes.items(.tag)[node]) {
+        .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node),
+        .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(buffer, node),
+        else => null,
+    };
+}
+
 /// Fully assembled AST node information.
 pub const full = struct {
     pub const VarDecl = struct {
lib/std/zig/render.zig
@@ -42,13 +42,8 @@ fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node
     if (members.len == 0) return;
     var is_tuple = true;
     for (members) |member| {
-        const tuple_like = switch (tree.nodes.items(.tag)[member]) {
-            .container_field_init => tree.containerFieldInit(member).ast.tuple_like,
-            .container_field_align => tree.containerFieldAlign(member).ast.tuple_like,
-            .container_field => tree.containerField(member).ast.tuple_like,
-            else => continue,
-        };
-        if (!tuple_like) {
+        const container_field = tree.fullContainerField(member) orelse continue;
+        if (!container_field.ast.tuple_like) {
             is_tuple = false;
             break;
         }
@@ -164,10 +159,11 @@ fn renderMember(
             return renderToken(ais, tree, tree.lastToken(expr) + 1, space); // ;
         },
 
-        .global_var_decl => return renderVarDecl(gpa, ais, tree, tree.globalVarDecl(decl)),
-        .local_var_decl => return renderVarDecl(gpa, ais, tree, tree.localVarDecl(decl)),
-        .simple_var_decl => return renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(decl)),
-        .aligned_var_decl => return renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(decl)),
+        .global_var_decl,
+        .local_var_decl,
+        .simple_var_decl,
+        .aligned_var_decl,
+        => return renderVarDecl(gpa, ais, tree, tree.fullVarDecl(decl).?),
 
         .test_decl => {
             const test_token = main_tokens[decl];
@@ -181,9 +177,11 @@ fn renderMember(
             try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
         },
 
-        .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), is_tuple, space),
-        .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), is_tuple, space),
-        .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), is_tuple, space),
+        .container_field_init,
+        .container_field_align,
+        .container_field,
+        => return renderContainerField(gpa, ais, tree, tree.fullContainerField(decl).?, is_tuple, space),
+
         .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),
 
         .root => unreachable,
@@ -437,54 +435,54 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
             return renderExpression(gpa, ais, tree, datas[node].lhs, space);
         },
 
-        .array_type => return renderArrayType(gpa, ais, tree, tree.arrayType(node), space),
-        .array_type_sentinel => return renderArrayType(gpa, ais, tree, tree.arrayTypeSentinel(node), space),
+        .array_type,
+        .array_type_sentinel,
+        => return renderArrayType(gpa, ais, tree, tree.fullArrayType(node).?, space),
 
-        .ptr_type_aligned => return renderPtrType(gpa, ais, tree, tree.ptrTypeAligned(node), space),
-        .ptr_type_sentinel => return renderPtrType(gpa, ais, tree, tree.ptrTypeSentinel(node), space),
-        .ptr_type => return renderPtrType(gpa, ais, tree, tree.ptrType(node), space),
-        .ptr_type_bit_range => return renderPtrType(gpa, ais, tree, tree.ptrTypeBitRange(node), space),
+        .ptr_type_aligned,
+        .ptr_type_sentinel,
+        .ptr_type,
+        .ptr_type_bit_range,
+        => return renderPtrType(gpa, ais, tree, tree.fullPtrType(node).?, space),
 
-        .array_init_one, .array_init_one_comma => {
-            var elements: [1]Ast.Node.Index = undefined;
-            return renderArrayInit(gpa, ais, tree, tree.arrayInitOne(&elements, node), space);
-        },
-        .array_init_dot_two, .array_init_dot_two_comma => {
-            var elements: [2]Ast.Node.Index = undefined;
-            return renderArrayInit(gpa, ais, tree, tree.arrayInitDotTwo(&elements, node), space);
-        },
+        .array_init_one,
+        .array_init_one_comma,
+        .array_init_dot_two,
+        .array_init_dot_two_comma,
         .array_init_dot,
         .array_init_dot_comma,
-        => return renderArrayInit(gpa, ais, tree, tree.arrayInitDot(node), space),
         .array_init,
         .array_init_comma,
-        => return renderArrayInit(gpa, ais, tree, tree.arrayInit(node), space),
-
-        .struct_init_one, .struct_init_one_comma => {
-            var fields: [1]Ast.Node.Index = undefined;
-            return renderStructInit(gpa, ais, tree, node, tree.structInitOne(&fields, node), space);
-        },
-        .struct_init_dot_two, .struct_init_dot_two_comma => {
-            var fields: [2]Ast.Node.Index = undefined;
-            return renderStructInit(gpa, ais, tree, node, tree.structInitDotTwo(&fields, node), space);
+        => {
+            var elements: [2]Ast.Node.Index = undefined;
+            return renderArrayInit(gpa, ais, tree, tree.fullArrayInit(&elements, node).?, space);
         },
+
+        .struct_init_one,
+        .struct_init_one_comma,
+        .struct_init_dot_two,
+        .struct_init_dot_two_comma,
         .struct_init_dot,
         .struct_init_dot_comma,
-        => return renderStructInit(gpa, ais, tree, node, tree.structInitDot(node), space),
         .struct_init,
         .struct_init_comma,
-        => return renderStructInit(gpa, ais, tree, node, tree.structInit(node), space),
-
-        .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
-            var params: [1]Ast.Node.Index = undefined;
-            return renderCall(gpa, ais, tree, tree.callOne(&params, node), space);
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            return renderStructInit(gpa, ais, tree, node, tree.fullStructInit(&buf, node).?, space);
         },
 
+        .call_one,
+        .call_one_comma,
+        .async_call_one,
+        .async_call_one_comma,
         .call,
         .call_comma,
         .async_call,
         .async_call_comma,
-        => return renderCall(gpa, ais, tree, tree.callFull(node), space),
+        => {
+            var buf: [1]Ast.Node.Index = undefined;
+            return renderCall(gpa, ais, tree, tree.fullCall(&buf, node).?, space);
+        },
 
         .array_access => {
             const suffix = datas[node];
@@ -500,9 +498,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
             return renderToken(ais, tree, rbracket, space); // ]
         },
 
-        .slice_open => return renderSlice(gpa, ais, tree, node, tree.sliceOpen(node), space),
-        .slice => return renderSlice(gpa, ais, tree, node, tree.slice(node), space),
-        .slice_sentinel => return renderSlice(gpa, ais, tree, node, tree.sliceSentinel(node), space),
+        .slice_open, .slice, .slice_sentinel => return renderSlice(gpa, ais, tree, node, tree.fullSlice(node).?, space),
 
         .deref => {
             try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
@@ -566,27 +562,20 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
 
         .container_decl,
         .container_decl_trailing,
-        => return renderContainerDecl(gpa, ais, tree, node, tree.containerDecl(node), space),
-
-        .container_decl_two, .container_decl_two_trailing => {
-            var buffer: [2]Ast.Node.Index = undefined;
-            return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclTwo(&buffer, node), space);
-        },
         .container_decl_arg,
         .container_decl_arg_trailing,
-        => return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclArg(node), space),
-
+        .container_decl_two,
+        .container_decl_two_trailing,
         .tagged_union,
         .tagged_union_trailing,
-        => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnion(node), space),
-
-        .tagged_union_two, .tagged_union_two_trailing => {
-            var buffer: [2]Ast.Node.Index = undefined;
-            return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionTwo(&buffer, node), space);
-        },
         .tagged_union_enum_tag,
         .tagged_union_enum_tag_trailing,
-        => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionEnumTag(node), space),
+        .tagged_union_two,
+        .tagged_union_two_trailing,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            return renderContainerDecl(gpa, ais, tree, node, tree.fullContainerDecl(&buf, node).?, space);
+        },
 
         .error_set_decl => {
             const error_token = main_tokens[node];
@@ -651,16 +640,14 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
             return renderBuiltinCall(gpa, ais, tree, main_tokens[node], params, space);
         },
 
-        .fn_proto_simple => {
-            var params: [1]Ast.Node.Index = undefined;
-            return renderFnProto(gpa, ais, tree, tree.fnProtoSimple(&params, node), space);
-        },
-        .fn_proto_multi => return renderFnProto(gpa, ais, tree, tree.fnProtoMulti(node), space),
-        .fn_proto_one => {
-            var params: [1]Ast.Node.Index = undefined;
-            return renderFnProto(gpa, ais, tree, tree.fnProtoOne(&params, node), space);
+        .fn_proto_simple,
+        .fn_proto_multi,
+        .fn_proto_one,
+        .fn_proto,
+        => {
+            var buf: [1]Ast.Node.Index = undefined;
+            return renderFnProto(gpa, ais, tree, tree.fullFnProto(&buf, node).?, space);
         },
-        .fn_proto => return renderFnProto(gpa, ais, tree, tree.fnProto(node), space),
 
         .anyframe_type => {
             const main_token = main_tokens[node];
@@ -698,20 +685,26 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
             return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
         },
 
-        .switch_case_one, .switch_case_inline_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space),
-        .switch_case, .switch_case_inline => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space),
+        .switch_case_one,
+        .switch_case_inline_one,
+        .switch_case,
+        .switch_case_inline,
+        => return renderSwitchCase(gpa, ais, tree, tree.fullSwitchCase(node).?, space),
 
-        .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space),
-        .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space),
-        .@"while" => return renderWhile(gpa, ais, tree, tree.whileFull(node), space),
-        .for_simple => return renderWhile(gpa, ais, tree, tree.forSimple(node), space),
-        .@"for" => return renderWhile(gpa, ais, tree, tree.forFull(node), space),
+        .while_simple,
+        .while_cont,
+        .@"while",
+        .for_simple,
+        .@"for",
+        => return renderWhile(gpa, ais, tree, tree.fullWhile(node).?, space),
 
-        .if_simple => return renderIf(gpa, ais, tree, tree.ifSimple(node), space),
-        .@"if" => return renderIf(gpa, ais, tree, tree.ifFull(node), space),
+        .if_simple,
+        .@"if",
+        => return renderIf(gpa, ais, tree, tree.fullIf(node).?, space),
 
-        .asm_simple => return renderAsm(gpa, ais, tree, tree.asmSimple(node), space),
-        .@"asm" => return renderAsm(gpa, ais, tree, tree.asmFull(node), space),
+        .asm_simple,
+        .@"asm",
+        => return renderAsm(gpa, ais, tree, tree.fullAsm(node).?, space),
 
         .enum_literal => {
             try renderToken(ais, tree, main_tokens[node] - 1, .none); // .
@@ -1632,10 +1625,11 @@ fn renderBlock(
         for (statements) |stmt, i| {
             if (i != 0) try renderExtraNewline(ais, tree, stmt);
             switch (node_tags[stmt]) {
-                .global_var_decl => try renderVarDecl(gpa, ais, tree, tree.globalVarDecl(stmt)),
-                .local_var_decl => try renderVarDecl(gpa, ais, tree, tree.localVarDecl(stmt)),
-                .simple_var_decl => try renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(stmt)),
-                .aligned_var_decl => try renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(stmt)),
+                .global_var_decl,
+                .local_var_decl,
+                .simple_var_decl,
+                .aligned_var_decl,
+                => try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(stmt).?),
                 else => try renderExpression(gpa, ais, tree, stmt, .semicolon),
             }
         }
@@ -1938,7 +1932,6 @@ fn renderContainerDecl(
     space: Space,
 ) Error!void {
     const token_tags = tree.tokens.items(.tag);
-    const node_tags = tree.nodes.items(.tag);
 
     if (container_decl.layout_token) |layout_token| {
         try renderToken(ais, tree, layout_token, .space);
@@ -1946,13 +1939,8 @@ fn renderContainerDecl(
 
     var is_tuple = token_tags[container_decl.ast.main_token] == .keyword_struct;
     if (is_tuple) for (container_decl.ast.members) |member| {
-        const tuple_like = switch (tree.nodes.items(.tag)[member]) {
-            .container_field_init => tree.containerFieldInit(member).ast.tuple_like,
-            .container_field_align => tree.containerFieldAlign(member).ast.tuple_like,
-            .container_field => tree.containerField(member).ast.tuple_like,
-            else => continue,
-        };
-        if (!tuple_like) {
+        const container_field = tree.fullContainerField(member) orelse continue;
+        if (!container_field.ast.tuple_like) {
             is_tuple = false;
             break;
         }
@@ -2018,7 +2006,7 @@ fn renderContainerDecl(
 
         // 4. The container has non-field members.
         for (container_decl.ast.members) |member| {
-            if (!node_tags[member].isContainerField()) break :one_line;
+            if (tree.fullContainerField(member) == null) break :one_line;
         }
 
         // Print all the declarations on the same line.
src/AstGen.zig
@@ -771,8 +771,9 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
 
         .identifier => return identifier(gz, scope, ri, node),
 
-        .asm_simple => return asmExpr(gz, scope, ri, node, tree.asmSimple(node)),
-        .@"asm"     => return asmExpr(gz, scope, ri, node, tree.asmFull(node)),
+        .asm_simple,
+        .@"asm",
+        => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?),
 
         .string_literal           => return stringLiteral(gz, ri, node),
         .multiline_string_literal => return multilineStringLiteral(gz, ri, node),
@@ -797,12 +798,17 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
             return builtinCall(gz, scope, ri, node, params);
         },
 
-        .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
-            var params: [1]Ast.Node.Index = undefined;
-            return callExpr(gz, scope, ri, node, tree.callOne(&params, node));
-        },
-        .call, .call_comma, .async_call, .async_call_comma => {
-            return callExpr(gz, scope, ri, node, tree.callFull(node));
+        .call_one,
+        .call_one_comma,
+        .async_call_one,
+        .async_call_one_comma,
+        .call,
+        .call_comma,
+        .async_call,
+        .async_call_comma,
+        => {
+            var buf: [1]Ast.Node.Index = undefined;
+            return callExpr(gz, scope, ri, node, tree.fullCall(&buf, node).?);
         },
 
         .unreachable_literal => {
@@ -819,15 +825,16 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
         .@"return" => return ret(gz, scope, node),
         .field_access => return fieldAccess(gz, scope, ri, node),
 
-        .if_simple => return ifExpr(gz, scope, ri.br(), node, tree.ifSimple(node)),
-        .@"if" => return ifExpr(gz, scope, ri.br(), node, tree.ifFull(node)),
+        .if_simple,
+        .@"if",
+        => return ifExpr(gz, scope, ri.br(), node, tree.fullIf(node).?),
 
-        .while_simple => return whileExpr(gz, scope, ri.br(), node, tree.whileSimple(node), false),
-        .while_cont => return whileExpr(gz, scope, ri.br(), node, tree.whileCont(node), false),
-        .@"while" => return whileExpr(gz, scope, ri.br(), node, tree.whileFull(node), false),
+        .while_simple,
+        .while_cont,
+        .@"while",
+        => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false),
 
-        .for_simple => return forExpr(gz, scope, ri.br(), node, tree.forSimple(node), false),
-        .@"for" => return forExpr(gz, scope, ri.br(), node, tree.forFull(node), false),
+        .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false),
 
         .slice_open => {
             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
@@ -1012,32 +1019,28 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
             ),
         },
 
-        .ptr_type_aligned => return ptrType(gz, scope, ri, node, tree.ptrTypeAligned(node)),
-        .ptr_type_sentinel => return ptrType(gz, scope, ri, node, tree.ptrTypeSentinel(node)),
-        .ptr_type => return ptrType(gz, scope, ri, node, tree.ptrType(node)),
-        .ptr_type_bit_range => return ptrType(gz, scope, ri, node, tree.ptrTypeBitRange(node)),
+        .ptr_type_aligned,
+        .ptr_type_sentinel,
+        .ptr_type,
+        .ptr_type_bit_range,
+        => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?),
 
         .container_decl,
         .container_decl_trailing,
-        => return containerDecl(gz, scope, ri, node, tree.containerDecl(node)),
-        .container_decl_two, .container_decl_two_trailing => {
-            var buffer: [2]Ast.Node.Index = undefined;
-            return containerDecl(gz, scope, ri, node, tree.containerDeclTwo(&buffer, node));
-        },
         .container_decl_arg,
         .container_decl_arg_trailing,
-        => return containerDecl(gz, scope, ri, node, tree.containerDeclArg(node)),
-
+        .container_decl_two,
+        .container_decl_two_trailing,
         .tagged_union,
         .tagged_union_trailing,
-        => return containerDecl(gz, scope, ri, node, tree.taggedUnion(node)),
-        .tagged_union_two, .tagged_union_two_trailing => {
-            var buffer: [2]Ast.Node.Index = undefined;
-            return containerDecl(gz, scope, ri, node, tree.taggedUnionTwo(&buffer, node));
-        },
         .tagged_union_enum_tag,
         .tagged_union_enum_tag_trailing,
-        => return containerDecl(gz, scope, ri, node, tree.taggedUnionEnumTag(node)),
+        .tagged_union_two,
+        .tagged_union_two_trailing,
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?);
+        },
 
         .@"break" => return breakExpr(gz, scope, node),
         .@"continue" => return continueExpr(gz, scope, node),
@@ -1057,49 +1060,39 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
 
         .@"try" => return tryExpr(gz, scope, ri, node, node_datas[node].lhs),
 
-        .array_init_one, .array_init_one_comma => {
-            var elements: [1]Ast.Node.Index = undefined;
-            return arrayInitExpr(gz, scope, ri, node, tree.arrayInitOne(&elements, node));
-        },
-        .array_init_dot_two, .array_init_dot_two_comma => {
-            var elements: [2]Ast.Node.Index = undefined;
-            return arrayInitExpr(gz, scope, ri, node, tree.arrayInitDotTwo(&elements, node));
-        },
+        .array_init_one,
+        .array_init_one_comma,
+        .array_init_dot_two,
+        .array_init_dot_two_comma,
         .array_init_dot,
         .array_init_dot_comma,
-        => return arrayInitExpr(gz, scope, ri, node, tree.arrayInitDot(node)),
         .array_init,
         .array_init_comma,
-        => return arrayInitExpr(gz, scope, ri, node, tree.arrayInit(node)),
-
-        .struct_init_one, .struct_init_one_comma => {
-            var fields: [1]Ast.Node.Index = undefined;
-            return structInitExpr(gz, scope, ri, node, tree.structInitOne(&fields, node));
-        },
-        .struct_init_dot_two, .struct_init_dot_two_comma => {
-            var fields: [2]Ast.Node.Index = undefined;
-            return structInitExpr(gz, scope, ri, node, tree.structInitDotTwo(&fields, node));
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?);
         },
+
+        .struct_init_one,
+        .struct_init_one_comma,
+        .struct_init_dot_two,
+        .struct_init_dot_two_comma,
         .struct_init_dot,
         .struct_init_dot_comma,
-        => return structInitExpr(gz, scope, ri, node, tree.structInitDot(node)),
         .struct_init,
         .struct_init_comma,
-        => return structInitExpr(gz, scope, ri, node, tree.structInit(node)),
-
-        .fn_proto_simple => {
-            var params: [1]Ast.Node.Index = undefined;
-            return fnProtoExpr(gz, scope, ri, node, tree.fnProtoSimple(&params, node));
-        },
-        .fn_proto_multi => {
-            return fnProtoExpr(gz, scope, ri, node, tree.fnProtoMulti(node));
-        },
-        .fn_proto_one => {
-            var params: [1]Ast.Node.Index = undefined;
-            return fnProtoExpr(gz, scope, ri, node, tree.fnProtoOne(&params, node));
+        => {
+            var buf: [2]Ast.Node.Index = undefined;
+            return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?);
         },
-        .fn_proto => {
-            return fnProtoExpr(gz, scope, ri, node, tree.fnProto(node));
+
+        .fn_proto_simple,
+        .fn_proto_multi,
+        .fn_proto_one,
+        .fn_proto,
+        => {
+            var buf: [1]Ast.Node.Index = undefined;
+            return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?);
         },
     }
 }
@@ -1374,11 +1367,7 @@ fn arrayInitExpr(
         };
 
         infer: {
-            const array_type: Ast.full.ArrayType = switch (node_tags[array_init.ast.type_expr]) {
-                .array_type => tree.arrayType(array_init.ast.type_expr),
-                .array_type_sentinel => tree.arrayTypeSentinel(array_init.ast.type_expr),
-                else => break :infer,
-            };
+            const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer;
             // This intentionally does not support `@"_"` syntax.
             if (node_tags[array_type.ast.elem_count] == .identifier and
                 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"))
@@ -1606,17 +1595,13 @@ fn structInitExpr(
     } else array: {
         const node_tags = tree.nodes.items(.tag);
         const main_tokens = tree.nodes.items(.main_token);
-        const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) {
-            .array_type => tree.arrayType(struct_init.ast.type_expr),
-            .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr),
-            else => {
-                if (struct_init.ast.fields.len == 0) {
-                    const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
-                    const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
-                    return rvalue(gz, ri, result, node);
-                }
-                break :array;
-            },
+        const array_type: Ast.full.ArrayType = tree.fullArrayType(struct_init.ast.type_expr) orelse {
+            if (struct_init.ast.fields.len == 0) {
+                const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
+                const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
+                return rvalue(gz, ri, result, node);
+            }
+            break :array;
         };
         const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
             // This intentionally does not support `@"_"` syntax.
@@ -2321,10 +2306,10 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
         while (true) {
             switch (node_tags[inner_node]) {
                 // zig fmt: off
-            .global_var_decl  => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.globalVarDecl(statement)),
-            .local_var_decl   => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.localVarDecl(statement)),
-            .simple_var_decl  => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
-            .aligned_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.alignedVarDecl(statement)),
+            .global_var_decl,
+            .local_var_decl,
+            .simple_var_decl,
+            .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?),
 
             .@"defer"    => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
             .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
@@ -2351,12 +2336,12 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
                 continue;
             },
 
-            .while_simple => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.whileSimple(inner_node), true),
-            .while_cont => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.whileCont(inner_node), true),
-            .@"while" => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.whileFull(inner_node), true),
+            .while_simple,
+            .while_cont,
+            .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.fullWhile(inner_node).?, true),
 
-            .for_simple => _ = try forExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.forSimple(inner_node), true),
-            .@"for" => _ = try forExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.forFull(inner_node), true),
+            .for_simple,
+            .@"for", => _ = try forExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.fullWhile(inner_node).?, true),
 
             else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
             // zig fmt: on
@@ -4491,12 +4476,8 @@ fn structDeclInner(
     var is_tuple = false;
     const node_tags = tree.nodes.items(.tag);
     for (container_decl.ast.members) |member_node| {
-        switch (node_tags[member_node]) {
-            .container_field_init => is_tuple = tree.containerFieldInit(member_node).ast.tuple_like,
-            .container_field_align => is_tuple = tree.containerFieldAlign(member_node).ast.tuple_like,
-            .container_field => is_tuple = tree.containerField(member_node).ast.tuple_like,
-            else => continue,
-        }
+        const container_field = tree.fullContainerField(member_node) orelse continue;
+        is_tuple = container_field.ast.tuple_like;
         if (is_tuple) break;
     }
     if (is_tuple) for (container_decl.ast.members) |member_node| {
@@ -4802,7 +4783,6 @@ fn containerDecl(
     const gpa = astgen.gpa;
     const tree = astgen.tree;
     const token_tags = tree.tokens.items(.tag);
-    const node_tags = tree.nodes.items(.tag);
 
     const prev_fn_block = astgen.fn_block;
     astgen.fn_block = null;
@@ -4844,14 +4824,9 @@ fn containerDecl(
                 var nonexhaustive_node: Ast.Node.Index = 0;
                 var nonfinal_nonexhaustive = false;
                 for (container_decl.ast.members) |member_node| {
-                    var member = switch (node_tags[member_node]) {
-                        .container_field_init => tree.containerFieldInit(member_node),
-                        .container_field_align => tree.containerFieldAlign(member_node),
-                        .container_field => tree.containerField(member_node),
-                        else => {
-                            decls += 1;
-                            continue;
-                        },
+                    var member = tree.fullContainerField(member_node) orelse {
+                        decls += 1;
+                        continue;
                     };
                     member.convertToNonTupleLike(astgen.tree.nodes);
                     if (member.ast.tuple_like) {
@@ -5114,90 +5089,33 @@ fn containerMember(
     const node_tags = tree.nodes.items(.tag);
     const node_datas = tree.nodes.items(.data);
     switch (node_tags[member_node]) {
-        .container_field_init => return ContainerMemberResult{ .field = tree.containerFieldInit(member_node) },
-        .container_field_align => return ContainerMemberResult{ .field = tree.containerFieldAlign(member_node) },
-        .container_field => return ContainerMemberResult{ .field = tree.containerField(member_node) },
-
-        .fn_decl => {
-            const fn_proto = node_datas[member_node].lhs;
-            const body = node_datas[member_node].rhs;
-            switch (node_tags[fn_proto]) {
-                .fn_proto_simple => {
-                    var params: [1]Ast.Node.Index = undefined;
-                    astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoSimple(&params, fn_proto)) catch |err| switch (err) {
-                        error.OutOfMemory => return error.OutOfMemory,
-                        error.AnalysisFail => {},
-                    };
-                },
-                .fn_proto_multi => {
-                    astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) {
-                        error.OutOfMemory => return error.OutOfMemory,
-                        error.AnalysisFail => {},
-                    };
-                },
-                .fn_proto_one => {
-                    var params: [1]Ast.Node.Index = undefined;
-                    astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoOne(&params, fn_proto)) catch |err| switch (err) {
-                        error.OutOfMemory => return error.OutOfMemory,
-                        error.AnalysisFail => {},
-                    };
-                },
-                .fn_proto => {
-                    astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) {
-                        error.OutOfMemory => return error.OutOfMemory,
-                        error.AnalysisFail => {},
-                    };
-                },
-                else => unreachable,
-            }
-        },
-        .fn_proto_simple => {
-            var params: [1]Ast.Node.Index = undefined;
-            astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoSimple(&params, member_node)) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.AnalysisFail => {},
-            };
-        },
-        .fn_proto_multi => {
-            astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.AnalysisFail => {},
-            };
-        },
-        .fn_proto_one => {
-            var params: [1]Ast.Node.Index = undefined;
-            astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoOne(&params, member_node)) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.AnalysisFail => {},
-            };
-        },
-        .fn_proto => {
-            astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.AnalysisFail => {},
-            };
-        },
+        .container_field_init,
+        .container_field_align,
+        .container_field,
+        => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? },
 
-        .global_var_decl => {
-            astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.AnalysisFail => {},
-            };
-        },
-        .local_var_decl => {
-            astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) {
-                error.OutOfMemory => return error.OutOfMemory,
-                error.AnalysisFail => {},
-            };
-        },
-        .simple_var_decl => {
-            astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) {
+        .fn_proto,
+        .fn_proto_multi,
+        .fn_proto_one,
+        .fn_proto_simple,
+        .fn_decl,
+        => {
+            var buf: [1]Ast.Node.Index = undefined;
+            const full = tree.fullFnProto(&buf, member_node).?;
+            const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0;
+
+            astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
                 error.OutOfMemory => return error.OutOfMemory,
                 error.AnalysisFail => {},
             };
         },
-        .aligned_var_decl => {
-            astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) {
+
+        .global_var_decl,
+        .local_var_decl,
+        .simple_var_decl,
+        .aligned_var_decl,
+        => {
+            astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.fullVarDecl(member_node).?) catch |err| switch (err) {
                 error.OutOfMemory => return error.OutOfMemory,
                 error.AnalysisFail => {},
             };
@@ -6538,11 +6456,7 @@ fn switchExpr(
     var else_src: ?Ast.TokenIndex = null;
     var underscore_src: ?Ast.TokenIndex = null;
     for (case_nodes) |case_node| {
-        const case = switch (node_tags[case_node]) {
-            .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
-            .switch_case, .switch_case_inline => tree.switchCase(case_node),
-            else => unreachable,
-        };
+        const case = tree.fullSwitchCase(case_node).?;
         if (case.payload_token) |payload_token| {
             if (token_tags[payload_token] == .asterisk) {
                 any_payload_is_ref = true;
@@ -6689,11 +6603,7 @@ fn switchExpr(
     var multi_case_index: u32 = 0;
     var scalar_case_index: u32 = 0;
     for (case_nodes) |case_node| {
-        const case = switch (node_tags[case_node]) {
-            .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
-            .switch_case, .switch_case_inline => tree.switchCase(case_node),
-            else => unreachable,
-        };
+        const case = tree.fullSwitchCase(case_node).?;
 
         const is_multi_case = case.ast.values.len > 1 or
             (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
src/Module.zig
@@ -1040,34 +1040,13 @@ pub const Struct = struct {
             return s.srcLoc(mod);
         };
         const node = owner_decl.relativeToNodeIndex(0);
-        const node_tags = tree.nodes.items(.tag);
-        switch (node_tags[node]) {
-            .container_decl,
-            .container_decl_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.containerDecl(node)),
-            .container_decl_two, .container_decl_two_trailing => {
-                var buffer: [2]Ast.Node.Index = undefined;
-                return queryFieldSrc(tree.*, query, file, tree.containerDeclTwo(&buffer, node));
-            },
-            .container_decl_arg,
-            .container_decl_arg_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.containerDeclArg(node)),
-
-            .tagged_union,
-            .tagged_union_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.taggedUnion(node)),
-            .tagged_union_two, .tagged_union_two_trailing => {
-                var buffer: [2]Ast.Node.Index = undefined;
-                return queryFieldSrc(tree.*, query, file, tree.taggedUnionTwo(&buffer, node));
-            },
-            .tagged_union_enum_tag,
-            .tagged_union_enum_tag_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.taggedUnionEnumTag(node)),
-
-            .root => return queryFieldSrc(tree.*, query, file, tree.containerDeclRoot()),
 
+        var buf: [2]Ast.Node.Index = undefined;
+        if (tree.fullContainerDecl(&buf, node)) |container_decl| {
+            return queryFieldSrc(tree.*, query, file, container_decl);
+        } else {
             // This struct was generated using @Type
-            else => return s.srcLoc(mod),
+            return s.srcLoc(mod);
         }
     }
 
@@ -1207,34 +1186,12 @@ pub const EnumFull = struct {
             return e.srcLoc(mod);
         };
         const node = owner_decl.relativeToNodeIndex(0);
-        const node_tags = tree.nodes.items(.tag);
-        switch (node_tags[node]) {
-            .container_decl,
-            .container_decl_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.containerDecl(node)),
-            .container_decl_two, .container_decl_two_trailing => {
-                var buffer: [2]Ast.Node.Index = undefined;
-                return queryFieldSrc(tree.*, query, file, tree.containerDeclTwo(&buffer, node));
-            },
-            .container_decl_arg,
-            .container_decl_arg_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.containerDeclArg(node)),
-
-            .tagged_union,
-            .tagged_union_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.taggedUnion(node)),
-            .tagged_union_two, .tagged_union_two_trailing => {
-                var buffer: [2]Ast.Node.Index = undefined;
-                return queryFieldSrc(tree.*, query, file, tree.taggedUnionTwo(&buffer, node));
-            },
-            .tagged_union_enum_tag,
-            .tagged_union_enum_tag_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.taggedUnionEnumTag(node)),
-
-            .root => return queryFieldSrc(tree.*, query, file, tree.containerDeclRoot()),
-
-            // This struct was generated using @Type
-            else => return e.srcLoc(mod),
+        var buf: [2]Ast.Node.Index = undefined;
+        if (tree.fullContainerDecl(&buf, node)) |container_decl| {
+            return queryFieldSrc(tree.*, query, file, container_decl);
+        } else {
+            // This enum was generated using @Type
+            return e.srcLoc(mod);
         }
     }
 };
@@ -1315,30 +1272,13 @@ pub const Union = struct {
             return u.srcLoc(mod);
         };
         const node = owner_decl.relativeToNodeIndex(0);
-        const node_tags = tree.nodes.items(.tag);
+
         var buf: [2]Ast.Node.Index = undefined;
-        switch (node_tags[node]) {
-            .container_decl,
-            .container_decl_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.containerDecl(node)),
-            .container_decl_two, .container_decl_two_trailing => {
-                var buffer: [2]Ast.Node.Index = undefined;
-                return queryFieldSrc(tree.*, query, file, tree.containerDeclTwo(&buffer, node));
-            },
-            .container_decl_arg,
-            .container_decl_arg_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.containerDeclArg(node)),
-            .tagged_union,
-            .tagged_union_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.taggedUnion(node)),
-            .tagged_union_two,
-            .tagged_union_two_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.taggedUnionTwo(&buf, node)),
-            .tagged_union_enum_tag,
-            .tagged_union_enum_tag_trailing,
-            => return queryFieldSrc(tree.*, query, file, tree.taggedUnionEnumTag(node)),
+        if (tree.fullContainerDecl(&buf, node)) |container_decl| {
+            return queryFieldSrc(tree.*, query, file, container_decl);
+        } else {
             // This union was generated using @Type
-            else => return u.srcLoc(mod),
+            return u.srcLoc(mod);
         }
     }
 
@@ -2304,10 +2244,11 @@ pub const SrcLoc = struct {
                 const node = src_loc.declRelativeToNodeIndex(node_off);
                 const node_tags = tree.nodes.items(.tag);
                 const full = switch (node_tags[node]) {
-                    .global_var_decl => tree.globalVarDecl(node),
-                    .local_var_decl => tree.localVarDecl(node),
-                    .simple_var_decl => tree.simpleVarDecl(node),
-                    .aligned_var_decl => tree.alignedVarDecl(node),
+                    .global_var_decl,
+                    .local_var_decl,
+                    .simple_var_decl,
+                    .aligned_var_decl,
+                    => tree.fullVarDecl(node).?,
                     .@"usingnamespace" => {
                         const node_data = tree.nodes.items(.data);
                         return nodeToSpan(tree, node_data[node].lhs);
@@ -2325,53 +2266,25 @@ pub const SrcLoc = struct {
             .node_offset_var_decl_align => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
-                const node_tags = tree.nodes.items(.tag);
-                const full: Ast.full.VarDecl = switch (node_tags[node]) {
-                    .global_var_decl => tree.globalVarDecl(node),
-                    .local_var_decl => tree.localVarDecl(node),
-                    .simple_var_decl => tree.simpleVarDecl(node),
-                    .aligned_var_decl => tree.alignedVarDecl(node),
-                    else => unreachable,
-                };
+                const full = tree.fullVarDecl(node).?;
                 return nodeToSpan(tree, full.ast.align_node);
             },
             .node_offset_var_decl_section => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
-                const node_tags = tree.nodes.items(.tag);
-                const full: Ast.full.VarDecl = switch (node_tags[node]) {
-                    .global_var_decl => tree.globalVarDecl(node),
-                    .local_var_decl => tree.localVarDecl(node),
-                    .simple_var_decl => tree.simpleVarDecl(node),
-                    .aligned_var_decl => tree.alignedVarDecl(node),
-                    else => unreachable,
-                };
+                const full = tree.fullVarDecl(node).?;
                 return nodeToSpan(tree, full.ast.section_node);
             },
             .node_offset_var_decl_addrspace => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
-                const node_tags = tree.nodes.items(.tag);
-                const full: Ast.full.VarDecl = switch (node_tags[node]) {
-                    .global_var_decl => tree.globalVarDecl(node),
-                    .local_var_decl => tree.localVarDecl(node),
-                    .simple_var_decl => tree.simpleVarDecl(node),
-                    .aligned_var_decl => tree.alignedVarDecl(node),
-                    else => unreachable,
-                };
+                const full = tree.fullVarDecl(node).?;
                 return nodeToSpan(tree, full.ast.addrspace_node);
             },
             .node_offset_var_decl_init => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
-                const node_tags = tree.nodes.items(.tag);
-                const full: Ast.full.VarDecl = switch (node_tags[node]) {
-                    .global_var_decl => tree.globalVarDecl(node),
-                    .local_var_decl => tree.localVarDecl(node),
-                    .simple_var_decl => tree.simpleVarDecl(node),
-                    .aligned_var_decl => tree.alignedVarDecl(node),
-                    else => unreachable,
-                };
+                const full = tree.fullVarDecl(node).?;
                 return nodeToSpan(tree, full.ast.init_node);
             },
             .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0),
@@ -2392,14 +2305,8 @@ pub const SrcLoc = struct {
             .node_offset_slice_sentinel,
             => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
-                const full = switch (node_tags[node]) {
-                    .slice_open => tree.sliceOpen(node),
-                    .slice => tree.slice(node),
-                    .slice_sentinel => tree.sliceSentinel(node),
-                    else => unreachable,
-                };
+                const full = tree.fullSlice(node).?;
                 const part_node = switch (src_loc.lazy) {
                     .node_offset_slice_ptr => full.ast.sliced,
                     .node_offset_slice_start => full.ast.start,
@@ -2411,24 +2318,9 @@ pub const SrcLoc = struct {
             },
             .node_offset_call_func => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
-                var params: [1]Ast.Node.Index = undefined;
-                const full = switch (node_tags[node]) {
-                    .call_one,
-                    .call_one_comma,
-                    .async_call_one,
-                    .async_call_one_comma,
-                    => tree.callOne(&params, node),
-
-                    .call,
-                    .call_comma,
-                    .async_call,
-                    .async_call_comma,
-                    => tree.callFull(node),
-
-                    else => unreachable,
-                };
+                var buf: [1]Ast.Node.Index = undefined;
+                const full = tree.fullCall(&buf, node).?;
                 return nodeToSpan(tree, full.ast.fn_expr);
             },
             .node_offset_field_name => |node_off| {
@@ -2451,24 +2343,14 @@ pub const SrcLoc = struct {
             },
             .node_offset_asm_source => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
-                const full = switch (node_tags[node]) {
-                    .asm_simple => tree.asmSimple(node),
-                    .@"asm" => tree.asmFull(node),
-                    else => unreachable,
-                };
+                const full = tree.fullAsm(node).?;
                 return nodeToSpan(tree, full.ast.template);
             },
             .node_offset_asm_ret_ty => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const node = src_loc.declRelativeToNodeIndex(node_off);
-                const full = switch (node_tags[node]) {
-                    .asm_simple => tree.asmSimple(node),
-                    .@"asm" => tree.asmFull(node),
-                    else => unreachable,
-                };
+                const full = tree.fullAsm(node).?;
                 const asm_output = full.outputs[0];
                 const node_datas = tree.nodes.items(.data);
                 return nodeToSpan(tree, node_datas[asm_output].lhs);
@@ -2479,13 +2361,17 @@ pub const SrcLoc = struct {
                 const node = src_loc.declRelativeToNodeIndex(node_off);
                 const node_tags = tree.nodes.items(.tag);
                 const src_node = switch (node_tags[node]) {
-                    .if_simple => tree.ifSimple(node).ast.cond_expr,
-                    .@"if" => tree.ifFull(node).ast.cond_expr,
-                    .while_simple => tree.whileSimple(node).ast.cond_expr,
-                    .while_cont => tree.whileCont(node).ast.cond_expr,
-                    .@"while" => tree.whileFull(node).ast.cond_expr,
-                    .for_simple => tree.forSimple(node).ast.cond_expr,
-                    .@"for" => tree.forFull(node).ast.cond_expr,
+                    .if_simple,
+                    .@"if",
+                    => tree.fullIf(node).?.ast.cond_expr,
+
+                    .while_simple,
+                    .while_cont,
+                    .@"while",
+                    .for_simple,
+                    .@"for",
+                    => tree.fullWhile(node).?.ast.cond_expr,
+
                     .@"orelse" => node,
                     .@"catch" => node,
                     else => unreachable,
@@ -2521,11 +2407,7 @@ pub const SrcLoc = struct {
                 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
                 const case_nodes = tree.extra_data[extra.start..extra.end];
                 for (case_nodes) |case_node| {
-                    const case = switch (node_tags[case_node]) {
-                        .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
-                        .switch_case, .switch_case_inline => tree.switchCase(case_node),
-                        else => unreachable,
-                    };
+                    const case = tree.fullSwitchCase(case_node).?;
                     const is_special = (case.ast.values.len == 0) or
                         (case.ast.values.len == 1 and
                         node_tags[case.ast.values[0]] == .identifier and
@@ -2545,11 +2427,7 @@ pub const SrcLoc = struct {
                 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
                 const case_nodes = tree.extra_data[extra.start..extra.end];
                 for (case_nodes) |case_node| {
-                    const case = switch (node_tags[case_node]) {
-                        .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
-                        .switch_case, .switch_case_inline => tree.switchCase(case_node),
-                        else => unreachable,
-                    };
+                    const case = tree.fullSwitchCase(case_node).?;
                     const is_special = (case.ast.values.len == 0) or
                         (case.ast.values.len == 1 and
                         node_tags[case.ast.values[0]] == .identifier and
@@ -2566,12 +2444,7 @@ pub const SrcLoc = struct {
             .node_offset_switch_prong_capture => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
                 const case_node = src_loc.declRelativeToNodeIndex(node_off);
-                const node_tags = tree.nodes.items(.tag);
-                const case = switch (node_tags[case_node]) {
-                    .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
-                    .switch_case, .switch_case_inline => tree.switchCase(case_node),
-                    else => unreachable,
-                };
+                const case = tree.fullSwitchCase(case_node).?;
                 const start_tok = case.payload_token.?;
                 const token_tags = tree.tokens.items(.tag);
                 const end_tok = switch (token_tags[start_tok]) {
@@ -2585,116 +2458,38 @@ pub const SrcLoc = struct {
             },
             .node_offset_fn_type_align => |node_off| {
                 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.declRelativeToNodeIndex(node_off);
-                var params: [1]Ast.Node.Index = undefined;
-                const full = switch (node_tags[node]) {
-                    .fn_proto_simple => tree.fnProtoSimple(&params, node),
-                    .fn_proto_multi => tree.fnProtoMulti(node),
-                    .fn_proto_one => tree.fnProtoOne(&params, node),
-                    .fn_proto => tree.fnProto(node),
-                    .fn_decl => switch (node_tags[node_datas[node].lhs]) {
-                        .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
-                        .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
-                        .fn_proto_one => tree.fnProtoOne(&params, node_datas[node].lhs),
-                        .fn_proto => tree.fnProto(node_datas[node].lhs),
-                        else => unreachable,
-                    },
-                    else => unreachable,
-                };
+                var buf: [1]Ast.Node.Index = undefined;
+                const full = tree.fullFnProto(&buf, node).?;
                 return nodeToSpan(tree, full.ast.align_expr);
             },
             .node_offset_fn_type_addrspace => |node_off| {
                 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.declRelativeToNodeIndex(node_off);
-                var params: [1]Ast.Node.Index = undefined;
-                const full = switch (node_tags[node]) {
-                    .fn_proto_simple => tree.fnProtoSimple(&params, node),
-                    .fn_proto_multi => tree.fnProtoMulti(node),
-                    .fn_proto_one => tree.fnProtoOne(&params, node),
-                    .fn_proto => tree.fnProto(node),
-                    .fn_decl => switch (node_tags[node_datas[node].lhs]) {
-                        .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
-                        .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
-                        .fn_proto_one => tree.fnProtoOne(&params, node_datas[node].lhs),
-                        .fn_proto => tree.fnProto(node_datas[node].lhs),
-                        else => unreachable,
-                    },
-                    else => unreachable,
-                };
+                var buf: [1]Ast.Node.Index = undefined;
+                const full = tree.fullFnProto(&buf, node).?;
                 return nodeToSpan(tree, full.ast.addrspace_expr);
             },
             .node_offset_fn_type_section => |node_off| {
                 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.declRelativeToNodeIndex(node_off);
-                var params: [1]Ast.Node.Index = undefined;
-                const full = switch (node_tags[node]) {
-                    .fn_proto_simple => tree.fnProtoSimple(&params, node),
-                    .fn_proto_multi => tree.fnProtoMulti(node),
-                    .fn_proto_one => tree.fnProtoOne(&params, node),
-                    .fn_proto => tree.fnProto(node),
-                    .fn_decl => switch (node_tags[node_datas[node].lhs]) {
-                        .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
-                        .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
-                        .fn_proto_one => tree.fnProtoOne(&params, node_datas[node].lhs),
-                        .fn_proto => tree.fnProto(node_datas[node].lhs),
-                        else => unreachable,
-                    },
-                    else => unreachable,
-                };
+                var buf: [1]Ast.Node.Index = undefined;
+                const full = tree.fullFnProto(&buf, node).?;
                 return nodeToSpan(tree, full.ast.section_expr);
             },
             .node_offset_fn_type_cc => |node_off| {
                 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.declRelativeToNodeIndex(node_off);
-                var params: [1]Ast.Node.Index = undefined;
-                const full = switch (node_tags[node]) {
-                    .fn_proto_simple => tree.fnProtoSimple(&params, node),
-                    .fn_proto_multi => tree.fnProtoMulti(node),
-                    .fn_proto_one => tree.fnProtoOne(&params, node),
-                    .fn_proto => tree.fnProto(node),
-                    .fn_decl => switch (node_tags[node_datas[node].lhs]) {
-                        .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
-                        .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
-                        .fn_proto_one => tree.fnProtoOne(&params, node_datas[node].lhs),
-                        .fn_proto => tree.fnProto(node_datas[node].lhs),
-                        else => unreachable,
-                    },
-                    else => unreachable,
-                };
+                var buf: [1]Ast.Node.Index = undefined;
+                const full = tree.fullFnProto(&buf, node).?;
                 return nodeToSpan(tree, full.ast.callconv_expr);
             },
 
             .node_offset_fn_type_ret_ty => |node_off| {
                 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.declRelativeToNodeIndex(node_off);
-                var params: [1]Ast.Node.Index = undefined;
-                const full = switch (node_tags[node]) {
-                    .fn_proto_simple => tree.fnProtoSimple(&params, node),
-                    .fn_proto_multi => tree.fnProtoMulti(node),
-                    .fn_proto_one => tree.fnProtoOne(&params, node),
-                    .fn_proto => tree.fnProto(node),
-                    .fn_decl => blk: {
-                        const fn_proto = node_datas[node].lhs;
-                        break :blk switch (node_tags[fn_proto]) {
-                            .fn_proto_simple => tree.fnProtoSimple(&params, fn_proto),
-                            .fn_proto_multi => tree.fnProtoMulti(fn_proto),
-                            .fn_proto_one => tree.fnProtoOne(&params, fn_proto),
-                            .fn_proto => tree.fnProto(fn_proto),
-                            else => unreachable,
-                        };
-                    },
-                    else => unreachable,
-                };
+                var buf: [1]Ast.Node.Index = undefined;
+                const full = tree.fullFnProto(&buf, node).?;
                 return nodeToSpan(tree, full.ast.return_type);
             },
             .node_offset_param => |node_off| {
@@ -2742,27 +2537,9 @@ pub const SrcLoc = struct {
 
             .node_offset_lib_name => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_datas = tree.nodes.items(.data);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
-                var params: [1]Ast.Node.Index = undefined;
-                const full = switch (node_tags[parent_node]) {
-                    .fn_proto_simple => tree.fnProtoSimple(&params, parent_node),
-                    .fn_proto_multi => tree.fnProtoMulti(parent_node),
-                    .fn_proto_one => tree.fnProtoOne(&params, parent_node),
-                    .fn_proto => tree.fnProto(parent_node),
-                    .fn_decl => blk: {
-                        const fn_proto = node_datas[parent_node].lhs;
-                        break :blk switch (node_tags[fn_proto]) {
-                            .fn_proto_simple => tree.fnProtoSimple(&params, fn_proto),
-                            .fn_proto_multi => tree.fnProtoMulti(fn_proto),
-                            .fn_proto_one => tree.fnProtoOne(&params, fn_proto),
-                            .fn_proto => tree.fnProto(fn_proto),
-                            else => unreachable,
-                        };
-                    },
-                    else => unreachable,
-                };
+                var buf: [1]Ast.Node.Index = undefined;
+                const full = tree.fullFnProto(&buf, parent_node).?;
                 const tok_index = full.lib_name.?;
                 const start = tree.tokens.items(.start)[tok_index];
                 const end = start + @intCast(u32, tree.tokenSlice(tok_index).len);
@@ -2771,38 +2548,23 @@ pub const SrcLoc = struct {
 
             .node_offset_array_type_len => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.ArrayType = switch (node_tags[parent_node]) {
-                    .array_type => tree.arrayType(parent_node),
-                    .array_type_sentinel => tree.arrayTypeSentinel(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullArrayType(parent_node).?;
                 return nodeToSpan(tree, full.ast.elem_count);
             },
             .node_offset_array_type_sentinel => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.ArrayType = switch (node_tags[parent_node]) {
-                    .array_type => tree.arrayType(parent_node),
-                    .array_type_sentinel => tree.arrayTypeSentinel(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullArrayType(parent_node).?;
                 return nodeToSpan(tree, full.ast.sentinel);
             },
             .node_offset_array_type_elem => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.ArrayType = switch (node_tags[parent_node]) {
-                    .array_type => tree.arrayType(parent_node),
-                    .array_type_sentinel => tree.arrayTypeSentinel(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullArrayType(parent_node).?;
                 return nodeToSpan(tree, full.ast.elem_type);
             },
             .node_offset_un_op => |node_off| {
@@ -2814,86 +2576,44 @@ pub const SrcLoc = struct {
             },
             .node_offset_ptr_elem => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.PtrType = switch (node_tags[parent_node]) {
-                    .ptr_type_aligned => tree.ptrTypeAligned(parent_node),
-                    .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node),
-                    .ptr_type => tree.ptrType(parent_node),
-                    .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullPtrType(parent_node).?;
                 return nodeToSpan(tree, full.ast.child_type);
             },
             .node_offset_ptr_sentinel => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.PtrType = switch (node_tags[parent_node]) {
-                    .ptr_type_aligned => tree.ptrTypeAligned(parent_node),
-                    .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node),
-                    .ptr_type => tree.ptrType(parent_node),
-                    .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullPtrType(parent_node).?;
                 return nodeToSpan(tree, full.ast.sentinel);
             },
             .node_offset_ptr_align => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.PtrType = switch (node_tags[parent_node]) {
-                    .ptr_type_aligned => tree.ptrTypeAligned(parent_node),
-                    .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node),
-                    .ptr_type => tree.ptrType(parent_node),
-                    .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullPtrType(parent_node).?;
                 return nodeToSpan(tree, full.ast.align_node);
             },
             .node_offset_ptr_addrspace => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.PtrType = switch (node_tags[parent_node]) {
-                    .ptr_type_aligned => tree.ptrTypeAligned(parent_node),
-                    .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node),
-                    .ptr_type => tree.ptrType(parent_node),
-                    .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullPtrType(parent_node).?;
                 return nodeToSpan(tree, full.ast.addrspace_node);
             },
             .node_offset_ptr_bitoffset => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.PtrType = switch (node_tags[parent_node]) {
-                    .ptr_type_aligned => tree.ptrTypeAligned(parent_node),
-                    .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node),
-                    .ptr_type => tree.ptrType(parent_node),
-                    .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullPtrType(parent_node).?;
                 return nodeToSpan(tree, full.ast.bit_range_start);
             },
             .node_offset_ptr_hostsize => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
-                const full: Ast.full.PtrType = switch (node_tags[parent_node]) {
-                    .ptr_type_aligned => tree.ptrTypeAligned(parent_node),
-                    .ptr_type_sentinel => tree.ptrTypeSentinel(parent_node),
-                    .ptr_type => tree.ptrType(parent_node),
-                    .ptr_type_bit_range => tree.ptrTypeBitRange(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullPtrType(parent_node).?;
                 return nodeToSpan(tree, full.ast.bit_range_end);
             },
             .node_offset_container_tag => |node_off| {
@@ -2933,17 +2653,10 @@ pub const SrcLoc = struct {
             },
             .node_offset_init_ty => |node_off| {
                 const tree = try src_loc.file_scope.getTree(gpa);
-                const node_tags = tree.nodes.items(.tag);
                 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
 
                 var buf: [2]Ast.Node.Index = undefined;
-                const full: Ast.full.ArrayInit = switch (node_tags[parent_node]) {
-                    .array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], parent_node),
-                    .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, parent_node),
-                    .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(parent_node),
-                    .array_init, .array_init_comma => tree.arrayInit(parent_node),
-                    else => unreachable,
-                };
+                const full = tree.fullArrayInit(&buf, parent_node).?;
                 return nodeToSpan(tree, full.ast.type_expr);
             },
             .node_offset_store_ptr => |node_off| {
@@ -6097,11 +5810,7 @@ pub const SwitchProngSrc = union(enum) {
         var multi_i: u32 = 0;
         var scalar_i: u32 = 0;
         for (case_nodes) |case_node| {
-            const case = switch (node_tags[case_node]) {
-                .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
-                .switch_case, .switch_case_inline => tree.switchCase(case_node),
-                else => unreachable,
-            };
+            const case = tree.fullSwitchCase(case_node).?;
             if (case.ast.values.len == 0)
                 continue;
             if (case.ast.values.len == 1 and
@@ -6223,15 +5932,9 @@ fn queryFieldSrc(
     file_scope: *File,
     container_decl: Ast.full.ContainerDecl,
 ) SrcLoc {
-    const node_tags = tree.nodes.items(.tag);
     var field_index: usize = 0;
     for (container_decl.ast.members) |member_node| {
-        const field = switch (node_tags[member_node]) {
-            .container_field_init => tree.containerFieldInit(member_node),
-            .container_field_align => tree.containerFieldAlign(member_node),
-            .container_field => tree.containerField(member_node),
-            else => continue,
-        };
+        const field = tree.fullContainerField(member_node) orelse continue;
         if (field_index == query.index) {
             return switch (query.range) {
                 .name => .{
@@ -6275,24 +5978,9 @@ pub fn paramSrc(
         });
         return LazySrcLoc.nodeOffset(0);
     };
-    const node_datas = tree.nodes.items(.data);
-    const node_tags = tree.nodes.items(.tag);
     const node = decl.relativeToNodeIndex(func_node_offset);
-    var params: [1]Ast.Node.Index = undefined;
-    const full = switch (node_tags[node]) {
-        .fn_proto_simple => tree.fnProtoSimple(&params, node),
-        .fn_proto_multi => tree.fnProtoMulti(node),
-        .fn_proto_one => tree.fnProtoOne(&params, node),
-        .fn_proto => tree.fnProto(node),
-        .fn_decl => switch (node_tags[node_datas[node].lhs]) {
-            .fn_proto_simple => tree.fnProtoSimple(&params, node_datas[node].lhs),
-            .fn_proto_multi => tree.fnProtoMulti(node_datas[node].lhs),
-            .fn_proto_one => tree.fnProtoOne(&params, node_datas[node].lhs),
-            .fn_proto => tree.fnProto(node_datas[node].lhs),
-            else => unreachable,
-        },
-        else => unreachable,
-    };
+    var buf: [1]Ast.Node.Index = undefined;
+    const full = tree.fullFnProto(&buf, node).?;
     var it = full.iterate(tree);
     var i: usize = 0;
     while (it.next()) |param| : (i += 1) {
@@ -6358,18 +6046,6 @@ pub fn initSrc(
     const node_tags = tree.nodes.items(.tag);
     const node = decl.relativeToNodeIndex(init_node_offset);
     var buf: [2]Ast.Node.Index = undefined;
-    const full = switch (node_tags[node]) {
-        .array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], node).ast.elements,
-        .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, node).ast.elements,
-        .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node).ast.elements,
-        .array_init, .array_init_comma => tree.arrayInit(node).ast.elements,
-
-        .struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], node).ast.fields,
-        .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node).ast.fields,
-        .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node).ast.fields,
-        .struct_init, .struct_init_comma => tree.structInit(node).ast.fields,
-        else => return LazySrcLoc.nodeOffset(init_node_offset),
-    };
     switch (node_tags[node]) {
         .array_init_one,
         .array_init_one_comma,
@@ -6379,7 +6055,10 @@ pub fn initSrc(
         .array_init_dot_comma,
         .array_init,
         .array_init_comma,
-        => return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full[init_index])),
+        => {
+            const full = tree.fullArrayInit(&buf, node).?.ast.elements;
+            return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full[init_index]));
+        },
         .struct_init_one,
         .struct_init_one_comma,
         .struct_init_dot_two,
@@ -6388,8 +6067,11 @@ pub fn initSrc(
         .struct_init_dot_comma,
         .struct_init,
         .struct_init_comma,
-        => return LazySrcLoc{ .node_offset_initializer = decl.nodeIndexToRelative(full[init_index]) },
-        else => unreachable,
+        => {
+            const full = tree.fullStructInit(&buf, node).?.ast.fields;
+            return LazySrcLoc{ .node_offset_initializer = decl.nodeIndexToRelative(full[init_index]) };
+        },
+        else => return LazySrcLoc.nodeOffset(init_node_offset),
     }
 }
 
@@ -6422,13 +6104,7 @@ pub fn optionsSrc(gpa: Allocator, decl: *Decl, base_src: LazySrcLoc, wanted: []c
         else => unreachable,
     };
     var buf: [2]std.zig.Ast.Node.Index = undefined;
-    const init_nodes = switch (node_tags[arg_node]) {
-        .struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], arg_node).ast.fields,
-        .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, arg_node).ast.fields,
-        .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(arg_node).ast.fields,
-        .struct_init, .struct_init_comma => tree.structInit(arg_node).ast.fields,
-        else => return base_src,
-    };
+    const init_nodes = if (tree.fullStructInit(&buf, arg_node)) |struct_init| struct_init.ast.fields else return base_src;
     for (init_nodes) |init_node| {
         // . IDENTIFIER = init_node
         const name_token = tree.firstToken(init_node) - 2;
src/Sema.zig
@@ -2150,29 +2150,8 @@ fn addFieldErrNote(
 
         const container_node = decl.relativeToNodeIndex(0);
         const node_tags = tree.nodes.items(.tag);
-        var buffer: [2]std.zig.Ast.Node.Index = undefined;
-        const container_decl = switch (node_tags[container_node]) {
-            .root => tree.containerDeclRoot(),
-            .container_decl,
-            .container_decl_trailing,
-            => tree.containerDecl(container_node),
-            .container_decl_two,
-            .container_decl_two_trailing,
-            => tree.containerDeclTwo(&buffer, container_node),
-            .container_decl_arg,
-            .container_decl_arg_trailing,
-            => tree.containerDeclArg(container_node),
-            .tagged_union,
-            .tagged_union_trailing,
-            => tree.taggedUnion(container_node),
-            .tagged_union_two,
-            .tagged_union_two_trailing,
-            => tree.taggedUnionTwo(&buffer, container_node),
-            .tagged_union_enum_tag,
-            .tagged_union_enum_tag_trailing,
-            => tree.taggedUnionEnumTag(container_node),
-            else => break :blk decl.srcLoc(),
-        };
+        var buf: [2]std.zig.Ast.Node.Index = undefined;
+        const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc();
 
         var it_index: usize = 0;
         for (container_decl.ast.members) |member_node| {