From b51b88e453ddab0822349539a4b190166bc14fe9 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 1/6] 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 35a6d85d7..45475540d 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 3084019bb..6200a53a5 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(self: *Analyser) void { + self.bound_type_params.deinit(self.gpa); + self.using_trail.deinit(self.gpa); + self.resolved_nodes.deinit(self.gpa); } -pub fn deinit() void { - using_trail.deinit(); - resolve_trail.deinit(); + +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 caad37c5f..15521da5d 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 1e3d3088b..6c6fa88a6 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 08305daa2..f8eea289e 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 84c0c6a3c..739cbebb7 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 6d5e3a942..37f9215fd 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 dee0b2aeb..14159fd0d 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 01f13d687..ffc670140 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)) }); From 26065c4bcd75bed742110caa5d35ca3c0c11bd18 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 12 Mar 2023 23:49:21 +0100 Subject: [PATCH 2/6] remove `had_root` global --- src/analysis.zig | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 6200a53a5..030693de8 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2418,9 +2418,7 @@ pub fn makeDocumentScope(allocator: std.mem.Allocator, tree: Ast) !DocumentScope }; errdefer document_scope.deinit(allocator); - // pass root node index ('0') - had_root = false; - try makeScopeInternal(allocator, .{ + try makeInnerScope(allocator, .{ .scopes = &document_scope.scopes, .errors = &document_scope.error_completions, .enums = &document_scope.enum_completions, @@ -2490,9 +2488,9 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx: } } -// Whether we have already visited the root node. -var had_root = true; fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_idx: Ast.Node.Index) error{OutOfMemory}!void { + if (node_idx == 0) return; + const scopes = context.scopes; const tree = context.tree; const tags = tree.nodes.items(.tag); @@ -2501,14 +2499,8 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const main_tokens = tree.nodes.items(.main_token); const node_tag = tags[node_idx]; - if (node_idx == 0) { - if (had_root) - return - else - had_root = true; - } - switch (node_tag) { + .root => unreachable, .container_decl, .container_decl_trailing, .container_decl_arg, @@ -2521,7 +2513,6 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i .tagged_union_two_trailing, .tagged_union_enum_tag, .tagged_union_enum_tag_trailing, - .root, => { try makeInnerScope(allocator, context, node_idx); }, From cd3def4018637d8275993552a0a3747abdf95735 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Sun, 12 Mar 2023 23:57:47 +0100 Subject: [PATCH 3/6] replace unnecessary `std.ArrayListUnmanaged` in document scope --- src/analysis.zig | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 030693de8..091740e74 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2026,7 +2026,7 @@ fn iterateSymbolsContainerInternal( try callback(context, decl); } - for (scope_uses[container_scope_index].items) |use| { + for (scope_uses[container_scope_index]) |use| { 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; @@ -2132,7 +2132,7 @@ fn iterateSymbolsGlobalInternal( try callback(context, DeclWithHandle{ .decl = entry.value_ptr, .handle = handle }); } - for (scope_uses[scope_index].items) |use| { + for (scope_uses[scope_index]) |use| { const gop = try analyser.using_trail.getOrPut(analyser.gpa, use); if (gop.found_existing) continue; @@ -2282,7 +2282,7 @@ pub fn lookupSymbolGlobal(analyser: *Analyser, handle: *const DocumentStore.Hand .handle = handle, }; } - if (try analyser.resolveUse(scope_uses[curr].items, symbol, handle)) |result| return result; + if (try analyser.resolveUse(scope_uses[curr], symbol, handle)) |result| return result; } if (curr == 0) break; } @@ -2323,7 +2323,7 @@ pub fn lookupSymbolContainer( return DeclWithHandle{ .decl = candidate.value_ptr, .handle = handle }; } - if (try analyser.resolveUse(scope_uses[container_scope_index].items, symbol, handle)) |result| return result; + if (try analyser.resolveUse(scope_uses[container_scope_index], symbol, handle)) |result| return result; } return null; @@ -2358,11 +2358,10 @@ pub const DocumentScope = struct { enum_completions: CompletionSet, pub fn deinit(self: *DocumentScope, allocator: std.mem.Allocator) void { - var i: usize = 0; - while (i < self.scopes.len) : (i += 1) { - self.scopes.items(.decls)[i].deinit(allocator); - self.scopes.items(.tests)[i].deinit(allocator); - self.scopes.items(.uses)[i].deinit(allocator); + for (self.scopes.items(.decls), self.scopes.items(.tests), self.scopes.items(.uses)) |*decls, tests, uses| { + decls.deinit(allocator); + allocator.free(tests); + allocator.free(uses); } self.scopes.deinit(allocator); @@ -2402,8 +2401,8 @@ pub const Scope = struct { loc: offsets.Loc, decls: std.StringHashMapUnmanaged(Declaration) = .{}, - tests: std.ArrayListUnmanaged(Ast.Node.Index) = .{}, - uses: std.ArrayListUnmanaged(Ast.Node.Index) = .{}, + tests: []const Ast.Node.Index = &.{}, + uses: []const Ast.Node.Index = &.{}, data: Data, }; @@ -2451,9 +2450,15 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx: var buf: [2]Ast.Node.Index = undefined; const container_decl = tree.fullContainerDecl(&buf, node_idx).?; + + var tests = std.ArrayListUnmanaged(Ast.Node.Index){}; + errdefer tests.deinit(allocator); + var uses = std.ArrayListUnmanaged(Ast.Node.Index){}; + errdefer uses.deinit(allocator); + for (container_decl.ast.members) |decl| { if (tags[decl] == .@"usingnamespace") { - try scopes.items(.uses)[scope_index].append(allocator, decl); + try uses.append(allocator, decl); continue; } @@ -2461,7 +2466,7 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx: const name = getDeclName(tree, decl) orelse continue; if (tags[decl] == .test_decl) { - try scopes.items(.tests)[scope_index].append(allocator, decl); + try tests.append(allocator, decl); continue; } if (try scopes.items(.decls)[scope_index].fetchPut(allocator, name, .{ .ast_node = decl })) |existing| { @@ -2486,6 +2491,9 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx: } } } + + scopes.items(.tests)[scope_index] = try tests.toOwnedSlice(allocator); + scopes.items(.uses)[scope_index] = try uses.toOwnedSlice(allocator); } fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_idx: Ast.Node.Index) error{OutOfMemory}!void { From a5e6df25c7730fbacff40e696fff8a1a43b609f3 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 13 Mar 2023 01:08:22 +0100 Subject: [PATCH 4/6] do not store Handle pointer in Analyser --- src/analysis.zig | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 091740e74..5ee719b18 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -17,7 +17,7 @@ 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) = .{}, +resolved_nodes: std.HashMapUnmanaged(NodeWithUri, ?TypeWithHandle, NodeWithUri.Context, std.hash_map.default_max_load_percentage) = .{}, pub fn init(gpa: std.mem.Allocator, arena: std.mem.Allocator, store: *DocumentStore) Analyser { return .{ @@ -662,14 +662,18 @@ pub fn isTypeIdent(text: []const u8) bool { /// Resolves the type of a node fn resolveTypeOfNodeInternal(analyser: *Analyser, node_handle: NodeWithHandle) error{OutOfMemory}!?TypeWithHandle { - const gop = try analyser.resolved_nodes.getOrPut(analyser.gpa, node_handle); + const node_with_uri = NodeWithUri{ + .node = node_handle.node, + .uri = node_handle.handle.uri, + }; + const gop = try analyser.resolved_nodes.getOrPut(analyser.gpa, node_with_uri); 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; + analyser.resolved_nodes.getPtr(node_with_uri).?.* = type_handle; return type_handle; @@ -1336,27 +1340,32 @@ pub fn collectCImportNodes(allocator: std.mem.Allocator, tree: Ast) error{OutOfM return import_nodes.toOwnedSlice(allocator); } -pub const NodeWithHandle = struct { +pub const NodeWithUri = struct { node: Ast.Node.Index, - handle: *const DocumentStore.Handle, + uri: []const u8, const Context = struct { - pub fn hash(self: @This(), item: NodeWithHandle) u64 { + pub fn hash(self: @This(), item: NodeWithUri) u64 { _ = self; var hasher = std.hash.Wyhash.init(0); std.hash.autoHash(&hasher, item.node); - hasher.update(item.handle.uri); + hasher.update(item.uri); return hasher.final(); } - pub fn eql(self: @This(), a: NodeWithHandle, b: NodeWithHandle) bool { + pub fn eql(self: @This(), a: NodeWithUri, b: NodeWithUri) bool { _ = self; if (a.node != b.node) return false; - return std.mem.eql(u8, a.handle.uri, b.handle.uri); + return std.mem.eql(u8, a.uri, b.uri); } }; }; +pub const NodeWithHandle = struct { + node: Ast.Node.Index, + handle: *const DocumentStore.Handle, +}; + pub const FieldAccessReturn = struct { original: TypeWithHandle, unwrapped: ?TypeWithHandle = null, From fb772f9d3d664b318b04d9fa80f237943ec879e7 Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Mon, 13 Mar 2023 01:43:13 +0100 Subject: [PATCH 5/6] improve variable lookup performance --- src/analysis.zig | 390 +++++++++++++++++++++++++---------------------- 1 file changed, 209 insertions(+), 181 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 5ee719b18..5ad3e3f96 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2065,30 +2065,30 @@ fn iterateSymbolsContainerInternal( pub const EnclosingScopeIterator = struct { scope_locs: []offsets.Loc, - current_scope: usize, + scope_children: []const std.ArrayListUnmanaged(Scope.Index), + current_scope: Scope.Index, source_index: usize, - pub fn next(self: *EnclosingScopeIterator) ?usize { - while (self.current_scope < self.scope_locs.len) : (self.current_scope += 1) { - const scope_loc = self.scope_locs[self.current_scope]; + pub fn next(self: *EnclosingScopeIterator) ?Scope.Index { + if (self.current_scope == .none) return null; - if (self.source_index >= scope_loc.start and self.source_index <= scope_loc.end) { - defer self.current_scope += 1; - return self.current_scope; + const child_scopes = self.scope_children[@enumToInt(self.current_scope)]; + defer self.current_scope = for (child_scopes.items) |child_scope| { + const child_loc = self.scope_locs[@enumToInt(child_scope)]; + if (child_loc.start <= self.source_index and self.source_index <= child_loc.end) { + break child_scope; } - if (scope_loc.start >= self.source_index) { - self.current_scope = self.scope_locs.len; - return null; - } - } - return null; + } else .none; + + return self.current_scope; } }; fn iterateEnclosingScopes(document_scope: DocumentScope, source_index: usize) EnclosingScopeIterator { return .{ .scope_locs = document_scope.scopes.items(.loc), - .current_scope = 0, + .scope_children = document_scope.scopes.items(.child_scopes), + .current_scope = @intToEnum(Scope.Index, 0), .source_index = source_index, }; } @@ -2110,7 +2110,7 @@ pub fn iterateLabels(handle: *const DocumentStore.Handle, source_index: usize, c var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); while (scope_iterator.next()) |scope_index| { - var decl_it = scope_decls[scope_index].iterator(); + var decl_it = scope_decls[@enumToInt(scope_index)].iterator(); while (decl_it.next()) |entry| { switch (entry.value_ptr.*) { .label_decl => {}, @@ -2133,7 +2133,7 @@ fn iterateSymbolsGlobalInternal( var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); while (scope_iterator.next()) |scope_index| { - var decl_it = scope_decls[scope_index].iterator(); + var decl_it = scope_decls[@enumToInt(scope_index)].iterator(); while (decl_it.next()) |entry| { if (entry.value_ptr.* == .ast_node and handle.tree.nodes.items(.tag)[entry.value_ptr.*.ast_node].isContainerField()) continue; @@ -2141,7 +2141,7 @@ fn iterateSymbolsGlobalInternal( try callback(context, DeclWithHandle{ .decl = entry.value_ptr, .handle = handle }); } - for (scope_uses[scope_index]) |use| { + for (scope_uses[@enumToInt(scope_index)]) |use| { const gop = try analyser.using_trail.getOrPut(analyser.gpa, use); if (gop.found_existing) continue; @@ -2174,30 +2174,18 @@ pub fn iterateSymbolsGlobal( return try analyser.iterateSymbolsGlobalInternal(handle, source_index, callback, context); } -pub fn innermostBlockScopeIndex(handle: DocumentStore.Handle, source_index: usize) usize { - if (handle.document_scope.scopes.len == 1) return 0; - - const scope_locs = handle.document_scope.scopes.items(.loc); - const scope_datas = handle.document_scope.scopes.items(.data); - - var innermost: usize = 0; - var scope_index: usize = 1; - while (scope_index < handle.document_scope.scopes.len) : (scope_index += 1) { - const scope_loc = scope_locs[scope_index]; - if (source_index >= scope_loc.start and source_index <= scope_loc.end) { - switch (scope_datas[scope_index]) { - .container, .function, .block => innermost = scope_index, - else => {}, - } - } - if (scope_loc.start > source_index) break; +pub fn innermostBlockScopeIndex(handle: DocumentStore.Handle, source_index: usize) Scope.Index { + var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); + var current_scope: Scope.Index = .none; + while (scope_iterator.next()) |inner_scope| { + current_scope = inner_scope; } - return innermost; + return current_scope; } pub fn innermostBlockScope(handle: DocumentStore.Handle, source_index: usize) Ast.Node.Index { const scope_index = innermostBlockScopeIndex(handle, source_index); - return handle.document_scope.scopes.items(.data)[scope_index].toNodeIndex().?; + return handle.document_scope.scopes.items(.data)[@enumToInt(scope_index)].toNodeIndex().?; } pub fn innermostContainer(handle: *const DocumentStore.Handle, source_index: usize) TypeWithHandle { @@ -2208,7 +2196,7 @@ pub fn innermostContainer(handle: *const DocumentStore.Handle, source_index: usi var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); while (scope_iterator.next()) |scope_index| { - switch (scope_datas[scope_index]) { + switch (scope_datas[@enumToInt(scope_index)]) { .container => |node| current = node, else => {}, } @@ -2253,7 +2241,7 @@ pub fn lookupLabel( var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); while (scope_iterator.next()) |scope_index| { - const candidate = scope_decls[scope_index].getEntry(symbol) orelse continue; + const candidate = scope_decls[@enumToInt(scope_index)].getEntry(symbol) orelse continue; switch (candidate.value_ptr.*) { .label_decl => {}, @@ -2268,33 +2256,31 @@ pub fn lookupLabel( } 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); + const scope_parents = handle.document_scope.scopes.items(.parent); const scope_decls = handle.document_scope.scopes.items(.decls); const scope_uses = handle.document_scope.scopes.items(.uses); - var curr = innermost_scope_idx; - while (curr >= 0) : (curr -= 1) { - const scope_loc = scope_locs[curr]; - if (source_index >= scope_loc.start and source_index <= scope_loc.end) blk: { - if (scope_decls[curr].getEntry(symbol)) |candidate| { - switch (candidate.value_ptr.*) { - .ast_node => |node| { - if (handle.tree.nodes.items(.tag)[node].isContainerField()) break :blk; - }, - .label_decl => break :blk, - else => {}, - } - return DeclWithHandle{ - .decl = candidate.value_ptr, - .handle = handle, - }; + var current_scope = innermostBlockScopeIndex(handle.*, source_index); + + while (current_scope != .none) { + const scope_index = @enumToInt(current_scope); + defer current_scope = scope_parents[scope_index]; + if (scope_decls[scope_index].getEntry(symbol)) |candidate| { + switch (candidate.value_ptr.*) { + .ast_node => |node| { + if (handle.tree.nodes.items(.tag)[node].isContainerField()) continue; + }, + .label_decl => continue, + else => {}, } - if (try analyser.resolveUse(scope_uses[curr], symbol, handle)) |result| return result; + return DeclWithHandle{ + .decl = candidate.value_ptr, + .handle = handle, + }; } - if (curr == 0) break; + if (try analyser.resolveUse(scope_uses[scope_index], symbol, handle)) |result| return result; } + return null; } @@ -2367,8 +2353,14 @@ pub const DocumentScope = struct { enum_completions: CompletionSet, pub fn deinit(self: *DocumentScope, allocator: std.mem.Allocator) void { - for (self.scopes.items(.decls), self.scopes.items(.tests), self.scopes.items(.uses)) |*decls, tests, uses| { + for ( + self.scopes.items(.decls), + self.scopes.items(.child_scopes), + self.scopes.items(.tests), + self.scopes.items(.uses), + ) |*decls, *child_scopes, tests, uses| { decls.deinit(allocator); + child_scopes.deinit(allocator); allocator.free(tests); allocator.free(uses); } @@ -2408,11 +2400,18 @@ pub const Scope = struct { } }; + pub const Index = enum(u32) { + none = std.math.maxInt(u32), + _, + }; + loc: offsets.Loc, + parent: Index, + data: Data, decls: std.StringHashMapUnmanaged(Declaration) = .{}, + child_scopes: std.ArrayListUnmanaged(Scope.Index) = .{}, tests: []const Ast.Node.Index = &.{}, uses: []const Ast.Node.Index = &.{}, - data: Data, }; pub fn makeDocumentScope(allocator: std.mem.Allocator, tree: Ast) !DocumentScope { @@ -2426,36 +2425,62 @@ pub fn makeDocumentScope(allocator: std.mem.Allocator, tree: Ast) !DocumentScope }; errdefer document_scope.deinit(allocator); - try makeInnerScope(allocator, .{ + var current_scope: Scope.Index = .none; + + try makeInnerScope(.{ + .allocator = allocator, .scopes = &document_scope.scopes, + .current_scope = ¤t_scope, + .tree = tree, .errors = &document_scope.error_completions, .enums = &document_scope.enum_completions, - .tree = tree, }, 0); return document_scope; } const ScopeContext = struct { + allocator: std.mem.Allocator, scopes: *std.MultiArrayList(Scope), + current_scope: *Scope.Index, + tree: Ast, enums: *CompletionSet, errors: *CompletionSet, - tree: Ast, + + fn pushScope(context: ScopeContext, loc: offsets.Loc, data: Scope.Data) error{OutOfMemory}!usize { + try context.scopes.append(context.allocator, .{ + .parent = context.current_scope.*, + .loc = loc, + .data = data, + }); + const new_scope = @intToEnum(Scope.Index, context.scopes.len - 1); + if (context.current_scope.* != .none) { + try context.scopes.items(.child_scopes)[@enumToInt(context.current_scope.*)].append(context.allocator, new_scope); + } + context.current_scope.* = new_scope; + return @enumToInt(new_scope); + } + + fn popScope(context: ScopeContext) void { + const parent_scope = context.scopes.items(.parent)[@enumToInt(context.current_scope.*)]; + context.current_scope.* = parent_scope; + } }; -fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx: Ast.Node.Index) error{OutOfMemory}!void { +fn makeInnerScope(context: ScopeContext, node_idx: Ast.Node.Index) error{OutOfMemory}!void { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); + const allocator = context.allocator; const scopes = context.scopes; const tree = context.tree; const tags = tree.nodes.items(.tag); - try scopes.append(allocator, .{ - .loc = offsets.nodeToLoc(tree, node_idx), - .data = .{ .container = node_idx }, - }); - const scope_index = scopes.len - 1; + const scope_index = try context.pushScope( + offsets.nodeToLoc(tree, node_idx), + .{ .container = node_idx }, + ); + defer context.popScope(); var buf: [2]Ast.Node.Index = undefined; const container_decl = tree.fullContainerDecl(&buf, node_idx).?; @@ -2471,7 +2496,7 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx: continue; } - try makeScopeInternal(allocator, context, decl); + try makeScopeInternal(context, decl); const name = getDeclName(tree, decl) orelse continue; if (tags[decl] == .test_decl) { @@ -2505,9 +2530,10 @@ fn makeInnerScope(allocator: std.mem.Allocator, context: ScopeContext, node_idx: scopes.items(.uses)[scope_index] = try uses.toOwnedSlice(allocator); } -fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_idx: Ast.Node.Index) error{OutOfMemory}!void { +fn makeScopeInternal(context: ScopeContext, node_idx: Ast.Node.Index) error{OutOfMemory}!void { if (node_idx == 0) return; + const allocator = context.allocator; const scopes = context.scopes; const tree = context.tree; const tags = tree.nodes.items(.tag); @@ -2531,14 +2557,14 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i .tagged_union_enum_tag, .tagged_union_enum_tag_trailing, => { - try makeInnerScope(allocator, context, node_idx); + try makeInnerScope(context, node_idx); }, .error_set_decl => { - try scopes.append(allocator, .{ - .loc = offsets.nodeToLoc(tree, node_idx), - .data = .{ .container = node_idx }, - }); - const scope_index = scopes.len - 1; + const scope_index = try context.pushScope( + offsets.nodeToLoc(tree, node_idx), + .{ .container = node_idx }, + ); + defer context.popScope(); // All identifiers in main_token..data.rhs are error fields. var tok_i = main_tokens[node_idx] + 2; @@ -2578,11 +2604,11 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i var buf: [1]Ast.Node.Index = undefined; const func = tree.fullFnProto(&buf, node_idx).?; - try scopes.append(allocator, .{ - .loc = offsets.nodeToLoc(tree, node_idx), - .data = .{ .function = node_idx }, - }); - const scope_index = scopes.len - 1; + const scope_index = try context.pushScope( + offsets.nodeToLoc(tree, node_idx), + .{ .function = node_idx }, + ); + defer context.popScope(); var it = func.iterate(&tree); while (ast.nextFnParam(&it)) |param| { @@ -2599,7 +2625,7 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i } // Visit parameter types to pick up any error sets and enum // completions - try makeScopeInternal(allocator, context, param.type_expr); + try makeScopeInternal(context, param.type_expr); } if (fn_tag == .fn_decl) blk: { @@ -2607,14 +2633,14 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const return_type_node = data[data[node_idx].lhs].rhs; // Visit the return type - try makeScopeInternal(allocator, context, return_type_node); + try makeScopeInternal(context, return_type_node); } // Visit the function body - try makeScopeInternal(allocator, context, data[node_idx].rhs); + try makeScopeInternal(context, data[node_idx].rhs); }, .test_decl => { - return try makeScopeInternal(allocator, context, data[node_idx].rhs); + return try makeScopeInternal(context, data[node_idx].rhs); }, .block, .block_semicolon, @@ -2626,31 +2652,32 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i // if labeled block if (token_tags[first_token] == .identifier) { - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, main_tokens[node_idx]), .end = offsets.tokenToLoc(tree, last_token).start, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); + try scopes.items(.decls)[scope_index].putNoClobber(allocator, tree.tokenSlice(first_token), .{ .label_decl = .{ .label = first_token, .block = node_idx, } }); } - try scopes.append(allocator, .{ - .loc = offsets.nodeToLoc(tree, node_idx), - .data = .{ .block = node_idx }, - }); - const scope_index = scopes.len - 1; + const scope_index = try context.pushScope( + offsets.nodeToLoc(tree, node_idx), + .{ .block = node_idx }, + ); + defer context.popScope(); var buffer: [2]Ast.Node.Index = undefined; const statements = ast.blockStatements(tree, node_idx, &buffer).?; for (statements) |idx| { - try makeScopeInternal(allocator, context, idx); + try makeScopeInternal(context, idx); if (tree.fullVarDecl(idx)) |var_decl| { const name = tree.tokenSlice(var_decl.ast.mut_token + 1); if (try scopes.items(.decls)[scope_index].fetchPut(allocator, name, .{ .ast_node = idx })) |existing| { @@ -2668,14 +2695,14 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const if_node = ast.fullIf(tree, node_idx).?; if (if_node.payload_token) |payload| { - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, payload), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, if_node.ast.then_expr)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); const name_token = payload + @boolToInt(token_tags[payload] == .asterisk); std.debug.assert(token_tags[name_token] == .identifier); @@ -2689,40 +2716,40 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i }); } - try makeScopeInternal(allocator, context, if_node.ast.then_expr); + try makeScopeInternal(context, if_node.ast.then_expr); if (if_node.ast.else_expr != 0) { if (if_node.error_token) |err_token| { std.debug.assert(token_tags[err_token] == .identifier); - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, err_token), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, if_node.ast.else_expr)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); const name = tree.tokenSlice(err_token); try scopes.items(.decls)[scope_index].putNoClobber(allocator, name, .{ .ast_node = if_node.ast.else_expr }); } - try makeScopeInternal(allocator, context, if_node.ast.else_expr); + try makeScopeInternal(context, if_node.ast.else_expr); } }, .@"catch" => { - try makeScopeInternal(allocator, context, data[node_idx].lhs); + try makeScopeInternal(context, data[node_idx].lhs); const catch_token = main_tokens[node_idx]; const catch_expr = data[node_idx].rhs; - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, tree.firstToken(catch_expr)), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, catch_expr)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); if (token_tags.len > catch_token + 2 and token_tags[catch_token + 1] == .pipe and @@ -2731,7 +2758,7 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const name = tree.tokenSlice(catch_token + 2); try scopes.items(.decls)[scope_index].putNoClobber(allocator, name, .{ .ast_node = catch_expr }); } - try makeScopeInternal(allocator, context, catch_expr); + try makeScopeInternal(context, catch_expr); }, .@"while", .while_simple, @@ -2741,14 +2768,15 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i if (while_node.label_token) |label| { std.debug.assert(token_tags[label] == .identifier); - try scopes.append(allocator, .{ - .loc = .{ + + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, while_node.ast.while_token), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, node_idx)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); try scopes.items(.decls)[scope_index].putNoClobber(allocator, tree.tokenSlice(label), .{ .label_decl = .{ .label = label, @@ -2757,14 +2785,14 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i } if (while_node.payload_token) |payload| { - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, payload), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, while_node.ast.then_expr)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); const name_token = payload + @boolToInt(token_tags[payload] == .asterisk); std.debug.assert(token_tags[name_token] == .identifier); @@ -2791,24 +2819,24 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i } } } - try makeScopeInternal(allocator, context, while_node.ast.then_expr); + try makeScopeInternal(context, while_node.ast.then_expr); if (while_node.ast.else_expr != 0) { if (while_node.error_token) |err_token| { std.debug.assert(token_tags[err_token] == .identifier); - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, err_token), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, while_node.ast.else_expr)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); const name = tree.tokenSlice(err_token); try scopes.items(.decls)[scope_index].putNoClobber(allocator, name, .{ .ast_node = while_node.ast.else_expr }); } - try makeScopeInternal(allocator, context, while_node.ast.else_expr); + try makeScopeInternal(context, while_node.ast.else_expr); } }, .@"for", @@ -2818,14 +2846,14 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i if (for_node.label_token) |label| { std.debug.assert(token_tags[label] == .identifier); - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, for_node.ast.for_token), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, node_idx)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); try scopes.items(.decls)[scope_index].putNoClobber(allocator, tree.tokenSlice(label), .{ .label_decl = .{ .label = label, @@ -2833,14 +2861,14 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i } }); } - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, for_node.payload_token), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, for_node.ast.then_expr)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); var capture_token = for_node.payload_token; for (for_node.ast.inputs) |input| { @@ -2858,10 +2886,10 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i }); } - try makeScopeInternal(allocator, context, for_node.ast.then_expr); + try makeScopeInternal(context, for_node.ast.then_expr); if (for_node.ast.else_expr != 0) { - try makeScopeInternal(allocator, context, for_node.ast.else_expr); + try makeScopeInternal(context, for_node.ast.else_expr); } }, .@"switch", @@ -2875,14 +2903,14 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const switch_case: Ast.full.SwitchCase = tree.fullSwitchCase(case).?; if (switch_case.payload_token) |payload| { - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, payload), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, switch_case.ast.target_expr)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); // if payload is *name than get next token const name_token = payload + @boolToInt(token_tags[payload] == .asterisk); @@ -2897,7 +2925,7 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i }); } - try makeScopeInternal(allocator, context, switch_case.ast.target_expr); + try makeScopeInternal(context, switch_case.ast.target_expr); } }, .switch_case, @@ -2915,11 +2943,11 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i => { const var_decl = tree.fullVarDecl(node_idx).?; if (var_decl.ast.type_node != 0) { - try makeScopeInternal(allocator, context, var_decl.ast.type_node); + try makeScopeInternal(context, var_decl.ast.type_node); } if (var_decl.ast.init_node != 0) { - try makeScopeInternal(allocator, context, var_decl.ast.init_node); + try makeScopeInternal(context, var_decl.ast.init_node); } }, .call, @@ -2934,9 +2962,9 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i var buf: [1]Ast.Node.Index = undefined; const call = tree.fullCall(&buf, node_idx).?; - try makeScopeInternal(allocator, context, call.ast.fn_expr); + try makeScopeInternal(context, call.ast.fn_expr); for (call.ast.params) |param| - try makeScopeInternal(allocator, context, param); + try makeScopeInternal(context, param); }, .struct_init, .struct_init_comma, @@ -2951,10 +2979,10 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const struct_init: Ast.full.StructInit = tree.fullStructInit(&buf, node_idx).?; if (struct_init.ast.type_expr != 0) - try makeScopeInternal(allocator, context, struct_init.ast.type_expr); + try makeScopeInternal(context, struct_init.ast.type_expr); for (struct_init.ast.fields) |field| { - try makeScopeInternal(allocator, context, field); + try makeScopeInternal(context, field); } }, .array_init, @@ -2970,9 +2998,9 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const array_init: Ast.full.ArrayInit = tree.fullArrayInit(&buf, node_idx).?; if (array_init.ast.type_expr != 0) - try makeScopeInternal(allocator, context, array_init.ast.type_expr); + try makeScopeInternal(context, array_init.ast.type_expr); for (array_init.ast.elements) |elem| { - try makeScopeInternal(allocator, context, elem); + try makeScopeInternal(context, elem); } }, .container_field, @@ -2981,9 +3009,9 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i => { const field = tree.fullContainerField(node_idx).?; - try makeScopeInternal(allocator, context, field.ast.type_expr); - try makeScopeInternal(allocator, context, field.ast.align_expr); - try makeScopeInternal(allocator, context, field.ast.value_expr); + try makeScopeInternal(context, field.ast.type_expr); + try makeScopeInternal(context, field.ast.align_expr); + try makeScopeInternal(context, field.ast.value_expr); }, .builtin_call, .builtin_call_comma, @@ -2994,7 +3022,7 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const params = ast.builtinCallParams(tree, node_idx, &buffer).?; for (params) |param| { - try makeScopeInternal(allocator, context, param); + try makeScopeInternal(context, param); } }, .ptr_type, @@ -3004,9 +3032,9 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i => { const ptr_type: Ast.full.PtrType = ast.fullPtrType(tree, node_idx).?; - try makeScopeInternal(allocator, context, ptr_type.ast.sentinel); - try makeScopeInternal(allocator, context, ptr_type.ast.align_node); - try makeScopeInternal(allocator, context, ptr_type.ast.child_type); + try makeScopeInternal(context, ptr_type.ast.sentinel); + try makeScopeInternal(context, ptr_type.ast.align_node); + try makeScopeInternal(context, ptr_type.ast.child_type); }, .slice, .slice_open, @@ -3014,29 +3042,29 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i => { const slice: Ast.full.Slice = tree.fullSlice(node_idx).?; - try makeScopeInternal(allocator, context, slice.ast.sliced); - try makeScopeInternal(allocator, context, slice.ast.start); - try makeScopeInternal(allocator, context, slice.ast.end); - try makeScopeInternal(allocator, context, slice.ast.sentinel); + try makeScopeInternal(context, slice.ast.sliced); + try makeScopeInternal(context, slice.ast.start); + try makeScopeInternal(context, slice.ast.end); + try makeScopeInternal(context, slice.ast.sentinel); }, .@"errdefer" => { const expr = data[node_idx].rhs; if (data[node_idx].lhs != 0) { const payload_token = data[node_idx].lhs; - try scopes.append(allocator, .{ - .loc = .{ + const scope_index = try context.pushScope( + .{ .start = offsets.tokenToIndex(tree, payload_token), .end = offsets.tokenToLoc(tree, ast.lastToken(tree, expr)).end, }, - .data = .other, - }); - const scope_index = scopes.len - 1; + .other, + ); + defer context.popScope(); const name = tree.tokenSlice(payload_token); try scopes.items(.decls)[scope_index].putNoClobber(allocator, name, .{ .ast_node = expr }); } - try makeScopeInternal(allocator, context, expr); + try makeScopeInternal(context, expr); }, .@"asm", @@ -3056,7 +3084,7 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i .@"continue", => {}, .@"break", .@"defer" => { - try makeScopeInternal(allocator, context, data[node_idx].rhs); + try makeScopeInternal(context, data[node_idx].rhs); }, .@"return", @@ -3078,7 +3106,7 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i .unwrap_optional, .@"usingnamespace", => { - try makeScopeInternal(allocator, context, data[node_idx].lhs); + try makeScopeInternal(context, data[node_idx].lhs); }, .equal_equal, @@ -3133,8 +3161,8 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i .error_union, .for_range, => { - try makeScopeInternal(allocator, context, data[node_idx].lhs); - try makeScopeInternal(allocator, context, data[node_idx].rhs); + try makeScopeInternal(context, data[node_idx].lhs); + try makeScopeInternal(context, data[node_idx].rhs); }, } } From 0a32a44b8834cce2c5db9c1095ac07140c74fa9f Mon Sep 17 00:00:00 2001 From: Techatrix <19954306+Techatrix@users.noreply.github.com> Date: Tue, 14 Mar 2023 00:54:31 +0100 Subject: [PATCH 6/6] only return block scopes from `innermostBlockScope` --- src/analysis.zig | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 5ad3e3f96..ba31da389 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -2176,16 +2176,25 @@ pub fn iterateSymbolsGlobal( pub fn innermostBlockScopeIndex(handle: DocumentStore.Handle, source_index: usize) Scope.Index { var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); - var current_scope: Scope.Index = .none; + var scope_index: Scope.Index = .none; while (scope_iterator.next()) |inner_scope| { - current_scope = inner_scope; + scope_index = inner_scope; } - return current_scope; + return scope_index; } pub fn innermostBlockScope(handle: DocumentStore.Handle, source_index: usize) Ast.Node.Index { - const scope_index = innermostBlockScopeIndex(handle, source_index); - return handle.document_scope.scopes.items(.data)[@enumToInt(scope_index)].toNodeIndex().?; + const scope_datas = handle.document_scope.scopes.items(.data); + const scope_parents = handle.document_scope.scopes.items(.parent); + + var scope_index = innermostBlockScopeIndex(handle, source_index); + while (true) { + defer scope_index = scope_parents[@enumToInt(scope_index)]; + switch (scope_datas[@enumToInt(scope_index)]) { + .container, .function, .block => return scope_datas[@enumToInt(scope_index)].toNodeIndex().?, + else => {}, + } + } } pub fn innermostContainer(handle: *const DocumentStore.Handle, source_index: usize) TypeWithHandle {