From 80dd19dd8b5d25124acb1d73a3a502d259f27cb7 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Thu, 11 Jun 2020 02:40:11 +0300 Subject: [PATCH] Usingnamespace support --- src/analysis.zig | 157 ++++++++++++++++++++++++++++++++++------------- src/main.zig | 36 ++++------- 2 files changed, 126 insertions(+), 67 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index 8b2a209..b8ae5f9 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -265,6 +265,16 @@ fn resolveUnwrapOptionalType(store: *DocumentStore, arena: *std.heap.ArenaAlloca return null; } +fn resolveUnwrapErrorType(store: *DocumentStore, arena: *std.heap.ArenaAllocator, rhs: NodeWithHandle) !?NodeWithHandle { + if (rhs.node.cast(ast.Node.InfixOp)) |infix_op| { + if (infix_op.op == .ErrorUnion) { + return try resolveTypeOfNode(store, arena, .{ .node = infix_op.rhs, .handle = rhs.handle }); + } + } + + return null; +} + /// Resolves the child type of a defer type fn resolveDerefType(store: *DocumentStore, arena: *std.heap.ArenaAllocator, deref: NodeWithHandle) !?NodeWithHandle { if (deref.node.cast(ast.Node.PrefixOp)) |pop| { @@ -352,7 +362,7 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, return try resolveTypeOfNode(store, arena, .{ .node = vari.type_node orelse vari.init_node.?, .handle = handle }); }, .Identifier => { - if (try lookupSymbolGlobal(store, handle, handle.tree.getNodeSource(node), handle.tree.token_locs[node.firstToken()].start)) |child| { + if (try lookupSymbolGlobal(store, arena, handle, handle.tree.getNodeSource(node), handle.tree.token_locs[node.firstToken()].start)) |child| { return try child.resolveType(store, arena); } return null; @@ -409,7 +419,7 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, })) orelse return null, ); - if (try lookupSymbolContainer(store, left_type, rhs_str, true)) |child| { + if (try lookupSymbolContainer(store, arena, left_type, rhs_str, true)) |child| { return try child.resolveType(store, arena); } else return null; }, @@ -420,6 +430,7 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, })) orelse return null; return try resolveUnwrapOptionalType(store, arena, left_type); }, + .ErrorUnion => return node_handle, else => return null, } }, @@ -433,17 +444,7 @@ pub fn resolveTypeOfNode(store: *DocumentStore, arena: *std.heap.ArenaAllocator, => return node_handle, .Try => { 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.node.cast(ast.Node.InfixOp).?; - if (infix_op.op == .ErrorUnion) return NodeWithHandle{ - .node = infix_op.rhs, - .handle = rhs_type.handle, - }; - }, - else => {}, - } - return rhs_type; + return try resolveUnwrapErrorType(store, arena, rhs_type); }, else => {}, } @@ -572,7 +573,7 @@ pub fn getFieldAccessTypeNode( switch (tok.id) { .Eof => return try resolveFieldAccessLhsType(store, arena, current_node), .Identifier => { - if (try lookupSymbolGlobal(store, current_node.handle, tokenizer.buffer[tok.loc.start..tok.loc.end], source_index)) |child| { + if (try lookupSymbolGlobal(store, arena, current_node.handle, tokenizer.buffer[tok.loc.start..tok.loc.end], source_index)) |child| { current_node = (try child.resolveType(store, arena)) orelse return null; } else return null; }, @@ -584,7 +585,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); - if (try lookupSymbolContainer(store, current_node, tokenizer.buffer[after_period.loc.start..after_period.loc.end], true)) |child| { + if (try lookupSymbolContainer(store, arena, current_node, tokenizer.buffer[after_period.loc.start..after_period.loc.end], true)) |child| { current_node = (try child.resolveType(store, arena)) orelse return null; } else return null; }, @@ -976,6 +977,13 @@ pub const DeclWithHandle = struct { }; } + fn isPublic(self: DeclWithHandle) bool { + return switch (self.decl.*) { + .ast_node => |node| isNodePublic(self.handle.tree, node), + else => true, + }; + } + fn resolveType(self: DeclWithHandle, store: *DocumentStore, arena: *std.heap.ArenaAllocator) !?NodeWithHandle { return switch (self.decl.*) { .ast_node => |node| try resolveTypeOfNode(store, arena, .{ .node = node, .handle = self.handle }), @@ -1006,13 +1014,65 @@ pub const DeclWithHandle = struct { } }; +fn findContainerScope(container_handle: NodeWithHandle) ?*Scope { + const container = container_handle.node; + const handle = container_handle.handle; + + if (container.id != .ContainerDecl and container.id != .Root and container.id != .ErrorSetDecl) { + return null; + } + + // Find the container scope. + var container_scope: ?*Scope = null; + for (handle.document_scope.scopes) |*scope| { + switch (scope.*.data) { + .container => |node| if (node == container) { + container_scope = scope; + break; + }, + else => {}, + } + } + return container_scope; +} + +pub fn iterateSymbolsContainer( + store: *DocumentStore, + arena: *std.heap.ArenaAllocator, + container_handle: NodeWithHandle, + orig_handle: *DocumentStore.Handle, + comptime callback: var, + context: var, +) error{OutOfMemory}!void { + const container = container_handle.node; + const handle = container_handle.handle; + + if (findContainerScope(container_handle)) |container_scope| { + var decl_it = container_scope.decls.iterator(); + while (decl_it.next()) |entry| { + const decl = DeclWithHandle{ .decl = &entry.value, .handle = handle }; + if (handle != orig_handle and !decl.isPublic()) continue; + try callback(context, decl); + } + + for (container_scope.uses) |use| { + if (handle != orig_handle and use.visib_token == null) continue; + const use_expr = (try resolveTypeOfNode(store, arena, .{ .node = use.expr, .handle = handle })) orelse continue; + try iterateSymbolsContainer(store, arena, use_expr, orig_handle, callback, context); + } + } + + std.debug.warn("Did not find container scope when iterating container {} (name: {})\n", .{ container, getDeclName(handle.tree, container) }); +} + pub fn iterateSymbolsGlobal( store: *DocumentStore, + arena: *std.heap.ArenaAllocator, handle: *DocumentStore.Handle, source_index: usize, comptime callback: var, context: var, -) !void { +) error{OutOfMemory}!void { for (handle.document_scope.scopes) |scope| { if (source_index >= scope.range.start and source_index < scope.range.end) { var decl_it = scope.decls.iterator(); @@ -1022,6 +1082,8 @@ pub fn iterateSymbolsGlobal( for (scope.uses) |use| { // @TODO Resolve uses, iterate over their symbols. + const use_expr = (try resolveTypeOfNode(store, arena, .{ .node = use.expr, .handle = handle })) orelse continue; + try iterateSymbolsContainer(store, arena, use_expr, handle, callback, context); } } @@ -1045,7 +1107,32 @@ pub fn innermostContainer(handle: *DocumentStore.Handle, source_index: usize) No return .{ .node = current, .handle = handle }; } -pub fn lookupSymbolGlobal(store: *DocumentStore, handle: *DocumentStore.Handle, symbol: []const u8, source_index: usize) !?DeclWithHandle { +fn resolveUse( + store: *DocumentStore, + arena: *std.heap.ArenaAllocator, + uses: []const *ast.Node.Use, + symbol: []const u8, + handle: *DocumentStore.Handle, +) error{OutOfMemory}!?DeclWithHandle { + for (uses) |use| { + const use_expr = (try resolveTypeOfNode(store, arena, .{ .node = use.expr, .handle = handle })) orelse continue; + if (try lookupSymbolContainer(store, arena, use_expr, symbol, false)) |candidate| { + if (candidate.handle != handle and !candidate.isPublic()) { + continue; + } + return candidate; + } + } + return null; +} + +pub fn lookupSymbolGlobal( + store: *DocumentStore, + arena: *std.heap.ArenaAllocator, + handle: *DocumentStore.Handle, + symbol: []const u8, + source_index: usize, +) error{OutOfMemory}!?DeclWithHandle { for (handle.document_scope.scopes) |scope| { if (source_index >= scope.range.start and source_index < scope.range.end) { if (scope.decls.get(symbol)) |candidate| { @@ -1061,9 +1148,7 @@ pub fn lookupSymbolGlobal(store: *DocumentStore, handle: *DocumentStore.Handle, }; } - for (scope.uses) |use| { - // @TODO Resolve use, lookup symbol in resulting scope. - } + if (try resolveUse(store, arena, scope.uses, symbol, handle)) |result| return result; } if (scope.range.start > source_index) return null; @@ -1072,27 +1157,17 @@ pub fn lookupSymbolGlobal(store: *DocumentStore, handle: *DocumentStore.Handle, return null; } -pub fn lookupSymbolContainer(store: *DocumentStore, container_handle: NodeWithHandle, symbol: []const u8, accept_fields: bool) !?DeclWithHandle { +pub fn lookupSymbolContainer( + store: *DocumentStore, + arena: *std.heap.ArenaAllocator, + container_handle: NodeWithHandle, + symbol: []const u8, + accept_fields: bool, +) error{OutOfMemory}!?DeclWithHandle { const container = container_handle.node; const handle = container_handle.handle; - if (container.id != .ContainerDecl and container.id != .Root and container.id != .ErrorSetDecl) { - return null; - } - - // Find the container scope. - var maybe_container_scope: ?*Scope = null; - for (handle.document_scope.scopes) |*scope| { - switch (scope.*.data) { - .container => |node| if (node == container) { - maybe_container_scope = scope; - break; - }, - else => {}, - } - } - - if (maybe_container_scope) |container_scope| { + if (findContainerScope(container_handle)) |container_scope| { if (container_scope.decls.get(symbol)) |candidate| { switch (candidate.value) { .ast_node => |node| { @@ -1103,13 +1178,11 @@ pub fn lookupSymbolContainer(store: *DocumentStore, container_handle: NodeWithHa return DeclWithHandle{ .decl = &candidate.value, .handle = handle }; } - for (container_scope.uses) |use| { - // @TODO Resolve use, lookup symbol in resulting scope. - } + if (try resolveUse(store, arena, container_scope.uses, symbol, handle)) |result| return result; return null; } - std.debug.warn("Did not find container scope when looking up in container {} (name: {})\n", .{container, getDeclName(handle.tree, container)}); + std.debug.warn("Did not find container scope when looking up in container {} (name: {})\n", .{ container, getDeclName(handle.tree, container) }); return null; } diff --git a/src/main.zig b/src/main.zig index f56e48f..5678a2c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -196,26 +196,6 @@ 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, - config: Config, -) !void { - // @TODO Something like iterateSymbolsGlobal but for container to support uses. - const container = container_handle.node; - const handle = container_handle.handle; - - var child_idx: usize = 0; - 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(arena, list, .{ .node = child_node, .handle = handle }, orig_handle, config); - } - } -} - fn resolveVarDeclFnAlias(arena: *std.heap.ArenaAllocator, decl_handle: analysis.NodeWithHandle) !analysis.NodeWithHandle { const decl = decl_handle.node; const handle = decl_handle.handle; @@ -263,7 +243,13 @@ fn nodeToCompletion( switch (node.id) { .ErrorSetDecl, .Root, .ContainerDecl => { - try containerToCompletion(arena, list, node_handle, orig_handle, config); + const context = DeclToCompletionContext{ + .completions = list, + .config = &config, + .arena = arena, + .orig_handle = orig_handle, + }; + try analysis.iterateSymbolsContainer(&document_store, arena, node_handle, orig_handle, declToCompletion, context); }, .FnProto => { const func = node.cast(std.zig.ast.Node.FnProto).?; @@ -505,7 +491,7 @@ fn getSymbolGlobal(arena: *std.heap.ArenaAllocator, pos_index: usize, handle: *D const name = identifierFromPosition(pos_index, handle.*); if (name.len == 0) return null; - return try analysis.lookupSymbolGlobal(&document_store, handle, name, pos_index); + return try analysis.lookupSymbolGlobal(&document_store, arena, handle, name, pos_index); } fn gotoDefinitionGlobal(id: types.RequestId, pos_index: usize, handle: *DocumentStore.Handle, config: Config) !void { @@ -539,7 +525,7 @@ fn getSymbolFieldAccess( var tokenizer = std.zig.Tokenizer.init(line[range.start..range.end]); if (try analysis.getFieldAccessTypeNode(&document_store, arena, handle, pos_index, &tokenizer)) |container_handle| { - return try analysis.lookupSymbolContainer(&document_store, container_handle, name, true); + return try analysis.lookupSymbolContainer(&document_store, arena, container_handle, name, true); } return null; } @@ -606,7 +592,7 @@ const DeclToCompletionContext = struct { orig_handle: *DocumentStore.Handle, }; -fn decltoCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void { +fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) !void { const tree = decl_handle.handle.tree; switch (decl_handle.decl.*) { @@ -661,7 +647,7 @@ fn completeGlobal(id: types.RequestId, pos_index: usize, handle: *DocumentStore. .arena = &arena, .orig_handle = handle, }; - try analysis.iterateSymbolsGlobal(&document_store, handle, pos_index, decltoCompletion, context); + try analysis.iterateSymbolsGlobal(&document_store, &arena, handle, pos_index, declToCompletion, context); try send(types.Response{ .id = id,