1const std = @import("std");
2const Ast = std.zig.Ast;
3const DocumentStore = @import("./DocumentStore.zig");
4const analysis = @import("./analysis.zig");
5const types = @import("./types.zig");
6const offsets = @import("./offsets.zig");
7const log = std.log.scoped(.references);
8const ast = @import("./ast.zig");
9
10fn tokenReference(handle: *DocumentStore.Handle, tok: Ast.TokenIndex, encoding: offsets.Encoding, context: anytype, comptime handler: anytype) !void {
11    const loc = offsets.tokenRelativeLocation(handle.tree, 0, handle.tree.tokens.items(.start)[tok], encoding) catch return;
12    try handler(context, types.Location{
13        .uri = handle.uri(),
14        .range = .{
15            .start = .{
16                .line = @intCast(i64, loc.line),
17                .character = @intCast(i64, loc.column),
18            },
19            .end = .{
20                .line = @intCast(i64, loc.line),
21                .character = @intCast(i64, loc.column + offsets.tokenLength(handle.tree, tok, encoding)),
22            },
23        },
24    });
25}
26
27pub fn labelReferences(arena: *std.heap.ArenaAllocator, decl: analysis.DeclWithHandle, encoding: offsets.Encoding, include_decl: bool, context: anytype, comptime handler: anytype) !void {
28    _ = arena;
29
30    std.debug.assert(decl.decl.* == .label_decl);
31    const handle = decl.handle;
32    const tree = handle.tree;
33    const token_tags = tree.tokens.items(.tag);
34
35    // Find while / for / block from label -> iterate over children nodes, find break and continues, change their labels if they match.
36    // This case can be implemented just by scanning tokens.
37    const first_tok = tree.firstToken(decl.decl.label_decl);
38    const last_tok = tree.firstToken(decl.decl.label_decl);
39
40    if (include_decl) {
41        // The first token is always going to be the label
42        try tokenReference(handle, first_tok, encoding, context, handler);
43    }
44
45    var curr_tok = first_tok + 1;
46    while (curr_tok < last_tok - 2) : (curr_tok += 1) {
47        const curr_id = token_tags[curr_tok];
48        if ((curr_id == .keyword_break or curr_id == .keyword_continue) and token_tags[curr_tok + 1] == .colon and
49            token_tags[curr_tok + 2] == .identifier)
50        {
51            if (std.mem.eql(u8, tree.tokenSlice(curr_tok + 2), tree.tokenSlice(first_tok))) {
52                try tokenReference(handle, first_tok, encoding, context, handler);
53            }
54        }
55    }
56}
57
58fn 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 {
59    const node = node_handle.node;
60    const handle = node_handle.handle;
61    const tree = handle.tree;
62    if (node > tree.nodes.len) return;
63    const node_tags = tree.nodes.items(.tag);
64    const datas = tree.nodes.items(.data);
65    const main_tokens = tree.nodes.items(.main_token);
66    const starts = tree.tokens.items(.start);
67
68    switch (node_tags[node]) {
69        .block, .block_semicolon, .block_two, .block_two_semicolon => {
70            const statements: []const Ast.Node.Index = switch (node_tags[node]) {
71                .block, .block_semicolon => tree.extra_data[datas[node].lhs..datas[node].rhs],
72                .block_two, .block_two_semicolon => blk: {
73                    const statements = &[_]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
74                    const len: usize = if (datas[node].lhs == 0)
75                        @as(usize, 0)
76                    else if (datas[node].rhs == 0)
77                        @as(usize, 1)
78                    else
79                        @as(usize, 2);
80                    break :blk statements[0..len];
81                },
82                else => unreachable,
83            };
84            for (statements) |stmt|
85                try symbolReferencesInternal(arena, store, .{ .node = stmt, .handle = handle }, decl, encoding, context, handler);
86        },
87        .container_decl,
88        .container_decl_trailing,
89        .container_decl_arg,
90        .container_decl_arg_trailing,
91        .container_decl_two,
92        .container_decl_two_trailing,
93        .tagged_union,
94        .tagged_union_trailing,
95        .tagged_union_two,
96        .tagged_union_two_trailing,
97        .tagged_union_enum_tag,
98        .tagged_union_enum_tag_trailing,
99        .root,
100        .error_set_decl,
101        => {
102            var buf: [2]Ast.Node.Index = undefined;
103            for (ast.declMembers(tree, node, &buf)) |member|
104                try symbolReferencesInternal(arena, store, .{ .node = member, .handle = handle }, decl, encoding, context, handler);
105        },
106        .global_var_decl,
107        .local_var_decl,
108        .simple_var_decl,
109        .aligned_var_decl,
110        => {
111            const var_decl = ast.varDecl(tree, node).?;
112            if (var_decl.ast.type_node != 0) {
113                try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.type_node, .handle = handle }, decl, encoding, context, handler);
114            }
115            if (var_decl.ast.init_node != 0) {
116                try symbolReferencesInternal(arena, store, .{ .node = var_decl.ast.init_node, .handle = handle }, decl, encoding, context, handler);
117            }
118        },
119        .@"usingnamespace" => {
120            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
121        },
122        .container_field,
123        .container_field_align,
124        .container_field_init,
125        => {
126            const field = ast.containerField(tree, node).?;
127            if (field.ast.type_expr != 0) {
128                try symbolReferencesInternal(arena, store, .{ .node = field.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
129            }
130            if (field.ast.value_expr != 0) {
131                try symbolReferencesInternal(arena, store, .{ .node = field.ast.value_expr, .handle = handle }, decl, encoding, context, handler);
132            }
133        },
134        .identifier => {
135            if (try analysis.lookupSymbolGlobal(store, arena, handle, tree.getNodeSource(node), starts[main_tokens[node]])) |child| {
136                if (std.meta.eql(decl, child)) {
137                    try tokenReference(handle, main_tokens[node], encoding, context, handler);
138                }
139            }
140        },
141        .fn_proto,
142        .fn_proto_multi,
143        .fn_proto_one,
144        .fn_proto_simple,
145        .fn_decl,
146        => {
147            var buf: [1]Ast.Node.Index = undefined;
148            const fn_proto = ast.fnProto(tree, node, &buf).?;
149            var it = fn_proto.iterate(tree);
150            while (it.next()) |param| {
151                if (param.type_expr != 0)
152                    try symbolReferencesInternal(arena, store, .{ .node = param.type_expr, .handle = handle }, decl, encoding, context, handler);
153            }
154
155            if (fn_proto.ast.return_type != 0) {
156                try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.return_type, .handle = handle }, decl, encoding, context, handler);
157            }
158            if (fn_proto.ast.align_expr != 0) {
159                try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.align_expr, .handle = handle }, decl, encoding, context, handler);
160            }
161            if (fn_proto.ast.section_expr != 0) {
162                try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.section_expr, .handle = handle }, decl, encoding, context, handler);
163            }
164            if (fn_proto.ast.callconv_expr != 0) {
165                try symbolReferencesInternal(arena, store, .{ .node = fn_proto.ast.callconv_expr, .handle = handle }, decl, encoding, context, handler);
166            }
167            if (node_tags[node] == .fn_decl) {
168                try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
169            }
170        },
171        .anyframe_type => {
172            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
173        },
174        .@"defer" => {
175            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
176        },
177        .@"comptime" => {
178            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
179        },
180        .@"nosuspend" => {
181            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
182        },
183        .@"switch",
184        .switch_comma,
185        => {
186            // TODO When renaming a union(enum) field, also rename switch items that refer to it.
187            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
188            const extra = tree.extraData(datas[node].rhs, Ast.Node.SubRange);
189            const cases = tree.extra_data[extra.start..extra.end];
190            for (cases) |case| {
191                try symbolReferencesInternal(arena, store, .{ .node = case, .handle = handle }, decl, encoding, context, handler);
192            }
193        },
194        .switch_case_one => {
195            const case_one = tree.switchCaseOne(node);
196            if (case_one.ast.target_expr != 0)
197                try symbolReferencesInternal(arena, store, .{ .node = case_one.ast.target_expr, .handle = handle }, decl, encoding, context, handler);
198            for (case_one.ast.values) |val|
199                try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler);
200        },
201        .switch_case => {
202            const case = tree.switchCase(node);
203            if (case.ast.target_expr != 0)
204                try symbolReferencesInternal(arena, store, .{ .node = case.ast.target_expr, .handle = handle }, decl, encoding, context, handler);
205            for (case.ast.values) |val|
206                try symbolReferencesInternal(arena, store, .{ .node = val, .handle = handle }, decl, encoding, context, handler);
207        },
208        .@"while",
209        .while_simple,
210        .while_cont,
211        .for_simple,
212        .@"for",
213        => {
214            const loop = ast.whileAst(tree, node).?;
215            try symbolReferencesInternal(arena, store, .{ .node = loop.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
216            if (loop.ast.cont_expr != 0) {
217                try symbolReferencesInternal(arena, store, .{ .node = loop.ast.cont_expr, .handle = handle }, decl, encoding, context, handler);
218            }
219            try symbolReferencesInternal(arena, store, .{ .node = loop.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
220            if (loop.ast.else_expr != 0) {
221                try symbolReferencesInternal(arena, store, .{ .node = loop.ast.else_expr, .handle = handle }, decl, encoding, context, handler);
222            }
223        },
224        .@"if",
225        .if_simple,
226        => {
227            const if_node = ast.ifFull(tree, node);
228
229            try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.cond_expr, .handle = handle }, decl, encoding, context, handler);
230            try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.then_expr, .handle = handle }, decl, encoding, context, handler);
231            if (if_node.ast.else_expr != 0) {
232                try symbolReferencesInternal(arena, store, .{ .node = if_node.ast.else_expr, .handle = handle }, decl, encoding, context, handler);
233            }
234        },
235        .array_type,
236        .array_type_sentinel,
237        => {
238            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
239            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
240        },
241        .ptr_type,
242        .ptr_type_aligned,
243        .ptr_type_bit_range,
244        .ptr_type_sentinel,
245        => {
246            const ptr_type = ast.ptrType(tree, node).?;
247
248            if (ptr_type.ast.align_node != 0) {
249                try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.align_node, .handle = handle }, decl, encoding, context, handler);
250                if (node_tags[node] == .ptr_type_bit_range) {
251                    try symbolReferencesInternal(arena, store, .{
252                        .node = ptr_type.ast.bit_range_start,
253                        .handle = handle,
254                    }, decl, encoding, context, handler);
255                    try symbolReferencesInternal(arena, store, .{
256                        .node = ptr_type.ast.bit_range_end,
257                        .handle = handle,
258                    }, decl, encoding, context, handler);
259                }
260            }
261            if (ptr_type.ast.sentinel != 0) {
262                try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.sentinel, .handle = handle }, decl, encoding, context, handler);
263            }
264
265            try symbolReferencesInternal(arena, store, .{ .node = ptr_type.ast.child_type, .handle = handle }, decl, encoding, context, handler);
266        },
267        .address_of, .@"await", .bit_not, .bool_not, .optional_type, .negation, .negation_wrap, .@"resume", .@"try" => {
268            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
269        },
270        .array_init,
271        .array_init_comma,
272        .array_init_dot,
273        .array_init_dot_comma,
274        .array_init_one,
275        .array_init_one_comma,
276        .array_init_dot_two,
277        .array_init_dot_two_comma,
278        => |n| {
279            var buf: [2]Ast.Node.Index = undefined;
280            const array_init = switch (n) {
281                .array_init, .array_init_comma => tree.arrayInit(node),
282                .array_init_dot, .array_init_dot_comma => tree.arrayInitDot(node),
283                .array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], node),
284                .array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, node),
285                else => unreachable,
286            };
287            if (array_init.ast.type_expr != 0)
288                try symbolReferencesInternal(arena, store, .{ .node = array_init.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
289            for (array_init.ast.elements) |e|
290                try symbolReferencesInternal(arena, store, .{ .node = e, .handle = handle }, decl, encoding, context, handler);
291        },
292        .struct_init,
293        .struct_init_comma,
294        .struct_init_dot,
295        .struct_init_dot_comma,
296        .struct_init_dot_two,
297        .struct_init_dot_two_comma,
298        .struct_init_one,
299        .struct_init_one_comma,
300        => |n| {
301            var buf: [2]Ast.Node.Index = undefined;
302            const struct_init: Ast.full.StructInit = switch (n) {
303                .struct_init, .struct_init_comma => tree.structInit(node),
304                .struct_init_dot, .struct_init_dot_comma => tree.structInitDot(node),
305                .struct_init_one, .struct_init_one_comma => tree.structInitOne(buf[0..1], node),
306                .struct_init_dot_two, .struct_init_dot_two_comma => tree.structInitDotTwo(&buf, node),
307                else => unreachable,
308            };
309            if (struct_init.ast.type_expr != 0)
310                try symbolReferencesInternal(arena, store, .{ .node = struct_init.ast.type_expr, .handle = handle }, decl, encoding, context, handler);
311            for (struct_init.ast.fields) |field|
312                try symbolReferencesInternal(arena, store, .{ .node = field, .handle = handle }, decl, encoding, context, handler);
313        },
314        .call,
315        .call_comma,
316        .call_one,
317        .call_one_comma,
318        .async_call,
319        .async_call_comma,
320        .async_call_one,
321        .async_call_one_comma,
322        => |c| {
323            var buf: [1]Ast.Node.Index = undefined;
324            const call: Ast.full.Call = switch (c) {
325                .call, .call_comma, .async_call, .async_call_comma => tree.callFull(node),
326                .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => tree.callOne(&buf, node),
327                else => unreachable,
328            };
329            if (call.ast.fn_expr != 0)
330                try symbolReferencesInternal(arena, store, .{ .node = call.ast.fn_expr, .handle = handle }, decl, encoding, context, handler);
331
332            for (call.ast.params) |param| {
333                try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
334            }
335        },
336        .slice,
337        .slice_sentinel,
338        .slice_open,
339        => |s| {
340            const slice: Ast.full.Slice = switch (s) {
341                .slice => tree.slice(node),
342                .slice_open => tree.sliceOpen(node),
343                .slice_sentinel => tree.sliceSentinel(node),
344                else => unreachable,
345            };
346
347            try symbolReferencesInternal(arena, store, .{ .node = slice.ast.sliced, .handle = handle }, decl, encoding, context, handler);
348            try symbolReferencesInternal(arena, store, .{ .node = slice.ast.start, .handle = handle }, decl, encoding, context, handler);
349            if (slice.ast.end != 0)
350                try symbolReferencesInternal(arena, store, .{ .node = slice.ast.end, .handle = handle }, decl, encoding, context, handler);
351            if (slice.ast.sentinel != 0)
352                try symbolReferencesInternal(arena, store, .{ .node = slice.ast.sentinel, .handle = handle }, decl, encoding, context, handler);
353        },
354        .array_access => {
355            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
356            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
357        },
358        .deref,
359        .unwrap_optional,
360        => {
361            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
362        },
363        .grouped_expression => {
364            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
365        },
366        .@"return",
367        .@"break",
368        .@"continue",
369        => {
370            if (datas[node].lhs != 0) {
371                try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
372            }
373        },
374        .@"suspend" => {
375            if (datas[node].lhs != 0) {
376                try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
377            }
378        },
379        .builtin_call,
380        .builtin_call_comma,
381        .builtin_call_two,
382        .builtin_call_two_comma,
383        => |builtin_tag| {
384            const data = datas[node];
385            const params = switch (builtin_tag) {
386                .builtin_call, .builtin_call_comma => tree.extra_data[data.lhs..data.rhs],
387                .builtin_call_two, .builtin_call_two_comma => if (data.lhs == 0)
388                    &[_]Ast.Node.Index{}
389                else if (data.rhs == 0)
390                    &[_]Ast.Node.Index{data.lhs}
391                else
392                    &[_]Ast.Node.Index{ data.lhs, data.rhs },
393                else => unreachable,
394            };
395
396            for (params) |param|
397                try symbolReferencesInternal(arena, store, .{ .node = param, .handle = handle }, decl, encoding, context, handler);
398        },
399        .@"asm",
400        .asm_simple,
401        => |a| {
402            const _asm: Ast.full.Asm = if (a == .@"asm") tree.asmFull(node) else tree.asmSimple(node);
403            if (_asm.ast.items.len == 0)
404                try symbolReferencesInternal(arena, store, .{ .node = _asm.ast.template, .handle = handle }, decl, encoding, context, handler);
405
406            for (_asm.inputs) |input|
407                try symbolReferencesInternal(arena, store, .{ .node = input, .handle = handle }, decl, encoding, context, handler);
408
409            for (_asm.outputs) |output|
410                try symbolReferencesInternal(arena, store, .{ .node = output, .handle = handle }, decl, encoding, context, handler);
411        },
412        .test_decl => {
413            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
414        },
415        .field_access => {
416            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
417
418            const rhs_str = tree.tokenSlice(datas[node].rhs);
419            var bound_type_params = analysis.BoundTypeParams.init(arena.allocator());
420            const left_type = try analysis.resolveFieldAccessLhsType(
421                store,
422                arena,
423                (try analysis.resolveTypeOfNodeInternal(store, arena, .{
424                    .node = datas[node].lhs,
425                    .handle = handle,
426                }, &bound_type_params)) orelse return,
427                &bound_type_params,
428            );
429
430            const left_type_node = switch (left_type.type.data) {
431                .other => |n| n,
432                else => return,
433            };
434
435            if (try analysis.lookupSymbolContainer(
436                store,
437                arena,
438                .{ .node = left_type_node, .handle = left_type.handle },
439                rhs_str,
440                !left_type.type.is_type_val,
441            )) |child| {
442                if (std.meta.eql(child, decl)) {
443                    try tokenReference(handle, datas[node].rhs, encoding, context, handler);
444                }
445            }
446        },
447        .add,
448        .add_wrap,
449        .array_cat,
450        .array_mult,
451        .assign,
452        .assign_bit_and,
453        .assign_bit_or,
454        .assign_shl,
455        .assign_shr,
456        .assign_bit_xor,
457        .assign_div,
458        .assign_sub,
459        .assign_sub_wrap,
460        .assign_mod,
461        .assign_add,
462        .assign_add_wrap,
463        .assign_mul,
464        .assign_mul_wrap,
465        .bang_equal,
466        .bit_and,
467        .bit_or,
468        .shl,
469        .shr,
470        .bit_xor,
471        .bool_or,
472        .div,
473        .equal_equal,
474        .error_union,
475        .greater_or_equal,
476        .greater_than,
477        .less_or_equal,
478        .less_than,
479        .merge_error_sets,
480        .mod,
481        .mul,
482        .mul_wrap,
483        .switch_range,
484        .sub,
485        .sub_wrap,
486        .@"orelse",
487        => {
488            try symbolReferencesInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle }, decl, encoding, context, handler);
489            try symbolReferencesInternal(arena, store, .{ .node = datas[node].rhs, .handle = handle }, decl, encoding, context, handler);
490        },
491        else => {},
492    }
493}
494
495pub 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 {
496    std.debug.assert(decl_handle.decl.* != .label_decl);
497    const curr_handle = decl_handle.handle;
498    if (include_decl) {
499        try tokenReference(curr_handle, decl_handle.nameToken(), encoding, context, handler);
500    }
501
502    switch (decl_handle.decl.*) {
503        .ast_node => {
504            try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler);
505
506            var imports = std.ArrayList(*DocumentStore.Handle).init(arena.allocator());
507
508            var handle_it = store.handles.iterator();
509            while (handle_it.next()) |entry| {
510                if (skip_std_references and std.mem.indexOf(u8, entry.key_ptr.*, "std") != null) {
511                    if (!include_decl or entry.value_ptr.* != curr_handle)
512                        continue;
513                }
514
515                // Check entry's transitive imports
516                try imports.append(entry.value_ptr.*);
517                var i: usize = 0;
518                blk: while (i < imports.items.len) : (i += 1) {
519                    const import = imports.items[i];
520                    for (import.imports_used.items) |uri| {
521                        const h = store.getHandle(uri) orelse break;
522
523                        if (h == curr_handle) {
524                            // entry does import curr_handle
525                            try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = entry.value_ptr.* }, decl_handle, encoding, context, handler);
526                            break :blk;
527                        }
528
529                        select: {
530                            for (imports.items) |item| {
531                                if (item == h) {
532                                    // already checked this import
533                                    break :select;
534                                }
535                            }
536                            try imports.append(h);
537                        }
538                    }
539                }
540                try imports.resize(0);
541            }
542        },
543        .param_decl => |param| {
544            // Rename the param tok.
545            const fn_node: Ast.full.FnProto = loop: for (curr_handle.document_scope.scopes) |scope| {
546                switch (scope.data) {
547                    .function => |proto| {
548                        var buf: [1]Ast.Node.Index = undefined;
549                        const fn_proto = ast.fnProto(curr_handle.tree, proto, &buf).?;
550                        var it = fn_proto.iterate(curr_handle.tree);
551                        while (it.next()) |candidate| {
552                            if (std.meta.eql(candidate, param)) {
553                                if (curr_handle.tree.nodes.items(.tag)[proto] == .fn_decl) {
554                                    try symbolReferencesInternal(
555                                        arena,
556                                        store,
557                                        .{ .node = curr_handle.tree.nodes.items(.data)[proto].rhs, .handle = curr_handle },
558                                        decl_handle,
559                                        encoding,
560                                        context,
561                                        handler,
562                                    );
563                                }
564                                break :loop fn_proto;
565                            }
566                        }
567                    },
568                    else => {},
569                }
570            } else {
571                log.warn("Could not find param decl's function", .{});
572                return;
573            };
574            _ = fn_node;
575        },
576        .pointer_payload, .switch_payload, .array_payload, .array_index => {
577            try symbolReferencesInternal(arena, store, .{ .node = 0, .handle = curr_handle }, decl_handle, encoding, context, handler);
578        },
579        .label_decl => unreachable,
580    }
581}
582