610 lines
27 KiB
Zig
610 lines
27 KiB
Zig
const std = @import("std");
|
|
const DocumentStore = @import("document_store.zig");
|
|
const analysis = @import("analysis.zig");
|
|
const types = @import("types.zig");
|
|
const offsets = @import("offsets.zig");
|
|
const log = std.log.scoped(.references);
|
|
usingnamespace @import("ast.zig");
|
|
|
|
const ast = std.zig.ast;
|
|
|
|
fn tokenReference(
|
|
handle: *DocumentStore.Handle,
|
|
tok: ast.TokenIndex,
|
|
encoding: offsets.Encoding,
|
|
context: anytype,
|
|
comptime handler: anytype,
|
|
) !void {
|
|
const loc = offsets.tokenRelativeLocation(handle.tree, 0, handle.tree.tokens.items(.start)[tok], encoding) catch return;
|
|
try handler(context, types.Location{
|
|
.uri = handle.uri(),
|
|
.range = .{
|
|
.start = .{
|
|
.line = @intCast(i64, loc.line),
|
|
.character = @intCast(i64, loc.column),
|
|
},
|
|
.end = .{
|
|
.line = @intCast(i64, loc.line),
|
|
.character = @intCast(i64, loc.column + offsets.tokenLength(handle.tree, tok, encoding)),
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
pub fn labelReferences(
|
|
arena: *std.heap.ArenaAllocator,
|
|
decl: analysis.DeclWithHandle,
|
|
encoding: offsets.Encoding,
|
|
include_decl: bool,
|
|
context: anytype,
|
|
comptime handler: anytype,
|
|
) !void {
|
|
std.debug.assert(decl.decl.* == .label_decl);
|
|
const handle = decl.handle;
|
|
const tree = handle.tree;
|
|
const token_tags = tree.tokens.items(.tag);
|
|
|
|
// Find while / for / block from label -> iterate over children nodes, find break and continues, change their labels if they match.
|
|
// This case can be implemented just by scanning tokens.
|
|
const first_tok = tree.firstToken(decl.decl.label_decl);
|
|
const last_tok = tree.firstToken(decl.decl.label_decl);
|
|
|
|
if (include_decl) {
|
|
// The first token is always going to be the label
|
|
try tokenReference(handle, first_tok, encoding, context, handler);
|
|
}
|
|
|
|
var curr_tok = first_tok + 1;
|
|
while (curr_tok < last_tok - 2) : (curr_tok += 1) {
|
|
const curr_id = token_tags[curr_tok];
|
|
if ((curr_id == .keyword_break or curr_id == .keyword_continue) and token_tags[curr_tok + 1] == .colon and
|
|
token_tags[curr_tok + 2] == .identifier)
|
|
{
|
|
if (std.mem.eql(u8, tree.tokenSlice(curr_tok + 2), tree.tokenSlice(first_tok))) {
|
|
try tokenReference(handle, first_tok, encoding, context, handler);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn symbolReferencesInternal(
|
|
arena: *std.heap.ArenaAllocator,
|
|
store: *DocumentStore,
|
|
node_handle: analysis.NodeWithHandle,
|
|
decl: analysis.DeclWithHandle,
|
|
encoding: offsets.Encoding,
|
|
context: anytype,
|
|
comptime handler: anytype,
|
|
) error{OutOfMemory}!void {
|
|
const node = node_handle.node;
|
|
const handle = node_handle.handle;
|
|
const tree = handle.tree;
|
|
if (node > tree.nodes.len) return;
|
|
const node_tags = tree.nodes.items(.tag);
|
|
const datas = tree.nodes.items(.data);
|
|
const main_tokens = tree.nodes.items(.main_token);
|
|
const starts = tree.tokens.items(.start);
|
|
|
|
switch (node_tags[node]) {
|
|
.block, .block_semicolon, .block_two, .block_two_semicolon => {
|
|
const statements: []const ast.Node.Index = switch (node_tags[node]) {
|
|
.block, .block_semicolon => tree.extra_data[datas[node].lhs..datas[node].rhs],
|
|
.block_two, .block_two_semicolon => blk: {
|
|
const statements = &[_]ast.Node.Index{ datas[node].lhs, datas[node].rhs };
|
|
const len: usize = if (datas[node].lhs == 0)
|
|
@as(usize, 0)
|
|
else if (datas[node].rhs == 0)
|
|
@as(usize, 1)
|
|
else
|
|
@as(usize, 2);
|
|
break :blk statements[0..len];
|
|
},
|
|
else => unreachable,
|
|
};
|
|
for (statements) |stmt|
|
|
try symbolReferencesInternal(arena, store, .{ .node = stmt, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.container_decl,
|
|
.container_decl_trailing,
|
|
.container_decl_arg,
|
|
.container_decl_arg_trailing,
|
|
.container_decl_two,
|
|
.container_decl_two_trailing,
|
|
.tagged_union,
|
|
.tagged_union_trailing,
|
|
.tagged_union_two,
|
|
.tagged_union_two_trailing,
|
|
.tagged_union_enum_tag,
|
|
.tagged_union_enum_tag_trailing,
|
|
.root,
|
|
.error_set_decl,
|
|
=> {
|
|
var buf: [2]ast.Node.Index = undefined;
|
|
for (declMembers(tree, node, &buf)) |member|
|
|
try symbolReferencesInternal(arena, store, .{ .node = member, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.global_var_decl,
|
|
.local_var_decl,
|
|
.simple_var_decl,
|
|
.aligned_var_decl,
|
|
=> {
|
|
const var_decl = varDecl(tree, node).?;
|
|
if (var_decl.ast.type_node != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.type_node, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
if (var_decl.ast.init_node != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.init_node, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.@"usingnamespace" => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.container_field,
|
|
.container_field_align,
|
|
.container_field_init,
|
|
=> {
|
|
const field = containerField(tree, node).?;
|
|
if (field.ast.type_expr != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = field.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
if (field.ast.value_expr != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = field.ast.value_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.identifier => {
|
|
if (try analysis.lookupSymbolGlobal(store, arena, handle, tree.getNodeSource(node), starts[main_tokens[node]])) |child| {
|
|
if (std.meta.eql(decl, child)) {
|
|
try tokenReference(handle, main_tokens[node], encoding, context, handler);
|
|
}
|
|
}
|
|
},
|
|
.fn_proto,
|
|
.fn_proto_multi,
|
|
.fn_proto_one,
|
|
.fn_proto_simple,
|
|
.fn_decl,
|
|
=> {
|
|
var buf: [1]ast.Node.Index = undefined;
|
|
const fn_proto = fnProto(tree, node, &buf).?;
|
|
var it = fn_proto.iterate(tree);
|
|
while (it.next()) |param| {
|
|
if (param.type_expr != 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = param.type_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
|
|
if (fn_proto.ast.return_type != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.return_type, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
if (fn_proto.ast.align_expr != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.align_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
if (fn_proto.ast.section_expr != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.section_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
if (fn_proto.ast.callconv_expr != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.callconv_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
if (node_tags[node] == .fn_decl) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.anyframe_type => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.@"defer" => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.@"comptime" => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.@"nosuspend" => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.@"switch",
|
|
.switch_comma,
|
|
=> {
|
|
// TODO When renaming a union(enum) field, also rename switch items that refer to it.
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
const extra = tree.extraData(datas[node].rhs, ast.Node.SubRange);
|
|
const cases = tree.extra_data[extra.start..extra.end];
|
|
for (cases) |case| {
|
|
try symbolReferencesInternal(arena, store, .{ .node = case, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.switch_case_one => {
|
|
const case_one = tree.switchCaseOne(node);
|
|
if (case_one.ast.target_expr != 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = case_one.ast.target_expr, .handle = handle }, decl, encoding, context, handler);
|
|
for (case_one.ast.values) |val|
|
|
try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.switch_case => {
|
|
const case = tree.switchCase(node);
|
|
if (case.ast.target_expr != 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = case.ast.target_expr, .handle = handle }, decl, encoding, context, handler);
|
|
for (case.ast.values) |val|
|
|
try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.@"while",
|
|
.while_simple,
|
|
.while_cont,
|
|
.for_simple,
|
|
.@"for",
|
|
=> {
|
|
const loop = whileAst(tree, node).?;
|
|
try symbolReferencesInternal(arena, store, .{ .node = loop.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
|
|
if (loop.ast.cont_expr != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = loop.ast.cont_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
try symbolReferencesInternal(arena, store, .{ .node = loop.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
|
|
if (loop.ast.else_expr != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = loop.ast.else_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.@"if",
|
|
.if_simple,
|
|
=> {
|
|
const if_node: ast.full.If = if (node_tags[node] == .@"if") ifFull(tree, node) else ifSimple(tree, node);
|
|
|
|
try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
|
|
try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
|
|
if (if_node.ast.else_expr != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.else_expr, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.array_type,
|
|
.array_type_sentinel,
|
|
=> {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.ptr_type,
|
|
.ptr_type_aligned,
|
|
.ptr_type_bit_range,
|
|
.ptr_type_sentinel,
|
|
=> {
|
|
const ptr_type = ptrType(tree, node).?;
|
|
|
|
if (ptr_type.ast.align_node != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.align_node, .handle = handle }, decl, encoding, context, handler);
|
|
if (node_tags[node] == .ptr_type_bit_range) {
|
|
try symbolReferencesInternal(arena, store, .{
|
|
.node = ptr_type.ast.bit_range_start,
|
|
.handle = handle,
|
|
}, decl, encoding, context, handler);
|
|
try symbolReferencesInternal(arena, store, .{
|
|
.node = ptr_type.ast.bit_range_end,
|
|
.handle = handle,
|
|
}, decl, encoding, context, handler);
|
|
}
|
|
}
|
|
if (ptr_type.ast.sentinel != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.sentinel, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
|
|
try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.child_type, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.address_of, .@"await", .bit_not, .bool_not, .optional_type, .negation, .negation_wrap, .@"resume", .@"try" => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.array_init,
|
|
.array_init_comma,
|
|
.array_init_dot,
|
|
.array_init_dot_comma,
|
|
.array_init_one,
|
|
.array_init_one_comma,
|
|
.array_init_dot_two,
|
|
.array_init_dot_two_comma,
|
|
=> |n| {
|
|
var buf: [2]ast.Node.Index = undefined;
|
|
const array_init = switch (n) {
|
|
.array_init, .array_init_comma => tree.arrayInit(node),
|
|
.array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node),
|
|
.array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], node),
|
|
.array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, node),
|
|
else => unreachable,
|
|
};
|
|
if (array_init.ast.type_expr != 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = array_init.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
|
|
for (array_init.ast.elements) |e|
|
|
try symbolReferencesInternal(arena, store, .{ .node = e, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.struct_init,
|
|
.struct_init_comma,
|
|
.struct_init_dot,
|
|
.struct_init_dot_comma,
|
|
.struct_init_dot_two,
|
|
.struct_init_dot_two_comma,
|
|
.struct_init_one,
|
|
.struct_init_one_comma,
|
|
=> |n| {
|
|
var buf: [2]ast.Node.Index = undefined;
|
|
const struct_init: ast.full.StructInit = switch (n) {
|
|
.struct_init, .struct_init_comma => tree.structInit(node),
|
|
.struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node),
|
|
.struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], node),
|
|
.struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node),
|
|
else => unreachable,
|
|
};
|
|
if (struct_init.ast.type_expr != 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = struct_init.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
|
|
for (struct_init.ast.fields) |field|
|
|
try symbolReferencesInternal(arena, store, .{ .node = field, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.call,
|
|
.call_comma,
|
|
.call_one,
|
|
.call_one_comma,
|
|
.async_call,
|
|
.async_call_comma,
|
|
.async_call_one,
|
|
.async_call_one_comma,
|
|
=> |c| {
|
|
var buf: [1]ast.Node.Index = undefined;
|
|
const call: ast.full.Call = switch (c) {
|
|
.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(&buf, node),
|
|
else => unreachable,
|
|
};
|
|
if (call.ast.fn_expr != 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = call.ast.fn_expr, .handle = handle }, decl, encoding, context, handler);
|
|
|
|
for (call.ast.params) |param| {
|
|
try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.slice,
|
|
.slice_sentinel,
|
|
.slice_open,
|
|
=> |s| {
|
|
const slice: ast.full.Slice = switch (s) {
|
|
.slice => tree.slice(node),
|
|
.slice_open => tree.sliceOpen(node),
|
|
.slice_sentinel => tree.sliceSentinel(node),
|
|
else => unreachable,
|
|
};
|
|
|
|
try symbolReferencesInternal(arena, store, .{ .node = slice.ast.sliced, .handle = handle }, decl, encoding, context, handler);
|
|
try symbolReferencesInternal(arena, store, .{ .node = slice.ast.start, .handle = handle }, decl, encoding, context, handler);
|
|
if (slice.ast.end != 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = slice.ast.end, .handle = handle }, decl, encoding, context, handler);
|
|
if (slice.ast.sentinel != 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = slice.ast.sentinel, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.array_access => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.deref,
|
|
.unwrap_optional,
|
|
=> {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.grouped_expression => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.@"return",
|
|
.@"break",
|
|
.@"continue",
|
|
=> {
|
|
if (datas[node].lhs != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.@"suspend" => {
|
|
if (datas[node].lhs != 0) {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
}
|
|
},
|
|
.builtin_call,
|
|
.builtin_call_comma,
|
|
.builtin_call_two,
|
|
.builtin_call_two_comma,
|
|
=> |builtin_tag| {
|
|
const data = datas[node];
|
|
const params = switch (builtin_tag) {
|
|
.builtin_call, .builtin_call_comma => tree.extra_data[data.lhs..data.rhs],
|
|
.builtin_call_two, .builtin_call_two_comma => if (data.lhs == 0)
|
|
&[_]ast.Node.Index{}
|
|
else if (data.rhs == 0)
|
|
&[_]ast.Node.Index{data.lhs}
|
|
else
|
|
&[_]ast.Node.Index{ data.lhs, data.rhs },
|
|
else => unreachable,
|
|
};
|
|
|
|
for (params) |param|
|
|
try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.@"asm",
|
|
.asm_simple,
|
|
=> |a| {
|
|
const _asm: ast.full.Asm = if (a == .@"asm") tree.asmFull(node) else tree.asmSimple(node);
|
|
if (_asm.ast.items.len == 0)
|
|
try symbolReferencesInternal(arena, store, .{ .node = _asm.ast.template, .handle = handle }, decl, encoding, context, handler);
|
|
|
|
for (_asm.inputs) |input|
|
|
try symbolReferencesInternal(arena, store, .{ .node = input, .handle = handle }, decl, encoding, context, handler);
|
|
|
|
for (_asm.outputs) |output|
|
|
try symbolReferencesInternal(arena, store, .{ .node = output, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.test_decl => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
.field_access => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
|
|
const rhs_str = tree.tokenSlice(datas[node].rhs);
|
|
var bound_type_params = analysis.BoundTypeParams.init(&arena.allocator);
|
|
const left_type = try analysis.resolveFieldAccessLhsType(
|
|
store,
|
|
arena,
|
|
(try analysis.resolveTypeOfNodeInternal(store, arena, .{
|
|
.node = datas[node].lhs,
|
|
.handle = handle,
|
|
}, &bound_type_params)) orelse return,
|
|
&bound_type_params,
|
|
);
|
|
|
|
const left_type_node = switch (left_type.type.data) {
|
|
.other => |n| n,
|
|
else => return,
|
|
};
|
|
|
|
if (try analysis.lookupSymbolContainer(
|
|
store,
|
|
arena,
|
|
.{ .node = left_type_node, .handle = left_type.handle },
|
|
rhs_str,
|
|
!left_type.type.is_type_val,
|
|
)) |child| {
|
|
if (std.meta.eql(child, decl)) {
|
|
try tokenReference(handle, datas[node].rhs, encoding, context, handler);
|
|
}
|
|
}
|
|
},
|
|
.add,
|
|
.add_wrap,
|
|
.array_cat,
|
|
.array_mult,
|
|
.assign,
|
|
.assign_bit_and,
|
|
.assign_bit_or,
|
|
.assign_bit_shift_left,
|
|
.assign_bit_shift_right,
|
|
.assign_bit_xor,
|
|
.assign_div,
|
|
.assign_sub,
|
|
.assign_sub_wrap,
|
|
.assign_mod,
|
|
.assign_add,
|
|
.assign_add_wrap,
|
|
.assign_mul,
|
|
.assign_mul_wrap,
|
|
.bang_equal,
|
|
.bit_and,
|
|
.bit_or,
|
|
.bit_shift_left,
|
|
.bit_shift_right,
|
|
.bit_xor,
|
|
.bool_or,
|
|
.div,
|
|
.equal_equal,
|
|
.error_union,
|
|
.greater_or_equal,
|
|
.greater_than,
|
|
.less_or_equal,
|
|
.less_than,
|
|
.merge_error_sets,
|
|
.mod,
|
|
.mul,
|
|
.mul_wrap,
|
|
.switch_range,
|
|
.sub,
|
|
.sub_wrap,
|
|
.@"orelse",
|
|
=> {
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
|
|
try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
|
|
pub fn symbolReferences(
|
|
arena: *std.heap.ArenaAllocator,
|
|
store: *DocumentStore,
|
|
decl_handle: analysis.DeclWithHandle,
|
|
encoding: offsets.Encoding,
|
|
include_decl: bool,
|
|
context: anytype,
|
|
comptime handler: anytype,
|
|
skip_std_references: bool,
|
|
) !void {
|
|
std.debug.assert(decl_handle.decl.* != .label_decl);
|
|
const curr_handle = decl_handle.handle;
|
|
if (include_decl) {
|
|
try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler);
|
|
}
|
|
|
|
switch (decl_handle.decl.*) {
|
|
.ast_node => |decl_node| {
|
|
try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler);
|
|
|
|
var imports = std.ArrayList(*DocumentStore.Handle).init(&arena.allocator);
|
|
|
|
var handle_it = store.handles.iterator();
|
|
while (handle_it.next()) |entry| {
|
|
if (skip_std_references and std.mem.indexOf(u8, entry.key, "std") != null) {
|
|
if (!include_decl or entry.value != curr_handle)
|
|
continue;
|
|
}
|
|
|
|
// Check entry's transitive imports
|
|
try imports.append(entry.value);
|
|
var i: usize = 0;
|
|
blk: while (i < imports.items.len) : (i += 1) {
|
|
const import = imports.items[i];
|
|
for (import.imports_used.items) |uri| {
|
|
const h = store.getHandle(uri) orelse break;
|
|
|
|
if (h == curr_handle) {
|
|
// entry does import curr_handle
|
|
try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = entry.value }, decl_handle, encoding, context, handler);
|
|
break :blk;
|
|
}
|
|
|
|
select: {
|
|
for (imports.items) |item| {
|
|
if (item == h) {
|
|
// already checked this import
|
|
break :select;
|
|
}
|
|
}
|
|
try imports.append(h);
|
|
}
|
|
}
|
|
}
|
|
try imports.resize(0);
|
|
}
|
|
},
|
|
.param_decl => |param| {
|
|
// Rename the param tok.
|
|
const fn_node: ast.full.FnProto = loop: for (curr_handle.document_scope.scopes) |scope| {
|
|
switch (scope.data) {
|
|
.function => |proto| {
|
|
var buf: [1]ast.Node.Index = undefined;
|
|
const fn_proto = fnProto(curr_handle.tree, proto, &buf).?;
|
|
var it = fn_proto.iterate(curr_handle.tree);
|
|
while (it.next()) |candidate| {
|
|
if (std.meta.eql(candidate, param)) {
|
|
if (curr_handle.tree.nodes.items(.tag)[proto] == .fn_decl) {
|
|
try symbolReferencesInternal(
|
|
arena,
|
|
store,
|
|
.{ .node = curr_handle.tree.nodes.items(.data)[proto].rhs, .handle = curr_handle },
|
|
decl_handle,
|
|
encoding,
|
|
context,
|
|
handler,
|
|
);
|
|
}
|
|
break :loop fn_proto;
|
|
}
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
} else {
|
|
log.warn("Could not find param decl's function", .{});
|
|
return;
|
|
};
|
|
},
|
|
.pointer_payload, .switch_payload, .array_payload, .array_index => {
|
|
try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler);
|
|
},
|
|
.label_decl => unreachable,
|
|
}
|
|
}
|