fix: fix server crash related to parsing incomplete function as a function's parameter
fixes zigtools#567
This commit is contained in:
parent
8cf96fe27c
commit
54be6d92c6
@ -135,7 +135,7 @@ pub fn getFunctionSnippet(allocator: std.mem.Allocator, tree: Ast, func: Ast.ful
|
||||
|
||||
var it = func.iterate(&tree);
|
||||
var i: usize = 0;
|
||||
while (it.next()) |param| : (i += 1) {
|
||||
while (ast.nextFnParam(&it)) |param| : (i += 1) {
|
||||
if (skip_self_param and i == 0) continue;
|
||||
if (i != @boolToInt(skip_self_param))
|
||||
try buf_stream.writeAll(", ${")
|
||||
@ -172,7 +172,7 @@ pub fn getFunctionSnippet(allocator: std.mem.Allocator, tree: Ast, func: Ast.ful
|
||||
try buf_stream.print("{}", .{fmtSnippetPlaceholder(tree.tokenSlice(curr_token))});
|
||||
if (is_comma or tag == .keyword_const) try buf_stream.writeByte(' ');
|
||||
}
|
||||
} else unreachable;
|
||||
} // else Incomplete and that's ok :)
|
||||
|
||||
try buf_stream.writeByte('}');
|
||||
}
|
||||
@ -188,7 +188,7 @@ pub fn hasSelfParam(arena: *std.heap.ArenaAllocator, document_store: *DocumentSt
|
||||
|
||||
const tree = handle.tree;
|
||||
var it = func.iterate(&tree);
|
||||
const param = it.next().?;
|
||||
const param = ast.nextFnParam(&it).?;
|
||||
if (param.type_expr == 0) return false;
|
||||
|
||||
const token_starts = tree.tokens.items(.start);
|
||||
@ -245,7 +245,7 @@ pub fn isTypeFunction(tree: Ast, func: Ast.full.FnProto) bool {
|
||||
|
||||
pub fn isGenericFunction(tree: Ast, func: Ast.full.FnProto) bool {
|
||||
var it = func.iterate(&tree);
|
||||
while (it.next()) |param| {
|
||||
while (ast.nextFnParam(&it)) |param| {
|
||||
if (param.anytype_ellipsis3 != null or param.comptime_noalias != null) {
|
||||
return true;
|
||||
}
|
||||
@ -714,7 +714,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl
|
||||
var it = fn_decl.iterate(&decl.handle.tree);
|
||||
if (token_tags[call.ast.lparen - 2] == .period) {
|
||||
if (try hasSelfParam(arena, store, decl.handle, fn_decl)) {
|
||||
_ = it.next();
|
||||
_ = ast.nextFnParam(&it);
|
||||
expected_params -= 1;
|
||||
}
|
||||
}
|
||||
@ -722,7 +722,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl
|
||||
// Bind type params to the arguments passed in the call.
|
||||
const param_len = std.math.min(call.ast.params.len, expected_params);
|
||||
var i: usize = 0;
|
||||
while (it.next()) |decl_param| : (i += 1) {
|
||||
while (ast.nextFnParam(&it)) |decl_param| : (i += 1) {
|
||||
if (i >= param_len) break;
|
||||
if (!isMetaType(decl.handle.tree, decl_param.type_expr))
|
||||
continue;
|
||||
@ -808,6 +808,7 @@ pub fn resolveTypeOfNodeInternal(store: *DocumentStore, arena: *std.heap.ArenaAl
|
||||
},
|
||||
.field_access => {
|
||||
if (datas[node].rhs == 0) return null;
|
||||
if (node >= tree.nodes.len - 1) return null; // #boundsCheck
|
||||
const rhs_str = tree.tokenSlice(datas[node].rhs);
|
||||
// If we are accessing a pointer type, remove one pointerness level :)
|
||||
const left_type = try resolveFieldAccessLhsType(
|
||||
@ -2605,7 +2606,7 @@ fn makeScopeInternal(allocator: std.mem.Allocator, context: ScopeContext, node_i
|
||||
const scope_idx = scopes.items.len - 1;
|
||||
|
||||
var it = func.iterate(&tree);
|
||||
while (it.next()) |param| {
|
||||
while (ast.nextFnParam(&it)) |param| {
|
||||
// Add parameter decls
|
||||
if (param.name_token) |name_token| {
|
||||
if (try scopes.items[scope_idx].decls.fetchPut(
|
||||
|
96
src/ast.zig
96
src/ast.zig
@ -1119,3 +1119,99 @@ pub fn blockStatements(tree: Ast, node: Ast.Node.Index, buf: *[2]Ast.Node.Index)
|
||||
else => return null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Iterates over FnProto Params w/ added bounds check to support incomplete ast nodes
|
||||
pub fn nextFnParam(it: *Ast.full.FnProto.Iterator) ?Ast.full.FnProto.Param {
|
||||
const token_tags = it.tree.tokens.items(.tag);
|
||||
while (true) {
|
||||
var first_doc_comment: ?Ast.TokenIndex = null;
|
||||
var comptime_noalias: ?Ast.TokenIndex = null;
|
||||
var name_token: ?Ast.TokenIndex = null;
|
||||
if (!it.tok_flag) {
|
||||
if (it.param_i >= it.fn_proto.ast.params.len) {
|
||||
return null;
|
||||
}
|
||||
const param_type = it.fn_proto.ast.params[it.param_i];
|
||||
var tok_i = it.tree.firstToken(param_type) - 1;
|
||||
while (true) : (tok_i -= 1) switch (token_tags[tok_i]) {
|
||||
.colon => continue,
|
||||
.identifier => name_token = tok_i,
|
||||
.doc_comment => first_doc_comment = tok_i,
|
||||
.keyword_comptime, .keyword_noalias => comptime_noalias = tok_i,
|
||||
else => break
|
||||
};
|
||||
it.param_i += 1;
|
||||
it.tok_i = it.tree.lastToken(param_type) + 1;
|
||||
|
||||
// #boundsCheck
|
||||
// https://github.com/zigtools/zls/issues/567
|
||||
if (it.tree.lastToken(param_type) >= it.tree.tokens.len - 1)
|
||||
return Ast.full.FnProto.Param{
|
||||
.first_doc_comment = first_doc_comment,
|
||||
.comptime_noalias = comptime_noalias,
|
||||
.name_token = name_token,
|
||||
.anytype_ellipsis3 = null,
|
||||
.type_expr = 0,
|
||||
};
|
||||
|
||||
// Look for anytype and ... params afterwards.
|
||||
if (token_tags[it.tok_i] == .comma) {
|
||||
it.tok_i += 1;
|
||||
}
|
||||
it.tok_flag = true;
|
||||
return Ast.full.FnProto.Param{
|
||||
.first_doc_comment = first_doc_comment,
|
||||
.comptime_noalias = comptime_noalias,
|
||||
.name_token = name_token,
|
||||
.anytype_ellipsis3 = null,
|
||||
.type_expr = param_type,
|
||||
};
|
||||
}
|
||||
if (token_tags[it.tok_i] == .comma) {
|
||||
it.tok_i += 1;
|
||||
}
|
||||
if (token_tags[it.tok_i] == .r_paren) {
|
||||
return null;
|
||||
}
|
||||
if (token_tags[it.tok_i] == .doc_comment) {
|
||||
first_doc_comment = it.tok_i;
|
||||
while (token_tags[it.tok_i] == .doc_comment) {
|
||||
it.tok_i += 1;
|
||||
}
|
||||
}
|
||||
switch (token_tags[it.tok_i]) {
|
||||
.ellipsis3 => {
|
||||
it.tok_flag = false; // Next iteration should return null.
|
||||
return Ast.full.FnProto.Param{
|
||||
.first_doc_comment = first_doc_comment,
|
||||
.comptime_noalias = null,
|
||||
.name_token = null,
|
||||
.anytype_ellipsis3 = it.tok_i,
|
||||
.type_expr = 0,
|
||||
};
|
||||
},
|
||||
.keyword_noalias, .keyword_comptime => {
|
||||
comptime_noalias = it.tok_i;
|
||||
it.tok_i += 1;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
if (token_tags[it.tok_i] == .identifier and
|
||||
token_tags[it.tok_i + 1] == .colon)
|
||||
{
|
||||
name_token = it.tok_i;
|
||||
it.tok_i += 2;
|
||||
}
|
||||
if (token_tags[it.tok_i] == .keyword_anytype) {
|
||||
it.tok_i += 1;
|
||||
return Ast.full.FnProto.Param{
|
||||
.first_doc_comment = first_doc_comment,
|
||||
.comptime_noalias = comptime_noalias,
|
||||
.name_token = name_token,
|
||||
.anytype_ellipsis3 = it.tok_i - 1,
|
||||
.type_expr = 0,
|
||||
};
|
||||
}
|
||||
it.tok_flag = false;
|
||||
}
|
||||
}
|
||||
|
@ -106,10 +106,10 @@ fn writeCallHint(builder: *Builder, arena: *std.heap.ArenaAllocator, store: *Doc
|
||||
var it = fn_proto.iterate(&decl_tree);
|
||||
|
||||
if (try analysis.hasSelfParam(arena, store, decl_handle.handle, fn_proto)) {
|
||||
_ = it.next();
|
||||
_ = ast.nextFnParam(&it);
|
||||
}
|
||||
|
||||
while (it.next()) |param| : (i += 1) {
|
||||
while (ast.nextFnParam(&it)) |param| : (i += 1) {
|
||||
if (param.name_token == null) continue;
|
||||
if (i >= call.ast.params.len) break;
|
||||
|
||||
@ -577,7 +577,7 @@ fn writeNodeInlayHint(builder: *Builder, arena: *std.heap.ArenaAllocator, store:
|
||||
const fn_proto: Ast.full.FnProto = ast.fnProto(tree, node, &buffer).?;
|
||||
|
||||
var it = fn_proto.iterate(&tree);
|
||||
while (it.next()) |param_decl| {
|
||||
while (ast.nextFnParam(&it)) |param_decl| {
|
||||
try await @asyncCall(child_frame, {}, writeNodeInlayHint, .{ builder, arena, store, param_decl.type_expr, range });
|
||||
}
|
||||
|
||||
|
@ -136,7 +136,7 @@ fn symbolReferencesInternal(arena: *std.heap.ArenaAllocator, store: *DocumentSto
|
||||
var buf: [1]Ast.Node.Index = undefined;
|
||||
const fn_proto = ast.fnProto(tree, node, &buf).?;
|
||||
var it = fn_proto.iterate(&tree);
|
||||
while (it.next()) |param| {
|
||||
while (ast.nextFnParam(&it)) |param| {
|
||||
if (param.type_expr != 0)
|
||||
try symbolReferencesInternal(arena, store, .{ .node = param.type_expr, .handle = handle }, decl, encoding, context, handler);
|
||||
}
|
||||
@ -527,7 +527,7 @@ pub fn symbolReferences(arena: *std.heap.ArenaAllocator, store: *DocumentStore,
|
||||
var buf: [1]Ast.Node.Index = undefined;
|
||||
const fn_proto = ast.fnProto(curr_handle.tree, proto, &buf).?;
|
||||
var it = fn_proto.iterate(&curr_handle.tree);
|
||||
while (it.next()) |candidate| {
|
||||
while (ast.nextFnParam(&it)) |candidate| {
|
||||
if (std.meta.eql(candidate, param)) {
|
||||
if (curr_handle.tree.nodes.items(.tag)[proto] == .fn_decl) {
|
||||
try symbolReferencesInternal(
|
||||
|
@ -462,7 +462,7 @@ fn writeNodeTokens(builder: *Builder, arena: *std.heap.ArenaAllocator, store: *D
|
||||
try writeTokenMod(builder, fn_proto.name_token, func_name_tok_type, tok_mod);
|
||||
|
||||
var it = fn_proto.iterate(&tree);
|
||||
while (it.next()) |param_decl| {
|
||||
while (ast.nextFnParam(&it)) |param_decl| {
|
||||
if (param_decl.first_doc_comment) |docs| try writeDocComments(builder, tree, docs);
|
||||
|
||||
try writeToken(builder, param_decl.comptime_noalias, .keyword);
|
||||
|
@ -24,7 +24,7 @@ fn fnProtoToSignatureInfo(document_store: *DocumentStore, arena: *std.heap.Arena
|
||||
|
||||
var params = std.ArrayListUnmanaged(ParameterInformation){};
|
||||
var param_it = proto.iterate(&tree);
|
||||
while (param_it.next()) |param| {
|
||||
while (ast.nextFnParam(¶m_it)) |param| {
|
||||
const param_comments = if (param.first_doc_comment) |dc|
|
||||
try analysis.collectDocComments(alloc, tree, dc, .Markdown, false)
|
||||
else
|
||||
|
Loading…
Reference in New Issue
Block a user