From cc9cc5f2022e40c88c2611f3a107632be861bdd8 Mon Sep 17 00:00:00 2001 From: Auguste Rame <19855629+SuperAuguste@users.noreply.github.com> Date: Wed, 15 Mar 2023 11:35:09 -0400 Subject: [PATCH 1/6] Add anytype resolution based on call references --- src/analysis.zig | 48 +++++++++- src/features/references.zig | 181 ++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) diff --git a/src/analysis.zig b/src/analysis.zig index 1b60b22c5..07c6fae45 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -9,6 +9,7 @@ const ast = @import("ast.zig"); const tracy = @import("tracy.zig"); const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); const InternPool = ComptimeInterpreter.InternPool; +const references = @import("references.zig"); const Analyser = @This(); @@ -1866,6 +1867,7 @@ pub const Declaration = union(enum) { /// Function parameter param_payload: struct { param: Ast.full.FnProto.Param, + param_idx: u16, func: Ast.Node.Index, }, pointer_payload: struct { @@ -1888,12 +1890,20 @@ pub const Declaration = union(enum) { }, /// always an identifier error_token: Ast.Node.Index, + + pub fn eql(a: Declaration, b: Declaration) bool { + return std.meta.eql(a, b); + } }; pub const DeclWithHandle = struct { decl: *Declaration, 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 { const tree = self.handle.tree; return switch (self.decl.*) { @@ -1916,6 +1926,8 @@ pub const DeclWithHandle = struct { } pub fn resolveType(self: DeclWithHandle, analyser: *Analyser) !?TypeWithHandle { + std.log.info("TRES", .{}); + const tree = self.handle.tree; const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); @@ -1924,6 +1936,40 @@ pub const DeclWithHandle = struct { .{ .node = node, .handle = self.handle }, ), .param_payload => |pay| { + // handle anytype + if (pay.param.type_expr == 0) { + var func_decl = Declaration{ .ast_node = pay.func }; + + var refs = try references.callsiteReferences(analyser.arena, analyser, .{ + .decl = &func_decl, + .handle = self.handle, + }, false, false, true); + + var possible = std.ArrayListUnmanaged(Type.EitherEntry){}; + + for (refs.items) |ref| { + var handle = analyser.store.getOrLoadHandle(ref.uri).?; + + var buf: [1]Ast.Node.Index = undefined; + var call = handle.tree.fullCall(&buf, ref.call_node).?; + + if (try analyser.resolveTypeOfNode(.{ + .node = call.ast.params[pay.param_idx], // TODO: add check + self call (-1) + .handle = handle, + })) |ty| { + try possible.append(analyser.arena, .{ // TODO: Dedup + .type_with_handle = ty, + .descriptor = "TODO", + }); + } + } + + return TypeWithHandle{ + .type = .{ .data = .{ .either = try possible.toOwnedSlice(analyser.arena) }, .is_type_val = false }, + .handle = self.handle, + }; + } + const param_decl = pay.param; if (isMetaType(self.handle.tree, param_decl.type_expr)) { var bound_param_it = analyser.bound_type_params.iterator(); @@ -2669,7 +2715,7 @@ fn makeScopeInternal(context: ScopeContext, node_idx: Ast.Node.Index) error{OutO try scopes.items(.decls)[scope_index].put( allocator, tree.tokenSlice(name_token), - .{ .param_payload = .{ .param = param, .func = node_idx } }, + .{ .param_payload = .{ .param = param, .param_idx = @intCast(u16, it.param_i), .func = node_idx } }, ); } // Visit parameter types to pick up any error sets and enum diff --git a/src/features/references.zig b/src/features/references.zig index 828fa5acc..f79fcf4d8 100644 --- a/src/features/references.zig +++ b/src/features/references.zig @@ -243,3 +243,184 @@ pub fn symbolReferences( 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); + } + }, + // TODO: Field access + 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.* != .label_decl); // use `labelReferences` instead + + 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()); + + switch (decl_handle.decl.*) { + .ast_node, + .pointer_payload, + .switch_payload, + .array_payload, + .array_index, + => { + try builder.collectReferences(curr_handle, 0); + + if (decl_handle.decl.* != .ast_node or !workspace) return builder.callsites; + + 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 { + 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: { + // Rename the param tok. + for (curr_handle.document_scope.scopes.items(.data)) |scope_data| { + if (scope_data != .function) continue; + + const proto = scope_data.function; + + var buf: [1]Ast.Node.Index = undefined; + const fn_proto = curr_handle.tree.fullFnProto(&buf, proto).?; + + var it = fn_proto.iterate(&curr_handle.tree); + while (ast.nextFnParam(&it)) |candidate| { + if (!std.meta.eql(candidate, payload.param)) continue; + + if (curr_handle.tree.nodes.items(.tag)[proto] != .fn_decl) break :blk; + try builder.collectReferences(curr_handle, curr_handle.tree.nodes.items(.data)[proto].rhs); + break :blk; + } + } + log.warn("Could not find param decl's function", .{}); + }, + .label_decl => unreachable, // handled separately by labelReferences + .error_token => {}, + } + + return builder.callsites; +} From eabc0edfec89fe4328325f6d9ccd3bc9ea15b68f Mon Sep 17 00:00:00 2001 From: Auguste Rame <19855629+SuperAuguste@users.noreply.github.com> Date: Wed, 15 Mar 2023 12:16:45 -0400 Subject: [PATCH 2/6] Add checks, descriptor; fix switch bug --- src/analysis.zig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 07c6fae45..685ee51ed 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1926,8 +1926,6 @@ pub const DeclWithHandle = struct { } pub fn resolveType(self: DeclWithHandle, analyser: *Analyser) !?TypeWithHandle { - std.log.info("TRES", .{}); - const tree = self.handle.tree; const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); @@ -1953,13 +1951,16 @@ pub const DeclWithHandle = struct { var buf: [1]Ast.Node.Index = undefined; var call = handle.tree.fullCall(&buf, ref.call_node).?; + if (pay.param_idx >= call.ast.params.len) continue; + if (try analyser.resolveTypeOfNode(.{ - .node = call.ast.params[pay.param_idx], // TODO: add check + self call (-1) + .node = call.ast.params[pay.param_idx], // TODO: self call (-1) .handle = handle, })) |ty| { + var loc = offsets.tokenToPosition(handle.tree, main_tokens[call.ast.params[pay.param_idx]], .@"utf-8"); try possible.append(analyser.arena, .{ // TODO: Dedup .type_with_handle = ty, - .descriptor = "TODO", + .descriptor = try std.fmt.allocPrint(analyser.arena, "{s}:{d}:{d}", .{ handle.uri, loc.line + 1, loc.character + 1 }), }); } } @@ -2941,6 +2942,8 @@ fn makeScopeInternal(context: ScopeContext, node_idx: Ast.Node.Index) error{OutO .items = switch_case.ast.values, }, }); + + try makeScopeInternal(context, switch_case.ast.target_expr); } else { try makeScopeInternal(context, switch_case.ast.target_expr); } From 9b18996328e153962b88a8604679550a5a7ae4c3 Mon Sep 17 00:00:00 2001 From: Auguste Rame <19855629+SuperAuguste@users.noreply.github.com> Date: Wed, 15 Mar 2023 22:50:45 -0400 Subject: [PATCH 3/6] Fix ref bugs, add checks, handle boundfns --- src/analysis.zig | 43 ++++++++++-- src/features/references.zig | 131 ++++++++++++++++-------------------- 2 files changed, 93 insertions(+), 81 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 685ee51ed..47a470bee 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1938,26 +1938,49 @@ pub const DeclWithHandle = struct { 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, analyser, .{ .decl = &func_decl, .handle = self.handle, - }, false, false, true); + }, 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){}; for (refs.items) |ref| { var handle = analyser.store.getOrLoadHandle(ref.uri).?; - var buf: [1]Ast.Node.Index = undefined; - var call = handle.tree.fullCall(&buf, ref.call_node).?; + var call_buf: [1]Ast.Node.Index = undefined; + var call = handle.tree.fullCall(&call_buf, ref.call_node).?; - if (pay.param_idx >= call.ast.params.len) continue; + 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(.{ - .node = call.ast.params[pay.param_idx], // TODO: self call (-1) + // 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 loc = offsets.tokenToPosition(handle.tree, main_tokens[call.ast.params[pay.param_idx]], .@"utf-8"); + var loc = offsets.tokenToPosition(handle.tree, main_tokens[call.ast.params[real_param_idx]], .@"utf-8"); try possible.append(analyser.arena, .{ // TODO: Dedup .type_with_handle = ty, .descriptor = try std.fmt.allocPrint(analyser.arena, "{s}:{d}:{d}", .{ handle.uri, loc.line + 1, loc.character + 1 }), @@ -2709,6 +2732,11 @@ fn makeScopeInternal(context: ScopeContext, node_idx: Ast.Node.Index) error{OutO ); 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); while (ast.nextFnParam(&it)) |param| { // Add parameter decls @@ -2716,12 +2744,13 @@ fn makeScopeInternal(context: ScopeContext, node_idx: Ast.Node.Index) error{OutO try scopes.items(.decls)[scope_index].put( allocator, tree.tokenSlice(name_token), - .{ .param_payload = .{ .param = param, .param_idx = @intCast(u16, it.param_i), .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 // completions try makeScopeInternal(context, param.type_expr); + param_index += 1; } if (fn_tag == .fn_decl) blk: { diff --git a/src/features/references.zig b/src/features/references.zig index f79fcf4d8..7293838e9 100644 --- a/src/features/references.zig +++ b/src/features/references.zig @@ -112,7 +112,7 @@ const Builder = struct { starts[identifier_token], )) orelse return; - if (std.meta.eql(builder.decl_handle, child)) { + if (builder.decl_handle.eql(child)) { try builder.add(handle, identifier_token); } }, @@ -132,7 +132,7 @@ const Builder = struct { !left_type.type.is_type_val, )) orelse return; - if (std.meta.eql(builder.decl_handle, child)) { + if (builder.decl_handle.eql(child)) { try builder.add(handle, datas[node].rhs); } }, @@ -195,17 +195,15 @@ pub fn symbolReferences( } var handle_dependencies = std.ArrayListUnmanaged([]const u8){}; - defer { - for (handle_dependencies.items) |uri| { - allocator.free(uri); - } - handle_dependencies.deinit(allocator); - } + 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| { - dependencies.putAssumeCapacity(uri, {}); + var gop = dependencies.getOrPutAssumeCapacity(uri); + if (gop.found_existing) { + allocator.free(uri); + } } } @@ -285,7 +283,7 @@ const CallBuilder = struct { const handle = self.handle; const node_tags = tree.nodes.items(.tag); - // const datas = tree.nodes.items(.data); + const datas = tree.nodes.items(.data); // const token_tags = tree.tokens.items(.tag); const starts = tree.tokens.items(.start); @@ -318,7 +316,26 @@ const CallBuilder = struct { try builder.add(handle, node); } }, - // TODO: Field access + .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 => {}, } }, @@ -338,7 +355,7 @@ pub fn callsiteReferences( /// search other files for references workspace: bool, ) error{OutOfMemory}!std.ArrayListUnmanaged(Callsite) { - std.debug.assert(decl_handle.decl.* != .label_decl); // use `labelReferences` instead + std.debug.assert(decl_handle.decl.* == .ast_node); var builder = CallBuilder{ .allocator = allocator, @@ -350,76 +367,42 @@ pub fn callsiteReferences( const curr_handle = decl_handle.handle; if (include_decl) try builder.add(curr_handle, decl_handle.nameToken()); - switch (decl_handle.decl.*) { - .ast_node, - .pointer_payload, - .switch_payload, - .array_payload, - .array_index, - => { - try builder.collectReferences(curr_handle, 0); - - if (decl_handle.decl.* != .ast_node or !workspace) return builder.callsites; + try builder.collectReferences(curr_handle, 0); - var dependencies = std.StringArrayHashMapUnmanaged(void){}; - defer { - for (dependencies.keys()) |uri| { - allocator.free(uri); - } - dependencies.deinit(allocator); - } + if (!workspace) return builder.callsites; - 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); + var dependencies = std.StringArrayHashMapUnmanaged(void){}; + defer { + for (dependencies.keys()) |uri| { + allocator.free(uri); + } + dependencies.deinit(allocator); + } - try dependencies.ensureUnusedCapacity(allocator, handle_dependencies.items.len); - for (handle_dependencies.items) |uri| { - dependencies.putAssumeCapacity(uri, {}); - } - } + 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; + } - for (dependencies.keys()) |uri| { - if (std.mem.eql(u8, uri, curr_handle.uri)) continue; - const handle = analyser.store.getHandle(uri) orelse continue; + var handle_dependencies = std.ArrayListUnmanaged([]const u8){}; + defer handle_dependencies.deinit(allocator); + try analyser.store.collectDependencies(allocator, handle.*, &handle_dependencies); - try builder.collectReferences(handle, 0); + 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); } - }, - .param_payload => |payload| blk: { - // Rename the param tok. - for (curr_handle.document_scope.scopes.items(.data)) |scope_data| { - if (scope_data != .function) continue; - - const proto = scope_data.function; - - var buf: [1]Ast.Node.Index = undefined; - const fn_proto = curr_handle.tree.fullFnProto(&buf, proto).?; + } + } - var it = fn_proto.iterate(&curr_handle.tree); - while (ast.nextFnParam(&it)) |candidate| { - if (!std.meta.eql(candidate, payload.param)) continue; + for (dependencies.keys()) |uri| { + if (std.mem.eql(u8, uri, curr_handle.uri)) continue; + const handle = analyser.store.getOrLoadHandle(uri) orelse continue; - if (curr_handle.tree.nodes.items(.tag)[proto] != .fn_decl) break :blk; - try builder.collectReferences(curr_handle, curr_handle.tree.nodes.items(.data)[proto].rhs); - break :blk; - } - } - log.warn("Could not find param decl's function", .{}); - }, - .label_decl => unreachable, // handled separately by labelReferences - .error_token => {}, + try builder.collectReferences(handle, 0); } return builder.callsites; From a1ca04cf005d7412edfde248040b648736f46d63 Mon Sep 17 00:00:00 2001 From: Lee Cannon Date: Tue, 21 Mar 2023 22:09:22 +0000 Subject: [PATCH 4/6] update references import path --- src/analysis.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/analysis.zig b/src/analysis.zig index 47a470bee..8e5bbcb03 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -9,7 +9,7 @@ const ast = @import("ast.zig"); const tracy = @import("tracy.zig"); const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); const InternPool = ComptimeInterpreter.InternPool; -const references = @import("references.zig"); +const references = @import("features/references.zig"); const Analyser = @This(); From d05788d6dd4c1582c71a397ba65c9f8d90a22e73 Mon Sep 17 00:00:00 2001 From: Auguste Rame <19855629+SuperAuguste@users.noreply.github.com> Date: Thu, 30 Mar 2023 16:25:32 -0400 Subject: [PATCH 5/6] Fix allocators, deduplicate! --- src/analysis.zig | 87 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 8e5bbcb03..853d7330c 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -1163,6 +1163,77 @@ pub const TypeWithHandle = struct { type: Type, 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 { return .{ .type = .{ @@ -1173,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) + /// Drops duplicates pub fn getAllTypesWithHandles(ty: TypeWithHandle, arena: std.mem.Allocator) ![]const TypeWithHandle { var all_types = std.ArrayListUnmanaged(TypeWithHandle){}; try ty.getAllTypesWithHandlesArrayList(arena, &all_types); @@ -1948,7 +2022,7 @@ pub const DeclWithHandle = struct { func_params_len += 1; } - var refs = try references.callsiteReferences(analyser.arena, analyser, .{ + var refs = try references.callsiteReferences(analyser.arena.allocator(), analyser, .{ .decl = &func_decl, .handle = self.handle, }, false, false, false); @@ -1958,6 +2032,8 @@ pub const DeclWithHandle = struct { // - 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).?; @@ -1980,16 +2056,19 @@ pub const DeclWithHandle = struct { .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, .{ // TODO: Dedup + try possible.append(analyser.arena.allocator(), .{ // TODO: Dedup .type_with_handle = ty, - .descriptor = try std.fmt.allocPrint(analyser.arena, "{s}:{d}:{d}", .{ handle.uri, loc.line + 1, loc.character + 1 }), + .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) }, .is_type_val = false }, + .type = .{ .data = .{ .either = try possible.toOwnedSlice(analyser.arena.allocator()) }, .is_type_val = false }, .handle = self.handle, }; } From 4bdc1c87c26952e041b4922475f537d3c363902e Mon Sep 17 00:00:00 2001 From: Auguste Rame <19855629+SuperAuguste@users.noreply.github.com> Date: Thu, 30 Mar 2023 17:50:23 -0400 Subject: [PATCH 6/6] Deduplicate reference gathering code --- src/features/references.zig | 115 +++++++++++++++--------------------- 1 file changed, 49 insertions(+), 66 deletions(-) diff --git a/src/features/references.zig b/src/features/references.zig index 7293838e9..8465e7ed2 100644 --- a/src/features/references.zig +++ b/src/features/references.zig @@ -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( allocator: std.mem.Allocator, analyser: *Analyser, @@ -180,39 +227,7 @@ pub fn symbolReferences( if (decl_handle.decl.* != .ast_node or !workspace) return builder.locations; - 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 = analyser.store.getHandle(uri) orelse continue; - - try builder.collectReferences(handle, 0); - } + try gatherReferences(allocator, analyser, curr_handle, skip_std_references, include_decl, &builder, .get); }, .param_payload => |payload| blk: { // Rename the param tok. @@ -371,39 +386,7 @@ pub fn callsiteReferences( if (!workspace) return builder.callsites; - 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 = analyser.store.getOrLoadHandle(uri) orelse continue; - - try builder.collectReferences(handle, 0); - } + try gatherReferences(allocator, analyser, curr_handle, skip_std_references, include_decl, &builder, .get_or_load); return builder.callsites; }