From 05d75781dee4acf0678b767d6bfc1c4d53849f4d Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Wed, 10 Jun 2020 21:24:17 +0300 Subject: [PATCH] Now builds! --- src/analysis.zig | 184 ++++++++++++++++++++++------------------------- src/main.zig | 59 +++++++-------- 2 files changed, 118 insertions(+), 125 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 339536d..e5264e6 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -376,110 +376,81 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, .Call => { const call = node.cast(ast.Node.Call).?; - // @TODO use BoundTypeParams: ParamDecl -> NodeWithHandle or something const decl = (try resolveTypeOfNode(store, arena, .{ .node = call.lhs, .handle = handle })) orelse return null; if (decl.node.cast(ast.Node.FnProto)) |fn_decl| { + // @TODO use BoundTypeParams: ParamDecl -> NodeWithHandle or something // Add type param values to the scope nodes - const param_len = std.math.min(call.params_len, fn_decl.params_len); - var scope_nodes = std.ArrayList(*ast.Node).fromOwnedSlice(&analysis_ctx.arena.allocator, analysis_ctx.scope_nodes); - var analysis_ctx_clone = analysis_ctx.clone() catch return null; - for (fn_decl.paramsConst()) |decl_param, param_idx| { - if (param_idx >= param_len) break; - if (decl_param.name_token == null) continue; - - const type_param = switch (decl_param.param_type) { - .type_expr => |type_node| if (type_node.cast(ast.Node.Identifier)) |ident| - std.mem.eql(u8, analysis_ctx.tree().tokenSlice(ident.token), "type") - else - false, - else => false, - }; - if (!type_param) continue; - - // TODO Handle errors better - // TODO This may invalidate the analysis context so we copy it. - // However, if the argument hits an import we just ignore it for now. - // Once we return our own types instead of directly using nodes we can fix this. - const call_param_type = resolveTypeOfNode(&analysis_ctx_clone, call.paramsConst()[param_idx]) orelse continue; - if (analysis_ctx_clone.handle != analysis_ctx.handle) { - analysis_ctx_clone = analysis_ctx.clone() catch return null; - continue; - } - - scope_nodes.append(makeVarDeclNode( - &analysis_ctx.arena.allocator, - decl_param.doc_comments, - decl_param.comptime_token, - decl_param.name_token.?, - null, - call_param_type, - ) catch return null) catch return null; - analysis_ctx.scope_nodes = scope_nodes.items; - } - - return resolveReturnType(analysis_ctx, fn_decl); + // const param_len = std.math.min(call.params_len, fn_decl.params_len); + // for (fn_decl.paramsConst()) |decl_param, param_idx| { + // if (param_idx >= param_len) break; + // if (decl_param.name_token == null) continue; + // const type_param = switch (decl_param.param_type) { + // .type_expr => |type_node| if (type_node.cast(ast.Node.Identifier)) |ident| + // std.mem.eql(u8, analysis_ctx.tree().tokenSlice(ident.token), "type") + // else + // false, + // else => false, + // }; + // if (!type_param) continue; + // const call_param_type = (try resolveTypeOfNode(store, arena, .{ .node = call.paramsConst()[param_idx], .handle = handle })) orelse continue; + // } + return try resolveReturnType(store, arena, fn_decl, decl.handle); } return decl; }, .StructInitializer => { const struct_init = node.cast(ast.Node.StructInitializer).?; - return resolveTypeOfNode(analysis_ctx, struct_init.lhs); + return try resolveTypeOfNode(store, arena, .{ .node = struct_init.lhs, .handle = handle }); }, .ErrorSetDecl => { const set = node.cast(ast.Node.ErrorSetDecl).?; var i: usize = 0; while (set.iterate(i)) |decl| : (i += 1) { - // TODO handle errors better? - analysis_ctx.error_completions.add(analysis_ctx.tree(), decl) catch {}; + try store.error_completions.add(handle.tree, decl); } - return node; + return node_handle; }, .SuffixOp => { const suffix_op = node.cast(ast.Node.SuffixOp).?; - switch (suffix_op.op) { - .UnwrapOptional => { - const left_type = resolveTypeOfNode(analysis_ctx, suffix_op.lhs) orelse return null; - return resolveUnwrapOptionalType(analysis_ctx, left_type); - }, - .Deref => { - const left_type = resolveTypeOfNode(analysis_ctx, suffix_op.lhs) orelse return null; - return resolveDerefType(analysis_ctx, left_type); - }, - .ArrayAccess => { - const left_type = resolveTypeOfNode(analysis_ctx, suffix_op.lhs) orelse return null; - return resolveBracketAccessType(analysis_ctx, left_type, .Single); - }, - .Slice => { - const left_type = resolveTypeOfNode(analysis_ctx, suffix_op.lhs) orelse return null; - return resolveBracketAccessType(analysis_ctx, left_type, .Range); - }, - else => {}, - } + const left_type = (try resolveTypeOfNode(store, arena, .{ .node = suffix_op.lhs, .handle = handle })) orelse return null; + return switch (suffix_op.op) { + .UnwrapOptional => try resolveUnwrapOptionalType(store, arena, left_type), + .Deref => try resolveDerefType(store, arena, left_type), + .ArrayAccess => try resolveBracketAccessType(store, arena, left_type, .Single), + .Slice => try resolveBracketAccessType(store, arena, left_type, .Range), + else => null, + }; }, .InfixOp => { const infix_op = node.cast(ast.Node.InfixOp).?; switch (infix_op.op) { .Period => { - // Save the child string from this tree since the tree may switch when processing - // an import lhs. - 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 rhs_str = nodeToString(handle.tree, infix_op.rhs) orelse return null; // If we are accessing a pointer type, remove one pointerness level :) - const left_type = resolveFieldAccessLhsType( - analysis_ctx, - resolveTypeOfNode(analysis_ctx, infix_op.lhs) orelse return null, + const left_type = try resolveFieldAccessLhsType( + store, + arena, + (try resolveTypeOfNode(store, arena, .{ + .node = infix_op.lhs, + .handle = handle, + })) orelse return null, ); - const child = getChild(analysis_ctx.tree(), left_type, rhs_str) orelse return null; - return resolveTypeOfNode(analysis_ctx, child); + // @TODO Error sets + if (left_type.node.id != .ContainerDecl and left_type.node.id != .Root) return null; + + if (try lookupSymbolContainer(store, left_type, rhs_str, true)) |child| { + return try child.resolveType(store, arena); + } else return null; }, .UnwrapOptional => { - const left_type = resolveTypeOfNode(analysis_ctx, infix_op.lhs) orelse return null; - return resolveUnwrapOptionalType(analysis_ctx, left_type); + const left_type = (try resolveTypeOfNode(store, arena, .{ + .node = infix_op.lhs, + .handle = handle, + })) orelse return null; + return try resolveUnwrapOptionalType(store, arena, left_type); }, - else => {}, + else => return null, } }, .PrefixOp => { @@ -489,13 +460,16 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, .ArrayType, .OptionalType, .PtrType, - => return node, + => return node_handle, .Try => { - const rhs_type = resolveTypeOfNode(analysis_ctx, prefix_op.rhs) orelse return null; - switch (rhs_type.id) { + const rhs_type = (try resolveTypeOfNode(store, arena, .{ .node = prefix_op.rhs, .handle = handle })) orelse return null; + switch (rhs_type.node.id) { .InfixOp => { - const infix_op = rhs_type.cast(ast.Node.InfixOp).?; - if (infix_op.op == .ErrorUnion) return infix_op.rhs; + const infix_op = rhs_type.node.cast(ast.Node.InfixOp).?; + if (infix_op.op == .ErrorUnion) return NodeWithHandle{ + .node = infix_op.rhs, + .handle = rhs_type.handle, + }; }, else => {}, } @@ -506,10 +480,10 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, }, .BuiltinCall => { const builtin_call = node.cast(ast.Node.BuiltinCall).?; - const call_name = analysis_ctx.tree().tokenSlice(builtin_call.builtin_token); + const call_name = handle.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.in_container; + return innermostContainer(handle, handle.tree.token_locs[builtin_call.firstToken()].start); } // TODO: https://github.com/ziglang/zig/issues/4335 @@ -528,7 +502,7 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, }); if (cast_map.has(call_name)) { if (builtin_call.params_len < 1) return null; - return resolveTypeOfNode(analysis_ctx, builtin_call.paramsConst()[0]); + return try resolveTypeOfNode(store, arena, .{ .node = builtin_call.paramsConst()[0], .handle = handle }); } if (!std.mem.eql(u8, call_name, "@import")) return null; @@ -537,31 +511,30 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, const import_param = builtin_call.paramsConst()[0]; if (import_param.id != .StringLiteral) return null; - const import_str = analysis_ctx.tree().tokenSlice(import_param.cast(ast.Node.StringLiteral).?.token); - return analysis_ctx.onImport(import_str[1 .. import_str.len - 1]) catch |err| block: { + const import_str = handle.tree.tokenSlice(import_param.cast(ast.Node.StringLiteral).?.token); + const new_handle = (store.resolveImport(handle, import_str[1 .. import_str.len - 1]) catch |err| block: { std.debug.warn("Error {} while processing import {}\n", .{ err, import_str }); - break :block null; - }; + return null; + }) orelse return null; + + return NodeWithHandle{ .node = &new_handle.tree.root_node.base, .handle = new_handle }; }, .ContainerDecl => { - analysis_ctx.onContainer(node) catch return null; - const container = node.cast(ast.Node.ContainerDecl).?; - const kind = analysis_ctx.tree().token_ids[container.kind_token]; + const kind = handle.tree.token_ids[container.kind_token]; if (kind == .Keyword_struct or (kind == .Keyword_union and container.init_arg_expr == .None)) { - return node; + return node_handle; } var i: usize = 0; while (container.iterate(i)) |decl| : (i += 1) { if (decl.id != .ContainerField) continue; - // TODO handle errors better? - analysis_ctx.enum_completions.add(analysis_ctx.tree(), decl) catch {}; + try store.enum_completions.add(handle.tree, decl); } - return node; + return node_handle; }, - .MultilineStringLiteral, .StringLiteral, .FnProto => return node, + .MultilineStringLiteral, .StringLiteral, .FnProto => return node_handle, else => std.debug.warn("Type resolution case not implemented; {}\n", .{node.id}), } return null; @@ -640,6 +613,7 @@ pub fn getFieldAccessTypeNode( if (after_period.loc.end == tokenizer.buffer.len) return try resolveFieldAccessLhsType(store, arena, current_node); current_node = try resolveFieldAccessLhsType(store, arena, current_node); + // @TODO Error sets if (current_node.node.id != .ContainerDecl and current_node.node.id != .Root) { // @TODO Is this ok? return null; @@ -1141,6 +1115,22 @@ pub fn iterateSymbolsGlobal( } } +pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) NodeWithHandle { + var current = handle.document_scope.scopes[0].data.container; + if (handle.document_scope.scopes.len == 1) return .{ .node = current, .handle = handle }; + + for (handle.document_scope.scopes[1..]) |scope| { + if (source_index >= scope.range.start and source_index < scope.range.end) { + switch (scope.data) { + .container => |node| current = node, + else => {}, + } + } + if (scope.range.start > source_index) break; + } + return .{ .node = current, .handle = handle }; +} + pub fn lookupSymbolGlobal(store: *DocumentStore, handle: *DocumentStore.Handle, symbol: []const u8, source_index: usize) !?DeclWithHandle { for (handle.document_scope.scopes) |scope| { if (source_index >= scope.range.start and source_index < scope.range.end) { @@ -1162,7 +1152,7 @@ pub fn lookupSymbolGlobal(store: *DocumentStore, handle: *DocumentStore.Handle, } } - if (scope.range.start >= source_index) return null; + if (scope.range.start > source_index) return null; } return null; diff --git a/src/main.zig b/src/main.zig index 30afcfd..8a84917 100644 --- a/src/main.zig +++ b/src/main.zig @@ -197,6 +197,7 @@ fn publishDiagnostics(handle: DocumentStore.Handle, config: Config) !void { } fn containerToCompletion( + arena: *std.heap.ArenaAllocator, list: *std.ArrayList(types.CompletionItem), container_handle: analysis.NodeWithHandle, orig_handle: *DocumentStore.Handle, @@ -211,7 +212,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 == handle or analysis.isNodePublic(handle.tree, child_node)) { - try nodeToCompletion(list, .{ .node = child_node, .handle = handle }, orig_handle, config); + try nodeToCompletion(arena, list, .{ .node = child_node, .handle = handle }, orig_handle, config); } } } @@ -244,6 +245,7 @@ fn resolveVarDeclFnAlias(arena: *std.heap.ArenaAllocator, decl_handle: analysis. } fn nodeToCompletion( + arena: *std.heap.ArenaAllocator, list: *std.ArrayList(types.CompletionItem), node_handle: analysis.NodeWithHandle, orig_handle: *DocumentStore.Handle, @@ -262,7 +264,7 @@ fn nodeToCompletion( switch (node.id) { .ErrorSetDecl, .Root, .ContainerDecl => { - try containerToCompletion(list, node_handle, orig_handle, config); + try containerToCompletion(arena, list, node_handle, orig_handle, config); }, .FnProto => { const func = node.cast(std.zig.ast.Node.FnProto).?; @@ -270,31 +272,32 @@ fn nodeToCompletion( const use_snippets = config.enable_snippets and client_capabilities.supports_snippets; const insert_text = if (use_snippets) blk: { - const skip_self_param = if (func.params_len > 0) param_check: { - var child_analysis_ctx = try analysis_ctx.clone(); - break :param_check switch (func.paramsConst()[0].param_type) { - .type_expr => |type_node| if (analysis_ctx.in_container == analysis.resolveTypeOfNode(&child_analysis_ctx, type_node)) - true - else if (type_node.cast(std.zig.ast.Node.PrefixOp)) |prefix_op| - prefix_op.op == .PtrType and analysis_ctx.in_container == analysis.resolveTypeOfNode(&child_analysis_ctx, prefix_op.rhs) - else - false, - else => false, - }; - } else - false; + // @TODO Rebuild this. + const skip_self_param = false; + // const skip_self_param = if (func.params_len > 0) param_check: { + // break :param_check switch (func.paramsConst()[0].param_type) { + // .type_expr => |type_node| if (analysis_ctx.in_container == analysis.resolveTypeOfNode(&child_analysis_ctx, type_node)) + // true + // else if (type_node.cast(std.zig.ast.Node.PrefixOp)) |prefix_op| + // prefix_op.op == .PtrType and analysis_ctx.in_container == analysis.resolveTypeOfNode(&child_analysis_ctx, prefix_op.rhs) + // else + // false, + // else => false, + // }; + // } else + // false; - break :blk try analysis.getFunctionSnippet(list.allocator, analysis_ctx.tree(), func, skip_self_param); + break :blk try analysis.getFunctionSnippet(&arena.allocator, handle.tree, func, skip_self_param); } else null; - const is_type_function = analysis.isTypeFunction(analysis_ctx.tree(), func); + const is_type_function = analysis.isTypeFunction(handle.tree, func); try list.append(.{ - .label = analysis_ctx.tree().tokenSlice(name_token), + .label = handle.tree.tokenSlice(name_token), .kind = if (is_type_function) .Struct else .Function, .documentation = doc, - .detail = analysis.getFunctionSignature(analysis_ctx.tree(), func), + .detail = analysis.getFunctionSignature(handle.tree, func), .insertText = insert_text, .insertTextFormat = if (use_snippets) .Snippet else .PlainText, }); @@ -302,18 +305,18 @@ fn nodeToCompletion( }, .VarDecl => { const var_decl = node.cast(std.zig.ast.Node.VarDecl).?; - const is_const = analysis_ctx.tree().token_ids[var_decl.mut_token] == .Keyword_const; + const is_const = handle.tree.token_ids[var_decl.mut_token] == .Keyword_const; - var result = try resolveVarDeclFnAlias(analysis_ctx, node); - if (result.decl != node) { - return try nodeToCompletion(list, &result.analysis_ctx, orig_handle, result.decl, config); + const result = try resolveVarDeclFnAlias(arena, node_handle); + if (result.node != node) { + return try nodeToCompletion(arena, list, result, orig_handle, config); } try list.append(.{ - .label = analysis_ctx.tree().tokenSlice(var_decl.name_token), + .label = handle.tree.tokenSlice(var_decl.name_token), .kind = if (is_const) .Constant else .Variable, .documentation = doc, - .detail = analysis.getVariableSignature(analysis_ctx.tree(), var_decl), + .detail = analysis.getVariableSignature(handle.tree, var_decl), }); }, .PrefixOp => { @@ -547,7 +550,7 @@ const DeclToCompletionContext = struct { fn decltoCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void { switch (decl_handle.decl.*) { .ast_node => |node| { - try nodeToCompletion(context.completions, .{ .node = node, .handle = decl_handle.handle }, context.orig_handle, context.config.*); + try nodeToCompletion(context.arena, context.completions, .{ .node = node, .handle = decl_handle.handle }, context.orig_handle, context.config.*); }, else => {}, // @TODO The rest @@ -590,7 +593,7 @@ fn completeFieldAccess(id: types.RequestId, handle: *DocumentStore.Handle, posit var tokenizer = std.zig.Tokenizer.init(line[range.start..range.end]); if (try analysis.getFieldAccessTypeNode(&document_store, &arena, handle, &tokenizer)) |node| { - try nodeToCompletion(&completions, node, handle, config); + try nodeToCompletion(&arena, &completions, node, handle, config); } try send(types.Response{ @@ -829,7 +832,7 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v } // Semantic highlighting else if (std.mem.eql(u8, method, "textDocument/semanticTokens")) { - // @TODO Implement this (we dont get here from vscode atm even when we get the client capab.) + // TODO Implement this (we dont get here from vscode atm even when we get the client capab.) return try respondGeneric(id, empty_array_response); } // Autocomplete / Signatures