Correctly handle inferred error sets, add self argument support when evaluating bound type params, semanticToken scaffolding

This commit is contained in:
Alexandros Naskos 2020-06-13 21:20:04 +03:00
parent 5c78e88b19
commit 112f6d735a
4 changed files with 146 additions and 11 deletions

View File

@ -277,7 +277,14 @@ fn resolveReturnType(
}
return switch (fn_decl.return_type) {
.Explicit, .InferErrorSet => |return_type| try resolveTypeOfNodeInternal(store, arena, .{
.InferErrorSet => |return_type| block: {
const child_type = (try resolveTypeOfNodeInternal(store, arena, .{
.node = return_type,
.handle = handle,
}, bound_type_params)) orelse return null;
break :block NodeWithHandle{ .node = try makeErrorUnionType(arena, child_type.node), .handle = child_type.handle };
},
.Explicit => |return_type| try resolveTypeOfNodeInternal(store, arena, .{
.node = return_type,
.handle = handle,
}, bound_type_params),
@ -345,9 +352,22 @@ fn resolveDerefType(
return null;
}
fn makeSliceType(arena: *std.heap.ArenaAllocator, child_type: *ast.Node) ?*ast.Node {
fn makeErrorUnionType(arena: *std.heap.ArenaAllocator, child_type: *ast.Node) !*ast.Node {
// TODO: Better values for fields, better way to do this?
var slice_type = arena.allocator.create(ast.Node.PrefixOp) catch return null;
var error_union_type = try arena.allocator.create(ast.Node.InfixOp);
error_union_type.* = .{
.op_token = child_type.firstToken(),
.op = .ErrorUnion,
.lhs = child_type, // This is a dummy value
.rhs = child_type,
};
return &error_union_type.base;
}
fn makeSliceType(arena: *std.heap.ArenaAllocator, child_type: *ast.Node) !*ast.Node {
// TODO: Better values for fields, better way to do this?
var slice_type = try arena.allocator.create(ast.Node.PrefixOp);
slice_type.* = .{
.op_token = child_type.firstToken(),
.op = .{
@ -389,7 +409,7 @@ fn resolveBracketAccessType(
.node = pop.rhs,
.handle = lhs.handle,
}, bound_type_params);
return NodeWithHandle{ .node = makeSliceType(arena, pop.rhs) orelse return null, .handle = lhs.handle };
return NodeWithHandle{ .node = try makeSliceType(arena, pop.rhs), .handle = lhs.handle };
},
.PtrType => {
if (pop.rhs.cast(std.zig.ast.Node.PrefixOp)) |child_pop| {
@ -455,9 +475,17 @@ fn resolveTypeOfNodeInternal(
const decl = (try resolveTypeOfNodeInternal(store, arena, .{ .node = call.lhs, .handle = handle }, bound_type_params)) orelse return null;
if (decl.node.cast(ast.Node.FnProto)) |fn_decl| {
var has_self_param: u8 = 0;
if (call.lhs.cast(ast.Node.InfixOp)) |lhs_infix_op| {
if (lhs_infix_op.op == .Period) {
has_self_param = 1;
}
}
// Bidn type params to the expressions passed in the calls.
const param_len = std.math.min(call.params_len, fn_decl.params_len);
const param_len = std.math.min(call.params_len + has_self_param, fn_decl.params_len);
for (fn_decl.paramsConst()) |*decl_param, param_idx| {
if (param_idx < has_self_param) continue;
if (param_idx >= param_len) break;
const type_param = switch (decl_param.param_type) {
@ -467,11 +495,11 @@ fn resolveTypeOfNodeInternal(
if (!type_param) continue;
const call_param_type = (try resolveTypeOfNodeInternal(store, arena, .{
.node = call.paramsConst()[param_idx],
.node = call.paramsConst()[param_idx - has_self_param],
.handle = handle,
}, bound_type_params)) orelse continue;
try bound_type_params.putNoClobber(decl_param, call_param_type);
_ = try bound_type_params.put(decl_param, call_param_type);
}
return try resolveReturnType(store, arena, fn_decl, decl.handle, bound_type_params);

View File

@ -28,7 +28,7 @@ const ClientCapabilities = struct {
var client_capabilities = ClientCapabilities{};
const initialize_response =
\\,"result":{"capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":1,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"hoverProvider":true,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":false,"referencesProvider":false,"documentSymbolProvider":true,"colorProvider":false,"documentFormattingProvider":false,"documentRangeFormattingProvider":false,"foldingRangeProvider":false,"selectionRangeProvider":false,"workspaceSymbolProvider":false,"semanticTokensProvider":{"legend":{"tokenTypes":["type","struct","enum","parameter","variable","enumMember","function","member","keyword","modifier","comment","string","number","operator"],"tokenModifiers":["definition","async","documentation"]},"rangeProvider":false,"documentProvider":true},"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}}}}}
\\,"result":{"capabilities":{"signatureHelpProvider":{"triggerCharacters":["(",","]},"textDocumentSync":1,"completionProvider":{"resolveProvider":false,"triggerCharacters":[".",":","@"]},"documentHighlightProvider":false,"hoverProvider":true,"codeActionProvider":false,"declarationProvider":true,"definitionProvider":true,"typeDefinitionProvider":true,"implementationProvider":false,"referencesProvider":false,"documentSymbolProvider":true,"colorProvider":false,"documentFormattingProvider":false,"documentRangeFormattingProvider":false,"foldingRangeProvider":false,"selectionRangeProvider":false,"workspaceSymbolProvider":false,"rangeProvider":false,"documentProvider":true},"workspace":{"workspaceFolders":{"supported":true,"changeNotifications":true}},"semanticTokensProvider":{"documentProvider":true, "legend":{"tokenTypes":["type","struct","enum","union","parameter","variable","tagField","field","function","keyword","modifier","comment","string","number","operator"],"tokenModifiers":["definition","async","documentation"]}}}}
;
const not_implemented_response =
@ -947,8 +947,24 @@ fn processJsonRpc(parser: *std.json.Parser, json: []const u8, config: Config) !v
// Semantic highlighting
else if (std.mem.eql(u8, method, "textDocument/semanticTokens")) {
const params = root.Object.getValue("params").?.Object;
// TODO Implement this (we dont get here from vscode atm even when we get the client capab.)
return try respondGeneric(id, empty_array_response);
const document = params.getValue("textDocument").?.Object;
const uri = document.getValue("uri").?.String;
const handle = document_store.getHandle(uri) orelse {
std.debug.warn("Trying to complete in non existent document {}", .{uri});
return try respondGeneric(id, no_completions_response);
};
// TODO Actually test this in some editor, VSCode won't send me requests -_-'.
const semantic_tokens = @import("semantic_tokens.zig");
const token_array = try semantic_tokens.writeAllSemanticTokens(allocator, handle.*);
defer allocator.free(token_array);
return try send(types.Response{
.id = id,
.result = .{ .SemanticTokens = token_array },
});
}
// Autocomplete / Signatures
else if (std.mem.eql(u8, method, "textDocument/completion")) {

90
src/semantic_tokens.zig Normal file
View File

@ -0,0 +1,90 @@
const std = @import("std");
const DocumentStore = @import("document_store.zig");
const ast = std.zig.ast;
// ["type","struct","enum","union","parameter","variable","tagField","field","function","keyword","modifier","comment","string","number","operator"]
const TokenType = enum(u32) {
type,
@"struct",
@"enum",
@"union",
parameter,
variable,
tagField,
field,
function,
keyword,
modifier,
comment,
string,
number,
operator,
};
const TokenModifiers = packed struct {
definition: bool = false,
@"async": bool = false,
documentation: bool = false,
pub fn toInt(value: u32) TokenModifiers {
return @bitCast(TokenModifiers, value);
}
fn toInt(self: TokenModifiers) u32 {
return @bitCast(u32, self);
}
fn with(lhs: TokenModifiers, rhs: TokenModifiers) TokenModifiers {
return fromInt(toInt(lhs) | toInt(rhs));
}
fn intersect(lhs: TokenModifiers, rhs: TokenModifiers) TokenModifiers {
return fromInt(toInt(lhs) & toInt(rhs));
}
};
const Builder = struct {
tree: *ast.Tree,
current_token: ?ast.TokenIndex,
arr: std.ArrayList(u32),
fn printToken(start_idx: usize, token: ast.TokenIndex, token_type: TokenType, token_modifiers: TokenModifiers) !void {
const delta_loc = self.tree.tokenLocationLoc(start_idx, token_loc);
try out_stream.print(prefix ++ "{},{},{},{},{}", .{
// TODO Is +1 on the column here correct? I think so.
delta_loc.line, delta_loc.column + 1,
token_loc.end - token_loc.start, @enumToInt(token_type),
token_modifiers.toInt(),
});
}
fn create(allocator: *std.mem.Allocator, tree: *ast.Tree) Builder {
return Builder{
.tree = tree,
.current_token = null,
.arr = std.ArrayList(u32).init(allocator),
};
}
fn add(self: *Builder, out_stream: var, token: ast.TokenIndex, token_type: TokenType, token_modifiers: TokenModifiers) !void {
if (self.current_token) |current_token| {
std.debug.assert(token > current_token);
try out_stream.print(",");
try printToken(self.tree.token_locs[current_token].end, token, token_type, token_modifiers);
} else {
try printToken(0, token, token_type, token_modifiers);
}
self.current_token = token;
}
pub fn toOwnedSlice(self: *Builder) []u32 {
return self.arr.toOwnedSlice();
}
};
pub fn writeAllSemanticTokens(allocator: *std.mem.Allocator, handle: DocumentStore.Handle) ![]u32 {
// TODO Actual implementation
var builder = Builder.create(allocator, handle.tree);
return builder.toOwnedSlice();
}

View File

@ -58,7 +58,8 @@ pub const ResponseParams = union(enum) {
CompletionList: CompletionList,
Location: Location,
Hover: Hover,
DocumentSymbols: []DocumentSymbol
DocumentSymbols: []DocumentSymbol,
SemanticTokens: []u32,
};
/// JSONRPC error