A lot of fixes

This commit is contained in:
Luuk de Gram 2021-02-27 16:38:06 +01:00
parent 0133f0d863
commit 72605c7549
No known key found for this signature in database
GPG Key ID: A002B174963DBB7D
2 changed files with 724 additions and 626 deletions

View File

@ -5,17 +5,33 @@ const types = @import("types.zig");
const offsets = @import("offsets.zig"); const offsets = @import("offsets.zig");
const log = std.log.scoped(.analysis); const log = std.log.scoped(.analysis);
/// Get a declaration's doc comment node /// Get a declaration's doc comment token index
pub fn getDocCommentNode(tree: ast.Tree, node: *ast.Node) ?*ast.Node.DocComment { pub fn getDocCommentTokenIndex(tree: ast.Tree, node: ast.Node.Index) ?ast.TokenIndex {
if (node.castTag(.FnProto)) |func| { const tags = tree.nodes.items(.tag);
return func.getDocComments(); const tokens = tree.tokens.items(.tag);
} else if (node.castTag(.VarDecl)) |var_decl| { const current = tree.nodes.items(.main_token)[node];
return var_decl.getDocComments();
} else if (node.castTag(.ContainerField)) |field| { switch (tags[node]) {
return field.doc_comments; .fn_proto, .fn_proto_one, .fn_proto_simple, .fn_proto_multi => {
} else if (node.castTag(.ErrorTag)) |tag| { var idx = current - 1;
return tag.doc_comments; idx -= @boolToInt(tokens[idx] == .keyword_extern);
idx -= @boolToInt(tokens[idx] == .keyword_pub);
return if (tokens[idx] == .doc_comment) idx else null;
},
.local_var_decl, .global_var_decl, .aligned_var_decl, .simple_var_decl => {
return if (tokens[current - 1] == .doc_comment) current - 1 else null;
},
.container_field, .container_field_init, .container_field_align => {
var idx = current - 2; // skip '.'
return if (tokens[idx] == .doc_comment) idx else null;
},
else => return null,
} }
// @TODO: Implement doc comments for tags
// } else if (node.castTag(.ErrorTag)) |tag| {
// return tag.doc_comments;
// }
return null; return null;
} }
@ -28,11 +44,11 @@ pub fn getDocCommentNode(tree: ast.Tree, node: *ast.Node) ?*ast.Node.DocComment
pub fn getDocComments( pub fn getDocComments(
allocator: *std.mem.Allocator, allocator: *std.mem.Allocator,
tree: ast.Tree, tree: ast.Tree,
node: *ast.Node, node: ast.Node.Index,
format: types.MarkupContent.Kind, format: types.MarkupContent.Kind,
) !?[]const u8 { ) !?[]const u8 {
if (getDocCommentNode(tree, node)) |doc_comment_node| { if (getDocCommentTokenIndex(tree, node)) |doc_comment_index| {
return try collectDocComments(allocator, tree, doc_comment_node, format); return try collectDocComments(allocator, tree, doc_comment_index, format);
} }
return null; return null;
} }
@ -40,17 +56,19 @@ pub fn getDocComments(
pub fn collectDocComments( pub fn collectDocComments(
allocator: *std.mem.Allocator, allocator: *std.mem.Allocator,
tree: ast.Tree, tree: ast.Tree,
doc_comments: *ast.Node.DocComment, doc_comments: ast.TokenIndex,
format: types.MarkupContent.Kind, format: types.MarkupContent.Kind,
) ![]const u8 { ) ![]const u8 {
var lines = std.ArrayList([]const u8).init(allocator); var lines = std.ArrayList([]const u8).init(allocator);
defer lines.deinit(); defer lines.deinit();
var curr_line_tok = doc_comments.first_line; const token_tags = tree.tokens.items(.tag);
const loc = tree.tokenLocation(0, doc_comments);
var curr_line_tok = doc_comments;
while (true) : (curr_line_tok += 1) { while (true) : (curr_line_tok += 1) {
switch (tree.token_ids[curr_line_tok]) { switch (token_tags[curr_line_tok]) {
.LineComment => continue, .doc_comment, .container_doc_comment => {
.DocComment, .ContainerDocComment => {
try lines.append(std.mem.trim(u8, tree.tokenSlice(curr_line_tok)[3..], &std.ascii.spaces)); try lines.append(std.mem.trim(u8, tree.tokenSlice(curr_line_tok)[3..], &std.ascii.spaces));
}, },
else => break, else => break,
@ -61,20 +79,15 @@ pub fn collectDocComments(
} }
/// Gets a function signature (keywords, name, return value) /// Gets a function signature (keywords, name, return value)
pub fn getFunctionSignature(tree: ast.Tree, func: *ast.Node.FnProto) []const u8 { pub fn getFunctionSignature(tree: ast.Tree, func: *ast.full.FnProto) []const u8 {
const start = tree.token_locs[func.firstToken()].start; const start = tree.tokenLocation(func.ast.fn_token).line_start;
const end = tree.token_locs[ const end = tree.tokenLocation(func.ast.return_type).line_end;
switch (func.return_type) {
.Explicit, .InferErrorSet => |node| node.lastToken(),
.Invalid => |r_paren| r_paren,
}
].end;
return tree.source[start..end]; return tree.source[start..end];
} }
/// Gets a function snippet insert text /// Gets a function snippet insert text
pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: ast.Tree, func: *ast.Node.FnProto, skip_self_param: bool) ![]const u8 { pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: ast.Tree, func: *ast.full.FnProto, skip_self_param: bool) ![]const u8 {
const name_tok = func.getNameToken() orelse unreachable; const name_index = func.name_token orelse unreachable;
var buffer = std.ArrayList(u8).init(allocator); var buffer = std.ArrayList(u8).init(allocator);
try buffer.ensureCapacity(128); try buffer.ensureCapacity(128);
@ -84,17 +97,19 @@ pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: ast.Tree, func: *
var buf_stream = buffer.writer(); var buf_stream = buffer.writer();
for (func.paramsConst()) |param, param_num| { const token_tags = tree.tokens.items(.tag);
if (skip_self_param and param_num == 0) continue;
if (param_num != @boolToInt(skip_self_param)) try buffer.appendSlice(", ${") else try buffer.appendSlice("${");
try buf_stream.print("{}:", .{param_num + 1}); var it = func.iterate(tree);
while (it.next()) |param| {
if (skip_self_param and it.param_i == 0) continue;
if (it.param_i != @boolToInt(skip_self_param)) try buffer.appendSlice(", ${") else try buffer.appendSlice("${");
if (param.comptime_token) |_| { try buf_stream.print("{d}", .{it.param_i + 1});
try buffer.appendSlice("comptime ");
}
if (param.noalias_token) |_| { if (param.comptime_noalias) |token_index| {
if (token_tags[token_index] == .keyword_comptime)
try buffer.appendSlice("comptime ")
else
try buffer.appendSlice("noalias "); try buffer.appendSlice("noalias ");
} }
@ -103,23 +118,23 @@ pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: ast.Tree, func: *
try buffer.appendSlice(": "); try buffer.appendSlice(": ");
} }
switch (param.param_type) { if (param.anytype_ellipsis3) |token_index| {
.any_type => try buffer.appendSlice("anytype"), if (token_tags[token_index] == .keyword_anytype)
.type_expr => |type_expr| { try buffer.appendSlice("anytype")
var curr_tok = type_expr.firstToken(); else
var end_tok = type_expr.lastToken(); try buffer.appendSlice("...");
while (curr_tok <= end_tok) : (curr_tok += 1) { } else {
const id = tree.token_ids[curr_tok]; var curr_token = param.type_expr;
const is_comma = id == .Comma; var end_token = tree.lastToken(func.ast.params[it.param_i]);
while (curr_token <= end_token) : (curr_token += 1) {
const tag = token_tags[curr_token];
const is_comma = tag == .comma;
if (curr_tok == end_tok and is_comma) continue; if (curr_token == end_token and is_comma) continue;
try buffer.appendSlice(tree.tokenSlice(curr_token));
try buffer.appendSlice(tree.tokenSlice(curr_tok)); if (is_comma or tag == .Keyword_const) try buffer.append(' ');
if (is_comma or id == .Keyword_const) try buffer.append(' ');
} }
},
} }
try buffer.append('}'); try buffer.append('}');
} }
try buffer.append(')'); try buffer.append(')');
@ -128,16 +143,16 @@ pub fn getFunctionSnippet(allocator: *std.mem.Allocator, tree: ast.Tree, func: *
} }
/// Gets a function signature (keywords, name, return value) /// Gets a function signature (keywords, name, return value)
pub fn getVariableSignature(tree: ast.Tree, var_decl: *ast.Node.VarDecl) []const u8 { pub fn getVariableSignature(tree: ast.Tree, var_decl: *ast.full.VarDecl) []const u8 {
const start = tree.token_locs[var_decl.firstToken()].start; const start = tree.tokenLocation(0, var_decl.ast.mut_token).line_start;
const end = tree.token_locs[var_decl.semicolon_token].start; const end = tree.tokenLocation(@truncate(u32, start), tree.lastToken(var_decl.ast.init_node)).line_end;
return tree.source[start..end]; return tree.source[start..end];
} }
// analysis.getContainerFieldSignature(handle.tree, field) // analysis.getContainerFieldSignature(handle.tree, field)
pub fn getContainerFieldSignature(tree: ast.Tree, field: *ast.Node.ContainerField) []const u8 { pub fn getContainerFieldSignature(tree: ast.Tree, field: *ast.full.ContainerField) []const u8 {
const start = tree.token_locs[field.firstToken()].start; const start = tree.tokenLocation(0, field.ast.name_token).line_start;
const end = tree.token_locs[field.lastToken()].end; const end = tree.tokenLocation(@truncate(u32, start), tree.lastToken(field.ast.value_expr)).line_start;
return tree.source[start..end]; return tree.source[start..end];
} }
@ -150,7 +165,7 @@ fn typeIsType(tree: ast.Tree, node: ast.Node.Index) bool {
} }
pub fn isTypeFunction(tree: ast.Tree, func: ast.full.FnProto) bool { pub fn isTypeFunction(tree: ast.Tree, func: ast.full.FnProto) bool {
return typeIsType(func.ast.return_type); return typeIsType(tree, func.ast.return_type);
} }
pub fn isGenericFunction(tree: ast.Tree, func: *ast.full.FnProto) bool { pub fn isGenericFunction(tree: ast.Tree, func: *ast.full.FnProto) bool {
@ -1570,7 +1585,9 @@ pub fn getDocumentSymbols(allocator: *std.mem.Allocator, tree: ast.Tree, encodin
} }
pub const Declaration = union(enum) { pub const Declaration = union(enum) {
ast_node: ast.Node, /// Index of the ast node
ast_node: ast.Node.Index,
/// Function parameter
param_decl: ast.full.FnProto.Param, param_decl: ast.full.FnProto.Param,
pointer_payload: struct { pointer_payload: struct {
node: ast.full.PtrType, node: ast.full.PtrType,
@ -2062,15 +2079,15 @@ pub const DocumentScope = struct {
pub const Scope = struct { pub const Scope = struct {
pub const Data = union(enum) { pub const Data = union(enum) {
container: *ast.Node, // .id is ContainerDecl or Root or ErrorSetDecl container: ast.Node.Index, // .tag is ContainerDecl or Root or ErrorSetDecl
function: *ast.Node, // .id is FnProto function: ast.Node.Index, // .tag is FnProto
block: *ast.Node, // .id is Block block: ast.Node.Index, // .tag is Block
other, other,
}; };
range: SourceRange, range: SourceRange,
decls: std.StringHashMap(Declaration), decls: std.StringHashMap(Declaration),
tests: []const *ast.Node, tests: []const ast.Node.Index,
// uses: []const *ast.Node.Data, // uses: []const *ast.Node.Data,
data: Data, data: Data,
@ -2088,8 +2105,8 @@ pub fn makeDocumentScope(allocator: *std.mem.Allocator, tree: ast.Tree) !Documen
for (enum_completions.items) |item| if (item.documentation) |doc| allocator.free(doc.value); for (enum_completions.items) |item| if (item.documentation) |doc| allocator.free(doc.value);
enum_completions.deinit(allocator); enum_completions.deinit(allocator);
} }
// pass root node index ('0')
try makeScopeInternal(allocator, &scopes, &error_completions, &enum_completions, tree); try makeScopeInternal(allocator, &scopes, &error_completions, &enum_completions, tree, 0);
return DocumentScope{ return DocumentScope{
.scopes = scopes.toOwnedSlice(allocator), .scopes = scopes.toOwnedSlice(allocator),
.error_completions = error_completions.toOwnedSlice(allocator), .error_completions = error_completions.toOwnedSlice(allocator),
@ -2105,6 +2122,51 @@ fn nodeSourceRange(tree: ast.Tree, node: ast.Node.Index) SourceRange {
}; };
} }
fn isContainer(tag: ast.Node.Tag) bool {
return switch (tag) {
.container_decl,
.container_decl_trailing,
.container_decl_arg,
.container_decl_arg_trailing,
.container_decl_two,
.container_decl_two_trailing,
.tagged_union,
.tagged_union_trailing,
.tagged_union_two,
.tagged_union_two_trailing,
.tagged_union_enum_tag,
.tagged_union_enum_tag_trailing,
.root,
.error_set_decl,
=> true,
else => false,
};
}
/// Returns the member indices of a given declaration container.
/// Asserts given `tag` is a container node
fn declMembers(tree: ast.Tree, tag: ast.Node.Tag) []ast.Node.index {
std.debug.assert(isContainer(tag));
return switch (tag) {
.container_decl, .container_decl_trailing => tree.containerDecl(node_idx).ast.members,
.container_decl_arg, .container_decl_arg_trailing => tree.containerDeclArg(node_idx).ast.members,
.container_decl_two, .container_decl_two_trailing => blk: {
var buffer: [2]ast.Node.Index = undefined;
break :blk tree.containerDeclTwo(&buffer, node_idx).ast.members;
},
.tagged_union, .tagged_union_trailing => tree.taggedUnion(node_idx).ast.members,
.tagged_union_enum_tag, .tagged_union_enum_tag_trailing => tree.taggedUnionEnumTag(node_idx).ast.members,
.tagged_union_two, .tagged_union_two_trailing => blk: {
var buffer: [2]ast.Node.Index = undefined;
break :blk tree.taggedUnionTwo(&buffer, node_idx).ast.members;
},
.root => tree.rootDecls(),
// @TODO: Fix error set declarations
.error_set_decl => &[_]ast.Node.Index{},
else => unreachable,
};
}
// TODO Possibly collect all imports to diff them on changes // TODO Possibly collect all imports to diff them on changes
// as well // as well
fn makeScopeInternal( fn makeScopeInternal(
@ -2115,27 +2177,23 @@ fn makeScopeInternal(
tree: ast.Tree, tree: ast.Tree,
node_idx: ast.Node.Index, node_idx: ast.Node.Index,
) error{OutOfMemory}!void { ) error{OutOfMemory}!void {
const nodes = tree.nodes.items(.tag); const tags = tree.nodes.items(.tag);
const node = nodes[node_idx]; const token_tags = tree.tokens.items(.tag);
if (node == .root or node == .container_decl or node == .error_set_decl) { const node = tags[node_idx];
const ast_decls = switch (node) {
.container_decl => tree.containerDecl(node_idx).ast.members, if (isContainer(node)) {
.root => tree.rootDecls(), const ast_decls = declMembers(tree, node);
// @TODO: Fix error set declarations
// .error_set_decl => node.castTag(.ErrorSetDecl).?.declsConst(),
else => unreachable,
};
(try scopes.addOne(allocator)).* = .{ (try scopes.addOne(allocator)).* = .{
.range = nodeSourceRange(tree, node), .range = nodeSourceRange(tree, node_idx),
.decls = std.StringHashMap(Declaration).init(allocator), .decls = std.StringHashMap(Declaration).init(allocator),
// .uses = &[0]*ast.Node.Use{}, // .uses = &[0]*ast.Node.Use{},
.tests = &[0]*ast.Node{}, .tests = &[0]*ast.Node{},
.data = .{ .container = node }, .data = .{ .container = node_idx },
}; };
const scope_idx = scopes.items.len - 1; const scope_idx = scopes.items.len - 1;
// var uses = std.ArrayList(*ast.Node.Use).init(allocator); // var uses = std.ArrayList(*ast.Node.Use).init(allocator);
var tests = std.ArrayList(*ast.Node).init(allocator); var tests = std.ArrayList(ast.Node.Index).init(allocator);
errdefer { errdefer {
scopes.items[scope_idx].decls.deinit(); scopes.items[scope_idx].decls.deinit();
@ -2144,6 +2202,7 @@ fn makeScopeInternal(
} }
for (ast_decls) |decl| { for (ast_decls) |decl| {
// @TODO: Implement using namespace
// if (decl.castTag(.Use)) |use| { // if (decl.castTag(.Use)) |use| {
// try uses.append(use); // try uses.append(use);
// continue; // continue;
@ -2151,12 +2210,13 @@ fn makeScopeInternal(
try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, decl); try makeScopeInternal(allocator, scopes, error_completions, enum_completions, tree, decl);
const name = getDeclName(tree, decl) orelse continue; const name = getDeclName(tree, decl) orelse continue;
if (decl.tag == .TestDecl) { // @TODO: implement tests
try tests.append(decl); // if (decl.tag == .TestDecl) {
continue; // try tests.append(decl);
} // continue;
// }
if (node.tag == .ErrorSetDecl) { if (tags[decl] == .error_set_decl) {
(try error_completions.addOne(allocator)).* = .{ (try error_completions.addOne(allocator)).* = .{
.label = name, .label = name,
.kind = .Constant, .kind = .Constant,
@ -2167,15 +2227,39 @@ fn makeScopeInternal(
}; };
} }
if (decl.castTag(.ContainerField)) |field| { const container_field: ?ast.full.ContainerField = switch (decl) {
const empty_field = field.type_expr == null and field.value_expr == null; .container_field => tree.containerField(decl),
if (empty_field and node.tag == .Root) { .container_field_align => tree.containerFieldAlign(decl),
.container_field_init => tree.containerFieldInit(decl),
else => null,
};
if (container_field) |field| {
const empty_field = field.type_expr == 0 and field.value_expr == 0;
if (empty_field and node == .root) {
continue; continue;
} }
if (node.castTag(.ContainerDecl)) |container| { // @TODO: We can probably just use node_idx directly instead of first transforming to container
const kind = tree.token_ids[container.kind_token]; const container_decl: ?ast.full.ContainerDecl = switch (node) {
if (empty_field and (kind == .Keyword_struct or (kind == .Keyword_union and container.init_arg_expr == .None))) { .container_decl, .container_decl_trailing => tree.containerDecl(node_idx),
.container_decl_arg, .container_decl_arg_trailing => tree.containerDeclArg(node_idx),
.container_decl_two, .container_decl_two_trailing => blk: {
var buffer: [2]ast.Node.Index = undefined;
break :blk tree.containerDeclTwo(&buffer, node_idx);
},
.tagged_union, .tagged_union_trailing => tree.taggedUnion(node_idx),
.tagged_union_enum_tag, .tagged_union_enum_tag_trailing => tree.taggedUnionEnumTag(node_idx),
.tagged_union_enum_tag, .tagged_union_enum_tag_trailing => blk: {
var buffer: [2]ast.Node.Index = undefined;
break :blk tree.taggedUnionTwo(&buffer, node_idx);
},
else => null,
};
if (container_decl) |container| {
const kind = token_tags[container.ast.main_token];
if (empty_field and (kind == .keyword_struct or (kind == .keyword_union and container.ast.arg == 0))) {
continue; continue;
} }
@ -2183,7 +2267,7 @@ fn makeScopeInternal(
(try enum_completions.addOne(allocator)).* = .{ (try enum_completions.addOne(allocator)).* = .{
.label = name, .label = name,
.kind = .Constant, .kind = .Constant,
.documentation = if (try getDocComments(allocator, tree, decl, .Markdown)) |docs| .documentation = if (try getDocComments(allocator, tree, node_idx, .Markdown)) |docs|
.{ .kind = .Markdown, .value = docs } .{ .kind = .Markdown, .value = docs }
else else
null, null,
@ -2202,7 +2286,7 @@ fn makeScopeInternal(
return; return;
} }
switch (node.tag) { switch (node) {
.FnProto => { .FnProto => {
const func = node.castTag(.FnProto).?; const func = node.castTag(.FnProto).?;

File diff suppressed because it is too large Load Diff