diff --git a/src/analysis.zig b/src/analysis.zig index 2b414fd..8bd4e59 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -237,7 +237,12 @@ pub fn getChildOfSlice(tree: *ast.Tree, nodes: []*ast.Node, name: []const u8) ?* return null; } -fn findReturnStatementInternal(base_node: *ast.Node, already_found: *bool) ?*ast.Node.ControlFlowExpression { +fn findReturnStatementInternal( + tree: *ast.Tree, + fn_decl: *ast.Node.FnProto, + base_node: *ast.Node, + already_found: *bool, +) ?*ast.Node.ControlFlowExpression { var result: ?*ast.Node.ControlFlowExpression = null; var child_idx: usize = 0; while (base_node.iterate(child_idx)) |child_node| : (child_idx += 1) { @@ -245,6 +250,17 @@ fn findReturnStatementInternal(base_node: *ast.Node, already_found: *bool) ?*ast .ControlFlowExpression => { const cfe = child_node.cast(ast.Node.ControlFlowExpression).?; if (cfe.kind == .Return) { + // If we are calling ourselves recursively, ignore this return. + if (cfe.rhs) |rhs| { + if (rhs.cast(ast.Node.Call)) |call_node| { + if (call_node.lhs.id == .Identifier) { + if (std.mem.eql(u8, getDeclName(tree, call_node.lhs).?, getDeclName(tree, &fn_decl.base).?)) { + continue; + } + } + } + } + if (already_found.*) return null; already_found.* = true; result = cfe; @@ -254,25 +270,28 @@ fn findReturnStatementInternal(base_node: *ast.Node, already_found: *bool) ?*ast else => {}, } - result = findReturnStatementInternal(child_node, already_found); + result = findReturnStatementInternal(tree, fn_decl, child_node, already_found); } return result; } -fn findReturnStatement(base_node: *ast.Node) ?*ast.Node.ControlFlowExpression { +fn findReturnStatement(tree: *ast.Tree, fn_decl: *ast.Node.FnProto) ?*ast.Node.ControlFlowExpression { var already_found = false; - return findReturnStatementInternal(base_node, &already_found); + return findReturnStatementInternal(tree, fn_decl, fn_decl.body_node.?, &already_found); } /// Resolves the return type of a function -fn resolveReturnType(analysis_ctx: *AnalysisContext, fn_decl: *ast.Node.FnProto, current_container: *ast.Node) ?*ast.Node { +fn resolveReturnType(analysis_ctx: *AnalysisContext, fn_decl: *ast.Node.FnProto) ?*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; + const ret = findReturnStatement(analysis_ctx.tree, fn_decl) orelse return null; if (ret.rhs) |rhs| - if (resolveTypeOfNode(analysis_ctx, rhs, current_container)) |res_rhs| switch (res_rhs.id) { - .ContainerDecl => return res_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; + }, else => return null, }; @@ -280,18 +299,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, current_container), + .Explicit, .InferErrorSet => |return_type| resolveTypeOfNode(analysis_ctx, return_type), .Invalid => null, }; } /// Resolves the type of a node -pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node, current_container: *ast.Node) ?*ast.Node { +pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *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.?, current_container) orelse null; + return resolveTypeOfNode(analysis_ctx, vari.type_node orelse vari.init_node.?) orelse null; }, // @TODO Param decl is no longer a node type. // .ParamDecl => { @@ -305,26 +324,26 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node, curren // }, .Identifier => { if (getChildOfSlice(analysis_ctx.tree, analysis_ctx.scope_nodes, analysis_ctx.tree.getNodeSource(node))) |child| { - return resolveTypeOfNode(analysis_ctx, child, current_container); + return resolveTypeOfNode(analysis_ctx, child); } else return null; }, .ContainerField => { const field = node.cast(ast.Node.ContainerField).?; - return resolveTypeOfNode(analysis_ctx, field.type_expr orelse return null, current_container); + return resolveTypeOfNode(analysis_ctx, field.type_expr orelse return null); }, .Call => { const call = node.cast(ast.Node.Call).?; - const decl = resolveTypeOfNode(analysis_ctx, call.lhs, current_container) orelse return null; + const decl = resolveTypeOfNode(analysis_ctx, call.lhs) orelse return null; return switch (decl.id) { - .FnProto => resolveReturnType(analysis_ctx, decl.cast(ast.Node.FnProto).?, current_container), + .FnProto => resolveReturnType(analysis_ctx, decl.cast(ast.Node.FnProto).?), else => decl, }; }, .StructInitializer => { const struct_init = node.cast(ast.Node.StructInitializer).?; - const decl = resolveTypeOfNode(analysis_ctx, struct_init.lhs, current_container) orelse return null; + const decl = resolveTypeOfNode(analysis_ctx, struct_init.lhs) orelse return null; return switch (decl.id) { - .FnProto => resolveReturnType(analysis_ctx, decl.cast(ast.Node.FnProto).?, current_container), + .FnProto => resolveReturnType(analysis_ctx, decl.cast(ast.Node.FnProto).?), else => decl, }; }, @@ -337,9 +356,9 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node, curren 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, current_container) orelse return null; + const left = resolveTypeOfNode(analysis_ctx, infix_op.lhs) orelse return null; const child = getChild(analysis_ctx.tree, left, rhs_str) orelse return null; - return resolveTypeOfNode(analysis_ctx, child, current_container); + return resolveTypeOfNode(analysis_ctx, child); }, else => {}, } @@ -351,13 +370,13 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node, curren .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, current_container), + .Asterisk => return resolveTypeOfNode(analysis_ctx, prefix_op.rhs), .LBracket, .AsteriskAsterisk => return null, else => unreachable, } }, .Try => { - const rhs_type = resolveTypeOfNode(analysis_ctx, prefix_op.rhs, current_container) orelse return null; + const rhs_type = resolveTypeOfNode(analysis_ctx, prefix_op.rhs) orelse return null; switch (rhs_type.id) { .InfixOp => { const infix_op = rhs_type.cast(ast.Node.InfixOp).?; @@ -375,7 +394,7 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node, curren 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 current_container; + return analysis_ctx.in_container; } if (!std.mem.eql(u8, call_name, "@import")) return null; @@ -390,7 +409,10 @@ pub fn resolveTypeOfNode(analysis_ctx: *AnalysisContext, node: *ast.Node, curren break :block null; }; }, - .ContainerDecl => return node, + .ContainerDecl => { + analysis_ctx.onContainer(node.cast(ast.Node.ContainerDecl).?) catch return null; + return node; + }, .MultilineStringLiteral, .StringLiteral, .ErrorSetDecl, .FnProto => return node, else => std.debug.warn("Type resolution case not implemented; {}\n", .{node.id}), } @@ -443,6 +465,7 @@ pub fn getFieldAccessTypeNode( line_length: usize, ) ?*ast.Node { var current_node = analysis_ctx.in_container; + var current_container = analysis_ctx.in_container; while (true) { var next = tokenizer.next(); @@ -450,7 +473,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, current_node)) |node_type| { + if (resolveTypeOfNode(analysis_ctx, child)) |node_type| { current_node = node_type; } else return null; } else return null; @@ -464,14 +487,50 @@ 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, current_node)) |child_type| { + if (resolveTypeOfNode(analysis_ctx, child)) |child_type| { current_node = child_type; } else return null; } else return null; } }, + .LParen => { + switch (current_node.id) { + .FnProto => { + const func = current_node.cast(ast.Node.FnProto).?; + if (resolveReturnType(analysis_ctx, func)) |ret| { + current_node = ret; + // Skip to the right paren + var paren_count: usize = 1; + next = tokenizer.next(); + while (next.id != .Eof) : (next = tokenizer.next()) { + if (next.id == .RParen) { + paren_count -= 1; + if (paren_count == 0) break; + } else if (next.id == .LParen) { + paren_count += 1; + } + } else return null; + } else { + return null; + } + }, + else => {}, + } + }, + .Keyword_const, .Keyword_var => { + next = tokenizer.next(); + if (next.id == .Identifier) { + next = tokenizer.next(); + if (next.id != .Equal) return null; + continue; + } + }, else => std.debug.warn("Not implemented; {}\n", .{next.id}), } + + if (current_node.id == .ContainerDecl or current_node.id == .Root) { + current_container = current_node; + } } return current_node; diff --git a/src/document_store.zig b/src/document_store.zig index f2c5faf..23cd5d4 100644 --- a/src/document_store.zig +++ b/src/document_store.zig @@ -266,6 +266,16 @@ pub const AnalysisContext = struct { self.in_container = &self.tree.root_node.base; } + pub fn onContainer(self: *AnalysisContext, container: *std.zig.ast.Node.ContainerDecl) !void { + if (self.in_container != &container.base) { + self.in_container = &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 onImport(self: *AnalysisContext, import_str: []const u8) !?*std.zig.ast.Node { const allocator = self.store.allocator; const final_uri = (try uriFromImportStr( diff --git a/src/main.zig b/src/main.zig index fa82776..e123518 100644 --- a/src/main.zig +++ b/src/main.zig @@ -180,7 +180,7 @@ fn containerToCompletion( while (container.iterate(child_idx)) |child_node| : (child_idx += 1) { // 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, container, config); + try nodeToCompletion(list, analysis_ctx, orig_handle, child_node, config); } } } @@ -190,7 +190,6 @@ 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| @@ -242,11 +241,11 @@ fn nodeToCompletion( break :block var_decl.init_node.?; }; - if (analysis.resolveTypeOfNode(&child_analysis_context, child_node, current_container)) |resolved_node| { + if (analysis.resolveTypeOfNode(&child_analysis_context, child_node)) |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, current_container, config); + try nodeToCompletion(list, &child_analysis_context, orig_handle, resolved_node, config); return; } } @@ -421,7 +420,7 @@ fn completeGlobal(id: i64, pos_index: usize, handle: *DocumentStore.Handle, conf for (analysis_ctx.scope_nodes) |decl_ptr| { var decl = decl_ptr.*; - try nodeToCompletion(&completions, &analysis_ctx, handle, decl_ptr, &analysis_ctx.tree.root_node.base, config); + try nodeToCompletion(&completions, &analysis_ctx, handle, decl_ptr, config); } try send(types.Response{ @@ -449,7 +448,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, node, config); + try nodeToCompletion(&completions, &analysis_ctx, handle, node, config); } try send(types.Response{ .id = .{ .Integer = id }, @@ -541,6 +540,11 @@ fn documentPositionContext(doc: types.TextDocument, pos_index: usize) PositionCo curr_position = 0; var expr_start: usize = skipped_ws; + // std.debug.warn("{}", .{curr_position}); + + if (pos_index != 0 and doc.text[pos_index - 1] == ')') + return .{ .field_access = expr_start }; + var new_token = true; var context: PositionContext = .other; var string_pop_ctx: PositionContext = .other; @@ -700,6 +704,12 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v const method = root.Object.getValue("method").?.String; const params = root.Object.getValue("params").?.Object; + const start_time = std.time.milliTimestamp(); + defer { + const end_time = std.time.milliTimestamp(); + std.debug.warn("Took {}ms to process method {}\n", .{end_time - start_time, method}); + } + // Core if (std.mem.eql(u8, method, "initialize")) { try respondGeneric(id, initialize_response);