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