Branching type resolution (#1031)

* Branching type resolution

* Add condition information to completions (borked rn i give up)

* Fix completion conditional descriptor

* Multi gotodef

* Multi hover

* Reenable references

* Fix getAllTypesWithHandles
This commit is contained in:
Auguste Rame 2023-03-07 12:17:45 -05:00 committed by GitHub
parent 515a0e4727
commit 2ce59a3bc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 351 additions and 155 deletions

View File

@ -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;

View File

@ -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(&params, 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);

View File

@ -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,

View File

@ -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),

View File

@ -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),

View File

@ -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, .{});
}
}

View File

@ -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| {