Source
pub fn lastToken(tree: Ast, node: Node.Index) TokenIndex {
var n = node;
var end_offset: u32 = 0;
while (true) switch (tree.nodeTag(n)) {
.root => return @intCast(tree.tokens.len - 1),
.@"usingnamespace",
.bool_not,
.negation,
.bit_not,
.negation_wrap,
.address_of,
.@"try",
.@"await",
.optional_type,
.@"suspend",
.@"resume",
.@"nosuspend",
.@"comptime",
=> n = tree.nodeData(n).node,
.@"catch",
.equal_equal,
.bang_equal,
.less_than,
.greater_than,
.less_or_equal,
.greater_or_equal,
.assign_mul,
.assign_div,
.assign_mod,
.assign_add,
.assign_sub,
.assign_shl,
.assign_shl_sat,
.assign_shr,
.assign_bit_and,
.assign_bit_xor,
.assign_bit_or,
.assign_mul_wrap,
.assign_add_wrap,
.assign_sub_wrap,
.assign_mul_sat,
.assign_add_sat,
.assign_sub_sat,
.assign,
.merge_error_sets,
.mul,
.div,
.mod,
.array_mult,
.mul_wrap,
.mul_sat,
.add,
.sub,
.array_cat,
.add_wrap,
.sub_wrap,
.add_sat,
.sub_sat,
.shl,
.shl_sat,
.shr,
.bit_and,
.bit_xor,
.bit_or,
.@"orelse",
.bool_and,
.bool_or,
.error_union,
.if_simple,
.while_simple,
.for_simple,
.fn_decl,
.array_type,
.switch_range,
=> n = tree.nodeData(n).node_and_node[1],
.test_decl, .@"errdefer" => n = tree.nodeData(n).opt_token_and_node[1],
.@"defer" => n = tree.nodeData(n).node,
.anyframe_type => n = tree.nodeData(n).token_and_node[1],
.switch_case_one,
.switch_case_inline_one,
.ptr_type_aligned,
.ptr_type_sentinel,
=> n = tree.nodeData(n).opt_node_and_node[1],
.assign_destructure,
.ptr_type,
.ptr_type_bit_range,
.switch_case,
.switch_case_inline,
=> n = tree.nodeData(n).extra_and_node[1],
.fn_proto_simple => n = tree.nodeData(n).opt_node_and_opt_node[1].unwrap().?,
.fn_proto_multi,
.fn_proto_one,
.fn_proto,
=> n = tree.nodeData(n).extra_and_opt_node[1].unwrap().?,
.for_range => {
n = tree.nodeData(n).node_and_opt_node[1].unwrap() orelse {
return tree.nodeMainToken(n) + end_offset;
};
},
.field_access,
.unwrap_optional,
.asm_simple,
=> return tree.nodeData(n).node_and_token[1] + end_offset,
.grouped_expression, .asm_input => return tree.nodeData(n).node_and_token[1] + end_offset,
.multiline_string_literal, .error_set_decl => return tree.nodeData(n).token_and_token[1] + end_offset,
.asm_output => return tree.nodeData(n).opt_node_and_token[1] + end_offset,
.error_value => return tree.nodeMainToken(n) + 2 + end_offset,
.anyframe_literal,
.char_literal,
.number_literal,
.unreachable_literal,
.identifier,
.deref,
.enum_literal,
.string_literal,
=> return tree.nodeMainToken(n) + end_offset,
.@"return" => {
n = tree.nodeData(n).opt_node.unwrap() orelse {
return tree.nodeMainToken(n) + end_offset;
};
},
.call, .async_call => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const params = tree.extraData(extra_index, Node.SubRange);
assert(params.start != params.end);
end_offset += 1; // for the rparen
n = @enumFromInt(tree.extra_data[@intFromEnum(params.end) - 1]); // last parameter
},
.tagged_union_enum_tag => {
const arg, const extra_index = tree.nodeData(n).node_and_extra;
const members = tree.extraData(extra_index, Node.SubRange);
if (members.start == members.end) {
end_offset += 4; // for the rparen + rparen + lbrace + rbrace
n = arg;
} else {
end_offset += 1; // for the rbrace
n = @enumFromInt(tree.extra_data[@intFromEnum(members.end) - 1]); // last parameter
}
},
.call_comma,
.async_call_comma,
.tagged_union_enum_tag_trailing,
=> {
_, const extra_index = tree.nodeData(n).node_and_extra;
const params = tree.extraData(extra_index, Node.SubRange);
assert(params.start != params.end);
end_offset += 2; // for the comma/semicolon + rparen/rbrace
n = @enumFromInt(tree.extra_data[@intFromEnum(params.end) - 1]); // last parameter
},
.@"switch" => {
const condition, const extra_index = tree.nodeData(n).node_and_extra;
const cases = tree.extraData(extra_index, Node.SubRange);
if (cases.start == cases.end) {
end_offset += 3; // rparen, lbrace, rbrace
n = condition;
} else {
end_offset += 1; // for the rbrace
n = @enumFromInt(tree.extra_data[@intFromEnum(cases.end) - 1]); // last case
}
},
.container_decl_arg => {
const arg, const extra_index = tree.nodeData(n).node_and_extra;
const members = tree.extraData(extra_index, Node.SubRange);
if (members.end == members.start) {
end_offset += 3; // for the rparen + lbrace + rbrace
n = arg;
} else {
end_offset += 1; // for the rbrace
n = @enumFromInt(tree.extra_data[@intFromEnum(members.end) - 1]); // last parameter
}
},
.@"asm" => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.Asm);
return extra.rparen + end_offset;
},
.array_init,
.struct_init,
=> {
_, const extra_index = tree.nodeData(n).node_and_extra;
const elements = tree.extraData(extra_index, Node.SubRange);
assert(elements.start != elements.end);
end_offset += 1; // for the rbrace
n = @enumFromInt(tree.extra_data[@intFromEnum(elements.end) - 1]); // last element
},
.array_init_comma,
.struct_init_comma,
.container_decl_arg_trailing,
.switch_comma,
=> {
_, const extra_index = tree.nodeData(n).node_and_extra;
const members = tree.extraData(extra_index, Node.SubRange);
assert(members.start != members.end);
end_offset += 2; // for the comma + rbrace
n = @enumFromInt(tree.extra_data[@intFromEnum(members.end) - 1]); // last parameter
},
.array_init_dot,
.struct_init_dot,
.block,
.container_decl,
.tagged_union,
.builtin_call,
=> {
const range = tree.nodeData(n).extra_range;
assert(range.start != range.end);
end_offset += 1; // for the rbrace
n = @enumFromInt(tree.extra_data[@intFromEnum(range.end) - 1]); // last statement
},
.array_init_dot_comma,
.struct_init_dot_comma,
.block_semicolon,
.container_decl_trailing,
.tagged_union_trailing,
.builtin_call_comma,
=> {
const range = tree.nodeData(n).extra_range;
assert(range.start != range.end);
end_offset += 2; // for the comma/semicolon + rbrace/rparen
n = @enumFromInt(tree.extra_data[@intFromEnum(range.end) - 1]); // last member
},
.call_one,
.async_call_one,
=> {
_, const first_param = tree.nodeData(n).node_and_opt_node;
end_offset += 1; // for the rparen
n = first_param.unwrap() orelse {
return tree.nodeMainToken(n) + end_offset;
};
},
.array_init_dot_two,
.block_two,
.builtin_call_two,
.struct_init_dot_two,
.container_decl_two,
.tagged_union_two,
=> {
const opt_lhs, const opt_rhs = tree.nodeData(n).opt_node_and_opt_node;
if (opt_rhs.unwrap()) |rhs| {
end_offset += 1; // for the rparen/rbrace
n = rhs;
} else if (opt_lhs.unwrap()) |lhs| {
end_offset += 1; // for the rparen/rbrace
n = lhs;
} else {
switch (tree.nodeTag(n)) {
.array_init_dot_two,
.block_two,
.struct_init_dot_two,
=> end_offset += 1, // rbrace
.builtin_call_two => end_offset += 2, // lparen/lbrace + rparen/rbrace
.container_decl_two => {
var i: u32 = 2; // lbrace + rbrace
while (tree.tokenTag(tree.nodeMainToken(n) + i) == .container_doc_comment) i += 1;
end_offset += i;
},
.tagged_union_two => {
var i: u32 = 5; // (enum) {}
while (tree.tokenTag(tree.nodeMainToken(n) + i) == .container_doc_comment) i += 1;
end_offset += i;
},
else => unreachable,
}
return tree.nodeMainToken(n) + end_offset;
}
},
.array_init_dot_two_comma,
.builtin_call_two_comma,
.block_two_semicolon,
.struct_init_dot_two_comma,
.container_decl_two_trailing,
.tagged_union_two_trailing,
=> {
const opt_lhs, const opt_rhs = tree.nodeData(n).opt_node_and_opt_node;
end_offset += 2; // for the comma/semicolon + rbrace/rparen
if (opt_rhs.unwrap()) |rhs| {
n = rhs;
} else if (opt_lhs.unwrap()) |lhs| {
n = lhs;
} else {
unreachable;
}
},
.simple_var_decl => {
const type_node, const init_node = tree.nodeData(n).opt_node_and_opt_node;
if (init_node.unwrap()) |rhs| {
n = rhs;
} else if (type_node.unwrap()) |lhs| {
n = lhs;
} else {
end_offset += 1; // from mut token to name
return tree.nodeMainToken(n) + end_offset;
}
},
.aligned_var_decl => {
const align_node, const init_node = tree.nodeData(n).node_and_opt_node;
if (init_node.unwrap()) |rhs| {
n = rhs;
} else {
end_offset += 1; // for the rparen
n = align_node;
}
},
.global_var_decl => {
const extra_index, const init_node = tree.nodeData(n).extra_and_opt_node;
if (init_node.unwrap()) |rhs| {
n = rhs;
} else {
const extra = tree.extraData(extra_index, Node.GlobalVarDecl);
if (extra.section_node.unwrap()) |section_node| {
end_offset += 1; // for the rparen
n = section_node;
} else if (extra.align_node.unwrap()) |align_node| {
end_offset += 1; // for the rparen
n = align_node;
} else if (extra.type_node.unwrap()) |type_node| {
n = type_node;
} else {
end_offset += 1; // from mut token to name
return tree.nodeMainToken(n) + end_offset;
}
}
},
.local_var_decl => {
const extra_index, const init_node = tree.nodeData(n).extra_and_opt_node;
if (init_node.unwrap()) |rhs| {
n = rhs;
} else {
const extra = tree.extraData(extra_index, Node.LocalVarDecl);
end_offset += 1; // for the rparen
n = extra.align_node;
}
},
.container_field_init => {
const type_expr, const value_expr = tree.nodeData(n).node_and_opt_node;
n = value_expr.unwrap() orelse type_expr;
},
.array_access,
.array_init_one,
.container_field_align,
=> {
_, const rhs = tree.nodeData(n).node_and_node;
end_offset += 1; // for the rbracket/rbrace/rparen
n = rhs;
},
.container_field => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.ContainerField);
n = extra.value_expr;
},
.struct_init_one => {
_, const first_field = tree.nodeData(n).node_and_opt_node;
end_offset += 1; // rbrace
n = first_field.unwrap() orelse {
return tree.nodeMainToken(n) + end_offset;
};
},
.slice_open => {
_, const start_node = tree.nodeData(n).node_and_node;
end_offset += 2; // ellipsis2 + rbracket, or comma + rparen
n = start_node;
},
.array_init_one_comma => {
_, const first_element = tree.nodeData(n).node_and_node;
end_offset += 2; // comma + rbrace
n = first_element;
},
.call_one_comma,
.async_call_one_comma,
.struct_init_one_comma,
=> {
_, const first_field = tree.nodeData(n).node_and_opt_node;
end_offset += 2; // ellipsis2 + rbracket, or comma + rparen
n = first_field.unwrap().?;
},
.slice => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.Slice);
end_offset += 1; // rbracket
n = extra.end;
},
.slice_sentinel => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.SliceSentinel);
end_offset += 1; // rbracket
n = extra.sentinel;
},
.@"continue", .@"break" => {
const opt_label, const opt_rhs = tree.nodeData(n).opt_token_and_opt_node;
if (opt_rhs.unwrap()) |rhs| {
n = rhs;
} else if (opt_label.unwrap()) |lhs| {
return lhs + end_offset;
} else {
return tree.nodeMainToken(n) + end_offset;
}
},
.while_cont => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.WhileCont);
n = extra.then_expr;
},
.@"while" => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.While);
n = extra.else_expr;
},
.@"if" => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.If);
n = extra.else_expr;
},
.@"for" => {
const extra_index, const extra = tree.nodeData(n).@"for";
const index = @intFromEnum(extra_index) + extra.inputs + @intFromBool(extra.has_else);
n = @enumFromInt(tree.extra_data[index]);
},
.array_type_sentinel => {
_, const extra_index = tree.nodeData(n).node_and_extra;
const extra = tree.extraData(extra_index, Node.ArrayTypeSentinel);
n = extra.elem_type;
},
};
}