From 0ff0a193ccaf49c339a78e39217a08fea166a592 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 12 Mar 2023 23:48:31 +0100 Subject: [PATCH] simplify analysis by introducing a Analysis Struct --- src/Server.zig | 139 ++++---- src/analysis.zig | 488 ++++++++++++++--------------- src/code_actions.zig | 12 +- src/inlay_hints.zig | 20 +- src/references.zig | 44 +-- src/semantic_tokens.zig | 64 ++-- src/signature_help.zig | 32 +- src/zls.zig | 2 +- tests/utility/position_context.zig | 6 +- 9 files changed, 375 insertions(+), 432 deletions(-) diff --git a/src/Server.zig b/src/Server.zig index 35a6d85..4547554 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -7,7 +7,7 @@ const Config = @import("Config.zig"); const configuration = @import("configuration.zig"); const DocumentStore = @import("DocumentStore.zig"); const types = @import("lsp.zig"); -const analysis = @import("analysis.zig"); +const Analyser = @import("analysis.zig"); const ast = @import("ast.zig"); const references = @import("references.zig"); const offsets = @import("offsets.zig"); @@ -37,6 +37,7 @@ const log = std.log.scoped(.zls_server); config: *Config, allocator: std.mem.Allocator, arena: std.heap.ArenaAllocator, +analyser: Analyser, document_store: DocumentStore, builtin_completions: ?std.ArrayListUnmanaged(types.CompletionItem), client_capabilities: ClientCapabilities = .{}, @@ -283,10 +284,10 @@ fn generateDiagnostics(server: *Server, handle: DocumentStore.Handle) error{OutO if (func.extern_export_inline_token != null) break :blk; if (func.name_token) |name_token| { - const is_type_function = analysis.isTypeFunction(tree, func); + const is_type_function = Analyser.isTypeFunction(tree, func); const func_name = tree.tokenSlice(name_token); - if (!is_type_function and !analysis.isCamelCase(func_name)) { + if (!is_type_function and !Analyser.isCamelCase(func_name)) { try diagnostics.append(allocator, .{ .range = offsets.tokenToRange(tree, name_token, server.offset_encoding), .severity = .Hint, @@ -294,7 +295,7 @@ fn generateDiagnostics(server: *Server, handle: DocumentStore.Handle) error{OutO .source = "zls", .message = "Functions should be camelCase", }); - } else if (is_type_function and !analysis.isPascalCase(func_name)) { + } else if (is_type_function and !Analyser.isPascalCase(func_name)) { try diagnostics.append(allocator, .{ .range = offsets.tokenToRange(tree, name_token, server.offset_encoding), .severity = .Hint, @@ -512,7 +513,7 @@ pub fn autofix(server: *Server, allocator: std.mem.Allocator, handle: *const Doc var builder = code_actions.Builder{ .arena = server.arena.allocator(), - .document_store = &server.document_store, + .analyser = &server.analyser, .handle = handle, .offset_encoding = server.offset_encoding, }; @@ -544,7 +545,7 @@ pub fn autofix(server: *Server, allocator: std.mem.Allocator, handle: *const Doc pub fn typeToCompletion( server: *Server, list: *std.ArrayListUnmanaged(types.CompletionItem), - field_access: analysis.FieldAccessReturn, + field_access: Analyser.FieldAccessReturn, orig_handle: *const DocumentStore.Handle, either_descriptor: ?[]const u8, ) error{OutOfMemory}!void { @@ -620,8 +621,8 @@ pub fn typeToCompletion( pub fn nodeToCompletion( server: *Server, list: *std.ArrayListUnmanaged(types.CompletionItem), - node_handle: analysis.NodeWithHandle, - unwrapped: ?analysis.TypeWithHandle, + node_handle: Analyser.NodeWithHandle, + unwrapped: ?Analyser.TypeWithHandle, orig_handle: *const DocumentStore.Handle, is_type_val: bool, parent_is_type_val: ?bool, @@ -645,7 +646,7 @@ pub fn nodeToCompletion( const Documentation = @TypeOf(@as(types.CompletionItem, undefined).documentation); - const doc: Documentation = if (try analysis.getDocComments( + const doc: Documentation = if (try Analyser.getDocComments( allocator, handle.tree, node, @@ -672,9 +673,7 @@ pub fn nodeToCompletion( .parent_is_type_val = is_type_val, .either_descriptor = either_descriptor, }; - try analysis.iterateSymbolsContainer( - allocator, - &server.document_store, + try server.analyser.iterateSymbolsContainer( node_handle, orig_handle, declToCompletion, @@ -698,17 +697,17 @@ pub fn nodeToCompletion( const use_snippets = server.config.enable_snippets and server.client_capabilities.supports_snippets; const insert_text = if (use_snippets) blk: { const skip_self_param = !(parent_is_type_val orelse true) and - try analysis.hasSelfParam(allocator, &server.document_store, handle, func); - break :blk try analysis.getFunctionSnippet(server.arena.allocator(), tree, func, skip_self_param); + try server.analyser.hasSelfParam(handle, func); + break :blk try Analyser.getFunctionSnippet(server.arena.allocator(), tree, func, skip_self_param); } else tree.tokenSlice(func.name_token.?); - const is_type_function = analysis.isTypeFunction(handle.tree, func); + const is_type_function = Analyser.isTypeFunction(handle.tree, func); try list.append(allocator, .{ .label = handle.tree.tokenSlice(name_token), .kind = if (is_type_function) .Struct else .Function, .documentation = doc, - .detail = analysis.getFunctionSignature(handle.tree, func), + .detail = Analyser.getFunctionSignature(handle.tree, func), .insertText = insert_text, .insertTextFormat = if (use_snippets) .Snippet else .PlainText, }); @@ -722,7 +721,7 @@ pub fn nodeToCompletion( const var_decl = tree.fullVarDecl(node).?; const is_const = token_tags[var_decl.ast.mut_token] == .keyword_const; - if (try analysis.resolveVarDeclAlias(allocator, &server.document_store, node_handle)) |result| { + if (try server.analyser.resolveVarDeclAlias(node_handle)) |result| { const context = DeclToCompletionContext{ .server = server, .completions = list, @@ -736,7 +735,7 @@ pub fn nodeToCompletion( .label = handle.tree.tokenSlice(var_decl.ast.mut_token + 1), .kind = if (is_const) .Constant else .Variable, .documentation = doc, - .detail = analysis.getVariableSignature(tree, var_decl), + .detail = Analyser.getVariableSignature(tree, var_decl), .insertText = tree.tokenSlice(var_decl.ast.mut_token + 1), .insertTextFormat = .PlainText, }); @@ -750,7 +749,7 @@ pub fn nodeToCompletion( .label = handle.tree.tokenSlice(field.ast.main_token), .kind = if (field.ast.tuple_like) .Enum else .Field, .documentation = doc, - .detail = analysis.getContainerFieldSignature(handle.tree, field), + .detail = Analyser.getContainerFieldSignature(handle.tree, field), .insertText = tree.tokenSlice(field.ast.main_token), .insertTextFormat = .PlainText, }); @@ -825,7 +824,7 @@ pub fn nodeToCompletion( .insertTextFormat = .PlainText, }); }, - else => if (analysis.nodeToString(tree, node)) |string| { + else => if (Analyser.nodeToString(tree, node)) |string| { try list.append(allocator, .{ .label = string, .kind = .Field, @@ -842,12 +841,12 @@ pub fn identifierFromPosition(pos_index: usize, handle: DocumentStore.Handle) [] if (pos_index + 1 >= handle.text.len) return ""; var start_idx = pos_index; - while (start_idx > 0 and analysis.isSymbolChar(handle.text[start_idx - 1])) { + while (start_idx > 0 and Analyser.isSymbolChar(handle.text[start_idx - 1])) { start_idx -= 1; } var end_idx = pos_index; - while (end_idx < handle.text.len and analysis.isSymbolChar(handle.text[end_idx])) { + while (end_idx < handle.text.len and Analyser.isSymbolChar(handle.text[end_idx])) { end_idx += 1; } @@ -857,7 +856,7 @@ pub fn identifierFromPosition(pos_index: usize, handle: DocumentStore.Handle) [] pub fn gotoDefinitionSymbol( server: *Server, - decl_handle: analysis.DeclWithHandle, + decl_handle: Analyser.DeclWithHandle, resolve_alias: bool, ) error{OutOfMemory}!?types.Location { const tracy_zone = tracy.trace(@src()); @@ -868,14 +867,14 @@ pub fn gotoDefinitionSymbol( const name_token = switch (decl_handle.decl.*) { .ast_node => |node| block: { if (resolve_alias) { - if (try analysis.resolveVarDeclAlias(server.arena.allocator(), &server.document_store, .{ .node = node, .handle = handle })) |result| { + if (try server.analyser.resolveVarDeclAlias(.{ .node = node, .handle = handle })) |result| { handle = result.handle; break :block result.nameToken(); } } - break :block analysis.getDeclNameToken(handle.tree, node) orelse return null; + break :block Analyser.getDeclNameToken(handle.tree, node) orelse return null; }, else => decl_handle.nameToken(), }; @@ -886,7 +885,7 @@ pub fn gotoDefinitionSymbol( }; } -pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle, markup_kind: types.MarkupKind) error{OutOfMemory}!?[]const u8 { +pub fn hoverSymbol(server: *Server, decl_handle: Analyser.DeclWithHandle, markup_kind: types.MarkupKind) error{OutOfMemory}!?[]const u8 { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -897,27 +896,27 @@ pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle, markup const def_str = switch (decl_handle.decl.*) { .ast_node => |node| def: { - if (try analysis.resolveVarDeclAlias(server.arena.allocator(), &server.document_store, .{ .node = node, .handle = handle })) |result| { + if (try server.analyser.resolveVarDeclAlias(.{ .node = node, .handle = handle })) |result| { return try server.hoverSymbol(result, markup_kind); } - doc_str = try analysis.getDocComments(server.arena.allocator(), tree, node, markup_kind); + doc_str = try Analyser.getDocComments(server.arena.allocator(), tree, node, markup_kind); var buf: [1]Ast.Node.Index = undefined; if (tree.fullVarDecl(node)) |var_decl| { - break :def analysis.getVariableSignature(tree, var_decl); + break :def Analyser.getVariableSignature(tree, var_decl); } else if (tree.fullFnProto(&buf, node)) |fn_proto| { - break :def analysis.getFunctionSignature(tree, fn_proto); + break :def Analyser.getFunctionSignature(tree, fn_proto); } else if (tree.fullContainerField(node)) |field| { - break :def analysis.getContainerFieldSignature(tree, field); + break :def Analyser.getContainerFieldSignature(tree, field); } else { - break :def analysis.nodeToString(tree, node) orelse return null; + break :def Analyser.nodeToString(tree, node) orelse return null; } }, .param_payload => |pay| def: { const param = pay.param; if (param.first_doc_comment) |doc_comments| { - doc_str = try analysis.collectDocComments(server.arena.allocator(), handle.tree, doc_comments, markup_kind, false); + doc_str = try Analyser.collectDocComments(server.arena.allocator(), handle.tree, doc_comments, markup_kind, false); } const first_token = ast.paramFirstToken(tree, param); @@ -936,9 +935,7 @@ pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle, markup => tree.tokenSlice(decl_handle.nameToken()), }; - var bound_type_params = analysis.BoundTypeParams{}; - defer bound_type_params.deinit(server.document_store.allocator); - const resolved_type = try decl_handle.resolveType(server.arena.allocator(), &server.document_store, &bound_type_params); + const resolved_type = try decl_handle.resolveType(&server.analyser); const resolved_type_str = if (resolved_type) |rt| if (rt.type.is_type_val) switch (rt.type.data) { @@ -1002,28 +999,28 @@ pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle, markup return hover_text; } -pub fn getLabelGlobal(pos_index: usize, handle: *const DocumentStore.Handle) error{OutOfMemory}!?analysis.DeclWithHandle { +pub fn getLabelGlobal(pos_index: usize, handle: *const DocumentStore.Handle) error{OutOfMemory}!?Analyser.DeclWithHandle { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); const name = identifierFromPosition(pos_index, handle.*); if (name.len == 0) return null; - return try analysis.lookupLabel(handle, name, pos_index); + return try Analyser.lookupLabel(handle, name, pos_index); } pub fn getSymbolGlobal( server: *Server, pos_index: usize, handle: *const DocumentStore.Handle, -) error{OutOfMemory}!?analysis.DeclWithHandle { +) error{OutOfMemory}!?Analyser.DeclWithHandle { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); const name = identifierFromPosition(pos_index, handle.*); if (name.len == 0) return null; - return try analysis.lookupSymbolGlobal(server.arena.allocator(), &server.document_store, handle, name, pos_index); + return try server.analyser.lookupSymbolGlobal(handle, name, pos_index); } pub fn gotoDefinitionLabel( @@ -1173,7 +1170,7 @@ pub fn getSymbolFieldAccesses( handle: *const DocumentStore.Handle, source_index: usize, loc: offsets.Loc, -) error{OutOfMemory}!?[]const analysis.DeclWithHandle { +) error{OutOfMemory}!?[]const Analyser.DeclWithHandle { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -1183,9 +1180,9 @@ pub fn getSymbolFieldAccesses( var held_range = try server.arena.allocator().dupeZ(u8, offsets.locToSlice(handle.text, loc)); var tokenizer = std.zig.Tokenizer.init(held_range); - var decls_with_handles = std.ArrayListUnmanaged(analysis.DeclWithHandle){}; + var decls_with_handles = std.ArrayListUnmanaged(Analyser.DeclWithHandle){}; - if (try analysis.getFieldAccessType(server.arena.allocator(), &server.document_store, handle, source_index, &tokenizer)) |result| { + if (try server.analyser.getFieldAccessType(handle, source_index, &tokenizer)) |result| { const container_handle = result.unwrapped orelse result.original; const container_handle_nodes = try container_handle.getAllTypesWithHandles(server.arena.allocator()); @@ -1195,9 +1192,7 @@ pub fn getSymbolFieldAccesses( .other => |n| n, else => continue, }; - try decls_with_handles.append(server.arena.allocator(), (try analysis.lookupSymbolContainer( - server.arena.allocator(), - &server.document_store, + try decls_with_handles.append(server.arena.allocator(), (try server.analyser.lookupSymbolContainer( .{ .node = container_handle_node, .handle = ty.handle }, name, true, @@ -1263,7 +1258,7 @@ pub fn hoverDefinitionFieldAccess( pub fn gotoDefinitionString( server: *Server, - pos_context: analysis.PositionContext, + pos_context: Analyser.PositionContext, handle: *const DocumentStore.Handle, ) error{OutOfMemory}!?types.Location { const tracy_zone = tracy.trace(@src()); @@ -1320,7 +1315,7 @@ const DeclToCompletionContext = struct { either_descriptor: ?[]const u8 = null, }; -pub fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) error{OutOfMemory}!void { +pub fn declToCompletion(context: DeclToCompletionContext, decl_handle: Analyser.DeclWithHandle) error{OutOfMemory}!void { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -1345,9 +1340,9 @@ pub fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis. const doc: Documentation = if (param.first_doc_comment) |doc_comments| .{ .MarkupContent = types.MarkupContent{ .kind = doc_kind, .value = if (context.either_descriptor) |ed| - try std.fmt.allocPrint(allocator, "`Conditionally available: {s}`\n\n{s}", .{ ed, try analysis.collectDocComments(allocator, tree, doc_comments, doc_kind, false) }) + try std.fmt.allocPrint(allocator, "`Conditionally available: {s}`\n\n{s}", .{ ed, try Analyser.collectDocComments(allocator, tree, doc_comments, doc_kind, false) }) else - try analysis.collectDocComments(allocator, tree, doc_comments, doc_kind, false), + try Analyser.collectDocComments(allocator, tree, doc_comments, doc_kind, false), } } else null; const first_token = ast.paramFirstToken(tree, param); @@ -1406,7 +1401,7 @@ pub fn completeLabel( .completions = &completions, .orig_handle = handle, }; - try analysis.iterateLabels(handle, pos_index, declToCompletion, context); + try Analyser.iterateLabels(handle, pos_index, declToCompletion, context); return completions.toOwnedSlice(server.arena.allocator()); } @@ -1490,7 +1485,7 @@ pub fn completeGlobal(server: *Server, pos_index: usize, handle: *const Document .completions = &completions, .orig_handle = handle, }; - try analysis.iterateSymbolsGlobal(server.arena.allocator(), &server.document_store, handle, pos_index, declToCompletion, context); + try server.analyser.iterateSymbolsGlobal(handle, pos_index, declToCompletion, context); try populateSnippedCompletions(server.arena.allocator(), &completions, &snipped_data.generic, server.config.*, null); if (server.client_capabilities.label_details_support) { @@ -1513,7 +1508,7 @@ pub fn completeFieldAccess(server: *Server, handle: *const DocumentStore.Handle, var held_loc = try allocator.dupeZ(u8, offsets.locToSlice(handle.text, loc)); var tokenizer = std.zig.Tokenizer.init(held_loc); - const result = (try analysis.getFieldAccessType(allocator, &server.document_store, handle, source_index, &tokenizer)) orelse return null; + const result = (try server.analyser.getFieldAccessType(handle, source_index, &tokenizer)) orelse return null; try server.typeToCompletion(&completions, result, handle, null); if (server.client_capabilities.label_details_support) { for (completions.items) |*item| { @@ -1739,9 +1734,9 @@ pub fn completeFileSystemStringLiteral( arena: std.mem.Allocator, store: DocumentStore, handle: DocumentStore.Handle, - pos_context: analysis.PositionContext, + pos_context: Analyser.PositionContext, ) ![]types.CompletionItem { - var completions: analysis.CompletionSet = .{}; + var completions: Analyser.CompletionSet = .{}; const loc = pos_context.loc().?; var completing = handle.tree.source[loc.start + 1 .. loc.end - 1]; @@ -2217,6 +2212,9 @@ fn changeDocumentHandler(server: *Server, notification: types.DidChangeTextDocum const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); + // whenever a document changes, any cached info is invalidated + server.analyser.invalidate(); + const handle = server.document_store.getHandle(notification.textDocument.uri) orelse return; const new_text = try diff.applyContentChanges(server.allocator, handle.text, notification.contentChanges, server.offset_encoding); @@ -2288,7 +2286,7 @@ fn semanticTokensFullHandler(server: *Server, request: types.SemanticTokensParam const handle = server.document_store.getHandle(request.textDocument.uri) orelse return null; - return try semantic_tokens.writeSemanticTokens(server.arena.allocator(), &server.document_store, handle, null, server.offset_encoding); + return try semantic_tokens.writeSemanticTokens(server.arena.allocator(), &server.analyser, handle, null, server.offset_encoding); } fn semanticTokensRangeHandler(server: *Server, request: types.SemanticTokensRangeParams) Error!?types.SemanticTokens { @@ -2300,7 +2298,7 @@ fn semanticTokensRangeHandler(server: *Server, request: types.SemanticTokensRang const handle = server.document_store.getHandle(request.textDocument.uri) orelse return null; const loc = offsets.rangeToLoc(handle.tree.source, request.range, server.offset_encoding); - return try semantic_tokens.writeSemanticTokens(server.arena.allocator(), &server.document_store, handle, loc, server.offset_encoding); + return try semantic_tokens.writeSemanticTokens(server.arena.allocator(), &server.analyser, handle, loc, server.offset_encoding); } pub fn completionHandler(server: *Server, request: types.CompletionParams) Error!?types.CompletionList { @@ -2317,7 +2315,7 @@ pub fn completionHandler(server: *Server, request: types.CompletionParams) Error } const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding); - const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, false); + const pos_context = try Analyser.getPositionContext(server.arena.allocator(), handle.text, source_index, false); const maybe_completions = switch (pos_context) { .builtin => try server.completeBuiltin(), @@ -2345,7 +2343,7 @@ pub fn completionHandler(server: *Server, request: types.CompletionParams) Error // The cursor is in the middle of a word or before a @, so we can replace // the remaining identifier with the completion instead of just inserting. // TODO Identify function call/struct init and replace the whole thing. - const lookahead_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, true); + const lookahead_context = try Analyser.getPositionContext(server.arena.allocator(), handle.text, source_index, true); if (server.client_capabilities.supports_apply_edits and pos_context != .import_string_literal and pos_context != .cinclude_string_literal and @@ -2403,7 +2401,7 @@ pub fn signatureHelpHandler(server: *Server, request: types.SignatureHelpParams) const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding); const signature_info = (try getSignatureInfo( - &server.document_store, + &server.analyser, server.arena.allocator(), handle, source_index, @@ -2429,7 +2427,7 @@ pub fn gotoHandler(server: *Server, request: types.TextDocumentPositionParams, r if (request.position.character == 0) return null; const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding); - const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, true); + const pos_context = try Analyser.getPositionContext(server.arena.allocator(), handle.text, source_index, true); return switch (pos_context) { .builtin => |loc| .{ .Location = (try server.gotoDefinitionBuiltin(handle, loc)) orelse return null }, @@ -2473,7 +2471,7 @@ pub fn hoverHandler(server: *Server, request: types.HoverParams) Error!?types.Ho if (request.position.character == 0) return null; const source_index = offsets.positionToIndex(handle.text, request.position, server.offset_encoding); - const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, true); + const pos_context = try Analyser.getPositionContext(server.arena.allocator(), handle.text, source_index, true); const response = switch (pos_context) { .builtin => try server.hoverDefinitionBuiltin(source_index, handle), @@ -2636,7 +2634,7 @@ pub fn generalReferencesHandler(server: *Server, request: GeneralReferencesReque if (request.position().character <= 0) return null; const source_index = offsets.positionToIndex(handle.text, request.position(), server.offset_encoding); - const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, true); + const pos_context = try Analyser.getPositionContext(server.arena.allocator(), handle.text, source_index, true); // TODO: Make this work with branching types const decl = switch (pos_context) { @@ -2663,7 +2661,7 @@ pub fn generalReferencesHandler(server: *Server, request: GeneralReferencesReque else try references.symbolReferences( allocator, - &server.document_store, + &server.analyser, decl, server.offset_encoding, include_decl, @@ -2736,7 +2734,7 @@ fn inlayHintHandler(server: *Server, request: types.InlayHintParams) Error!?[]ty const hints = try inlay_hints.writeRangeInlayHint( server.arena.allocator(), server.config.*, - &server.document_store, + &server.analyser, handle, loc, hover_kind, @@ -2786,7 +2784,7 @@ fn codeActionHandler(server: *Server, request: types.CodeActionParams) Error!?[] var builder = code_actions.Builder{ .arena = server.arena.allocator(), - .document_store = &server.document_store, + .analyser = &server.analyser, .handle = handle, .offset_encoding = server.offset_encoding, }; @@ -3203,16 +3201,12 @@ pub fn create( recording_enabled: bool, replay_enabled: bool, ) !*Server { - // TODO replace global with something like an Analyser struct - // which contains using_trail & resolve_trail and place it inside Server - // see: https://github.com/zigtools/zls/issues/536 - analysis.init(allocator); - const server = try allocator.create(Server); server.* = Server{ .config = config, .runtime_zig_version = null, .allocator = allocator, + .analyser = undefined, .arena = std.heap.ArenaAllocator.init(allocator), .document_store = .{ .allocator = allocator, @@ -3224,6 +3218,7 @@ pub fn create( .replay_enabled = replay_enabled, .status = .uninitialized, }; + server.analyser = Analyser.init(allocator, server.arena.allocator(), &server.document_store); try configuration.configChanged(config, &server.runtime_zig_version, allocator, config_path); @@ -3232,7 +3227,7 @@ pub fn create( pub fn destroy(server: *Server) void { server.document_store.deinit(); - analysis.deinit(); + server.analyser.deinit(); if (server.builtin_completions) |*completions| completions.deinit(server.allocator); diff --git a/src/analysis.zig b/src/analysis.zig index 3084019..6200a53 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -10,15 +10,33 @@ const tracy = @import("tracy.zig"); const ComptimeInterpreter = @import("ComptimeInterpreter.zig"); const InternPool = ComptimeInterpreter.InternPool; -var using_trail: std.ArrayList([*]const u8) = undefined; -var resolve_trail: std.ArrayList(NodeWithHandle) = undefined; -pub fn init(allocator: std.mem.Allocator) void { - using_trail = std.ArrayList([*]const u8).init(allocator); - resolve_trail = std.ArrayList(NodeWithHandle).init(allocator); +const Analyser = @This(); + +gpa: std.mem.Allocator, +arena: std.mem.Allocator, +store: *DocumentStore, +bound_type_params: std.AutoHashMapUnmanaged(Ast.full.FnProto.Param, TypeWithHandle) = .{}, +using_trail: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{}, +resolved_nodes: std.HashMapUnmanaged(NodeWithHandle, ?TypeWithHandle, NodeWithHandle.Context, std.hash_map.default_max_load_percentage) = .{}, + +pub fn init(gpa: std.mem.Allocator, arena: std.mem.Allocator, store: *DocumentStore) Analyser { + return .{ + .gpa = gpa, + .arena = arena, + .store = store, + }; } -pub fn deinit() void { - using_trail.deinit(); - resolve_trail.deinit(); + +pub fn deinit(self: *Analyser) void { + self.bound_type_params.deinit(self.gpa); + self.using_trail.deinit(self.gpa); + self.resolved_nodes.deinit(self.gpa); +} + +pub fn invalidate(self: *Analyser) void { + self.bound_type_params.clearRetainingCapacity(); + self.using_trail.clearRetainingCapacity(); + self.resolved_nodes.clearRetainingCapacity(); } /// Gets a declaration's doc comments. Caller owns returned memory. @@ -187,7 +205,7 @@ pub fn getFunctionSnippet(allocator: std.mem.Allocator, tree: Ast, func: Ast.ful return buffer.toOwnedSlice(allocator); } -pub fn hasSelfParam(arena: std.mem.Allocator, document_store: *DocumentStore, handle: *const DocumentStore.Handle, func: Ast.full.FnProto) !bool { +pub fn hasSelfParam(analyser: *Analyser, handle: *const DocumentStore.Handle, func: Ast.full.FnProto) !bool { // Non-decl prototypes cannot have a self parameter. if (func.name_token == null) return false; if (func.ast.params.len == 0) return false; @@ -200,7 +218,7 @@ pub fn hasSelfParam(arena: std.mem.Allocator, document_store: *DocumentStore, ha const token_starts = tree.tokens.items(.start); const in_container = innermostContainer(handle, token_starts[func.ast.fn_token]); - if (try resolveTypeOfNode(arena, document_store, .{ + if (try analyser.resolveTypeOfNode(.{ .node = param.type_expr, .handle = handle, })) |resolved_type| { @@ -209,7 +227,7 @@ pub fn hasSelfParam(arena: std.mem.Allocator, document_store: *DocumentStore, ha } if (ast.fullPtrType(tree, param.type_expr)) |ptr_type| { - if (try resolveTypeOfNode(arena, document_store, .{ + if (try analyser.resolveTypeOfNode(.{ .node = ptr_type.ast.child_type, .handle = handle, })) |resolved_prefix_op| { @@ -340,8 +358,7 @@ pub fn getDeclName(tree: Ast, node: Ast.Node.Index) ?[]const u8 { return name; } -fn resolveVarDeclAliasInternal(arena: std.mem.Allocator, store: *DocumentStore, node_handle: NodeWithHandle, root: bool) error{OutOfMemory}!?DeclWithHandle { - _ = root; +fn resolveVarDeclAliasInternal(analyser: *Analyser, node_handle: NodeWithHandle) error{OutOfMemory}!?DeclWithHandle { const handle = node_handle.handle; const tree = handle.tree; const node_tags = tree.nodes.items(.tag); @@ -350,9 +367,7 @@ fn resolveVarDeclAliasInternal(arena: std.mem.Allocator, store: *DocumentStore, if (node_tags[node_handle.node] == .identifier) { const token = main_tokens[node_handle.node]; - return try lookupSymbolGlobal( - arena, - store, + return try analyser.lookupSymbolGlobal( handle, tree.tokenSlice(token), tree.tokens.items(.start)[token], @@ -367,13 +382,13 @@ fn resolveVarDeclAliasInternal(arena: std.mem.Allocator, store: *DocumentStore, if (!std.mem.eql(u8, name, "@import") and !std.mem.eql(u8, name, "@cImport")) return null; - const inner_node = (try resolveTypeOfNode(arena, store, .{ .node = lhs, .handle = handle })) orelse return null; + const inner_node = (try analyser.resolveTypeOfNode(.{ .node = lhs, .handle = handle })) orelse return null; // assert root node std.debug.assert(inner_node.type.data.other == 0); break :block NodeWithHandle{ .node = inner_node.type.data.other, .handle = inner_node.handle }; - } else if (try resolveVarDeclAliasInternal(arena, store, .{ .node = lhs, .handle = handle }, false)) |decl_handle| block: { + } else if (try analyser.resolveVarDeclAliasInternal(.{ .node = lhs, .handle = handle })) |decl_handle| block: { if (decl_handle.decl.* != .ast_node) return null; - const resolved = (try resolveTypeOfNode(arena, store, .{ .node = decl_handle.decl.ast_node, .handle = decl_handle.handle })) orelse return null; + const resolved = (try analyser.resolveTypeOfNode(.{ .node = decl_handle.decl.ast_node, .handle = decl_handle.handle })) orelse return null; const resolved_node = switch (resolved.type.data) { .other => |n| n, else => return null, @@ -382,7 +397,7 @@ fn resolveVarDeclAliasInternal(arena: std.mem.Allocator, store: *DocumentStore, break :block NodeWithHandle{ .node = resolved_node, .handle = resolved.handle }; } else return null; - return try lookupSymbolContainer(arena, store, container_node, tree.tokenSlice(datas[node_handle.node].rhs), false); + return try analyser.lookupSymbolContainer(container_node, tree.tokenSlice(datas[node_handle.node].rhs), false); } return null; } @@ -393,7 +408,7 @@ fn resolveVarDeclAliasInternal(arena: std.mem.Allocator, store: *DocumentStore, /// const decl = @import("decl-file.zig").decl; /// const other = decl.middle.other; ///``` -pub fn resolveVarDeclAlias(arena: std.mem.Allocator, store: *DocumentStore, decl_handle: NodeWithHandle) !?DeclWithHandle { +pub fn resolveVarDeclAlias(analyser: *Analyser, decl_handle: NodeWithHandle) !?DeclWithHandle { const decl = decl_handle.node; const handle = decl_handle.handle; const tree = handle.tree; @@ -410,7 +425,7 @@ pub fn resolveVarDeclAlias(arena: std.mem.Allocator, store: *DocumentStore, decl if (!std.mem.eql(u8, tree.tokenSlice(var_decl.ast.mut_token + 1), name)) return null; - return try resolveVarDeclAliasInternal(arena, store, .{ .node = base_exp, .handle = handle }, true); + return try analyser.resolveVarDeclAliasInternal(.{ .node = base_exp, .handle = handle }); } } @@ -458,7 +473,7 @@ fn findReturnStatement(tree: Ast, fn_decl: Ast.full.FnProto, body: Ast.Node.Inde return findReturnStatementInternal(tree, fn_decl, body, &already_found); } -pub fn resolveReturnType(arena: std.mem.Allocator, store: *DocumentStore, fn_decl: Ast.full.FnProto, handle: *const DocumentStore.Handle, bound_type_params: *BoundTypeParams, fn_body: ?Ast.Node.Index) !?TypeWithHandle { +fn resolveReturnType(analyser: *Analyser, fn_decl: Ast.full.FnProto, handle: *const DocumentStore.Handle, fn_body: ?Ast.Node.Index) !?TypeWithHandle { const tree = handle.tree; if (isTypeFunction(tree, fn_decl) and fn_body != null) { // If this is a type function and it only contains a single return statement that returns @@ -466,10 +481,7 @@ pub fn resolveReturnType(arena: std.mem.Allocator, store: *DocumentStore, fn_dec const ret = findReturnStatement(tree, fn_decl, fn_body.?) orelse return null; const data = tree.nodes.items(.data)[ret]; if (data.lhs != 0) { - return try resolveTypeOfNodeInternal(arena, store, .{ - .node = data.lhs, - .handle = handle, - }, bound_type_params); + return try analyser.resolveTypeOfNodeInternal(.{ .node = data.lhs, .handle = handle }); } return null; @@ -478,7 +490,7 @@ pub fn resolveReturnType(arena: std.mem.Allocator, store: *DocumentStore, fn_dec if (fn_decl.ast.return_type == 0) return null; const return_type = fn_decl.ast.return_type; const ret = .{ .node = return_type, .handle = handle }; - const child_type = (try resolveTypeOfNodeInternal(arena, store, ret, bound_type_params)) orelse + const child_type = (try analyser.resolveTypeOfNodeInternal(ret)) orelse return null; const is_inferred_error = tree.tokens.items(.tag)[tree.firstToken(return_type) - 1] == .bang; @@ -495,23 +507,23 @@ pub fn resolveReturnType(arena: std.mem.Allocator, store: *DocumentStore, fn_dec } /// Resolves the child type of an optional type -fn resolveUnwrapOptionalType(arena: std.mem.Allocator, store: *DocumentStore, opt: TypeWithHandle, bound_type_params: *BoundTypeParams) !?TypeWithHandle { +fn resolveUnwrapOptionalType(analyser: *Analyser, opt: TypeWithHandle) !?TypeWithHandle { const opt_node = switch (opt.type.data) { .other => |n| n, else => return null, }; if (opt.handle.tree.nodes.items(.tag)[opt_node] == .optional_type) { - return ((try resolveTypeOfNodeInternal(arena, store, .{ + return ((try analyser.resolveTypeOfNodeInternal(.{ .node = opt.handle.tree.nodes.items(.data)[opt_node].lhs, .handle = opt.handle, - }, bound_type_params)) orelse return null).instanceTypeVal(); + })) orelse return null).instanceTypeVal(); } return null; } -fn resolveUnwrapErrorType(arena: std.mem.Allocator, store: *DocumentStore, rhs: TypeWithHandle, bound_type_params: *BoundTypeParams) !?TypeWithHandle { +fn resolveUnwrapErrorType(analyser: *Analyser, rhs: TypeWithHandle) !?TypeWithHandle { const rhs_node = switch (rhs.type.data) { .other => |n| n, .error_union => |n| return TypeWithHandle{ @@ -522,17 +534,17 @@ fn resolveUnwrapErrorType(arena: std.mem.Allocator, store: *DocumentStore, rhs: }; if (rhs.handle.tree.nodes.items(.tag)[rhs_node] == .error_union) { - return ((try resolveTypeOfNodeInternal(arena, store, .{ + return ((try analyser.resolveTypeOfNodeInternal(.{ .node = rhs.handle.tree.nodes.items(.data)[rhs_node].rhs, .handle = rhs.handle, - }, bound_type_params)) orelse return null).instanceTypeVal(); + })) orelse return null).instanceTypeVal(); } return null; } /// Resolves the child type of a deref type -fn resolveDerefType(arena: std.mem.Allocator, store: *DocumentStore, deref: TypeWithHandle, bound_type_params: *BoundTypeParams) !?TypeWithHandle { +fn resolveDerefType(analyser: *Analyser, deref: TypeWithHandle) !?TypeWithHandle { const deref_node = switch (deref.type.data) { .other => |n| n, .pointer => |n| return TypeWithHandle{ @@ -551,10 +563,10 @@ fn resolveDerefType(arena: std.mem.Allocator, store: *DocumentStore, deref: Type if (ast.fullPtrType(tree, deref_node)) |ptr_type| { switch (token_tag) { .asterisk => { - return ((try resolveTypeOfNodeInternal(arena, store, .{ + return ((try analyser.resolveTypeOfNodeInternal(.{ .node = ptr_type.ast.child_type, .handle = deref.handle, - }, bound_type_params)) orelse return null).instanceTypeVal(); + })) orelse return null).instanceTypeVal(); }, .l_bracket, .asterisk_asterisk => return null, else => unreachable, @@ -564,7 +576,7 @@ fn resolveDerefType(arena: std.mem.Allocator, store: *DocumentStore, deref: Type } /// Resolves slicing and array access -fn resolveBracketAccessType(arena: std.mem.Allocator, store: *DocumentStore, lhs: TypeWithHandle, rhs: enum { Single, Range }, bound_type_params: *BoundTypeParams) !?TypeWithHandle { +fn resolveBracketAccessType(analyser: *Analyser, lhs: TypeWithHandle, rhs: enum { Single, Range }) !?TypeWithHandle { const lhs_node = switch (lhs.type.data) { .other => |n| n, else => return null, @@ -577,10 +589,10 @@ fn resolveBracketAccessType(arena: std.mem.Allocator, store: *DocumentStore, lhs if (tag == .array_type or tag == .array_type_sentinel) { if (rhs == .Single) - return ((try resolveTypeOfNodeInternal(arena, store, .{ + return ((try analyser.resolveTypeOfNodeInternal(.{ .node = data.rhs, .handle = lhs.handle, - }, bound_type_params)) orelse return null).instanceTypeVal(); + })) orelse return null).instanceTypeVal(); return TypeWithHandle{ .type = .{ .data = .{ .slice = data.rhs }, .is_type_val = false }, .handle = lhs.handle, @@ -588,10 +600,10 @@ fn resolveBracketAccessType(arena: std.mem.Allocator, store: *DocumentStore, lhs } else if (ast.fullPtrType(tree, lhs_node)) |ptr_type| { if (ptr_type.size == .Slice) { if (rhs == .Single) { - return ((try resolveTypeOfNodeInternal(arena, store, .{ + return ((try analyser.resolveTypeOfNodeInternal(.{ .node = ptr_type.ast.child_type, .handle = lhs.handle, - }, bound_type_params)) orelse return null).instanceTypeVal(); + })) orelse return null).instanceTypeVal(); } return lhs; } @@ -601,12 +613,11 @@ fn resolveBracketAccessType(arena: std.mem.Allocator, store: *DocumentStore, lhs } /// Called to remove one level of pointerness before a field access -pub fn resolveFieldAccessLhsType(arena: std.mem.Allocator, store: *DocumentStore, lhs: TypeWithHandle, bound_type_params: *BoundTypeParams) !TypeWithHandle { - return (try resolveDerefType(arena, store, lhs, bound_type_params)) orelse lhs; +pub fn resolveFieldAccessLhsType(analyser: *Analyser, lhs: TypeWithHandle) !TypeWithHandle { + // analyser.bound_type_params.clearRetainingCapacity(); + return (try analyser.resolveDerefType(lhs)) orelse lhs; } -pub const BoundTypeParams = std.AutoHashMapUnmanaged(Ast.full.FnProto.Param, TypeWithHandle); - fn allDigits(str: []const u8) bool { for (str) |c| { if (!std.ascii.isDigit(c)) return false; @@ -650,21 +661,32 @@ pub fn isTypeIdent(text: []const u8) bool { } /// Resolves the type of a node -pub fn resolveTypeOfNodeInternal( - arena: std.mem.Allocator, - store: *DocumentStore, - node_handle: NodeWithHandle, - bound_type_params: *BoundTypeParams, -) error{OutOfMemory}!?TypeWithHandle { - // If we were asked to resolve this node before, - // it is self-referential and we cannot resolve it. - for (resolve_trail.items) |i| { - if (std.meta.eql(i, node_handle)) - return null; - } - try resolve_trail.append(node_handle); - defer _ = resolve_trail.pop(); +fn resolveTypeOfNodeInternal(analyser: *Analyser, node_handle: NodeWithHandle) error{OutOfMemory}!?TypeWithHandle { + const gop = try analyser.resolved_nodes.getOrPut(analyser.gpa, node_handle); + if (gop.found_existing) return gop.value_ptr.*; + // we insert null before resolving the type so that a recursive definition doesn't result in an infinite loop + gop.value_ptr.* = null; + + const type_handle = try analyser.resolveTypeOfNodeUncached(node_handle); + analyser.resolved_nodes.getPtr(node_handle).?.* = type_handle; + + return type_handle; + + // if (analyser.resolved_nodes.get(node_handle)) |type_handle| return type_handle; + + //// If we were asked to resolve this node before, + //// it is self-referential and we cannot resolve it. + //for (analyser.resolve_trail.items) |i| { + // if (std.meta.eql(i, node_handle)) + // return null; + //} + //try analyser.resolve_trail.append(analyser.gpa, node_handle); + //defer _ = analyser.resolve_trail.pop(); + +} + +fn resolveTypeOfNodeUncached(analyser: *Analyser, node_handle: NodeWithHandle) error{OutOfMemory}!?TypeWithHandle { const node = node_handle.node; const handle = node_handle.handle; const tree = handle.tree; @@ -684,14 +706,14 @@ pub fn resolveTypeOfNodeInternal( const var_decl = tree.fullVarDecl(node).?; if (var_decl.ast.type_node != 0) { const decl_type = .{ .node = var_decl.ast.type_node, .handle = handle }; - if (try resolveTypeOfNodeInternal(arena, store, decl_type, bound_type_params)) |typ| + if (try analyser.resolveTypeOfNodeInternal(decl_type)) |typ| return typ.instanceTypeVal(); } if (var_decl.ast.init_node == 0) return null; const value = .{ .node = var_decl.ast.init_node, .handle = handle }; - return try resolveTypeOfNodeInternal(arena, store, value, bound_type_params); + return try analyser.resolveTypeOfNodeInternal(value); }, .identifier => { const name = offsets.nodeToSlice(tree, node); @@ -703,9 +725,7 @@ pub fn resolveTypeOfNodeInternal( }; } - if (try lookupSymbolGlobal( - arena, - store, + if (try analyser.lookupSymbolGlobal( handle, name, starts[main_tokens[node]], @@ -720,7 +740,7 @@ pub fn resolveTypeOfNodeInternal( }, else => {}, } - return try child.resolveType(arena, store, bound_type_params); + return try child.resolveType(analyser); } return null; }, @@ -737,7 +757,7 @@ pub fn resolveTypeOfNodeInternal( const call = tree.fullCall(¶ms, node) orelse unreachable; const callee = .{ .node = call.ast.fn_expr, .handle = handle }; - const decl = (try resolveTypeOfNodeInternal(arena, store, callee, bound_type_params)) orelse + const decl = (try analyser.resolveTypeOfNodeInternal(callee)) orelse return null; if (decl.type.is_type_val) return null; @@ -754,7 +774,7 @@ pub fn resolveTypeOfNodeInternal( // TODO: Back-parse to extract the self argument? var it = fn_decl.iterate(&decl.handle.tree); if (token_tags[call.ast.lparen - 2] == .period) { - if (try hasSelfParam(arena, store, decl.handle, fn_decl)) { + if (try analyser.hasSelfParam(decl.handle, fn_decl)) { _ = ast.nextFnParam(&it); expected_params -= 1; } @@ -769,23 +789,20 @@ pub fn resolveTypeOfNodeInternal( continue; const argument = .{ .node = call.ast.params[i], .handle = handle }; - const argument_type = (try resolveTypeOfNodeInternal( - arena, - store, + const argument_type = (try analyser.resolveTypeOfNodeInternal( argument, - bound_type_params, )) orelse continue; if (!argument_type.type.is_type_val) continue; - try bound_type_params.put(store.allocator, decl_param, argument_type); + try analyser.bound_type_params.put(analyser.gpa, decl_param, argument_type); } const has_body = decl.handle.tree.nodes.items(.tag)[decl_node] == .fn_decl; const body = decl.handle.tree.nodes.items(.data)[decl_node].rhs; - if (try resolveReturnType(arena, store, fn_decl, decl.handle, bound_type_params, if (has_body) body else null)) |ret| { + if (try analyser.resolveReturnType(fn_decl, decl.handle, if (has_body) body else null)) |ret| { return ret; - } else if (store.config.use_comptime_interpreter) { + } else if (analyser.store.config.use_comptime_interpreter) { // TODO: Better case-by-case; we just use the ComptimeInterpreter when all else fails, // probably better to use it more liberally // TODO: Handle non-isolate args; e.g. `const T = u8; TypeFunc(T);` @@ -798,7 +815,7 @@ pub fn resolveTypeOfNodeInternal( log.info("Invoking interpreter!", .{}); - const interpreter = store.ensureInterpreterExists(handle.uri) catch |err| { + const interpreter = analyser.store.ensureInterpreterExists(handle.uri) catch |err| { log.err("Failed to interpret file: {s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -861,7 +878,7 @@ pub fn resolveTypeOfNodeInternal( .address_of, => { const base = .{ .node = datas[node].lhs, .handle = handle }; - const base_type = (try resolveTypeOfNodeInternal(arena, store, base, bound_type_params)) orelse + const base_type = (try analyser.resolveTypeOfNodeInternal(base)) orelse return null; return switch (node_tags[node]) { .@"comptime", @@ -879,13 +896,13 @@ pub fn resolveTypeOfNodeInternal( .slice, .slice_sentinel, .slice_open, - => try resolveBracketAccessType(arena, store, base_type, .Range, bound_type_params), - .deref => try resolveDerefType(arena, store, base_type, bound_type_params), - .unwrap_optional => try resolveUnwrapOptionalType(arena, store, base_type, bound_type_params), - .array_access => try resolveBracketAccessType(arena, store, base_type, .Single, bound_type_params), - .@"orelse" => try resolveUnwrapOptionalType(arena, store, base_type, bound_type_params), - .@"catch" => try resolveUnwrapErrorType(arena, store, base_type, bound_type_params), - .@"try" => try resolveUnwrapErrorType(arena, store, base_type, bound_type_params), + => try analyser.resolveBracketAccessType(base_type, .Range), + .deref => try analyser.resolveDerefType(base_type), + .unwrap_optional => try analyser.resolveUnwrapOptionalType(base_type), + .array_access => try analyser.resolveBracketAccessType(base_type, .Single), + .@"orelse" => try analyser.resolveUnwrapOptionalType(base_type), + .@"catch" => try analyser.resolveUnwrapErrorType(base_type), + .@"try" => try analyser.resolveUnwrapErrorType(base_type), .address_of => { const lhs_node = switch (base_type.type.data) { .other => |n| n, @@ -902,30 +919,25 @@ pub fn resolveTypeOfNodeInternal( .field_access => { if (datas[node].rhs == 0) return null; + const lhs = (try analyser.resolveTypeOfNodeInternal(.{ + .node = datas[node].lhs, + .handle = handle, + })) orelse return null; + // If we are accessing a pointer type, remove one pointerness level :) - const left_type = try resolveFieldAccessLhsType( - arena, - store, - (try resolveTypeOfNodeInternal(arena, store, .{ - .node = datas[node].lhs, - .handle = handle, - }, bound_type_params)) orelse return null, - bound_type_params, - ); + const left_type = (try analyser.resolveDerefType(lhs)) orelse lhs; const left_type_node = switch (left_type.type.data) { .other => |n| n, else => return null, }; - if (try lookupSymbolContainer( - arena, - store, + if (try analyser.lookupSymbolContainer( .{ .node = left_type_node, .handle = left_type.handle }, tree.tokenSlice(datas[node].rhs), !left_type.type.is_type_val, )) |child| { - return try child.resolveType(arena, store, bound_type_params); + return try child.resolveType(analyser); } else return null; }, .array_type, @@ -979,20 +991,20 @@ pub fn resolveTypeOfNodeInternal( }); if (cast_map.has(call_name)) { if (params.len < 1) return null; - return ((try resolveTypeOfNodeInternal(arena, store, .{ + return ((try analyser.resolveTypeOfNodeInternal(.{ .node = params[0], .handle = handle, - }, bound_type_params)) orelse return null).instanceTypeVal(); + })) orelse return null).instanceTypeVal(); } // Almost the same as the above, return a type value though. // TODO Do peer type resolution, we just keep the first for now. if (std.mem.eql(u8, call_name, "@TypeOf")) { if (params.len < 1) return null; - var resolved_type = (try resolveTypeOfNodeInternal(arena, store, .{ + var resolved_type = (try analyser.resolveTypeOfNodeInternal(.{ .node = params[0], .handle = handle, - }, bound_type_params)) orelse return null; + })) orelse return null; if (resolved_type.type.is_type_val) return null; resolved_type.type.is_type_val = true; @@ -1000,16 +1012,14 @@ pub fn resolveTypeOfNodeInternal( } if (std.mem.eql(u8, call_name, "@typeInfo")) { - const zig_lib_path = try URI.fromPath(store.allocator, store.config.zig_lib_path orelse return null); - defer store.allocator.free(zig_lib_path); + const zig_lib_path = try URI.fromPath(analyser.arena, analyser.store.config.zig_lib_path orelse return null); - const builtin_uri = URI.pathRelative(store.allocator, zig_lib_path, "/std/builtin.zig") catch |err| switch (err) { + const builtin_uri = URI.pathRelative(analyser.arena, zig_lib_path, "/std/builtin.zig") catch |err| switch (err) { error.OutOfMemory => |e| return e, else => return null, }; - defer store.allocator.free(builtin_uri); - const new_handle = store.getOrLoadHandle(builtin_uri) orelse return null; + const new_handle = analyser.store.getOrLoadHandle(builtin_uri) orelse return null; const root_scope_decls = new_handle.document_scope.scopes.items(.decls)[0]; const decl = root_scope_decls.get("Type") orelse return null; if (decl != .ast_node) return null; @@ -1031,17 +1041,16 @@ pub fn resolveTypeOfNodeInternal( if (node_tags[import_param] != .string_literal) return null; const import_str = tree.tokenSlice(main_tokens[import_param]); - const import_uri = (try store.uriFromImportStr(store.allocator, handle.*, import_str[1 .. import_str.len - 1])) orelse return null; - defer store.allocator.free(import_uri); + const import_uri = (try analyser.store.uriFromImportStr(analyser.arena, handle.*, import_str[1 .. import_str.len - 1])) orelse return null; - const new_handle = store.getOrLoadHandle(import_uri) orelse return null; + const new_handle = analyser.store.getOrLoadHandle(import_uri) orelse return null; // reference to node '0' which is root return TypeWithHandle.typeVal(.{ .node = 0, .handle = new_handle }); } else if (std.mem.eql(u8, call_name, "@cImport")) { - const cimport_uri = (try store.resolveCImport(handle.*, node)) orelse return null; + const cimport_uri = (try analyser.store.resolveCImport(handle.*, node)) orelse return null; - const new_handle = store.getOrLoadHandle(cimport_uri) orelse return null; + const new_handle = analyser.store.getOrLoadHandle(cimport_uri) orelse return null; // reference to node '0' which is root return TypeWithHandle.typeVal(.{ .node = 0, .handle = new_handle }); @@ -1073,15 +1082,14 @@ pub fn resolveTypeOfNodeInternal( .@"if", .if_simple => { const if_node = ast.fullIf(tree, node).?; - // TODO: Fix allocator need - var either = std.ArrayList(Type.EitherEntry).init(arena); - if (try resolveTypeOfNodeInternal(arena, store, .{ .handle = handle, .node = if_node.ast.then_expr }, bound_type_params)) |t| - try either.append(.{ .type_with_handle = t, .descriptor = tree.getNodeSource(if_node.ast.cond_expr) }); - if (try resolveTypeOfNodeInternal(arena, store, .{ .handle = handle, .node = if_node.ast.else_expr }, bound_type_params)) |t| - try either.append(.{ .type_with_handle = t, .descriptor = try std.fmt.allocPrint(arena, "!({s})", .{tree.getNodeSource(if_node.ast.cond_expr)}) }); + var either = std.ArrayListUnmanaged(Type.EitherEntry){}; + if (try analyser.resolveTypeOfNodeInternal(.{ .handle = handle, .node = if_node.ast.then_expr })) |t| + try either.append(analyser.arena, .{ .type_with_handle = t, .descriptor = tree.getNodeSource(if_node.ast.cond_expr) }); + if (try analyser.resolveTypeOfNodeInternal(.{ .handle = handle, .node = if_node.ast.else_expr })) |t| + try either.append(analyser.arena, .{ .type_with_handle = t, .descriptor = try std.fmt.allocPrint(analyser.arena, "!({s})", .{tree.getNodeSource(if_node.ast.cond_expr)}) }); return TypeWithHandle{ - .type = .{ .data = .{ .either = try either.toOwnedSlice() }, .is_type_val = false }, + .type = .{ .data = .{ .either = try either.toOwnedSlice(analyser.arena) }, .is_type_val = false }, .handle = handle, }; }, @@ -1091,26 +1099,26 @@ pub fn resolveTypeOfNodeInternal( const extra = tree.extraData(datas[node].rhs, Ast.Node.SubRange); const cases = tree.extra_data[extra.start..extra.end]; - var either = std.ArrayList(Type.EitherEntry).init(arena); + var either = std.ArrayListUnmanaged(Type.EitherEntry){}; for (cases) |case| { const switch_case = tree.fullSwitchCase(case).?; - var descriptor = std.ArrayList(u8).init(arena); + var descriptor = std.ArrayListUnmanaged(u8){}; for (switch_case.ast.values, 0..) |vals, index| { - try descriptor.appendSlice(tree.getNodeSource(vals)); - if (index != switch_case.ast.values.len - 1) try descriptor.appendSlice(", "); + try descriptor.appendSlice(analyser.arena, tree.getNodeSource(vals)); + if (index != switch_case.ast.values.len - 1) try descriptor.appendSlice(analyser.arena, ", "); } - if (try resolveTypeOfNodeInternal(arena, store, .{ .handle = handle, .node = switch_case.ast.target_expr }, bound_type_params)) |t| - try either.append(.{ + if (try analyser.resolveTypeOfNodeInternal(.{ .handle = handle, .node = switch_case.ast.target_expr })) |t| + try either.append(analyser.arena, .{ .type_with_handle = t, - .descriptor = try descriptor.toOwnedSlice(), + .descriptor = try descriptor.toOwnedSlice(analyser.arena), }); } return TypeWithHandle{ - .type = .{ .data = .{ .either = try either.toOwnedSlice() }, .is_type_val = false }, + .type = .{ .data = .{ .either = try either.toOwnedSlice(analyser.arena) }, .is_type_val = false }, .handle = handle, }; }, @@ -1267,14 +1275,9 @@ pub const TypeWithHandle = struct { } }; -pub fn resolveTypeOfNode( - arena: std.mem.Allocator, - store: *DocumentStore, - node_handle: NodeWithHandle, -) error{OutOfMemory}!?TypeWithHandle { - var bound_type_params = BoundTypeParams{}; - defer bound_type_params.deinit(store.allocator); - return resolveTypeOfNodeInternal(arena, store, node_handle, &bound_type_params); +pub fn resolveTypeOfNode(analyser: *Analyser, node_handle: NodeWithHandle) error{OutOfMemory}!?TypeWithHandle { + analyser.bound_type_params.clearRetainingCapacity(); + return analyser.resolveTypeOfNodeInternal(node_handle); } /// Collects all `@import`'s we can find into a slice of import paths (without quotes). @@ -1336,6 +1339,22 @@ pub fn collectCImportNodes(allocator: std.mem.Allocator, tree: Ast) error{OutOfM pub const NodeWithHandle = struct { node: Ast.Node.Index, handle: *const DocumentStore.Handle, + + const Context = struct { + pub fn hash(self: @This(), item: NodeWithHandle) u64 { + _ = self; + var hasher = std.hash.Wyhash.init(0); + std.hash.autoHash(&hasher, item.node); + hasher.update(item.handle.uri); + return hasher.final(); + } + + pub fn eql(self: @This(), a: NodeWithHandle, b: NodeWithHandle) bool { + _ = self; + if (a.node != b.node) return false; + return std.mem.eql(u8, a.handle.uri, b.handle.uri); + } + }; }; pub const FieldAccessReturn = struct { @@ -1343,29 +1362,26 @@ pub const FieldAccessReturn = struct { unwrapped: ?TypeWithHandle = null, }; -pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handle: *const DocumentStore.Handle, source_index: usize, tokenizer: *std.zig.Tokenizer) !?FieldAccessReturn { - var current_type: ?TypeWithHandle = null; +pub fn getFieldAccessType(analyser: *Analyser, handle: *const DocumentStore.Handle, source_index: usize, tokenizer: *std.zig.Tokenizer) !?FieldAccessReturn { + analyser.bound_type_params.clearRetainingCapacity(); - var bound_type_params = BoundTypeParams{}; - defer bound_type_params.deinit(store.allocator); + var current_type: ?TypeWithHandle = null; while (true) { const tok = tokenizer.next(); switch (tok.tag) { .eof => return FieldAccessReturn{ .original = current_type orelse return null, - .unwrapped = try resolveDerefType(arena, store, current_type orelse return null, &bound_type_params), + .unwrapped = try analyser.resolveDerefType(current_type orelse return null), }, .identifier => { const ct_handle = if (current_type) |c| c.handle else handle; - if (try lookupSymbolGlobal( - arena, - store, + if (try analyser.lookupSymbolGlobal( ct_handle, tokenizer.buffer[tok.loc.start..tok.loc.end], source_index, )) |child| { - current_type = (try child.resolveType(arena, store, &bound_type_params)) orelse return null; + current_type = (try child.resolveType(analyser)) orelse return null; } else return null; }, .period => { @@ -1377,7 +1393,7 @@ pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handl if (ct.isFunc()) return null; return FieldAccessReturn{ .original = ct, - .unwrapped = try resolveDerefType(arena, store, ct, &bound_type_params), + .unwrapped = try analyser.resolveDerefType(ct), }; } else { return null; @@ -1388,16 +1404,19 @@ pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handl if (current_type) |ct| { return FieldAccessReturn{ .original = ct, - .unwrapped = try resolveDerefType(arena, store, ct, &bound_type_params), + .unwrapped = try analyser.resolveDerefType(ct), }; } else { return null; } } - current_type = try resolveFieldAccessLhsType(arena, store, current_type orelse return null, &bound_type_params); + const deref_type = if (current_type) |ty| + if (try analyser.resolveDerefType(ty)) |deref_ty| deref_ty else ty + else + return null; - const current_type_nodes = try current_type.?.getAllTypesWithHandles(arena); + const current_type_nodes = try deref_type.getAllTypesWithHandles(analyser.arena); // TODO: Return all options instead of first valid one // (this would require a huge rewrite and im lazy) @@ -1407,18 +1426,12 @@ pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handl else => continue, }; - if (try lookupSymbolContainer( - arena, - store, + if (try analyser.lookupSymbolContainer( .{ .node = current_type_node, .handle = ty.handle }, tokenizer.buffer[after_period.loc.start..after_period.loc.end], !current_type.?.type.is_type_val, )) |child| { - current_type.? = (try child.resolveType( - arena, - store, - &bound_type_params, - )) orelse continue; + current_type.? = (try child.resolveType(analyser)) orelse continue; break; } else continue; } else { @@ -1426,12 +1439,7 @@ pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handl } }, .question_mark => { - current_type = (try resolveUnwrapOptionalType( - arena, - store, - current_type orelse return null, - &bound_type_params, - )) orelse return null; + current_type = (try analyser.resolveUnwrapOptionalType(current_type orelse return null)) orelse return null; }, else => { log.debug("Unrecognized token {} after period.", .{after_period.tag}); @@ -1440,12 +1448,7 @@ pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handl } }, .period_asterisk => { - current_type = (try resolveDerefType( - arena, - store, - current_type orelse return null, - &bound_type_params, - )) orelse return null; + current_type = (try analyser.resolveDerefType(current_type orelse return null)) orelse return null; }, .l_paren => { if (current_type == null) { @@ -1468,7 +1471,7 @@ pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handl const body = cur_tree.nodes.items(.data)[current_type_node].rhs; // TODO Actually bind params here when calling functions instead of just skipping args. - if (try resolveReturnType(arena, store, func, current_type.?.handle, &bound_type_params, if (has_body) body else null)) |ret| { + if (try analyser.resolveReturnType(func, current_type.?.handle, if (has_body) body else null)) |ret| { current_type = ret; // Skip to the right paren var paren_count: usize = 1; @@ -1499,7 +1502,7 @@ pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handl } } else return null; - current_type = (try resolveBracketAccessType(arena, store, current_type orelse return null, if (is_range) .Range else .Single, &bound_type_params)) orelse return null; + current_type = (try analyser.resolveBracketAccessType(current_type orelse return null, if (is_range) .Range else .Single)) orelse return null; }, else => { log.debug("Unimplemented token: {}", .{tok.tag}); @@ -1508,10 +1511,11 @@ pub fn getFieldAccessType(arena: std.mem.Allocator, store: *DocumentStore, handl } } + std.debug.print("current_type: {?}\n", .{current_type}); if (current_type) |ct| { return FieldAccessReturn{ .original = ct, - .unwrapped = try resolveDerefType(arena, store, ct, &bound_type_params), + .unwrapped = try analyser.resolveDerefType(ct), }; } else { return null; @@ -1575,11 +1579,6 @@ pub fn nodeToString(tree: Ast, node: Ast.Node.Index) ?[]const u8 { }; } -fn nodeContainsSourceIndex(tree: Ast, node: Ast.Node.Index, source_index: usize) bool { - const loc = offsets.nodeToLoc(tree, node); - return source_index >= loc.start and source_index <= loc.end; -} - pub const PositionContext = union(enum) { builtin: offsets.Loc, comment, @@ -1885,21 +1884,18 @@ pub const DeclWithHandle = struct { }; } - pub fn resolveType(self: DeclWithHandle, arena: std.mem.Allocator, store: *DocumentStore, bound_type_params: *BoundTypeParams) !?TypeWithHandle { + pub fn resolveType(self: DeclWithHandle, analyser: *Analyser) !?TypeWithHandle { const tree = self.handle.tree; const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); return switch (self.decl.*) { - .ast_node => |node| try resolveTypeOfNodeInternal( - arena, - store, + .ast_node => |node| try analyser.resolveTypeOfNodeInternal( .{ .node = node, .handle = self.handle }, - bound_type_params, ), .param_payload => |pay| { const param_decl = pay.param; if (isMetaType(self.handle.tree, param_decl.type_expr)) { - var bound_param_it = bound_type_params.iterator(); + var bound_param_it = analyser.bound_type_params.iterator(); while (bound_param_it.next()) |entry| { if (std.meta.eql(entry.key_ptr.*, param_decl)) return entry.value_ptr.*; } @@ -1910,31 +1906,22 @@ pub const DeclWithHandle = struct { return null; } } - return ((try resolveTypeOfNodeInternal( - arena, - store, + return ((try analyser.resolveTypeOfNodeInternal( .{ .node = param_decl.type_expr, .handle = self.handle }, - bound_type_params, )) orelse return null).instanceTypeVal(); }, - .pointer_payload => |pay| try resolveUnwrapOptionalType( - arena, - store, - (try resolveTypeOfNodeInternal(arena, store, .{ + .pointer_payload => |pay| try analyser.resolveUnwrapOptionalType( + (try analyser.resolveTypeOfNodeInternal(.{ .node = pay.condition, .handle = self.handle, - }, bound_type_params)) orelse return null, - bound_type_params, + })) orelse return null, ), - .array_payload => |pay| try resolveBracketAccessType( - arena, - store, - (try resolveTypeOfNodeInternal(arena, store, .{ + .array_payload => |pay| try analyser.resolveBracketAccessType( + (try analyser.resolveTypeOfNodeInternal(.{ .node = pay.array_expr, .handle = self.handle, - }, bound_type_params)) orelse return null, + })) orelse return null, .Single, - bound_type_params, ), .array_index => TypeWithHandle{ .type = .{ .data = .array_index, .is_type_val = false }, @@ -1944,10 +1931,10 @@ pub const DeclWithHandle = struct { .switch_payload => |pay| { if (pay.items.len == 0) return null; // TODO Peer type resolution, we just use the first item for now. - const switch_expr_type = (try resolveTypeOfNodeInternal(arena, store, .{ + const switch_expr_type = (try analyser.resolveTypeOfNodeInternal(.{ .node = pay.switch_expr, .handle = self.handle, - }, bound_type_params)) orelse return null; + })) orelse return null; if (!switch_expr_type.isUnionType()) return null; @@ -1962,11 +1949,8 @@ pub const DeclWithHandle = struct { .ast_node => |node| { if (switch_expr_type.handle.tree.fullContainerField(node)) |container_field| { if (container_field.ast.type_expr != 0) { - return ((try resolveTypeOfNodeInternal( - arena, - store, + return ((try analyser.resolveTypeOfNodeInternal( .{ .node = container_field.ast.type_expr, .handle = switch_expr_type.handle }, - bound_type_params, )) orelse return null).instanceTypeVal(); } } @@ -1996,7 +1980,14 @@ fn findContainerScopeIndex(container_handle: NodeWithHandle) ?usize { } else null; } -fn iterateSymbolsContainerInternal(arena: std.mem.Allocator, store: *DocumentStore, container_handle: NodeWithHandle, orig_handle: *const DocumentStore.Handle, comptime callback: anytype, context: anytype, instance_access: bool, use_trail: *std.ArrayList(Ast.Node.Index)) error{OutOfMemory}!void { +fn iterateSymbolsContainerInternal( + analyser: *Analyser, + container_handle: NodeWithHandle, + orig_handle: *const DocumentStore.Handle, + comptime callback: anytype, + context: anytype, + instance_access: bool, +) error{OutOfMemory}!void { const container = container_handle.node; const handle = container_handle.handle; @@ -2039,11 +2030,12 @@ fn iterateSymbolsContainerInternal(arena: std.mem.Allocator, store: *DocumentSto const use_token = tree.nodes.items(.main_token)[use]; const is_pub = use_token > 0 and token_tags[use_token - 1] == .keyword_pub; if (handle != orig_handle and !is_pub) continue; - if (std.mem.indexOfScalar(Ast.Node.Index, use_trail.items, use) != null) continue; - try use_trail.append(use); + + const gop = try analyser.using_trail.getOrPut(analyser.gpa, use); + if (gop.found_existing) continue; const lhs = tree.nodes.items(.data)[use].lhs; - const use_expr = (try resolveTypeOfNode(arena, store, .{ + const use_expr = (try analyser.resolveTypeOfNode(.{ .node = lhs, .handle = handle, })) orelse continue; @@ -2052,15 +2044,12 @@ fn iterateSymbolsContainerInternal(arena: std.mem.Allocator, store: *DocumentSto .other => |n| n, else => continue, }; - try iterateSymbolsContainerInternal( - arena, - store, + try analyser.iterateSymbolsContainerInternal( .{ .node = use_expr_node, .handle = use_expr.handle }, orig_handle, callback, context, false, - use_trail, ); } } @@ -2087,7 +2076,7 @@ pub const EnclosingScopeIterator = struct { } }; -pub fn iterateEnclosingScopes(document_scope: DocumentScope, source_index: usize) EnclosingScopeIterator { +fn iterateEnclosingScopes(document_scope: DocumentScope, source_index: usize) EnclosingScopeIterator { return .{ .scope_locs = document_scope.scopes.items(.loc), .current_scope = 0, @@ -2095,10 +2084,16 @@ pub fn iterateEnclosingScopes(document_scope: DocumentScope, source_index: usize }; } -pub fn iterateSymbolsContainer(arena: std.mem.Allocator, store: *DocumentStore, container_handle: NodeWithHandle, orig_handle: *const DocumentStore.Handle, comptime callback: anytype, context: anytype, instance_access: bool) error{OutOfMemory}!void { - var use_trail = std.ArrayList(Ast.Node.Index).init(store.allocator); - defer use_trail.deinit(); - return try iterateSymbolsContainerInternal(arena, store, container_handle, orig_handle, callback, context, instance_access, &use_trail); +pub fn iterateSymbolsContainer( + analyser: *Analyser, + container_handle: NodeWithHandle, + orig_handle: *const DocumentStore.Handle, + comptime callback: anytype, + context: anytype, + instance_access: bool, +) error{OutOfMemory}!void { + analyser.using_trail.clearRetainingCapacity(); + return try analyser.iterateSymbolsContainerInternal(container_handle, orig_handle, callback, context, instance_access); } pub fn iterateLabels(handle: *const DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype) error{OutOfMemory}!void { @@ -2117,7 +2112,13 @@ pub fn iterateLabels(handle: *const DocumentStore.Handle, source_index: usize, c } } -fn iterateSymbolsGlobalInternal(arena: std.mem.Allocator, store: *DocumentStore, handle: *const DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype, use_trail: *std.ArrayList(Ast.Node.Index)) error{OutOfMemory}!void { +fn iterateSymbolsGlobalInternal( + analyser: *Analyser, + handle: *const DocumentStore.Handle, + source_index: usize, + comptime callback: anytype, + context: anytype, +) error{OutOfMemory}!void { const scope_decls = handle.document_scope.scopes.items(.decls); const scope_uses = handle.document_scope.scopes.items(.uses); @@ -2132,36 +2133,36 @@ fn iterateSymbolsGlobalInternal(arena: std.mem.Allocator, store: *DocumentStore, } for (scope_uses[scope_index].items) |use| { - if (std.mem.indexOfScalar(Ast.Node.Index, use_trail.items, use) != null) continue; - try use_trail.append(use); + const gop = try analyser.using_trail.getOrPut(analyser.gpa, use); + if (gop.found_existing) continue; - const use_expr = (try resolveTypeOfNode( - arena, - store, + const use_expr = (try analyser.resolveTypeOfNodeInternal( .{ .node = handle.tree.nodes.items(.data)[use].lhs, .handle = handle }, )) orelse continue; const use_expr_node = switch (use_expr.type.data) { .other => |n| n, else => continue, }; - try iterateSymbolsContainerInternal( - arena, - store, + try analyser.iterateSymbolsContainerInternal( .{ .node = use_expr_node, .handle = use_expr.handle }, handle, callback, context, false, - use_trail, ); } } } -pub fn iterateSymbolsGlobal(arena: std.mem.Allocator, store: *DocumentStore, handle: *const DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype) error{OutOfMemory}!void { - var use_trail = std.ArrayList(Ast.Node.Index).init(store.allocator); - defer use_trail.deinit(); - return try iterateSymbolsGlobalInternal(arena, store, handle, source_index, callback, context, &use_trail); +pub fn iterateSymbolsGlobal( + analyser: *Analyser, + handle: *const DocumentStore.Handle, + source_index: usize, + comptime callback: anytype, + context: anytype, +) error{OutOfMemory}!void { + analyser.using_trail.clearRetainingCapacity(); + return try analyser.iterateSymbolsGlobalInternal(handle, source_index, callback, context); } pub fn innermostBlockScopeIndex(handle: DocumentStore.Handle, source_index: usize) usize { @@ -2206,21 +2207,15 @@ pub fn innermostContainer(handle: *const DocumentStore.Handle, source_index: usi return TypeWithHandle.typeVal(.{ .node = current, .handle = handle }); } -fn resolveUse(arena: std.mem.Allocator, store: *DocumentStore, uses: []const Ast.Node.Index, symbol: []const u8, handle: *const DocumentStore.Handle) error{OutOfMemory}!?DeclWithHandle { - if (uses.len == 0) return null; - - // If we were asked to resolve this symbol before, - // it is self-referential and we cannot resolve it. - if (std.mem.indexOfScalar([*]const u8, using_trail.items, symbol.ptr) != null) - return null; - try using_trail.append(symbol.ptr); - defer _ = using_trail.pop(); - +fn resolveUse(analyser: *Analyser, uses: []const Ast.Node.Index, symbol: []const u8, handle: *const DocumentStore.Handle) error{OutOfMemory}!?DeclWithHandle { for (uses) |index| { + const gop = try analyser.using_trail.getOrPut(analyser.gpa, index); + if (gop.found_existing) continue; + if (handle.tree.nodes.items(.data).len <= index) continue; const expr = .{ .node = handle.tree.nodes.items(.data)[index].lhs, .handle = handle }; - const expr_type_node = (try resolveTypeOfNode(arena, store, expr)) orelse + const expr_type_node = (try analyser.resolveTypeOfNode(expr)) orelse continue; const expr_type = .{ @@ -2231,7 +2226,7 @@ fn resolveUse(arena: std.mem.Allocator, store: *DocumentStore, uses: []const Ast .handle = expr_type_node.handle, }; - if (try lookupSymbolContainer(arena, store, expr_type, symbol, false)) |candidate| { + if (try analyser.lookupSymbolContainer(expr_type, symbol, false)) |candidate| { if (candidate.handle == handle or candidate.isPublic()) { return candidate; } @@ -2263,7 +2258,7 @@ pub fn lookupLabel( return null; } -pub fn lookupSymbolGlobal(arena: std.mem.Allocator, store: *DocumentStore, handle: *const DocumentStore.Handle, symbol: []const u8, source_index: usize) error{OutOfMemory}!?DeclWithHandle { +pub fn lookupSymbolGlobal(analyser: *Analyser, handle: *const DocumentStore.Handle, symbol: []const u8, source_index: usize) error{OutOfMemory}!?DeclWithHandle { const innermost_scope_idx = innermostBlockScopeIndex(handle.*, source_index); const scope_locs = handle.document_scope.scopes.items(.loc); @@ -2287,7 +2282,7 @@ pub fn lookupSymbolGlobal(arena: std.mem.Allocator, store: *DocumentStore, handl .handle = handle, }; } - if (try resolveUse(arena, store, scope_uses[curr].items, symbol, handle)) |result| return result; + if (try analyser.resolveUse(scope_uses[curr].items, symbol, handle)) |result| return result; } if (curr == 0) break; } @@ -2295,8 +2290,7 @@ pub fn lookupSymbolGlobal(arena: std.mem.Allocator, store: *DocumentStore, handl } pub fn lookupSymbolContainer( - arena: std.mem.Allocator, - store: *DocumentStore, + analyser: *Analyser, container_handle: NodeWithHandle, symbol: []const u8, /// If true, we are looking up the symbol like we are accessing through a field access @@ -2329,7 +2323,7 @@ pub fn lookupSymbolContainer( return DeclWithHandle{ .decl = candidate.value_ptr, .handle = handle }; } - if (try resolveUse(arena, store, scope_uses[container_scope_index].items, symbol, handle)) |result| return result; + if (try analyser.resolveUse(scope_uses[container_scope_index].items, symbol, handle)) |result| return result; } return null; diff --git a/src/code_actions.zig b/src/code_actions.zig index caad37c..15521da 100644 --- a/src/code_actions.zig +++ b/src/code_actions.zig @@ -2,7 +2,7 @@ const std = @import("std"); const Ast = std.zig.Ast; const DocumentStore = @import("DocumentStore.zig"); -const analysis = @import("analysis.zig"); +const Analyser = @import("analysis.zig"); const ast = @import("ast.zig"); const types = @import("lsp.zig"); @@ -10,7 +10,7 @@ const offsets = @import("offsets.zig"); pub const Builder = struct { arena: std.mem.Allocator, - document_store: *DocumentStore, + analyser: *Analyser, handle: *const DocumentStore.Handle, offset_encoding: offsets.Encoding, @@ -88,9 +88,7 @@ fn handleUnusedFunctionParameter(builder: *Builder, actions: *std.ArrayListUnman const token_starts = tree.tokens.items(.start); - const decl = (try analysis.lookupSymbolGlobal( - builder.arena, - builder.document_store, + const decl = (try builder.analyser.lookupSymbolGlobal( builder.handle, identifier_name, loc.start, @@ -134,9 +132,7 @@ fn handleUnusedVariableOrConstant(builder: *Builder, actions: *std.ArrayListUnma const token_tags = tree.tokens.items(.tag); const token_starts = tree.tokens.items(.start); - const decl = (try analysis.lookupSymbolGlobal( - builder.arena, - builder.document_store, + const decl = (try builder.analyser.lookupSymbolGlobal( builder.handle, identifier_name, loc.start, diff --git a/src/inlay_hints.zig b/src/inlay_hints.zig index 1e3d308..6c6fa88 100644 --- a/src/inlay_hints.zig +++ b/src/inlay_hints.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zig_builtin = @import("builtin"); const DocumentStore = @import("DocumentStore.zig"); -const analysis = @import("analysis.zig"); +const Analyser = @import("analysis.zig"); const types = @import("lsp.zig"); const offsets = @import("offsets.zig"); const tracy = @import("tracy.zig"); @@ -26,7 +26,7 @@ pub const InlayHint = struct { const Builder = struct { arena: std.mem.Allocator, - store: *DocumentStore, + analyser: *Analyser, config: *const Config, handle: *const DocumentStore.Handle, hints: std.ArrayListUnmanaged(InlayHint), @@ -64,7 +64,7 @@ const Builder = struct { /// `call` is the function call /// `decl_handle` should be a function protototype /// writes parameter hints into `builder.hints` -fn writeCallHint(builder: *Builder, call: Ast.full.Call, decl_handle: analysis.DeclWithHandle) !void { +fn writeCallHint(builder: *Builder, call: Ast.full.Call, decl_handle: Analyser.DeclWithHandle) !void { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -85,7 +85,7 @@ fn writeCallHint(builder: *Builder, call: Ast.full.Call, decl_handle: analysis.D var i: usize = 0; var it = fn_proto.iterate(&decl_tree); - if (try analysis.hasSelfParam(builder.arena, builder.store, decl_handle.handle, fn_proto)) { + if (try builder.analyser.hasSelfParam(decl_handle.handle, fn_proto)) { _ = ast.nextFnParam(&it); } @@ -187,7 +187,7 @@ fn writeCallNodeHint(builder: *Builder, call: Ast.full.Call) !void { const source_index = offsets.tokenToIndex(tree, main_tokens[call.ast.fn_expr]); const name = offsets.tokenToSlice(tree, main_tokens[call.ast.fn_expr]); - if (try analysis.lookupSymbolGlobal(builder.arena, builder.store, handle, name, source_index)) |decl_handle| { + if (try builder.analyser.lookupSymbolGlobal(handle, name, source_index)) |decl_handle| { try writeCallHint(builder, call, decl_handle); } }, @@ -204,13 +204,11 @@ fn writeCallNodeHint(builder: *Builder, call: Ast.full.Call) !void { // note: we have the ast node, traversing it would probably yield better results // than trying to re-tokenize and re-parse it - if (try analysis.getFieldAccessType(builder.arena, builder.store, handle, rhs_loc.end, &tokenizer)) |result| { + if (try builder.analyser.getFieldAccessType(handle, rhs_loc.end, &tokenizer)) |result| { const container_handle = result.unwrapped orelse result.original; switch (container_handle.type.data) { .other => |container_handle_node| { - if (try analysis.lookupSymbolContainer( - builder.arena, - builder.store, + if (try builder.analyser.lookupSymbolContainer( .{ .node = container_handle_node, .handle = container_handle.handle }, tree.tokenSlice(rhsToken), true, @@ -285,7 +283,7 @@ fn writeNodeInlayHint( pub fn writeRangeInlayHint( arena: std.mem.Allocator, config: Config, - store: *DocumentStore, + analyser: *Analyser, handle: *const DocumentStore.Handle, loc: offsets.Loc, hover_kind: types.MarkupKind, @@ -295,7 +293,7 @@ pub fn writeRangeInlayHint( var builder: Builder = .{ .arena = arena, - .store = store, + .analyser = analyser, .config = &config, .handle = handle, .hints = .{}, diff --git a/src/references.zig b/src/references.zig index 08305da..f8eea28 100644 --- a/src/references.zig +++ b/src/references.zig @@ -1,7 +1,7 @@ const std = @import("std"); const Ast = std.zig.Ast; const DocumentStore = @import("DocumentStore.zig"); -const analysis = @import("analysis.zig"); +const Analyser = @import("analysis.zig"); const types = @import("lsp.zig"); const offsets = @import("offsets.zig"); const log = std.log.scoped(.zls_references); @@ -9,7 +9,7 @@ const ast = @import("ast.zig"); pub fn labelReferences( allocator: std.mem.Allocator, - decl: analysis.DeclWithHandle, + decl: Analyser.DeclWithHandle, encoding: offsets.Encoding, include_decl: bool, ) error{OutOfMemory}!std.ArrayListUnmanaged(types.Location) { @@ -57,8 +57,8 @@ const Builder = struct { allocator: std.mem.Allocator, locations: std.ArrayListUnmanaged(types.Location) = .{}, /// this is the declaration we are searching for - decl_handle: analysis.DeclWithHandle, - store: *DocumentStore, + decl_handle: Analyser.DeclWithHandle, + analyser: *Analyser, encoding: offsets.Encoding, const Context = struct { @@ -98,12 +98,10 @@ const Builder = struct { .identifier, .test_decl, => { - const identifier_token = analysis.getDeclNameToken(tree, node).?; + const identifier_token = Analyser.getDeclNameToken(tree, node).?; if (token_tags[identifier_token] != .identifier) return; - const child = (try analysis.lookupSymbolGlobal( - builder.allocator, - builder.store, + const child = (try builder.analyser.lookupSymbolGlobal( handle, offsets.tokenToSlice(tree, identifier_token), starts[identifier_token], @@ -114,18 +112,8 @@ const Builder = struct { } }, .field_access => { - var bound_type_params = analysis.BoundTypeParams{}; - defer bound_type_params.deinit(builder.store.allocator); - const left_type = try analysis.resolveFieldAccessLhsType( - builder.allocator, - builder.store, - (try analysis.resolveTypeOfNodeInternal( - builder.allocator, - builder.store, - .{ .node = datas[node].lhs, .handle = handle }, - &bound_type_params, - )) orelse return, - &bound_type_params, + const left_type = try builder.analyser.resolveFieldAccessLhsType( + (try builder.analyser.resolveTypeOfNode(.{ .node = datas[node].lhs, .handle = handle })) orelse return, ); const left_type_node = switch (left_type.type.data) { @@ -133,9 +121,7 @@ const Builder = struct { else => return, }; - const child = (try analysis.lookupSymbolContainer( - self.builder.allocator, - builder.store, + const child = (try builder.analyser.lookupSymbolContainer( .{ .node = left_type_node, .handle = left_type.handle }, offsets.tokenToSlice(tree, datas[node].rhs), !left_type.type.is_type_val, @@ -152,8 +138,8 @@ const Builder = struct { pub fn symbolReferences( allocator: std.mem.Allocator, - store: *DocumentStore, - decl_handle: analysis.DeclWithHandle, + analyser: *Analyser, + decl_handle: Analyser.DeclWithHandle, encoding: offsets.Encoding, /// add `decl_handle` as a references include_decl: bool, @@ -166,7 +152,7 @@ pub fn symbolReferences( var builder = Builder{ .allocator = allocator, - .store = store, + .analyser = analyser, .decl_handle = decl_handle, .encoding = encoding, }; @@ -194,7 +180,7 @@ pub fn symbolReferences( dependencies.deinit(allocator); } - for (store.handles.values()) |handle| { + 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; @@ -207,7 +193,7 @@ pub fn symbolReferences( } handle_dependencies.deinit(allocator); } - try store.collectDependencies(allocator, handle.*, &handle_dependencies); + try analyser.store.collectDependencies(allocator, handle.*, &handle_dependencies); try dependencies.ensureUnusedCapacity(allocator, handle_dependencies.items.len); for (handle_dependencies.items) |uri| { @@ -217,7 +203,7 @@ pub fn symbolReferences( for (dependencies.keys()) |uri| { if (std.mem.eql(u8, uri, curr_handle.uri)) continue; - const handle = store.getHandle(uri) orelse continue; + const handle = analyser.store.getHandle(uri) orelse continue; try builder.collectReferences(handle, 0); } diff --git a/src/semantic_tokens.zig b/src/semantic_tokens.zig index 84c0c6a..739cbeb 100644 --- a/src/semantic_tokens.zig +++ b/src/semantic_tokens.zig @@ -2,7 +2,7 @@ const std = @import("std"); const zig_builtin = @import("builtin"); const offsets = @import("offsets.zig"); const DocumentStore = @import("DocumentStore.zig"); -const analysis = @import("analysis.zig"); +const Analyser = @import("analysis.zig"); const Ast = std.zig.Ast; const ast = @import("ast.zig"); const types = @import("lsp.zig"); @@ -40,7 +40,7 @@ pub const TokenModifiers = packed struct(u16) { const Builder = struct { arena: std.mem.Allocator, - store: *DocumentStore, + analyser: *Analyser, handle: *const DocumentStore.Handle, previous_source_index: usize = 0, previous_token: ?Ast.TokenIndex = null, @@ -203,7 +203,7 @@ fn fieldTokenType(container_decl: Ast.Node.Index, handle: *const DocumentStore.H }); } -fn colorIdentifierBasedOnType(builder: *Builder, type_node: analysis.TypeWithHandle, target_tok: Ast.TokenIndex, tok_mod: TokenModifiers) !void { +fn colorIdentifierBasedOnType(builder: *Builder, type_node: Analyser.TypeWithHandle, target_tok: Ast.TokenIndex, tok_mod: TokenModifiers) !void { if (type_node.type.is_type_val) { var new_tok_mod = tok_mod; if (type_node.isNamespace()) @@ -300,7 +300,7 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v .aligned_var_decl, => { const var_decl = tree.fullVarDecl(node).?; - if (analysis.getDocCommentTokenIndex(token_tags, main_token)) |comment_idx| + if (Analyser.getDocCommentTokenIndex(token_tags, main_token)) |comment_idx| try writeDocComments(builder, tree, comment_idx); try writeToken(builder, var_decl.visib_token, .keyword); @@ -309,7 +309,7 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v try writeToken(builder, var_decl.comptime_token, .keyword); try writeToken(builder, var_decl.ast.mut_token, .keyword); - if (try analysis.resolveTypeOfNode(allocator, builder.store, .{ .node = node, .handle = handle })) |decl_type| { + if (try builder.analyser.resolveTypeOfNode(.{ .node = node, .handle = handle })) |decl_type| { try colorIdentifierBasedOnType(builder, decl_type, var_decl.ast.mut_token + 1, .{ .declaration = true }); } else { try writeTokenMod(builder, var_decl.ast.mut_token + 1, .variable, .{ .declaration = true }); @@ -393,15 +393,13 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v if (std.mem.eql(u8, name, "_")) { return; - } else if (analysis.isValueIdent(name)) { + } else if (Analyser.isValueIdent(name)) { return try writeToken(builder, main_token, .keywordLiteral); - } else if (analysis.isTypeIdent(name)) { + } else if (Analyser.isTypeIdent(name)) { return try writeToken(builder, main_token, .type); } - if (try analysis.lookupSymbolGlobal( - allocator, - builder.store, + if (try builder.analyser.lookupSymbolGlobal( handle, name, tree.tokens.items(.start)[main_token], @@ -409,10 +407,7 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v if (child.decl.* == .param_payload) { return try writeToken(builder, main_token, .parameter); } - var bound_type_params = analysis.BoundTypeParams{}; - defer bound_type_params.deinit(builder.store.allocator); - - if (try child.resolveType(allocator, builder.store, &bound_type_params)) |decl_type| { + if (try child.resolveType(builder.analyser)) |decl_type| { return try colorIdentifierBasedOnType(builder, decl_type, main_token, .{}); } } @@ -426,7 +421,7 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v => { var buf: [1]Ast.Node.Index = undefined; const fn_proto: Ast.full.FnProto = tree.fullFnProto(&buf, node).?; - if (analysis.getDocCommentTokenIndex(token_tags, main_token)) |docs| + if (Analyser.getDocCommentTokenIndex(token_tags, main_token)) |docs| try writeDocComments(builder, tree, docs); try writeToken(builder, fn_proto.visib_token, .keyword); @@ -434,14 +429,14 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v try writeToken(builder, fn_proto.lib_name, .string); try writeToken(builder, fn_proto.ast.fn_token, .keyword); - const func_name_tok_type: TokenType = if (analysis.isTypeFunction(tree, fn_proto)) + const func_name_tok_type: TokenType = if (Analyser.isTypeFunction(tree, fn_proto)) .type else .function; const tok_mod = TokenModifiers{ .declaration = true, - .generic = analysis.isGenericFunction(tree, fn_proto), + .generic = Analyser.isGenericFunction(tree, fn_proto), }; try writeTokenMod(builder, fn_proto.name_token, func_name_tok_type, tok_mod); @@ -477,7 +472,7 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v .@"comptime", .@"nosuspend", => { - if (analysis.getDocCommentTokenIndex(token_tags, main_token)) |doc| + if (Analyser.getDocCommentTokenIndex(token_tags, main_token)) |doc| try writeDocComments(builder, tree, doc); try writeToken(builder, main_token, .keyword); try callWriteNodeTokens(allocator, .{ builder, node_data[node].lhs }); @@ -617,9 +612,7 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v if (struct_init.ast.type_expr != 0) { try callWriteNodeTokens(allocator, .{ builder, struct_init.ast.type_expr }); - field_token_type = if (try analysis.resolveTypeOfNode( - allocator, - builder.store, + field_token_type = if (try builder.analyser.resolveTypeOfNode( .{ .node = struct_init.ast.type_expr, .handle = handle }, )) |struct_type| switch (struct_type.type.data) { .other => |type_node| if (ast.isContainer(struct_type.handle.tree, type_node)) @@ -775,7 +768,7 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v .asm_input, => unreachable, .test_decl => { - if (analysis.getDocCommentTokenIndex(token_tags, main_token)) |doc| + if (Analyser.getDocCommentTokenIndex(token_tags, main_token)) |doc| try writeDocComments(builder, tree, doc); try writeToken(builder, main_token, .keyword); @@ -869,27 +862,14 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v // TODO This is basically exactly the same as what is done in analysis.resolveTypeOfNode, with the added // writeToken code. // Maybe we can hook into it instead? Also applies to Identifier and VarDecl - var bound_type_params = analysis.BoundTypeParams{}; - defer bound_type_params.deinit(builder.store.allocator); - - const lhs_type = try analysis.resolveFieldAccessLhsType( - allocator, - builder.store, - (try analysis.resolveTypeOfNodeInternal( - allocator, - builder.store, - .{ .node = data.lhs, .handle = handle }, - &bound_type_params, - )) orelse return, - &bound_type_params, + const lhs_type = try builder.analyser.resolveFieldAccessLhsType( + (try builder.analyser.resolveTypeOfNode(.{ .node = data.lhs, .handle = handle })) orelse return, ); const left_type_node = switch (lhs_type.type.data) { .other => |n| n, else => return, }; - if (try analysis.lookupSymbolContainer( - allocator, - builder.store, + if (try builder.analyser.lookupSymbolContainer( .{ .node = left_type_node, .handle = lhs_type.handle }, tree.tokenSlice(data.rhs), !lhs_type.type.is_type_val, @@ -913,7 +893,7 @@ fn writeNodeTokens(builder: *Builder, node: Ast.Node.Index) error{OutOfMemory}!v else => {}, } - if (try decl_type.resolveType(allocator, builder.store, &bound_type_params)) |resolved_type| { + if (try decl_type.resolveType(builder.analyser)) |resolved_type| { try colorIdentifierBasedOnType(builder, resolved_type, data.rhs, .{}); } } @@ -992,7 +972,7 @@ fn writeContainerField(builder: *Builder, node: Ast.Node.Index, field_token_type var allocator = builder.arena; - if (analysis.getDocCommentTokenIndex(tokens, base)) |docs| + if (Analyser.getDocCommentTokenIndex(tokens, base)) |docs| try writeDocComments(builder, tree, docs); try writeToken(builder, container_field.comptime_token, .keyword); @@ -1026,14 +1006,14 @@ fn writeContainerField(builder: *Builder, node: Ast.Node.Index, field_token_type /// TODO edit version. pub fn writeSemanticTokens( arena: std.mem.Allocator, - store: *DocumentStore, + analyser: *Analyser, handle: *const DocumentStore.Handle, loc: ?offsets.Loc, encoding: offsets.Encoding, ) error{OutOfMemory}!types.SemanticTokens { var builder = Builder{ .arena = arena, - .store = store, + .analyser = analyser, .handle = handle, .encoding = encoding, }; diff --git a/src/signature_help.zig b/src/signature_help.zig index 6d5e3a9..37f9215 100644 --- a/src/signature_help.zig +++ b/src/signature_help.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const analysis = @import("analysis.zig"); +const Analyser = @import("analysis.zig"); const offsets = @import("offsets.zig"); const DocumentStore = @import("DocumentStore.zig"); const types = @import("lsp.zig"); @@ -8,14 +8,14 @@ const Token = std.zig.Token; const identifierFromPosition = @import("Server.zig").identifierFromPosition; const ast = @import("ast.zig"); -fn fnProtoToSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocator, commas: u32, skip_self_param: bool, handle: *const DocumentStore.Handle, fn_node: Ast.Node.Index, proto: Ast.full.FnProto) !types.SignatureInformation { +fn fnProtoToSignatureInfo(analyser: *Analyser, alloc: std.mem.Allocator, commas: u32, skip_self_param: bool, handle: *const DocumentStore.Handle, fn_node: Ast.Node.Index, proto: Ast.full.FnProto) !types.SignatureInformation { const tree = handle.tree; const token_starts = tree.tokens.items(.start); - const label = analysis.getFunctionSignature(tree, proto); - const proto_comments = (try analysis.getDocComments(alloc, tree, fn_node, .markdown)) orelse ""; + const label = Analyser.getFunctionSignature(tree, proto); + const proto_comments = (try Analyser.getDocComments(alloc, tree, fn_node, .markdown)) orelse ""; const arg_idx = if (skip_self_param) blk: { - const has_self_param = try analysis.hasSelfParam(alloc, document_store, handle, proto); + const has_self_param = try analyser.hasSelfParam(handle, proto); break :blk commas + @boolToInt(has_self_param); } else commas; @@ -23,7 +23,7 @@ fn fnProtoToSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocat var param_it = proto.iterate(&tree); while (ast.nextFnParam(¶m_it)) |param| { const param_comments = if (param.first_doc_comment) |dc| - try analysis.collectDocComments(alloc, tree, dc, .markdown, false) + try Analyser.collectDocComments(alloc, tree, dc, .markdown, false) else ""; @@ -70,8 +70,8 @@ fn fnProtoToSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocat }; } -pub fn getSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocator, handle: *const DocumentStore.Handle, absolute_index: usize, comptime data: type) !?types.SignatureInformation { - const innermost_block = analysis.innermostBlockScope(handle.*, absolute_index); +pub fn getSignatureInfo(analyser: *Analyser, alloc: std.mem.Allocator, handle: *const DocumentStore.Handle, absolute_index: usize, comptime data: type) !?types.SignatureInformation { + const innermost_block = Analyser.innermostBlockScope(handle.*, absolute_index); const tree = handle.tree; const token_tags = tree.tokens.items(.tag); const token_starts = tree.tokens.items(.start); @@ -256,9 +256,7 @@ pub fn getSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocator // Resolve the expression. var tokenizer = std.zig.Tokenizer.init(held_expr); - if (try analysis.getFieldAccessType( - alloc, - document_store, + if (try analyser.getFieldAccessType( handle, expr_start, &tokenizer, @@ -275,7 +273,7 @@ pub fn getSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocator var buf: [1]Ast.Node.Index = undefined; if (type_handle.handle.tree.fullFnProto(&buf, node)) |proto| { return try fnProtoToSignatureInfo( - document_store, + analyser, alloc, paren_commas, false, @@ -292,9 +290,7 @@ pub fn getSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocator } const skip_self_param = !type_handle.type.is_type_val; - const decl_handle = (try analysis.lookupSymbolContainer( - alloc, - document_store, + const decl_handle = (try analyser.lookupSymbolContainer( .{ .node = node, .handle = type_handle.handle }, name, true, @@ -311,9 +307,7 @@ pub fn getSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocator }, }; - if (try analysis.resolveVarDeclAlias( - alloc, - document_store, + if (try analyser.resolveVarDeclAlias( .{ .node = node, .handle = decl_handle.handle }, )) |resolved| { switch (resolved.decl.*) { @@ -327,7 +321,7 @@ pub fn getSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocator if (res_handle.tree.fullFnProto(&buf, node)) |proto| { return try fnProtoToSignatureInfo( - document_store, + analyser, alloc, paren_commas, skip_self_param, diff --git a/src/zls.zig b/src/zls.zig index dee0b2a..14159fd 100644 --- a/src/zls.zig +++ b/src/zls.zig @@ -2,7 +2,7 @@ //! zigbot9001 to take advantage of zls' tools pub const ast = @import("ast.zig"); -pub const analysis = @import("analysis.zig"); +pub const Analyser = @import("analysis.zig"); pub const Header = @import("Header.zig"); pub const debug = @import("debug.zig"); pub const offsets = @import("offsets.zig"); diff --git a/tests/utility/position_context.zig b/tests/utility/position_context.zig index 01f13d6..ffc6701 100644 --- a/tests/utility/position_context.zig +++ b/tests/utility/position_context.zig @@ -1,7 +1,7 @@ const std = @import("std"); const zls = @import("zls"); -const analysis = zls.analysis; +const Analyser = zls.Analyser; const types = zls.types; const offsets = zls.offsets; @@ -510,12 +510,12 @@ test "position context - empty" { ); } -fn testContext(line: []const u8, tag: std.meta.Tag(analysis.PositionContext), maybe_range: ?[]const u8) !void { +fn testContext(line: []const u8, tag: std.meta.Tag(Analyser.PositionContext), maybe_range: ?[]const u8) !void { const cursor_idx = std.mem.indexOf(u8, line, "").?; const final_line = try std.mem.concat(allocator, u8, &.{ line[0..cursor_idx], line[cursor_idx + "".len ..] }); defer allocator.free(final_line); - const ctx = try analysis.getPositionContext(allocator, final_line, cursor_idx, true); + const ctx = try Analyser.getPositionContext(allocator, final_line, cursor_idx, true); if (std.meta.activeTag(ctx) != tag) { std.debug.print("Expected tag `{s}`, got `{s}`\n", .{ @tagName(tag), @tagName(std.meta.activeTag(ctx)) });