diff --git a/src/analysis.zig b/src/analysis.zig index a137142..95e31e7 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -265,17 +265,14 @@ fn findReturnStatement(base_node: *ast.Node) ?*ast.Node.ControlFlowExpression { } /// Resolves the return type of a function -fn resolveReturnType(analysis_ctx: *AnalysisContext, fn_decl: *ast.Node.FnProto) ?*ast.Node { +fn resolveReturnType(analysis_ctx: *AnalysisContext, fn_decl: *ast.Node.FnProto, current_container: *ast.Node) ?*ast.Node { if (isTypeFunction(analysis_ctx.tree, fn_decl) and fn_decl.body_node != null) { // If this is a type function and it only contains a single return statement that returns // a container declaration, we will return that declaration. const ret = findReturnStatement(fn_decl.body_node.?) orelse return null; if (ret.rhs) |rhs| - if (resolveTypeOfNode(analysis_ctx, rhs)) |res_rhs| switch (res_rhs.id) { - .ContainerDecl => { - analysis_ctx.onContainer(res_rhs.cast(ast.Node.ContainerDecl).?) catch return null; - return res_rhs; - }, + if (resolveTypeOfNode(analysis_ctx, rhs, current_container)) |res_rhs| switch (res_rhs.id) { + .ContainerDecl => return res_rhs, else => return null, }; @@ -283,18 +280,18 @@ fn resolveReturnType(analysis_ctx: *AnalysisContext, fn_decl: *ast.Node.FnProto) } return switch (fn_decl.return_type) { - .Explicit, .InferErrorSet => |return_type| resolveTypeOfNode(analysis_ctx, return_type), + .Explicit, .InferErrorSet => |return_type| resolveTypeOfNode(analysis_ctx, return_type, current_container), .Invalid => null, }; } /// Resolves the type of a node -pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast.Node { +pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node, current_container: *ast.Node) ?*ast.Node { switch (node.id) { .VarDecl => { const vari = node.cast(ast.Node.VarDecl).?; - return resolveTypeOfNode(analysis_ctx, vari.type_node orelse vari.init_node.?) orelse null; + return resolveTypeOfNode(analysis_ctx, vari.type_node orelse vari.init_node.?, current_container) orelse null; }, // @TODO Param decl is no longer a node type. // .ParamDecl => { @@ -308,26 +305,26 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. // }, .Identifier => { if (getChildOfSlice(analysis_ctx.tree, analysis_ctx.scope_nodes, analysis_ctx.tree.getNodeSource(node))) |child| { - return resolveTypeOfNode(analysis_ctx, child); + return resolveTypeOfNode(analysis_ctx, child, current_container); } else return null; }, .ContainerField => { const field = node.cast(ast.Node.ContainerField).?; - return resolveTypeOfNode(analysis_ctx, field.type_expr orelse return null); + return resolveTypeOfNode(analysis_ctx, field.type_expr orelse return null, current_container); }, .Call => { const call = node.cast(ast.Node.Call).?; - const decl = resolveTypeOfNode(analysis_ctx, call.lhs) orelse return null; + const decl = resolveTypeOfNode(analysis_ctx, call.lhs, current_container) orelse return null; return switch (decl.id) { - .FnProto => resolveReturnType(analysis_ctx, decl.cast(ast.Node.FnProto).?), + .FnProto => resolveReturnType(analysis_ctx, decl.cast(ast.Node.FnProto).?, current_container), else => decl, }; }, .StructInitializer => { const struct_init = node.cast(ast.Node.StructInitializer).?; - const decl = resolveTypeOfNode(analysis_ctx, struct_init.lhs) orelse return null; + const decl = resolveTypeOfNode(analysis_ctx, struct_init.lhs, current_container) orelse return null; return switch (decl.id) { - .FnProto => resolveReturnType(analysis_ctx, decl.cast(ast.Node.FnProto).?), + .FnProto => resolveReturnType(analysis_ctx, decl.cast(ast.Node.FnProto).?, current_container), else => decl, }; }, @@ -340,8 +337,9 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. var rhs_str = nodeToString(analysis_ctx.tree, infix_op.rhs) orelse return null; // Use the analysis context temporary arena to store the rhs string. rhs_str = std.mem.dupe(&analysis_ctx.arena.allocator, u8, rhs_str) catch return null; - const left = resolveTypeOfNode(analysis_ctx, infix_op.lhs) orelse return null; - return resolveTypeOfNode(analysis_ctx, getChild(analysis_ctx.tree, left, rhs_str) orelse return null); + const left = resolveTypeOfNode(analysis_ctx, infix_op.lhs, current_container) orelse return null; + const child = getChild(analysis_ctx.tree, left, rhs_str) orelse return null; + return resolveTypeOfNode(analysis_ctx, child, current_container); }, else => {}, } @@ -353,13 +351,13 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. .PtrType => { const op_token_id = analysis_ctx.tree.token_ids[prefix_op.op_token]; switch (op_token_id) { - .Asterisk => return resolveTypeOfNode(analysis_ctx, prefix_op.rhs), + .Asterisk => return resolveTypeOfNode(analysis_ctx, prefix_op.rhs, current_container), .LBracket, .AsteriskAsterisk => return null, else => unreachable, } }, .Try => { - const rhs_type = resolveTypeOfNode(analysis_ctx, prefix_op.rhs) orelse return null; + const rhs_type = resolveTypeOfNode(analysis_ctx, prefix_op.rhs, current_container) orelse return null; switch (rhs_type.id) { .InfixOp => { const infix_op = rhs_type.cast(ast.Node.InfixOp).?; @@ -377,7 +375,7 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. const call_name = analysis_ctx.tree.tokenSlice(builtin_call.builtin_token); if (std.mem.eql(u8, call_name, "@This")) { if (builtin_call.params_len != 0) return null; - return analysis_ctx.last_this_node; + return current_container; } if (!std.mem.eql(u8, call_name, "@import")) return null; @@ -392,10 +390,7 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node) ?*ast. break :block null; }; }, - .ContainerDecl => { - analysis_ctx.onContainer(node.cast(ast.Node.ContainerDecl).?) catch return null; - return node; - }, + .ContainerDecl => return node, .MultilineStringLiteral, .StringLiteral, .ErrorSetDecl, .FnProto => return node, else => std.debug.warn("Type resolution case not implemented; {}\n", .{node.id}), } @@ -447,7 +442,7 @@ pub fn getFieldAccessTypeNode( tokenizer: *std.zig.Tokenizer, line_length: usize, ) ?*ast.Node { - var current_node = &analysis_ctx.tree.root_node.base; + var current_node = analysis_ctx.in_container; while (true) { var next = tokenizer.next(); @@ -455,7 +450,7 @@ pub fn getFieldAccessTypeNode( .Eof => return current_node, .Identifier => { if (getChildOfSlice(analysis_ctx.tree, analysis_ctx.scope_nodes, tokenizer.buffer[next.loc.start..next.loc.end])) |child| { - if (resolveTypeOfNode(analysis_ctx, child)) |node_type| { + if (resolveTypeOfNode(analysis_ctx, child, current_node)) |node_type| { current_node = node_type; } else return null; } else return null; @@ -469,7 +464,7 @@ pub fn getFieldAccessTypeNode( if (after_period.loc.end == line_length) return current_node; if (getChild(analysis_ctx.tree, current_node, tokenizer.buffer[after_period.loc.start..after_period.loc.end])) |child| { - if (resolveTypeOfNode(analysis_ctx, child)) |child_type| { + if (resolveTypeOfNode(analysis_ctx, child, current_node)) |child_type| { current_node = child_type; } else return null; } else return null; @@ -524,9 +519,16 @@ pub fn nodeToString(tree: *ast.Tree, node: *ast.Node) ?[]const u8 { return null; } -pub fn declsFromIndexInternal(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, node: *ast.Node, source_index: usize) error{OutOfMemory}!void { +pub fn declsFromIndexInternal( + decls: *std.ArrayList(*ast.Node), + tree: *ast.Tree, + node: *ast.Node, + container: **ast.Node, + source_index: usize, +) error{OutOfMemory}!void { switch (node.id) { .Root, .ContainerDecl => { + container.* = node; var node_it = node.iterate(); while (node_it.next()) |child_node| { // Skip over container fields, we can only dot access those. @@ -536,55 +538,54 @@ pub fn declsFromIndexInternal(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, // If the cursor is in a variable decls it will insert itself anyway, we don't need to take care of it. if ((is_contained and child_node.id != .VarDecl) or !is_contained) try decls.append(child_node); if (is_contained) { - try declsFromIndexInternal(decls, tree, child_node, source_index); + try declsFromIndexInternal(decls, tree, child_node, container, source_index); } } }, .FnProto => { const func = node.cast(ast.Node.FnProto).?; - for (func.paramsConst()) |param| { - // try decls.append(node) - // @TODO We need to store something else than nodes from now on. - } + // @TODO Param decls are no longer nodes. + // for (func.paramsConst()) |param| { + // } if (func.body_node) |body_node| { if (!nodeContainsSourceIndex(tree, body_node, source_index)) return; - try declsFromIndexInternal(decls, tree, body_node, source_index); + try declsFromIndexInternal(decls, tree, body_node, container, source_index); } }, .TestDecl => { const test_decl = node.cast(ast.Node.TestDecl).?; if (!nodeContainsSourceIndex(tree, test_decl.body_node, source_index)) return; - try declsFromIndexInternal(decls, tree, test_decl.body_node, source_index); + try declsFromIndexInternal(decls, tree, test_decl.body_node, container, source_index); }, .Block => { var node_it = node.iterate(); while (node_it.next()) |inode| { if (nodeComesAfterSourceIndex(tree, inode, source_index)) return; - try declsFromIndexInternal(decls, tree, inode, source_index); + try declsFromIndexInternal(decls, tree, inode, container, source_index); } }, .Comptime => { const comptime_stmt = node.cast(ast.Node.Comptime).?; if (nodeComesAfterSourceIndex(tree, comptime_stmt.expr, source_index)) return; - try declsFromIndexInternal(decls, tree, comptime_stmt.expr, source_index); + try declsFromIndexInternal(decls, tree, comptime_stmt.expr, container, source_index); }, .If => { const if_node = node.cast(ast.Node.If).?; if (nodeContainsSourceIndex(tree, if_node.body, source_index)) { if (if_node.payload) |payload| { - try declsFromIndexInternal(decls, tree, payload, source_index); + try declsFromIndexInternal(decls, tree, payload, container, source_index); } - return try declsFromIndexInternal(decls, tree, if_node.body, source_index); + return try declsFromIndexInternal(decls, tree, if_node.body, container, source_index); } if (if_node.@"else") |else_node| { if (nodeContainsSourceIndex(tree, else_node.body, source_index)) { if (else_node.payload) |payload| { - try declsFromIndexInternal(decls, tree, payload, source_index); + try declsFromIndexInternal(decls, tree, payload, container, source_index); } - return try declsFromIndexInternal(decls, tree, else_node.body, source_index); + return try declsFromIndexInternal(decls, tree, else_node.body, container, source_index); } } }, @@ -592,33 +593,33 @@ pub fn declsFromIndexInternal(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, const while_node = node.cast(ast.Node.While).?; if (nodeContainsSourceIndex(tree, while_node.body, source_index)) { if (while_node.payload) |payload| { - try declsFromIndexInternal(decls, tree, payload, source_index); + try declsFromIndexInternal(decls, tree, payload, container, source_index); } - return try declsFromIndexInternal(decls, tree, while_node.body, source_index); + return try declsFromIndexInternal(decls, tree, while_node.body, container, source_index); } if (while_node.@"else") |else_node| { if (nodeContainsSourceIndex(tree, else_node.body, source_index)) { if (else_node.payload) |payload| { - try declsFromIndexInternal(decls, tree, payload, source_index); + try declsFromIndexInternal(decls, tree, payload, container, source_index); } - return try declsFromIndexInternal(decls, tree, else_node.body, source_index); + return try declsFromIndexInternal(decls, tree, else_node.body, container, source_index); } } }, .For => { const for_node = node.cast(ast.Node.For).?; if (nodeContainsSourceIndex(tree, for_node.body, source_index)) { - try declsFromIndexInternal(decls, tree, for_node.payload, source_index); - return try declsFromIndexInternal(decls, tree, for_node.body, source_index); + try declsFromIndexInternal(decls, tree, for_node.payload, container, source_index); + return try declsFromIndexInternal(decls, tree, for_node.body, container, source_index); } if (for_node.@"else") |else_node| { if (nodeContainsSourceIndex(tree, else_node.body, source_index)) { if (else_node.payload) |payload| { - try declsFromIndexInternal(decls, tree, payload, source_index); + try declsFromIndexInternal(decls, tree, payload, container, source_index); } - return try declsFromIndexInternal(decls, tree, else_node.body, source_index); + return try declsFromIndexInternal(decls, tree, else_node.body, container, source_index); } } }, @@ -628,9 +629,9 @@ pub fn declsFromIndexInternal(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, const case_node = case.*.cast(ast.Node.SwitchCase).?; if (nodeContainsSourceIndex(tree, case_node.expr, source_index)) { if (case_node.payload) |payload| { - try declsFromIndexInternal(decls, tree, payload, source_index); + try declsFromIndexInternal(decls, tree, payload, container, source_index); } - return try declsFromIndexInternal(decls, tree, case_node.expr, source_index); + return try declsFromIndexInternal(decls, tree, case_node.expr, container, source_index); } } }, @@ -648,7 +649,7 @@ pub fn declsFromIndexInternal(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, try decls.append(node); if (node.cast(ast.Node.VarDecl).?.init_node) |child| { if (nodeContainsSourceIndex(tree, child, source_index)) { - try declsFromIndexInternal(decls, tree, child, source_index); + try declsFromIndexInternal(decls, tree, child, container, source_index); } } }, @@ -663,9 +664,10 @@ pub fn addChildrenNodes(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, node: } } -pub fn declsFromIndex(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, source_index: usize) !void { - var node = &tree.root_node.base; - try declsFromIndexInternal(decls, tree, &tree.root_node.base, source_index); +pub fn declsFromIndex(decls: *std.ArrayList(*ast.Node), tree: *ast.Tree, source_index: usize) !*ast.Node { + var result = &tree.root_node.base; + try declsFromIndexInternal(decls, tree, &tree.root_node.base, &result, source_index); + return result; } fn nodeContainsSourceIndex(tree: *ast.Tree, node: *ast.Node, source_index: usize) bool { diff --git a/src/document_store.zig b/src/document_store.zig index 90a7c6d..f2c5faf 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -256,14 +256,14 @@ pub const AnalysisContext = struct { arena: *std.heap.ArenaAllocator, tree: *std.zig.ast.Tree, scope_nodes: []*std.zig.ast.Node, - last_this_node: *std.zig.ast.Node, + in_container: *std.zig.ast.Node, std_uri: ?[]const u8, fn refreshScopeNodes(self: *AnalysisContext) !void { var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&self.arena.allocator); try analysis.addChildrenNodes(&scope_nodes, self.tree, &self.tree.root_node.base); self.scope_nodes = scope_nodes.items; - self.last_this_node = &self.tree.root_node.base; + self.in_container = &self.tree.root_node.base; } pub fn onImport(self: *AnalysisContext, import_str: []const u8) !?*std.zig.ast.Node { @@ -355,21 +355,11 @@ pub const AnalysisContext = struct { .arena = self.arena, .tree = tree, .scope_nodes = self.scope_nodes, - .last_this_node = &tree.root_node.base, + .in_container = self.in_container, .std_uri = self.std_uri, }; } - pub fn onContainer(self: *AnalysisContext, container: *std.zig.ast.Node.ContainerDecl) !void { - if (self.last_this_node != &container.base) { - self.last_this_node = &container.base; - - var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&self.arena.allocator); - try analysis.addChildrenNodes(&scope_nodes, self.tree, &container.base); - self.scope_nodes = scope_nodes.items; - } - } - pub fn deinit(self: *AnalysisContext) void { self.tree.deinit(); } @@ -396,13 +386,13 @@ pub fn analysisContext( self: *DocumentStore, handle: *Handle, arena: *std.heap.ArenaAllocator, - position: types.Position, + position: usize, zig_lib_path: ?[]const u8, ) !AnalysisContext { const tree = try handle.tree(self.allocator); var scope_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator); - try analysis.declsFromIndex(&scope_nodes, tree, try handle.document.positionToIndex(position)); + const in_container = try analysis.declsFromIndex(&scope_nodes, tree, position); const std_uri = try stdUriFromLibPath(&arena.allocator, zig_lib_path); return AnalysisContext{ @@ -411,7 +401,7 @@ pub fn analysisContext( .arena = arena, .tree = tree, .scope_nodes = scope_nodes.items, - .last_this_node = &tree.root_node.base, + .in_container = in_container, .std_uri = std_uri, }; } diff --git a/src/main.zig b/src/main.zig index 9d71567..8cf1b69 100644 --- a/src/main.zig +++ b/src/main.zig @@ -180,7 +180,7 @@ fn containerToCompletion( while (child_it.next()) |child_node| { // Declarations in the same file do not need to be public. if (orig_handle == analysis_ctx.handle or analysis.isNodePublic(analysis_ctx.tree, child_node)) { - try nodeToCompletion(list, analysis_ctx, orig_handle, child_node, config); + try nodeToCompletion(list, analysis_ctx, orig_handle, child_node, container, config); } } } @@ -190,6 +190,7 @@ fn nodeToCompletion( analysis_ctx: *DocumentStore.AnalysisContext, orig_handle: *DocumentStore.Handle, node: *std.zig.ast.Node, + current_container: *std.zig.ast.Node, config: Config, ) error{OutOfMemory}!void { var doc = if (try analysis.getDocComments(list.allocator, analysis_ctx.tree, node)) |doc_comments| @@ -241,11 +242,11 @@ fn nodeToCompletion( break :block var_decl.init_node.?; }; - if (analysis.resolveTypeOfNode(&child_analysis_context, child_node)) |resolved_node| { + if (analysis.resolveTypeOfNode(&child_analysis_context, child_node, current_container)) |resolved_node| { // Special case for function aliases // In the future it might be used to print types of values instead of their declarations if (resolved_node.id == .FnProto) { - try nodeToCompletion(list, &child_analysis_context, orig_handle, resolved_node, config); + try nodeToCompletion(list, &child_analysis_context, orig_handle, resolved_node, current_container, config); return; } } @@ -323,7 +324,7 @@ fn gotoDefinitionGlobal(id: i64, pos_index: usize, handle: DocumentStore.Handle) defer arena.deinit(); var decl_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator); - try analysis.declsFromIndex(&decl_nodes, tree, pos_index); + _ = try analysis.declsFromIndex(&decl_nodes, tree, pos_index); const decl = analysis.getChildOfSlice(tree, decl_nodes.items, name) orelse return try respondGeneric(id, null_result_response); const name_token = analysis.getDeclNameToken(tree, decl) orelse unreachable; @@ -353,7 +354,7 @@ fn gotoDefinitionFieldAccess( var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); - var analysis_ctx = try document_store.analysisContext(handle, &arena, position, config.zig_lib_path); + var analysis_ctx = try document_store.analysisContext(handle, &arena, try handle.document.positionToIndex(position), config.zig_lib_path); defer analysis_ctx.deinit(); const line = try handle.document.getLine(@intCast(usize, position.line)); @@ -415,17 +416,12 @@ fn completeGlobal(id: i64, pos_index: usize, handle: *DocumentStore.Handle, conf // Deallocate all temporary data. defer arena.deinit(); - var analysis_ctx = try document_store.analysisContext(handle, &arena, types.Position{ - .line = 0, - .character = 0, - }, config.zig_lib_path); + var analysis_ctx = try document_store.analysisContext(handle, &arena, pos_index, config.zig_lib_path); defer analysis_ctx.deinit(); - var decl_nodes = std.ArrayList(*std.zig.ast.Node).init(&arena.allocator); - try analysis.declsFromIndex(&decl_nodes, analysis_ctx.tree, pos_index); - for (decl_nodes.items) |decl_ptr| { + for (analysis_ctx.scope_nodes) |decl_ptr| { var decl = decl_ptr.*; - try nodeToCompletion(&completions, &analysis_ctx, handle, decl_ptr, config); + try nodeToCompletion(&completions, &analysis_ctx, handle, decl_ptr, &analysis_ctx.tree.root_node.base, config); } try send(types.Response{ @@ -443,7 +439,7 @@ fn completeFieldAccess(id: i64, handle: *DocumentStore.Handle, position: types.P var arena = std.heap.ArenaAllocator.init(allocator); defer arena.deinit(); - var analysis_ctx = try document_store.analysisContext(handle, &arena, position, config.zig_lib_path); + var analysis_ctx = try document_store.analysisContext(handle, &arena, try handle.document.positionToIndex(position), config.zig_lib_path); defer analysis_ctx.deinit(); var completions = std.ArrayList(types.CompletionItem).init(&arena.allocator); @@ -453,7 +449,7 @@ fn completeFieldAccess(id: i64, handle: *DocumentStore.Handle, position: types.P const line_length = line.len - line_start_idx; if (analysis.getFieldAccessTypeNode(&analysis_ctx, &tokenizer, line_length)) |node| { - try nodeToCompletion(&completions, &analysis_ctx, handle, node, config); + try nodeToCompletion(&completions, &analysis_ctx, handle, node, node, config); } try send(types.Response{ .id = .{ .Integer = id },