diff --git a/src/Server.zig b/src/Server.zig index 4d13006..0334f0c 100644 --- a/src/Server.zig +++ b/src/Server.zig @@ -545,6 +545,7 @@ pub fn typeToCompletion( list: *std.ArrayListUnmanaged(types.CompletionItem), field_access: analysis.FieldAccessReturn, orig_handle: *const DocumentStore.Handle, + either_descriptor: ?[]const u8, ) error{OutOfMemory}!void { var allocator = server.arena.allocator(); @@ -587,6 +588,7 @@ pub fn typeToCompletion( orig_handle, type_handle.type.is_type_val, null, + either_descriptor, ); }, .other => |n| try server.nodeToCompletion( @@ -596,6 +598,7 @@ pub fn typeToCompletion( orig_handle, type_handle.type.is_type_val, null, + either_descriptor, ), .primitive, .array_index => {}, .@"comptime" => |co| try analyser.completions.dotCompletions( @@ -606,6 +609,10 @@ pub fn typeToCompletion( type_handle.type.is_type_val, co.value.node_idx, ), + .either => |bruh| { + for (bruh) |a| + try server.typeToCompletion(list, .{ .original = a.type_with_handle }, orig_handle, a.descriptor); + }, } } @@ -617,6 +624,7 @@ pub fn nodeToCompletion( orig_handle: *const DocumentStore.Handle, is_type_val: bool, parent_is_type_val: ?bool, + either_descriptor: ?[]const u8, ) error{OutOfMemory}!void { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -643,8 +651,17 @@ pub fn nodeToCompletion( doc_kind, )) |doc_comments| .{ .MarkupContent = types.MarkupContent{ .kind = doc_kind, - .value = doc_comments, - } } else null; + .value = if (either_descriptor) |ed| + try std.fmt.allocPrint(allocator, "`Conditionally available: {s}`\n\n{s}", .{ ed, doc_comments }) + else + doc_comments, + } } else (if (either_descriptor) |ed| + .{ .MarkupContent = types.MarkupContent{ + .kind = doc_kind, + .value = try std.fmt.allocPrint(allocator, "`Conditionally available: {s}`", .{ed}), + } } + else + null); if (ast.isContainer(handle.tree, node)) { const context = DeclToCompletionContext{ @@ -652,8 +669,10 @@ pub fn nodeToCompletion( .completions = list, .orig_handle = orig_handle, .parent_is_type_val = is_type_val, + .either_descriptor = either_descriptor, }; try analysis.iterateSymbolsContainer( + allocator, &server.document_store, node_handle, orig_handle, @@ -678,7 +697,7 @@ 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(&server.document_store, handle, func); + try analysis.hasSelfParam(allocator, &server.document_store, handle, func); break :blk try analysis.getFunctionSnippet(server.arena.allocator(), tree, func, skip_self_param); } else tree.tokenSlice(func.name_token.?); @@ -702,11 +721,12 @@ 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(&server.document_store, node_handle)) |result| { + if (try analysis.resolveVarDeclAlias(allocator, &server.document_store, node_handle)) |result| { const context = DeclToCompletionContext{ .server = server, .completions = list, .orig_handle = orig_handle, + .either_descriptor = either_descriptor, }; return try declToCompletion(context, result); } @@ -780,7 +800,7 @@ pub fn nodeToCompletion( } if (unwrapped) |actual_type| { - try server.typeToCompletion(list, .{ .original = actual_type }, orig_handle); + try server.typeToCompletion(list, .{ .original = actual_type }, orig_handle, either_descriptor); } return; }, @@ -847,7 +867,7 @@ pub fn gotoDefinitionSymbol( const name_token = switch (decl_handle.decl.*) { .ast_node => |node| block: { if (resolve_alias) { - if (try analysis.resolveVarDeclAlias(&server.document_store, .{ .node = node, .handle = handle })) |result| { + if (try analysis.resolveVarDeclAlias(server.arena.allocator(), &server.document_store, .{ .node = node, .handle = handle })) |result| { handle = result.handle; break :block result.nameToken(); @@ -865,22 +885,21 @@ pub fn gotoDefinitionSymbol( }; } -pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{OutOfMemory}!?types.Hover { +pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle, markup_kind: types.MarkupKind) error{OutOfMemory}!?[]const u8 { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); const handle = decl_handle.handle; const tree = handle.tree; - const hover_kind: types.MarkupKind = if (server.client_capabilities.hover_supports_md) .markdown else .plaintext; var doc_str: ?[]const u8 = null; const def_str = switch (decl_handle.decl.*) { .ast_node => |node| def: { - if (try analysis.resolveVarDeclAlias(&server.document_store, .{ .node = node, .handle = handle })) |result| { - return try server.hoverSymbol(result); + if (try analysis.resolveVarDeclAlias(server.arena.allocator(), &server.document_store, .{ .node = node, .handle = handle })) |result| { + return try server.hoverSymbol(result, markup_kind); } - doc_str = try analysis.getDocComments(server.arena.allocator(), tree, node, hover_kind); + doc_str = try analysis.getDocComments(server.arena.allocator(), tree, node, markup_kind); var buf: [1]Ast.Node.Index = undefined; @@ -897,7 +916,7 @@ pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{ .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, hover_kind, false); + doc_str = try analysis.collectDocComments(server.arena.allocator(), handle.tree, doc_comments, markup_kind, false); } const first_token = ast.paramFirstToken(tree, param); @@ -918,7 +937,7 @@ pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{ var bound_type_params = analysis.BoundTypeParams{}; defer bound_type_params.deinit(server.document_store.allocator); - const resolved_type = try decl_handle.resolveType(&server.document_store, &bound_type_params); + const resolved_type = try decl_handle.resolveType(server.arena.allocator(), &server.document_store, &bound_type_params); const resolved_type_str = if (resolved_type) |rt| if (rt.type.is_type_val) switch (rt.type.data) { @@ -965,7 +984,7 @@ pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{ "unknown"; var hover_text: []const u8 = undefined; - if (hover_kind == .markdown) { + if (markup_kind == .markdown) { hover_text = if (doc_str) |doc| try std.fmt.allocPrint(server.arena.allocator(), "```zig\n{s}\n```\n```zig\n({s})\n```\n{s}", .{ def_str, resolved_type_str, doc }) @@ -979,12 +998,7 @@ pub fn hoverSymbol(server: *Server, decl_handle: analysis.DeclWithHandle) error{ try std.fmt.allocPrint(server.arena.allocator(), "{s} ({s})", .{ def_str, resolved_type_str }); } - return types.Hover{ - .contents = .{ .MarkupContent = .{ - .kind = hover_kind, - .value = hover_text, - } }, - }; + return hover_text; } pub fn getLabelGlobal(pos_index: usize, handle: *const DocumentStore.Handle) error{OutOfMemory}!?analysis.DeclWithHandle { @@ -1008,7 +1022,7 @@ pub fn getSymbolGlobal( const name = identifierFromPosition(pos_index, handle.*); if (name.len == 0) return null; - return try analysis.lookupSymbolGlobal(&server.document_store, handle, name, pos_index); + return try analysis.lookupSymbolGlobal(server.arena.allocator(), &server.document_store, handle, name, pos_index); } pub fn gotoDefinitionLabel( @@ -1072,8 +1086,17 @@ pub fn hoverDefinitionLabel(server: *Server, pos_index: usize, handle: *const Do const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); + const markup_kind: types.MarkupKind = if (server.client_capabilities.hover_supports_md) .markdown else .plaintext; const decl = (try getLabelGlobal(pos_index, handle)) orelse return null; - return try server.hoverSymbol(decl); + + return .{ + .contents = .{ + .MarkupContent = .{ + .kind = markup_kind, + .value = (try server.hoverSymbol(decl, markup_kind)) orelse return null, + }, + }, + }; } pub fn hoverDefinitionBuiltin(server: *Server, pos_index: usize, handle: *const DocumentStore.Handle) error{OutOfMemory}!?types.Hover { @@ -1130,16 +1153,26 @@ pub fn hoverDefinitionGlobal(server: *Server, pos_index: usize, handle: *const D const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); + const markup_kind: types.MarkupKind = if (server.client_capabilities.hover_supports_md) .markdown else .plaintext; const decl = (try server.getSymbolGlobal(pos_index, handle)) orelse return null; - return try server.hoverSymbol(decl); + + return .{ + .contents = .{ + .MarkupContent = .{ + .kind = markup_kind, + .value = (try server.hoverSymbol(decl, markup_kind)) orelse return null, + }, + }, + }; } -pub fn getSymbolFieldAccess( +/// Multiple when using branched types +pub fn getSymbolFieldAccesses( server: *Server, handle: *const DocumentStore.Handle, source_index: usize, loc: offsets.Loc, -) error{OutOfMemory}!?analysis.DeclWithHandle { +) error{OutOfMemory}!?[]const analysis.DeclWithHandle { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -1149,20 +1182,29 @@ pub fn getSymbolFieldAccess( var held_range = try server.arena.allocator().dupeZ(u8, offsets.locToSlice(handle.text, loc)); var tokenizer = std.zig.Tokenizer.init(held_range); - if (try analysis.getFieldAccessType(&server.document_store, handle, source_index, &tokenizer)) |result| { + var decls_with_handles = std.ArrayListUnmanaged(analysis.DeclWithHandle){}; + + if (try analysis.getFieldAccessType(server.arena.allocator(), &server.document_store, handle, source_index, &tokenizer)) |result| { const container_handle = result.unwrapped orelse result.original; - const container_handle_node = switch (container_handle.type.data) { - .other => |n| n, - else => return null, - }; - return try analysis.lookupSymbolContainer( - &server.document_store, - .{ .node = container_handle_node, .handle = container_handle.handle }, - name, - true, - ); + + const container_handle_nodes = try container_handle.getAllTypesWithHandles(server.arena.allocator()); + + for (container_handle_nodes) |ty| { + const container_handle_node = switch (ty.type.data) { + .other => |n| n, + else => continue, + }; + try decls_with_handles.append(server.arena.allocator(), (try analysis.lookupSymbolContainer( + server.arena.allocator(), + &server.document_store, + .{ .node = container_handle_node, .handle = ty.handle }, + name, + true, + )) orelse continue); + } } - return null; + + return try decls_with_handles.toOwnedSlice(server.arena.allocator()); } pub fn gotoDefinitionFieldAccess( @@ -1171,12 +1213,22 @@ pub fn gotoDefinitionFieldAccess( source_index: usize, loc: offsets.Loc, resolve_alias: bool, -) error{OutOfMemory}!?types.Location { +) error{OutOfMemory}!?[]const types.Location { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const decl = (try server.getSymbolFieldAccess(handle, source_index, loc)) orelse return null; - return try server.gotoDefinitionSymbol(decl, resolve_alias); + const accesses = (try server.getSymbolFieldAccesses(handle, source_index, loc)) orelse return null; + var locs = std.ArrayListUnmanaged(types.Location){}; + + for (accesses) |access| { + if (try server.gotoDefinitionSymbol(access, resolve_alias)) |l| + try locs.append(server.arena.allocator(), l); + } + + if (locs.items.len == 0) + return null; + + return try locs.toOwnedSlice(server.arena.allocator()); } pub fn hoverDefinitionFieldAccess( @@ -1188,8 +1240,24 @@ pub fn hoverDefinitionFieldAccess( const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); - const decl = (try server.getSymbolFieldAccess(handle, source_index, loc)) orelse return null; - return try server.hoverSymbol(decl); + const markup_kind: types.MarkupKind = if (server.client_capabilities.hover_supports_md) .markdown else .plaintext; + const decls = (try server.getSymbolFieldAccesses(handle, source_index, loc)) orelse return null; + + var content = std.ArrayListUnmanaged(types.MarkedString){}; + + for (decls) |decl| { + try content.append(server.arena.allocator(), .{ + .string = (try server.hoverSymbol(decl, markup_kind)) orelse continue, + }); + } + + // Yes, this is deprecated; the issue is that there's no better + // solution for multiple hover entries :( + return .{ + .contents = .{ + .array_of_MarkedString = try content.toOwnedSlice(server.arena.allocator()), + }, + }; } pub fn gotoDefinitionString( @@ -1248,6 +1316,7 @@ const DeclToCompletionContext = struct { completions: *std.ArrayListUnmanaged(types.CompletionItem), orig_handle: *const DocumentStore.Handle, parent_is_type_val: ?bool = null, + either_descriptor: ?[]const u8 = null, }; pub fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis.DeclWithHandle) error{OutOfMemory}!void { @@ -1265,6 +1334,7 @@ pub fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis. context.orig_handle, false, context.parent_is_type_val, + context.either_descriptor, ), .param_payload => |pay| { const Documentation = @TypeOf(@as(types.CompletionItem, undefined).documentation); @@ -1273,7 +1343,10 @@ pub fn declToCompletion(context: DeclToCompletionContext, decl_handle: analysis. const doc_kind: types.MarkupKind = if (context.server.client_capabilities.completion_doc_supports_md) .markdown else .plaintext; const doc: Documentation = if (param.first_doc_comment) |doc_comments| .{ .MarkupContent = types.MarkupContent{ .kind = doc_kind, - .value = try analysis.collectDocComments(allocator, tree, doc_comments, doc_kind, false), + .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) }) + else + try analysis.collectDocComments(allocator, tree, doc_comments, doc_kind, false), } } else null; const first_token = ast.paramFirstToken(tree, param); @@ -1416,7 +1489,7 @@ pub fn completeGlobal(server: *Server, pos_index: usize, handle: *const Document .completions = &completions, .orig_handle = handle, }; - try analysis.iterateSymbolsGlobal(&server.document_store, handle, pos_index, declToCompletion, context); + try analysis.iterateSymbolsGlobal(server.arena.allocator(), &server.document_store, handle, pos_index, declToCompletion, context); try populateSnippedCompletions(server.arena.allocator(), &completions, &snipped_data.generic, server.config.*, null); if (server.client_capabilities.label_details_support) { @@ -1439,8 +1512,8 @@ 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(&server.document_store, handle, source_index, &tokenizer)) orelse return null; - try server.typeToCompletion(&completions, result, handle); + const result = (try analysis.getFieldAccessType(allocator, &server.document_store, 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| { try formatDetailledLabel(item, allocator); @@ -2335,7 +2408,7 @@ pub fn signatureHelpHandler(server: *Server, request: types.SignatureHelpParams) }; } -pub fn gotoHandler(server: *Server, request: types.TextDocumentPositionParams, resolve_alias: bool) Error!?types.Location { +pub fn gotoHandler(server: *Server, request: types.TextDocumentPositionParams, resolve_alias: bool) Error!?types.Definition { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -2347,14 +2420,14 @@ pub fn gotoHandler(server: *Server, request: types.TextDocumentPositionParams, r const pos_context = try analysis.getPositionContext(server.arena.allocator(), handle.text, source_index, true); return switch (pos_context) { - .builtin => |loc| try server.gotoDefinitionBuiltin(handle, loc), - .var_access => try server.gotoDefinitionGlobal(source_index, handle, resolve_alias), - .field_access => |loc| try server.gotoDefinitionFieldAccess(handle, source_index, loc, resolve_alias), + .builtin => |loc| .{ .Location = (try server.gotoDefinitionBuiltin(handle, loc)) orelse return null }, + .var_access => .{ .Location = (try server.gotoDefinitionGlobal(source_index, handle, resolve_alias)) orelse return null }, + .field_access => |loc| .{ .array_of_Location = (try server.gotoDefinitionFieldAccess(handle, source_index, loc, resolve_alias)) orelse return null }, .import_string_literal, .cinclude_string_literal, .embedfile_string_literal, - => try server.gotoDefinitionString(pos_context, handle), - .label => try server.gotoDefinitionLabel(source_index, handle), + => .{ .Location = (try server.gotoDefinitionString(pos_context, handle)) orelse return null }, + .label => .{ .Location = (try server.gotoDefinitionLabel(source_index, handle)) orelse return null }, else => null, }; } @@ -2362,7 +2435,7 @@ pub fn gotoHandler(server: *Server, request: types.TextDocumentPositionParams, r fn gotoDefinitionHandler( server: *Server, request: types.TextDocumentPositionParams, -) Error!?types.Location { +) Error!?types.Definition { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -2372,7 +2445,7 @@ fn gotoDefinitionHandler( fn gotoDeclarationHandler( server: *Server, request: types.TextDocumentPositionParams, -) Error!?types.Location { +) Error!?types.Definition { const tracy_zone = tracy.trace(@src()); defer tracy_zone.end(); @@ -2553,9 +2626,17 @@ pub fn generalReferencesHandler(server: *Server, request: GeneralReferencesReque 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); + // TODO: Make this work with branching types const decl = switch (pos_context) { .var_access => try server.getSymbolGlobal(source_index, handle), - .field_access => |range| try server.getSymbolFieldAccess(handle, source_index, range), + .field_access => |range| z: { + const a = try server.getSymbolFieldAccesses(handle, source_index, range); + if (a) |b| { + if (b.len != 0) break :z b[0]; + } + + break :z null; + }, .label => try getLabelGlobal(source_index, handle), else => null, } orelse return null; diff --git a/src/analysis.zig b/src/analysis.zig index d966588..3e8019a 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -186,7 +186,7 @@ pub fn getFunctionSnippet(allocator: std.mem.Allocator, tree: Ast, func: Ast.ful return buffer.toOwnedSlice(allocator); } -pub fn hasSelfParam(document_store: *DocumentStore, handle: *const DocumentStore.Handle, func: Ast.full.FnProto) !bool { +pub fn hasSelfParam(arena: std.mem.Allocator, document_store: *DocumentStore, 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; @@ -199,7 +199,7 @@ pub fn hasSelfParam(document_store: *DocumentStore, handle: *const DocumentStore const token_starts = tree.tokens.items(.start); const in_container = innermostContainer(handle, token_starts[func.ast.fn_token]); - if (try resolveTypeOfNode(document_store, .{ + if (try resolveTypeOfNode(arena, document_store, .{ .node = param.type_expr, .handle = handle, })) |resolved_type| { @@ -208,7 +208,7 @@ pub fn hasSelfParam(document_store: *DocumentStore, handle: *const DocumentStore } if (ast.fullPtrType(tree, param.type_expr)) |ptr_type| { - if (try resolveTypeOfNode(document_store, .{ + if (try resolveTypeOfNode(arena, document_store, .{ .node = ptr_type.ast.child_type, .handle = handle, })) |resolved_prefix_op| { @@ -323,7 +323,7 @@ pub fn getDeclName(tree: Ast, node: Ast.Node.Index) ?[]const u8 { }; } -fn resolveVarDeclAliasInternal(store: *DocumentStore, node_handle: NodeWithHandle, root: bool) error{OutOfMemory}!?DeclWithHandle { +fn resolveVarDeclAliasInternal(arena: std.mem.Allocator, store: *DocumentStore, node_handle: NodeWithHandle, root: bool) error{OutOfMemory}!?DeclWithHandle { _ = root; const handle = node_handle.handle; const tree = handle.tree; @@ -334,6 +334,7 @@ fn resolveVarDeclAliasInternal(store: *DocumentStore, node_handle: NodeWithHandl if (node_tags[node_handle.node] == .identifier) { const token = main_tokens[node_handle.node]; return try lookupSymbolGlobal( + arena, store, handle, tree.tokenSlice(token), @@ -349,13 +350,13 @@ fn resolveVarDeclAliasInternal(store: *DocumentStore, node_handle: NodeWithHandl if (!std.mem.eql(u8, name, "@import") and !std.mem.eql(u8, name, "@cImport")) return null; - const inner_node = (try resolveTypeOfNode(store, .{ .node = lhs, .handle = handle })) orelse return null; + const inner_node = (try resolveTypeOfNode(arena, store, .{ .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(store, .{ .node = lhs, .handle = handle }, false)) |decl_handle| block: { + } else if (try resolveVarDeclAliasInternal(arena, store, .{ .node = lhs, .handle = handle }, false)) |decl_handle| block: { if (decl_handle.decl.* != .ast_node) return null; - const resolved = (try resolveTypeOfNode(store, .{ .node = decl_handle.decl.ast_node, .handle = decl_handle.handle })) orelse return null; + const resolved = (try resolveTypeOfNode(arena, store, .{ .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, @@ -364,7 +365,7 @@ fn resolveVarDeclAliasInternal(store: *DocumentStore, node_handle: NodeWithHandl break :block NodeWithHandle{ .node = resolved_node, .handle = resolved.handle }; } else return null; - return try lookupSymbolContainer(store, container_node, tree.tokenSlice(datas[node_handle.node].rhs), false); + return try lookupSymbolContainer(arena, store, container_node, tree.tokenSlice(datas[node_handle.node].rhs), false); } return null; } @@ -375,7 +376,7 @@ fn resolveVarDeclAliasInternal(store: *DocumentStore, node_handle: NodeWithHandl /// const decl = @import("decl-file.zig").decl; /// const other = decl.middle.other; ///``` -pub fn resolveVarDeclAlias(store: *DocumentStore, decl_handle: NodeWithHandle) !?DeclWithHandle { +pub fn resolveVarDeclAlias(arena: std.mem.Allocator, store: *DocumentStore, decl_handle: NodeWithHandle) !?DeclWithHandle { const decl = decl_handle.node; const handle = decl_handle.handle; const tree = handle.tree; @@ -392,7 +393,7 @@ pub fn resolveVarDeclAlias(store: *DocumentStore, decl_handle: NodeWithHandle) ! if (!std.mem.eql(u8, tree.tokenSlice(var_decl.ast.mut_token + 1), name)) return null; - return try resolveVarDeclAliasInternal(store, .{ .node = base_exp, .handle = handle }, true); + return try resolveVarDeclAliasInternal(arena, store, .{ .node = base_exp, .handle = handle }, true); } } @@ -440,7 +441,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(store: *DocumentStore, fn_decl: Ast.full.FnProto, handle: *const DocumentStore.Handle, bound_type_params: *BoundTypeParams, fn_body: ?Ast.Node.Index) !?TypeWithHandle { +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 { 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 @@ -448,7 +449,7 @@ pub fn resolveReturnType(store: *DocumentStore, fn_decl: Ast.full.FnProto, handl 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(store, .{ + return try resolveTypeOfNodeInternal(arena, store, .{ .node = data.lhs, .handle = handle, }, bound_type_params); @@ -460,7 +461,7 @@ pub fn resolveReturnType(store: *DocumentStore, fn_decl: Ast.full.FnProto, handl 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(store, ret, bound_type_params)) orelse + const child_type = (try resolveTypeOfNodeInternal(arena, store, ret, bound_type_params)) orelse return null; const is_inferred_error = tree.tokens.items(.tag)[tree.firstToken(return_type) - 1] == .bang; @@ -477,14 +478,14 @@ pub fn resolveReturnType(store: *DocumentStore, fn_decl: Ast.full.FnProto, handl } /// Resolves the child type of an optional type -fn resolveUnwrapOptionalType(store: *DocumentStore, opt: TypeWithHandle, bound_type_params: *BoundTypeParams) !?TypeWithHandle { +fn resolveUnwrapOptionalType(arena: std.mem.Allocator, store: *DocumentStore, opt: TypeWithHandle, bound_type_params: *BoundTypeParams) !?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(store, .{ + return ((try resolveTypeOfNodeInternal(arena, store, .{ .node = opt.handle.tree.nodes.items(.data)[opt_node].lhs, .handle = opt.handle, }, bound_type_params)) orelse return null).instanceTypeVal(); @@ -493,18 +494,18 @@ fn resolveUnwrapOptionalType(store: *DocumentStore, opt: TypeWithHandle, bound_t return null; } -fn resolveUnwrapErrorType(store: *DocumentStore, rhs: TypeWithHandle, bound_type_params: *BoundTypeParams) !?TypeWithHandle { +fn resolveUnwrapErrorType(arena: std.mem.Allocator, store: *DocumentStore, rhs: TypeWithHandle, bound_type_params: *BoundTypeParams) !?TypeWithHandle { const rhs_node = switch (rhs.type.data) { .other => |n| n, .error_union => |n| return TypeWithHandle{ .type = .{ .data = .{ .other = n }, .is_type_val = rhs.type.is_type_val }, .handle = rhs.handle, }, - .primitive, .slice, .pointer, .array_index, .@"comptime" => return null, + .primitive, .slice, .pointer, .array_index, .@"comptime", .either => return null, }; if (rhs.handle.tree.nodes.items(.tag)[rhs_node] == .error_union) { - return ((try resolveTypeOfNodeInternal(store, .{ + return ((try resolveTypeOfNodeInternal(arena, store, .{ .node = rhs.handle.tree.nodes.items(.data)[rhs_node].rhs, .handle = rhs.handle, }, bound_type_params)) orelse return null).instanceTypeVal(); @@ -514,7 +515,7 @@ fn resolveUnwrapErrorType(store: *DocumentStore, rhs: TypeWithHandle, bound_type } /// Resolves the child type of a deref type -fn resolveDerefType(store: *DocumentStore, deref: TypeWithHandle, bound_type_params: *BoundTypeParams) !?TypeWithHandle { +fn resolveDerefType(arena: std.mem.Allocator, store: *DocumentStore, deref: TypeWithHandle, bound_type_params: *BoundTypeParams) !?TypeWithHandle { const deref_node = switch (deref.type.data) { .other => |n| n, .pointer => |n| return TypeWithHandle{ @@ -533,7 +534,7 @@ fn resolveDerefType(store: *DocumentStore, deref: TypeWithHandle, bound_type_par if (ast.fullPtrType(tree, deref_node)) |ptr_type| { switch (token_tag) { .asterisk => { - return ((try resolveTypeOfNodeInternal(store, .{ + return ((try resolveTypeOfNodeInternal(arena, store, .{ .node = ptr_type.ast.child_type, .handle = deref.handle, }, bound_type_params)) orelse return null).instanceTypeVal(); @@ -546,7 +547,7 @@ fn resolveDerefType(store: *DocumentStore, deref: TypeWithHandle, bound_type_par } /// Resolves slicing and array access -fn resolveBracketAccessType(store: *DocumentStore, lhs: TypeWithHandle, rhs: enum { Single, Range }, bound_type_params: *BoundTypeParams) !?TypeWithHandle { +fn resolveBracketAccessType(arena: std.mem.Allocator, store: *DocumentStore, lhs: TypeWithHandle, rhs: enum { Single, Range }, bound_type_params: *BoundTypeParams) !?TypeWithHandle { const lhs_node = switch (lhs.type.data) { .other => |n| n, else => return null, @@ -559,7 +560,7 @@ fn resolveBracketAccessType(store: *DocumentStore, lhs: TypeWithHandle, rhs: enu if (tag == .array_type or tag == .array_type_sentinel) { if (rhs == .Single) - return ((try resolveTypeOfNodeInternal(store, .{ + return ((try resolveTypeOfNodeInternal(arena, store, .{ .node = data.rhs, .handle = lhs.handle, }, bound_type_params)) orelse return null).instanceTypeVal(); @@ -570,7 +571,7 @@ fn resolveBracketAccessType(store: *DocumentStore, lhs: TypeWithHandle, rhs: enu } else if (ast.fullPtrType(tree, lhs_node)) |ptr_type| { if (ptr_type.size == .Slice) { if (rhs == .Single) { - return ((try resolveTypeOfNodeInternal(store, .{ + return ((try resolveTypeOfNodeInternal(arena, store, .{ .node = ptr_type.ast.child_type, .handle = lhs.handle, }, bound_type_params)) orelse return null).instanceTypeVal(); @@ -583,8 +584,8 @@ fn resolveBracketAccessType(store: *DocumentStore, lhs: TypeWithHandle, rhs: enu } /// Called to remove one level of pointerness before a field access -pub fn resolveFieldAccessLhsType(store: *DocumentStore, lhs: TypeWithHandle, bound_type_params: *BoundTypeParams) !TypeWithHandle { - return (try resolveDerefType(store, lhs, bound_type_params)) orelse lhs; +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 const BoundTypeParams = std.AutoHashMapUnmanaged(Ast.full.FnProto.Param, TypeWithHandle); @@ -622,7 +623,12 @@ pub fn isTypeIdent(text: []const u8) bool { } /// Resolves the type of a node -pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHandle, bound_type_params: *BoundTypeParams) error{OutOfMemory}!?TypeWithHandle { +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| { @@ -651,14 +657,14 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan 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(store, decl_type, bound_type_params)) |typ| + if (try resolveTypeOfNodeInternal(arena, store, decl_type, bound_type_params)) |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(store, value, bound_type_params); + return try resolveTypeOfNodeInternal(arena, store, value, bound_type_params); }, .identifier => { const name = offsets.nodeToSlice(tree, node); @@ -671,6 +677,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan } if (try lookupSymbolGlobal( + arena, store, handle, name, @@ -686,7 +693,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan }, else => {}, } - return try child.resolveType(store, bound_type_params); + return try child.resolveType(arena, store, bound_type_params); } return null; }, @@ -703,7 +710,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan const call = tree.fullCall(¶ms, node) orelse unreachable; const callee = .{ .node = call.ast.fn_expr, .handle = handle }; - const decl = (try resolveTypeOfNodeInternal(store, callee, bound_type_params)) orelse + const decl = (try resolveTypeOfNodeInternal(arena, store, callee, bound_type_params)) orelse return null; if (decl.type.is_type_val) return null; @@ -720,7 +727,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan // 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(store, decl.handle, fn_decl)) { + if (try hasSelfParam(arena, store, decl.handle, fn_decl)) { _ = ast.nextFnParam(&it); expected_params -= 1; } @@ -736,6 +743,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan const argument = .{ .node = call.ast.params[i], .handle = handle }; const argument_type = (try resolveTypeOfNodeInternal( + arena, store, argument, bound_type_params, @@ -748,7 +756,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan 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(store, fn_decl, decl.handle, bound_type_params, if (has_body) body else null)) |ret| { + if (try resolveReturnType(arena, store, fn_decl, decl.handle, bound_type_params, if (has_body) body else null)) |ret| { return ret; } else if (store.config.use_comptime_interpreter) { // TODO: Better case-by-case; we just use the ComptimeInterpreter when all else fails, @@ -826,7 +834,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan .address_of, => { const base = .{ .node = datas[node].lhs, .handle = handle }; - const base_type = (try resolveTypeOfNodeInternal(store, base, bound_type_params)) orelse + const base_type = (try resolveTypeOfNodeInternal(arena, store, base, bound_type_params)) orelse return null; return switch (node_tags[node]) { .@"comptime", @@ -844,13 +852,13 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan .slice, .slice_sentinel, .slice_open, - => try resolveBracketAccessType(store, base_type, .Range, bound_type_params), - .deref => try resolveDerefType(store, base_type, bound_type_params), - .unwrap_optional => try resolveUnwrapOptionalType(store, base_type, bound_type_params), - .array_access => try resolveBracketAccessType(store, base_type, .Single, bound_type_params), - .@"orelse" => try resolveUnwrapOptionalType(store, base_type, bound_type_params), - .@"catch" => try resolveUnwrapErrorType(store, base_type, bound_type_params), - .@"try" => try resolveUnwrapErrorType(store, base_type, bound_type_params), + => 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), .address_of => { const lhs_node = switch (base_type.type.data) { .other => |n| n, @@ -869,8 +877,9 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan // If we are accessing a pointer type, remove one pointerness level :) const left_type = try resolveFieldAccessLhsType( + arena, store, - (try resolveTypeOfNodeInternal(store, .{ + (try resolveTypeOfNodeInternal(arena, store, .{ .node = datas[node].lhs, .handle = handle, }, bound_type_params)) orelse return null, @@ -883,12 +892,13 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan }; if (try lookupSymbolContainer( + arena, store, .{ .node = left_type_node, .handle = left_type.handle }, tree.tokenSlice(datas[node].rhs), !left_type.type.is_type_val, )) |child| { - return try child.resolveType(store, bound_type_params); + return try child.resolveType(arena, store, bound_type_params); } else return null; }, .array_type, @@ -942,7 +952,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan }); if (cast_map.has(call_name)) { if (params.len < 1) return null; - return ((try resolveTypeOfNodeInternal(store, .{ + return ((try resolveTypeOfNodeInternal(arena, store, .{ .node = params[0], .handle = handle, }, bound_type_params)) orelse return null).instanceTypeVal(); @@ -952,7 +962,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan // 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(store, .{ + var resolved_type = (try resolveTypeOfNodeInternal(arena, store, .{ .node = params[0], .handle = handle, }, bound_type_params)) orelse return null; @@ -1033,6 +1043,50 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan .type = .{ .data = .{ .other = node }, .is_type_val = false }, .handle = handle, }, + .@"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)}) }); + + return TypeWithHandle{ + .type = .{ .data = .{ .either = try either.toOwnedSlice() }, .is_type_val = false }, + .handle = handle, + }; + }, + .@"switch", + .switch_comma, + => { + 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); + + for (cases) |case| { + const switch_case = tree.fullSwitchCase(case).?; + var descriptor = std.ArrayList(u8).init(arena); + + 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(", "); + } + + if (try resolveTypeOfNodeInternal(arena, store, .{ .handle = handle, .node = switch_case.ast.target_expr }, bound_type_params)) |t| + try either.append(.{ + .type_with_handle = t, + .descriptor = try descriptor.toOwnedSlice(), + }); + } + + return TypeWithHandle{ + .type = .{ .data = .{ .either = try either.toOwnedSlice() }, .is_type_val = false }, + .handle = handle, + }; + }, else => {}, } return null; @@ -1041,12 +1095,18 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, node_handle: NodeWithHan // TODO Reorganize this file, perhaps split into a couple as well // TODO Make this better, nested levels of type vals pub const Type = struct { + pub const EitherEntry = struct { + type_with_handle: TypeWithHandle, + descriptor: []const u8, + }; + data: union(enum) { pointer: Ast.Node.Index, slice: Ast.Node.Index, error_union: Ast.Node.Index, other: Ast.Node.Index, primitive: Ast.Node.Index, + either: []const EitherEntry, array_index, @"comptime": struct { interpreter: *ComptimeInterpreter, @@ -1071,6 +1131,20 @@ pub const TypeWithHandle = struct { }; } + /// Resolves possible types of a type (single for all except array_index and either) + pub fn getAllTypesWithHandles(ty: TypeWithHandle, arena: std.mem.Allocator) ![]const TypeWithHandle { + var all_types = std.ArrayListUnmanaged(TypeWithHandle){}; + try ty.getAllTypesWithHandlesArrayList(arena, &all_types); + return try all_types.toOwnedSlice(arena); + } + + pub fn getAllTypesWithHandlesArrayList(ty: TypeWithHandle, arena: std.mem.Allocator, all_types: *std.ArrayListUnmanaged(TypeWithHandle)) !void { + switch (ty.type.data) { + .either => |e| for (e) |i| try i.type_with_handle.getAllTypesWithHandlesArrayList(arena, all_types), + else => try all_types.append(arena, ty), + } + } + fn instanceTypeVal(self: TypeWithHandle) ?TypeWithHandle { if (!self.type.is_type_val) return null; return TypeWithHandle{ @@ -1166,10 +1240,14 @@ pub const TypeWithHandle = struct { } }; -pub fn resolveTypeOfNode(store: *DocumentStore, node_handle: NodeWithHandle) error{OutOfMemory}!?TypeWithHandle { +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(store, node_handle, &bound_type_params); + return resolveTypeOfNodeInternal(arena, store, node_handle, &bound_type_params); } /// Collects all `@import`'s we can find into a slice of import paths (without quotes). @@ -1235,7 +1313,7 @@ pub const FieldAccessReturn = struct { unwrapped: ?TypeWithHandle = null, }; -pub fn getFieldAccessType(store: *DocumentStore, handle: *const DocumentStore.Handle, source_index: usize, tokenizer: *std.zig.Tokenizer) !?FieldAccessReturn { +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; var bound_type_params = BoundTypeParams{}; @@ -1246,17 +1324,18 @@ pub fn getFieldAccessType(store: *DocumentStore, handle: *const DocumentStore.Ha switch (tok.tag) { .eof => return FieldAccessReturn{ .original = current_type orelse return null, - .unwrapped = try resolveDerefType(store, current_type orelse return null, &bound_type_params), + .unwrapped = try resolveDerefType(arena, store, current_type orelse return null, &bound_type_params), }, .identifier => { const ct_handle = if (current_type) |c| c.handle else handle; if (try lookupSymbolGlobal( + arena, store, ct_handle, tokenizer.buffer[tok.loc.start..tok.loc.end], source_index, )) |child| { - current_type = (try child.resolveType(store, &bound_type_params)) orelse return null; + current_type = (try child.resolveType(arena, store, &bound_type_params)) orelse return null; } else return null; }, .period => { @@ -1268,7 +1347,7 @@ pub fn getFieldAccessType(store: *DocumentStore, handle: *const DocumentStore.Ha if (ct.isFunc()) return null; return FieldAccessReturn{ .original = ct, - .unwrapped = try resolveDerefType(store, ct, &bound_type_params), + .unwrapped = try resolveDerefType(arena, store, ct, &bound_type_params), }; } else { return null; @@ -1279,34 +1358,46 @@ pub fn getFieldAccessType(store: *DocumentStore, handle: *const DocumentStore.Ha if (current_type) |ct| { return FieldAccessReturn{ .original = ct, - .unwrapped = try resolveDerefType(store, ct, &bound_type_params), + .unwrapped = try resolveDerefType(arena, store, ct, &bound_type_params), }; } else { return null; } } - current_type = try resolveFieldAccessLhsType(store, current_type orelse return null, &bound_type_params); + current_type = try resolveFieldAccessLhsType(arena, store, current_type orelse return null, &bound_type_params); - const current_type_node = switch (current_type.?.type.data) { - .other => |n| n, - else => return null, - }; + const current_type_nodes = try current_type.?.getAllTypesWithHandles(arena); - if (try lookupSymbolContainer( - store, - .{ .node = current_type_node, .handle = current_type.?.handle }, - tokenizer.buffer[after_period.loc.start..after_period.loc.end], - !current_type.?.type.is_type_val, - )) |child| { - current_type.? = (try child.resolveType( + // TODO: Return all options instead of first valid one + // (this would require a huge rewrite and im lazy) + for (current_type_nodes) |ty| { + const current_type_node = switch (ty.type.data) { + .other => |n| n, + else => continue, + }; + + if (try lookupSymbolContainer( + arena, store, - &bound_type_params, - )) orelse return null; - } else return null; + .{ .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; + break; + } else continue; + } else { + return null; + } }, .question_mark => { current_type = (try resolveUnwrapOptionalType( + arena, store, current_type orelse return null, &bound_type_params, @@ -1320,6 +1411,7 @@ pub fn getFieldAccessType(store: *DocumentStore, handle: *const DocumentStore.Ha }, .period_asterisk => { current_type = (try resolveDerefType( + arena, store, current_type orelse return null, &bound_type_params, @@ -1346,7 +1438,7 @@ pub fn getFieldAccessType(store: *DocumentStore, handle: *const DocumentStore.Ha 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(store, func, current_type.?.handle, &bound_type_params, if (has_body) body else null)) |ret| { + if (try resolveReturnType(arena, store, func, current_type.?.handle, &bound_type_params, if (has_body) body else null)) |ret| { current_type = ret; // Skip to the right paren var paren_count: usize = 1; @@ -1377,7 +1469,7 @@ pub fn getFieldAccessType(store: *DocumentStore, handle: *const DocumentStore.Ha } } else return null; - current_type = (try resolveBracketAccessType(store, current_type orelse return null, if (is_range) .Range else .Single, &bound_type_params)) orelse 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; }, else => { log.debug("Unimplemented token: {}", .{tok.tag}); @@ -1389,7 +1481,7 @@ pub fn getFieldAccessType(store: *DocumentStore, handle: *const DocumentStore.Ha if (current_type) |ct| { return FieldAccessReturn{ .original = ct, - .unwrapped = try resolveDerefType(store, ct, &bound_type_params), + .unwrapped = try resolveDerefType(arena, store, ct, &bound_type_params), }; } else { return null; @@ -1991,12 +2083,13 @@ pub const DeclWithHandle = struct { }; } - pub fn resolveType(self: DeclWithHandle, store: *DocumentStore, bound_type_params: *BoundTypeParams) !?TypeWithHandle { + pub fn resolveType(self: DeclWithHandle, arena: std.mem.Allocator, store: *DocumentStore, bound_type_params: *BoundTypeParams) !?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, .{ .node = node, .handle = self.handle }, bound_type_params, @@ -2016,22 +2109,25 @@ pub const DeclWithHandle = struct { } } return ((try resolveTypeOfNodeInternal( + arena, store, .{ .node = param_decl.type_expr, .handle = self.handle }, bound_type_params, )) orelse return null).instanceTypeVal(); }, .pointer_payload => |pay| try resolveUnwrapOptionalType( + arena, store, - (try resolveTypeOfNodeInternal(store, .{ + (try resolveTypeOfNodeInternal(arena, store, .{ .node = pay.condition, .handle = self.handle, }, bound_type_params)) orelse return null, bound_type_params, ), .array_payload => |pay| try resolveBracketAccessType( + arena, store, - (try resolveTypeOfNodeInternal(store, .{ + (try resolveTypeOfNodeInternal(arena, store, .{ .node = pay.array_expr, .handle = self.handle, }, bound_type_params)) orelse return null, @@ -2046,7 +2142,7 @@ 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(store, .{ + const switch_expr_type = (try resolveTypeOfNodeInternal(arena, store, .{ .node = pay.switch_expr, .handle = self.handle, }, bound_type_params)) orelse return null; @@ -2065,6 +2161,7 @@ pub const DeclWithHandle = struct { if (switch_expr_type.handle.tree.fullContainerField(node)) |container_field| { if (container_field.ast.type_expr != 0) { return ((try resolveTypeOfNodeInternal( + arena, store, .{ .node = container_field.ast.type_expr, .handle = switch_expr_type.handle }, bound_type_params, @@ -2097,7 +2194,7 @@ fn findContainerScopeIndex(container_handle: NodeWithHandle) ?usize { } else null; } -fn iterateSymbolsContainerInternal(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(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 { const container = container_handle.node; const handle = container_handle.handle; @@ -2144,7 +2241,7 @@ fn iterateSymbolsContainerInternal(store: *DocumentStore, container_handle: Node try use_trail.append(use); const lhs = tree.nodes.items(.data)[use].lhs; - const use_expr = (try resolveTypeOfNode(store, .{ + const use_expr = (try resolveTypeOfNode(arena, store, .{ .node = lhs, .handle = handle, })) orelse continue; @@ -2154,6 +2251,7 @@ fn iterateSymbolsContainerInternal(store: *DocumentStore, container_handle: Node else => continue, }; try iterateSymbolsContainerInternal( + arena, store, .{ .node = use_expr_node, .handle = use_expr.handle }, orig_handle, @@ -2195,10 +2293,10 @@ pub fn iterateEnclosingScopes(document_scope: DocumentScope, source_index: usize }; } -pub fn iterateSymbolsContainer(store: *DocumentStore, container_handle: NodeWithHandle, orig_handle: *const DocumentStore.Handle, comptime callback: anytype, context: anytype, instance_access: bool) error{OutOfMemory}!void { +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(store, container_handle, orig_handle, callback, context, instance_access, &use_trail); + return try iterateSymbolsContainerInternal(arena, store, container_handle, orig_handle, callback, context, instance_access, &use_trail); } pub fn iterateLabels(handle: *const DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype) error{OutOfMemory}!void { @@ -2217,7 +2315,7 @@ pub fn iterateLabels(handle: *const DocumentStore.Handle, source_index: usize, c } } -fn iterateSymbolsGlobalInternal(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(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 { const scope_decls = handle.document_scope.scopes.items(.decls); const scope_uses = handle.document_scope.scopes.items(.uses); @@ -2236,6 +2334,7 @@ fn iterateSymbolsGlobalInternal(store: *DocumentStore, handle: *const DocumentSt try use_trail.append(use); const use_expr = (try resolveTypeOfNode( + arena, store, .{ .node = handle.tree.nodes.items(.data)[use].lhs, .handle = handle }, )) orelse continue; @@ -2244,6 +2343,7 @@ fn iterateSymbolsGlobalInternal(store: *DocumentStore, handle: *const DocumentSt else => continue, }; try iterateSymbolsContainerInternal( + arena, store, .{ .node = use_expr_node, .handle = use_expr.handle }, handle, @@ -2256,10 +2356,10 @@ fn iterateSymbolsGlobalInternal(store: *DocumentStore, handle: *const DocumentSt } } -pub fn iterateSymbolsGlobal(store: *DocumentStore, handle: *const DocumentStore.Handle, source_index: usize, comptime callback: anytype, context: anytype) error{OutOfMemory}!void { +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(store, handle, source_index, callback, context, &use_trail); + return try iterateSymbolsGlobalInternal(arena, store, handle, source_index, callback, context, &use_trail); } pub fn innermostBlockScopeIndex(handle: DocumentStore.Handle, source_index: usize) usize { @@ -2304,7 +2404,7 @@ pub fn innermostContainer(handle: *const DocumentStore.Handle, source_index: usi return TypeWithHandle.typeVal(.{ .node = current, .handle = handle }); } -fn resolveUse(store: *DocumentStore, uses: []const Ast.Node.Index, symbol: []const u8, handle: *const DocumentStore.Handle) error{OutOfMemory}!?DeclWithHandle { +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, @@ -2318,7 +2418,7 @@ fn resolveUse(store: *DocumentStore, uses: []const Ast.Node.Index, symbol: []con 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(store, expr)) orelse + const expr_type_node = (try resolveTypeOfNode(arena, store, expr)) orelse continue; const expr_type = .{ @@ -2329,7 +2429,7 @@ fn resolveUse(store: *DocumentStore, uses: []const Ast.Node.Index, symbol: []con .handle = expr_type_node.handle, }; - if (try lookupSymbolContainer(store, expr_type, symbol, false)) |candidate| { + if (try lookupSymbolContainer(arena, store, expr_type, symbol, false)) |candidate| { if (candidate.handle == handle or candidate.isPublic()) { return candidate; } @@ -2338,7 +2438,11 @@ fn resolveUse(store: *DocumentStore, uses: []const Ast.Node.Index, symbol: []con return null; } -pub fn lookupLabel(handle: *const DocumentStore.Handle, symbol: []const u8, source_index: usize) error{OutOfMemory}!?DeclWithHandle { +pub fn lookupLabel( + handle: *const DocumentStore.Handle, + symbol: []const u8, + source_index: usize, +) error{OutOfMemory}!?DeclWithHandle { const scope_decls = handle.document_scope.scopes.items(.decls); var scope_iterator = iterateEnclosingScopes(handle.document_scope, source_index); @@ -2357,7 +2461,7 @@ pub fn lookupLabel(handle: *const DocumentStore.Handle, symbol: []const u8, sour return null; } -pub fn lookupSymbolGlobal(store: *DocumentStore, handle: *const DocumentStore.Handle, symbol: []const u8, source_index: usize) error{OutOfMemory}!?DeclWithHandle { +pub fn lookupSymbolGlobal(arena: std.mem.Allocator, store: *DocumentStore, 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); @@ -2381,7 +2485,7 @@ pub fn lookupSymbolGlobal(store: *DocumentStore, handle: *const DocumentStore.Ha .handle = handle, }; } - if (try resolveUse(store, scope_uses[curr].items, symbol, handle)) |result| return result; + if (try resolveUse(arena, store, scope_uses[curr].items, symbol, handle)) |result| return result; } if (curr == 0) break; } @@ -2389,6 +2493,7 @@ pub fn lookupSymbolGlobal(store: *DocumentStore, handle: *const DocumentStore.Ha } pub fn lookupSymbolContainer( + arena: std.mem.Allocator, store: *DocumentStore, container_handle: NodeWithHandle, symbol: []const u8, @@ -2422,7 +2527,7 @@ pub fn lookupSymbolContainer( return DeclWithHandle{ .decl = candidate.value_ptr, .handle = handle }; } - if (try resolveUse(store, scope_uses[container_scope_index].items, symbol, handle)) |result| return result; + if (try resolveUse(arena, store, scope_uses[container_scope_index].items, symbol, handle)) |result| return result; } return null; @@ -2723,7 +2828,7 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i try scopes.append(allocator, .{ .loc = offsets.nodeToLoc(tree, node_idx), - .data = .{ .container = node_idx }, + .data = .{ .block = node_idx }, }); const scope_index = scopes.len - 1; @@ -2731,11 +2836,6 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i const statements = ast.blockStatements(tree, node_idx, &buffer).?; for (statements) |idx| { - if (tags[idx] == .@"usingnamespace") { - try scopes.items(.uses)[scope_index].append(allocator, idx); - continue; - } - try makeScopeInternal(allocator, context, idx); if (tree.fullVarDecl(idx)) |var_decl| { const name = tree.tokenSlice(var_decl.ast.mut_token + 1); diff --git a/src/code_actions.zig b/src/code_actions.zig index 680b5d6..caad37c 100644 --- a/src/code_actions.zig +++ b/src/code_actions.zig @@ -89,6 +89,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, builder.handle, identifier_name, @@ -134,6 +135,7 @@ fn handleUnusedVariableOrConstant(builder: *Builder, actions: *std.ArrayListUnma const token_starts = tree.tokens.items(.start); const decl = (try analysis.lookupSymbolGlobal( + builder.arena, builder.document_store, builder.handle, identifier_name, diff --git a/src/inlay_hints.zig b/src/inlay_hints.zig index 15da56e..5fc788c 100644 --- a/src/inlay_hints.zig +++ b/src/inlay_hints.zig @@ -81,7 +81,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.store, decl_handle.handle, fn_proto)) { + if (try analysis.hasSelfParam(builder.arena, builder.store, decl_handle.handle, fn_proto)) { _ = ast.nextFnParam(&it); } @@ -177,7 +177,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.store, handle, name, source_index)) |decl_handle| { + if (try analysis.lookupSymbolGlobal(builder.arena, builder.store, handle, name, source_index)) |decl_handle| { try writeCallHint(builder, call, decl_handle); } }, @@ -194,11 +194,12 @@ 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.store, handle, rhs_loc.end, &tokenizer)) |result| { + if (try analysis.getFieldAccessType(builder.arena, builder.store, 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, .{ .node = container_handle_node, .handle = container_handle.handle }, tree.tokenSlice(rhsToken), diff --git a/src/references.zig b/src/references.zig index 1b4f447..3df8596 100644 --- a/src/references.zig +++ b/src/references.zig @@ -99,6 +99,7 @@ const Builder = struct { const identifier_token = main_tokens[node]; const child = (try analysis.lookupSymbolGlobal( + builder.allocator, builder.store, handle, offsets.tokenToSlice(handle.tree, identifier_token), @@ -113,8 +114,10 @@ const Builder = struct { 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, @@ -128,6 +131,7 @@ const Builder = struct { }; const child = (try analysis.lookupSymbolContainer( + self.builder.allocator, builder.store, .{ .node = left_type_node, .handle = left_type.handle }, offsets.tokenToSlice(handle.tree, datas[node].rhs), diff --git a/src/semantic_tokens.zig b/src/semantic_tokens.zig index 4ebf189..c6fec9f 100644 --- a/src/semantic_tokens.zig +++ b/src/semantic_tokens.zig @@ -344,7 +344,7 @@ fn writeNodeTokens(builder: *Builder, maybe_node: ?Ast.Node.Index) error{OutOfMe try writeToken(builder, var_decl.comptime_token, .keyword); try writeToken(builder, var_decl.ast.mut_token, .keyword); - if (try analysis.resolveTypeOfNode(builder.store, .{ .node = node, .handle = handle })) |decl_type| { + if (try analysis.resolveTypeOfNode(allocator, builder.store, .{ .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 }); @@ -417,6 +417,7 @@ fn writeNodeTokens(builder: *Builder, maybe_node: ?Ast.Node.Index) error{OutOfMe } if (try analysis.lookupSymbolGlobal( + allocator, builder.store, handle, name, @@ -428,7 +429,7 @@ fn writeNodeTokens(builder: *Builder, maybe_node: ?Ast.Node.Index) error{OutOfMe var bound_type_params = analysis.BoundTypeParams{}; defer bound_type_params.deinit(builder.store.allocator); - if (try child.resolveType(builder.store, &bound_type_params)) |decl_type| { + if (try child.resolveType(allocator, builder.store, &bound_type_params)) |decl_type| { try colorIdentifierBasedOnType(builder, decl_type, main_token, .{}); } else { try writeTokenMod(builder, main_token, .variable, .{}); @@ -659,6 +660,7 @@ fn writeNodeTokens(builder: *Builder, maybe_node: ?Ast.Node.Index) error{OutOfMe try callWriteNodeTokens(allocator, .{ builder, struct_init.ast.type_expr }); field_token_type = if (try analysis.resolveTypeOfNode( + allocator, builder.store, .{ .node = struct_init.ast.type_expr, .handle = handle }, )) |struct_type| switch (struct_type.type.data) { @@ -878,8 +880,10 @@ fn writeNodeTokens(builder: *Builder, maybe_node: ?Ast.Node.Index) error{OutOfMe 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, @@ -891,6 +895,7 @@ fn writeNodeTokens(builder: *Builder, maybe_node: ?Ast.Node.Index) error{OutOfMe else => return, }; if (try analysis.lookupSymbolContainer( + allocator, builder.store, .{ .node = left_type_node, .handle = lhs_type.handle }, tree.tokenSlice(data.rhs), @@ -915,7 +920,7 @@ fn writeNodeTokens(builder: *Builder, maybe_node: ?Ast.Node.Index) error{OutOfMe else => {}, } - if (try decl_type.resolveType(builder.store, &bound_type_params)) |resolved_type| { + if (try decl_type.resolveType(allocator, builder.store, &bound_type_params)) |resolved_type| { try colorIdentifierBasedOnType(builder, resolved_type, data.rhs, .{}); } } diff --git a/src/signature_help.zig b/src/signature_help.zig index c1dbeda..6d5e3a9 100644 --- a/src/signature_help.zig +++ b/src/signature_help.zig @@ -15,7 +15,7 @@ fn fnProtoToSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocat const proto_comments = (try analysis.getDocComments(alloc, tree, fn_node, .markdown)) orelse ""; const arg_idx = if (skip_self_param) blk: { - const has_self_param = try analysis.hasSelfParam(document_store, handle, proto); + const has_self_param = try analysis.hasSelfParam(alloc, document_store, handle, proto); break :blk commas + @boolToInt(has_self_param); } else commas; @@ -257,6 +257,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, handle, expr_start, @@ -292,6 +293,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, .{ .node = node, .handle = type_handle.handle }, name, @@ -310,6 +312,7 @@ pub fn getSignatureInfo(document_store: *DocumentStore, alloc: std.mem.Allocator }; if (try analysis.resolveVarDeclAlias( + alloc, document_store, .{ .node = node, .handle = decl_handle.handle }, )) |resolved| {