If there are parse errors, an AST can contain uninitialized nodes. Walking the tree in this case can lead to horribly nasty crashes.
620 lines
27 KiB
Zig
620 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);
|
|
|
|
if (tree.errors.len > 0)
|
|
return;
|
|
|
|
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 (analysis.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 = analysis.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 = analysis.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 = analysis.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: ast.full.While = switch (node_tags[node]) {
|
|
.@"while" => tree.whileFull(node),
|
|
.while_simple => tree.whileSimple(node),
|
|
.while_cont => tree.whileCont(node),
|
|
.for_simple => tree.forSimple(node),
|
|
.@"for" => tree.forFull(node),
|
|
else => unreachable,
|
|
};
|
|
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 tree.ifSimple(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 = analysis.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.import_uris.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 = analysis.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,
|
|
}
|
|
}
|