Add anytype resolution based on call references (#1067)
This commit is contained in:
		
							parent
							
								
									c217502670
								
							
						
					
					
						commit
						8b5c649805
					
				
							
								
								
									
										159
									
								
								src/analysis.zig
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								src/analysis.zig
									
									
									
									
									
								
							@ -9,6 +9,7 @@ const ast = @import("ast.zig");
 | 
				
			|||||||
const tracy = @import("tracy.zig");
 | 
					const tracy = @import("tracy.zig");
 | 
				
			||||||
const ComptimeInterpreter = @import("ComptimeInterpreter.zig");
 | 
					const ComptimeInterpreter = @import("ComptimeInterpreter.zig");
 | 
				
			||||||
const InternPool = ComptimeInterpreter.InternPool;
 | 
					const InternPool = ComptimeInterpreter.InternPool;
 | 
				
			||||||
 | 
					const references = @import("features/references.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Analyser = @This();
 | 
					const Analyser = @This();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1162,6 +1163,77 @@ pub const TypeWithHandle = struct {
 | 
				
			|||||||
    type: Type,
 | 
					    type: Type,
 | 
				
			||||||
    handle: *const DocumentStore.Handle,
 | 
					    handle: *const DocumentStore.Handle,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const Context = struct {
 | 
				
			||||||
 | 
					        // Note that we don't hash/equate descriptors to remove
 | 
				
			||||||
 | 
					        // duplicates
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fn hashType(hasher: *std.hash.Wyhash, ty: Type) void {
 | 
				
			||||||
 | 
					            hasher.update(&.{ @boolToInt(ty.is_type_val), @enumToInt(ty.data) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            switch (ty.data) {
 | 
				
			||||||
 | 
					                .pointer,
 | 
				
			||||||
 | 
					                .slice,
 | 
				
			||||||
 | 
					                .error_union,
 | 
				
			||||||
 | 
					                .other,
 | 
				
			||||||
 | 
					                .primitive,
 | 
				
			||||||
 | 
					                => |idx| hasher.update(&std.mem.toBytes(idx)),
 | 
				
			||||||
 | 
					                .either => |entries| {
 | 
				
			||||||
 | 
					                    for (entries) |e| {
 | 
				
			||||||
 | 
					                        hasher.update(e.descriptor);
 | 
				
			||||||
 | 
					                        hasher.update(e.type_with_handle.handle.uri);
 | 
				
			||||||
 | 
					                        hashType(hasher, e.type_with_handle.type);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                .array_index => {},
 | 
				
			||||||
 | 
					                .@"comptime" => {
 | 
				
			||||||
 | 
					                    // TODO
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pub fn hash(self: @This(), item: TypeWithHandle) u64 {
 | 
				
			||||||
 | 
					            _ = self;
 | 
				
			||||||
 | 
					            var hasher = std.hash.Wyhash.init(0);
 | 
				
			||||||
 | 
					            hashType(&hasher, item.type);
 | 
				
			||||||
 | 
					            hasher.update(item.handle.uri);
 | 
				
			||||||
 | 
					            return hasher.final();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        pub fn eql(self: @This(), a: TypeWithHandle, b: TypeWithHandle) bool {
 | 
				
			||||||
 | 
					            _ = self;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (!std.mem.eql(u8, a.handle.uri, b.handle.uri)) return false;
 | 
				
			||||||
 | 
					            if (a.type.is_type_val != b.type.is_type_val) return false;
 | 
				
			||||||
 | 
					            if (@enumToInt(a.type.data) != @enumToInt(b.type.data)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            switch (a.type.data) {
 | 
				
			||||||
 | 
					                inline .pointer,
 | 
				
			||||||
 | 
					                .slice,
 | 
				
			||||||
 | 
					                .error_union,
 | 
				
			||||||
 | 
					                .other,
 | 
				
			||||||
 | 
					                .primitive,
 | 
				
			||||||
 | 
					                => |a_idx, name| {
 | 
				
			||||||
 | 
					                    if (a_idx != @field(b.type.data, @tagName(name))) return false;
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                .either => |a_entries| {
 | 
				
			||||||
 | 
					                    const b_entries = b.type.data.either;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (a_entries.len != b_entries.len) return false;
 | 
				
			||||||
 | 
					                    for (a_entries, b_entries) |ae, be| {
 | 
				
			||||||
 | 
					                        if (!std.mem.eql(u8, ae.descriptor, be.descriptor)) return false;
 | 
				
			||||||
 | 
					                        if (!eql(.{}, ae.type_with_handle, be.type_with_handle)) return false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					                .array_index => {},
 | 
				
			||||||
 | 
					                .@"comptime" => {
 | 
				
			||||||
 | 
					                    // TODO
 | 
				
			||||||
 | 
					                },
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn typeVal(node_handle: NodeWithHandle) TypeWithHandle {
 | 
					    pub fn typeVal(node_handle: NodeWithHandle) TypeWithHandle {
 | 
				
			||||||
        return .{
 | 
					        return .{
 | 
				
			||||||
            .type = .{
 | 
					            .type = .{
 | 
				
			||||||
@ -1172,7 +1244,10 @@ pub const TypeWithHandle = struct {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub const Deduplicator = std.HashMapUnmanaged(TypeWithHandle, void, TypeWithHandle.Context, std.hash_map.default_max_load_percentage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Resolves possible types of a type (single for all except array_index and either)
 | 
					    /// Resolves possible types of a type (single for all except array_index and either)
 | 
				
			||||||
 | 
					    /// Drops duplicates
 | 
				
			||||||
    pub fn getAllTypesWithHandles(ty: TypeWithHandle, arena: std.mem.Allocator) ![]const TypeWithHandle {
 | 
					    pub fn getAllTypesWithHandles(ty: TypeWithHandle, arena: std.mem.Allocator) ![]const TypeWithHandle {
 | 
				
			||||||
        var all_types = std.ArrayListUnmanaged(TypeWithHandle){};
 | 
					        var all_types = std.ArrayListUnmanaged(TypeWithHandle){};
 | 
				
			||||||
        try ty.getAllTypesWithHandlesArrayList(arena, &all_types);
 | 
					        try ty.getAllTypesWithHandlesArrayList(arena, &all_types);
 | 
				
			||||||
@ -1866,6 +1941,7 @@ pub const Declaration = union(enum) {
 | 
				
			|||||||
    /// Function parameter
 | 
					    /// Function parameter
 | 
				
			||||||
    param_payload: struct {
 | 
					    param_payload: struct {
 | 
				
			||||||
        param: Ast.full.FnProto.Param,
 | 
					        param: Ast.full.FnProto.Param,
 | 
				
			||||||
 | 
					        param_idx: u16,
 | 
				
			||||||
        func: Ast.Node.Index,
 | 
					        func: Ast.Node.Index,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    pointer_payload: struct {
 | 
					    pointer_payload: struct {
 | 
				
			||||||
@ -1888,12 +1964,20 @@ pub const Declaration = union(enum) {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    /// always an identifier
 | 
					    /// always an identifier
 | 
				
			||||||
    error_token: Ast.Node.Index,
 | 
					    error_token: Ast.Node.Index,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn eql(a: Declaration, b: Declaration) bool {
 | 
				
			||||||
 | 
					        return std.meta.eql(a, b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const DeclWithHandle = struct {
 | 
					pub const DeclWithHandle = struct {
 | 
				
			||||||
    decl: *Declaration,
 | 
					    decl: *Declaration,
 | 
				
			||||||
    handle: *const DocumentStore.Handle,
 | 
					    handle: *const DocumentStore.Handle,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn eql(a: DeclWithHandle, b: DeclWithHandle) bool {
 | 
				
			||||||
 | 
					        return a.decl.eql(b.decl.*) and std.mem.eql(u8, a.handle.uri, b.handle.uri);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn nameToken(self: DeclWithHandle) Ast.TokenIndex {
 | 
					    pub fn nameToken(self: DeclWithHandle) Ast.TokenIndex {
 | 
				
			||||||
        const tree = self.handle.tree;
 | 
					        const tree = self.handle.tree;
 | 
				
			||||||
        return switch (self.decl.*) {
 | 
					        return switch (self.decl.*) {
 | 
				
			||||||
@ -1924,6 +2008,71 @@ pub const DeclWithHandle = struct {
 | 
				
			|||||||
                .{ .node = node, .handle = self.handle },
 | 
					                .{ .node = node, .handle = self.handle },
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            .param_payload => |pay| {
 | 
					            .param_payload => |pay| {
 | 
				
			||||||
 | 
					                // handle anytype
 | 
				
			||||||
 | 
					                if (pay.param.type_expr == 0) {
 | 
				
			||||||
 | 
					                    var func_decl = Declaration{ .ast_node = pay.func };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var func_buf: [1]Ast.Node.Index = undefined;
 | 
				
			||||||
 | 
					                    const func = tree.fullFnProto(&func_buf, pay.func).?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var func_params_len: usize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var it = func.iterate(&tree);
 | 
				
			||||||
 | 
					                    while (ast.nextFnParam(&it)) |_| {
 | 
				
			||||||
 | 
					                        func_params_len += 1;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var refs = try references.callsiteReferences(analyser.arena.allocator(), analyser, .{
 | 
				
			||||||
 | 
					                        .decl = &func_decl,
 | 
				
			||||||
 | 
					                        .handle = self.handle,
 | 
				
			||||||
 | 
					                    }, false, false, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // TODO: Set `workspace` to true; current problems
 | 
				
			||||||
 | 
					                    // - we gather dependencies, not dependents
 | 
				
			||||||
 | 
					                    // - stack overflow due to cyclically anytype resolution(?)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    var possible = std.ArrayListUnmanaged(Type.EitherEntry){};
 | 
				
			||||||
 | 
					                    var deduplicator = TypeWithHandle.Deduplicator{};
 | 
				
			||||||
 | 
					                    defer deduplicator.deinit(analyser.gpa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    for (refs.items) |ref| {
 | 
				
			||||||
 | 
					                        var handle = analyser.store.getOrLoadHandle(ref.uri).?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        var call_buf: [1]Ast.Node.Index = undefined;
 | 
				
			||||||
 | 
					                        var call = handle.tree.fullCall(&call_buf, ref.call_node).?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        const real_param_idx = if (func_params_len != 0 and pay.param_idx != 0 and call.ast.params.len == func_params_len - 1)
 | 
				
			||||||
 | 
					                            pay.param_idx - 1
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                            pay.param_idx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (real_param_idx >= call.ast.params.len) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (try analyser.resolveTypeOfNode(.{
 | 
				
			||||||
 | 
					                            // TODO?: this is a """heuristic based approach"""
 | 
				
			||||||
 | 
					                            // perhaps it would be better to use proper self detection
 | 
				
			||||||
 | 
					                            // maybe it'd be a perf issue and this is fine?
 | 
				
			||||||
 | 
					                            // you figure it out future contributor <3
 | 
				
			||||||
 | 
					                            .node = call.ast.params[real_param_idx],
 | 
				
			||||||
 | 
					                            .handle = handle,
 | 
				
			||||||
 | 
					                        })) |ty| {
 | 
				
			||||||
 | 
					                            var gop = try deduplicator.getOrPut(analyser.gpa, ty);
 | 
				
			||||||
 | 
					                            if (gop.found_existing) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            var loc = offsets.tokenToPosition(handle.tree, main_tokens[call.ast.params[real_param_idx]], .@"utf-8");
 | 
				
			||||||
 | 
					                            try possible.append(analyser.arena.allocator(), .{ // TODO: Dedup
 | 
				
			||||||
 | 
					                                .type_with_handle = ty,
 | 
				
			||||||
 | 
					                                .descriptor = try std.fmt.allocPrint(analyser.arena.allocator(), "{s}:{d}:{d}", .{ handle.uri, loc.line + 1, loc.character + 1 }),
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return TypeWithHandle{
 | 
				
			||||||
 | 
					                        .type = .{ .data = .{ .either = try possible.toOwnedSlice(analyser.arena.allocator()) }, .is_type_val = false },
 | 
				
			||||||
 | 
					                        .handle = self.handle,
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                const param_decl = pay.param;
 | 
					                const param_decl = pay.param;
 | 
				
			||||||
                if (isMetaType(self.handle.tree, param_decl.type_expr)) {
 | 
					                if (isMetaType(self.handle.tree, param_decl.type_expr)) {
 | 
				
			||||||
                    var bound_param_it = analyser.bound_type_params.iterator();
 | 
					                    var bound_param_it = analyser.bound_type_params.iterator();
 | 
				
			||||||
@ -2662,6 +2811,11 @@ fn makeScopeInternal(context: ScopeContext, node_idx: Ast.Node.Index) error{OutO
 | 
				
			|||||||
            );
 | 
					            );
 | 
				
			||||||
            defer context.popScope();
 | 
					            defer context.popScope();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // NOTE: We count the param index ourselves
 | 
				
			||||||
 | 
					            // as param_i stops counting; TODO: change this
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            var param_index: usize = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var it = func.iterate(&tree);
 | 
					            var it = func.iterate(&tree);
 | 
				
			||||||
            while (ast.nextFnParam(&it)) |param| {
 | 
					            while (ast.nextFnParam(&it)) |param| {
 | 
				
			||||||
                // Add parameter decls
 | 
					                // Add parameter decls
 | 
				
			||||||
@ -2669,12 +2823,13 @@ fn makeScopeInternal(context: ScopeContext, node_idx: Ast.Node.Index) error{OutO
 | 
				
			|||||||
                    try scopes.items(.decls)[scope_index].put(
 | 
					                    try scopes.items(.decls)[scope_index].put(
 | 
				
			||||||
                        allocator,
 | 
					                        allocator,
 | 
				
			||||||
                        tree.tokenSlice(name_token),
 | 
					                        tree.tokenSlice(name_token),
 | 
				
			||||||
                        .{ .param_payload = .{ .param = param, .func = node_idx } },
 | 
					                        .{ .param_payload = .{ .param = param, .param_idx = @intCast(u16, param_index), .func = node_idx } },
 | 
				
			||||||
                    );
 | 
					                    );
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                // Visit parameter types to pick up any error sets and enum
 | 
					                // Visit parameter types to pick up any error sets and enum
 | 
				
			||||||
                //   completions
 | 
					                //   completions
 | 
				
			||||||
                try makeScopeInternal(context, param.type_expr);
 | 
					                try makeScopeInternal(context, param.type_expr);
 | 
				
			||||||
 | 
					                param_index += 1;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (fn_tag == .fn_decl) blk: {
 | 
					            if (fn_tag == .fn_decl) blk: {
 | 
				
			||||||
@ -2895,6 +3050,8 @@ fn makeScopeInternal(context: ScopeContext, node_idx: Ast.Node.Index) error{OutO
 | 
				
			|||||||
                            .items = switch_case.ast.values,
 | 
					                            .items = switch_case.ast.values,
 | 
				
			||||||
                        },
 | 
					                        },
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    try makeScopeInternal(context, switch_case.ast.target_expr);
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    try makeScopeInternal(context, switch_case.ast.target_expr);
 | 
					                    try makeScopeInternal(context, switch_case.ast.target_expr);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
				
			|||||||
@ -112,7 +112,7 @@ const Builder = struct {
 | 
				
			|||||||
                    starts[identifier_token],
 | 
					                    starts[identifier_token],
 | 
				
			||||||
                )) orelse return;
 | 
					                )) orelse return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (std.meta.eql(builder.decl_handle, child)) {
 | 
					                if (builder.decl_handle.eql(child)) {
 | 
				
			||||||
                    try builder.add(handle, identifier_token);
 | 
					                    try builder.add(handle, identifier_token);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -132,7 +132,7 @@ const Builder = struct {
 | 
				
			|||||||
                    !left_type.type.is_type_val,
 | 
					                    !left_type.type.is_type_val,
 | 
				
			||||||
                )) orelse return;
 | 
					                )) orelse return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (std.meta.eql(builder.decl_handle, child)) {
 | 
					                if (builder.decl_handle.eql(child)) {
 | 
				
			||||||
                    try builder.add(handle, datas[node].rhs);
 | 
					                    try builder.add(handle, datas[node].rhs);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
@ -141,6 +141,53 @@ const Builder = struct {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn gatherReferences(
 | 
				
			||||||
 | 
					    allocator: std.mem.Allocator,
 | 
				
			||||||
 | 
					    analyser: *Analyser,
 | 
				
			||||||
 | 
					    curr_handle: *const DocumentStore.Handle,
 | 
				
			||||||
 | 
					    skip_std_references: bool,
 | 
				
			||||||
 | 
					    include_decl: bool,
 | 
				
			||||||
 | 
					    builder: anytype,
 | 
				
			||||||
 | 
					    handle_behavior: enum { get, get_or_load },
 | 
				
			||||||
 | 
					) !void {
 | 
				
			||||||
 | 
					    var dependencies = std.StringArrayHashMapUnmanaged(void){};
 | 
				
			||||||
 | 
					    defer {
 | 
				
			||||||
 | 
					        for (dependencies.keys()) |uri| {
 | 
				
			||||||
 | 
					            allocator.free(uri);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        dependencies.deinit(allocator);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (analyser.store.handles.values()) |handle| {
 | 
				
			||||||
 | 
					        if (skip_std_references and std.mem.indexOf(u8, handle.uri, "std") != null) {
 | 
				
			||||||
 | 
					            if (!include_decl or !std.mem.eql(u8, handle.uri, curr_handle.uri))
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var handle_dependencies = std.ArrayListUnmanaged([]const u8){};
 | 
				
			||||||
 | 
					        defer handle_dependencies.deinit(allocator);
 | 
				
			||||||
 | 
					        try analyser.store.collectDependencies(allocator, handle.*, &handle_dependencies);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try dependencies.ensureUnusedCapacity(allocator, handle_dependencies.items.len);
 | 
				
			||||||
 | 
					        for (handle_dependencies.items) |uri| {
 | 
				
			||||||
 | 
					            var gop = dependencies.getOrPutAssumeCapacity(uri);
 | 
				
			||||||
 | 
					            if (gop.found_existing) {
 | 
				
			||||||
 | 
					                allocator.free(uri);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (dependencies.keys()) |uri| {
 | 
				
			||||||
 | 
					        if (std.mem.eql(u8, uri, curr_handle.uri)) continue;
 | 
				
			||||||
 | 
					        const handle = switch (handle_behavior) {
 | 
				
			||||||
 | 
					            .get => analyser.store.getHandle(uri),
 | 
				
			||||||
 | 
					            .get_or_load => analyser.store.getOrLoadHandle(uri),
 | 
				
			||||||
 | 
					        } orelse continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try builder.collectReferences(handle, 0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn symbolReferences(
 | 
					pub fn symbolReferences(
 | 
				
			||||||
    allocator: std.mem.Allocator,
 | 
					    allocator: std.mem.Allocator,
 | 
				
			||||||
    analyser: *Analyser,
 | 
					    analyser: *Analyser,
 | 
				
			||||||
@ -180,41 +227,7 @@ pub fn symbolReferences(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            if (decl_handle.decl.* != .ast_node or !workspace) return builder.locations;
 | 
					            if (decl_handle.decl.* != .ast_node or !workspace) return builder.locations;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var dependencies = std.StringArrayHashMapUnmanaged(void){};
 | 
					            try gatherReferences(allocator, analyser, curr_handle, skip_std_references, include_decl, &builder, .get);
 | 
				
			||||||
            defer {
 | 
					 | 
				
			||||||
                for (dependencies.keys()) |uri| {
 | 
					 | 
				
			||||||
                    allocator.free(uri);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                dependencies.deinit(allocator);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (analyser.store.handles.values()) |handle| {
 | 
					 | 
				
			||||||
                if (skip_std_references and std.mem.indexOf(u8, handle.uri, "std") != null) {
 | 
					 | 
				
			||||||
                    if (!include_decl or !std.mem.eql(u8, handle.uri, curr_handle.uri))
 | 
					 | 
				
			||||||
                        continue;
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                var handle_dependencies = std.ArrayListUnmanaged([]const u8){};
 | 
					 | 
				
			||||||
                defer {
 | 
					 | 
				
			||||||
                    for (handle_dependencies.items) |uri| {
 | 
					 | 
				
			||||||
                        allocator.free(uri);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    handle_dependencies.deinit(allocator);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                try analyser.store.collectDependencies(allocator, handle.*, &handle_dependencies);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                try dependencies.ensureUnusedCapacity(allocator, handle_dependencies.items.len);
 | 
					 | 
				
			||||||
                for (handle_dependencies.items) |uri| {
 | 
					 | 
				
			||||||
                    dependencies.putAssumeCapacity(uri, {});
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            for (dependencies.keys()) |uri| {
 | 
					 | 
				
			||||||
                if (std.mem.eql(u8, uri, curr_handle.uri)) continue;
 | 
					 | 
				
			||||||
                const handle = analyser.store.getHandle(uri) orelse continue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                try builder.collectReferences(handle, 0);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        .param_payload => |payload| blk: {
 | 
					        .param_payload => |payload| blk: {
 | 
				
			||||||
            // Rename the param tok.
 | 
					            // Rename the param tok.
 | 
				
			||||||
@ -243,3 +256,137 @@ pub fn symbolReferences(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return builder.locations;
 | 
					    return builder.locations;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub const Callsite = struct {
 | 
				
			||||||
 | 
					    uri: []const u8,
 | 
				
			||||||
 | 
					    call_node: Ast.Node.Index,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const CallBuilder = struct {
 | 
				
			||||||
 | 
					    allocator: std.mem.Allocator,
 | 
				
			||||||
 | 
					    callsites: std.ArrayListUnmanaged(Callsite) = .{},
 | 
				
			||||||
 | 
					    /// this is the declaration we are searching for
 | 
				
			||||||
 | 
					    decl_handle: Analyser.DeclWithHandle,
 | 
				
			||||||
 | 
					    analyser: *Analyser,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const Context = struct {
 | 
				
			||||||
 | 
					        builder: *CallBuilder,
 | 
				
			||||||
 | 
					        handle: *const DocumentStore.Handle,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn deinit(self: *CallBuilder) void {
 | 
				
			||||||
 | 
					        self.callsites.deinit(self.allocator);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn add(self: *CallBuilder, handle: *const DocumentStore.Handle, call_node: Ast.Node.Index) error{OutOfMemory}!void {
 | 
				
			||||||
 | 
					        try self.callsites.append(self.allocator, .{
 | 
				
			||||||
 | 
					            .uri = handle.uri,
 | 
				
			||||||
 | 
					            .call_node = call_node,
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn collectReferences(self: *CallBuilder, handle: *const DocumentStore.Handle, node: Ast.Node.Index) error{OutOfMemory}!void {
 | 
				
			||||||
 | 
					        const context = Context{
 | 
				
			||||||
 | 
					            .builder = self,
 | 
				
			||||||
 | 
					            .handle = handle,
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        try ast.iterateChildrenRecursive(handle.tree, node, &context, error{OutOfMemory}, referenceNode);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn referenceNode(self: *const Context, tree: Ast, node: Ast.Node.Index) error{OutOfMemory}!void {
 | 
				
			||||||
 | 
					        const builder = self.builder;
 | 
				
			||||||
 | 
					        const handle = self.handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const node_tags = tree.nodes.items(.tag);
 | 
				
			||||||
 | 
					        const datas = tree.nodes.items(.data);
 | 
				
			||||||
 | 
					        // const token_tags = tree.tokens.items(.tag);
 | 
				
			||||||
 | 
					        const starts = tree.tokens.items(.start);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (node_tags[node]) {
 | 
				
			||||||
 | 
					            .call,
 | 
				
			||||||
 | 
					            .call_comma,
 | 
				
			||||||
 | 
					            .async_call,
 | 
				
			||||||
 | 
					            .async_call_comma,
 | 
				
			||||||
 | 
					            .call_one,
 | 
				
			||||||
 | 
					            .call_one_comma,
 | 
				
			||||||
 | 
					            .async_call_one,
 | 
				
			||||||
 | 
					            .async_call_one_comma,
 | 
				
			||||||
 | 
					            => {
 | 
				
			||||||
 | 
					                var buf: [1]Ast.Node.Index = undefined;
 | 
				
			||||||
 | 
					                var call = tree.fullCall(&buf, node).?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const called_node = call.ast.fn_expr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                switch (node_tags[called_node]) {
 | 
				
			||||||
 | 
					                    .identifier => {
 | 
				
			||||||
 | 
					                        const identifier_token = Analyser.getDeclNameToken(tree, called_node).?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        const child = (try builder.analyser.lookupSymbolGlobal(
 | 
				
			||||||
 | 
					                            handle,
 | 
				
			||||||
 | 
					                            offsets.tokenToSlice(tree, identifier_token),
 | 
				
			||||||
 | 
					                            starts[identifier_token],
 | 
				
			||||||
 | 
					                        )) orelse return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (builder.decl_handle.eql(child)) {
 | 
				
			||||||
 | 
					                            try builder.add(handle, node);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    .field_access => {
 | 
				
			||||||
 | 
					                        const left_type = try builder.analyser.resolveFieldAccessLhsType(
 | 
				
			||||||
 | 
					                            (try builder.analyser.resolveTypeOfNode(.{ .node = datas[called_node].lhs, .handle = handle })) orelse return,
 | 
				
			||||||
 | 
					                        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        const left_type_node = switch (left_type.type.data) {
 | 
				
			||||||
 | 
					                            .other => |n| n,
 | 
				
			||||||
 | 
					                            else => return,
 | 
				
			||||||
 | 
					                        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        const child = (try builder.analyser.lookupSymbolContainer(
 | 
				
			||||||
 | 
					                            .{ .node = left_type_node, .handle = left_type.handle },
 | 
				
			||||||
 | 
					                            offsets.tokenToSlice(tree, datas[called_node].rhs),
 | 
				
			||||||
 | 
					                            !left_type.type.is_type_val,
 | 
				
			||||||
 | 
					                        )) orelse return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (builder.decl_handle.eql(child)) {
 | 
				
			||||||
 | 
					                            try builder.add(handle, node);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    else => {},
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            else => {},
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn callsiteReferences(
 | 
				
			||||||
 | 
					    allocator: std.mem.Allocator,
 | 
				
			||||||
 | 
					    analyser: *Analyser,
 | 
				
			||||||
 | 
					    decl_handle: Analyser.DeclWithHandle,
 | 
				
			||||||
 | 
					    /// add `decl_handle` as a references
 | 
				
			||||||
 | 
					    include_decl: bool,
 | 
				
			||||||
 | 
					    /// exclude references from the std library
 | 
				
			||||||
 | 
					    skip_std_references: bool,
 | 
				
			||||||
 | 
					    /// search other files for references
 | 
				
			||||||
 | 
					    workspace: bool,
 | 
				
			||||||
 | 
					) error{OutOfMemory}!std.ArrayListUnmanaged(Callsite) {
 | 
				
			||||||
 | 
					    std.debug.assert(decl_handle.decl.* == .ast_node);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var builder = CallBuilder{
 | 
				
			||||||
 | 
					        .allocator = allocator,
 | 
				
			||||||
 | 
					        .analyser = analyser,
 | 
				
			||||||
 | 
					        .decl_handle = decl_handle,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    errdefer builder.deinit();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const curr_handle = decl_handle.handle;
 | 
				
			||||||
 | 
					    if (include_decl) try builder.add(curr_handle, decl_handle.nameToken());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try builder.collectReferences(curr_handle, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!workspace) return builder.callsites;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    try gatherReferences(allocator, analyser, curr_handle, skip_std_references, include_decl, &builder, .get_or_load);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return builder.callsites;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user