Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions internal/cbm/lsp/c_lsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,15 @@ static void c_add_pending_template_call(CLSPContext* ctx, const char* func_qn,
ctx->pending_template_calls = new_arr;
ctx->pending_tc_cap = new_cap;
}
const char* func_qn_copy = cbm_arena_strdup(ctx->arena, func_qn);
const char* type_param_copy = cbm_arena_strdup(ctx->arena, type_param);
const char* method_name_copy = cbm_arena_strdup(ctx->arena, method_name);
if (!func_qn_copy || !type_param_copy || !method_name_copy) return;

int i = ctx->pending_tc_count++;
ctx->pending_template_calls[i].func_qn = cbm_arena_strdup(ctx->arena, func_qn);
ctx->pending_template_calls[i].type_param = cbm_arena_strdup(ctx->arena, type_param);
ctx->pending_template_calls[i].method_name = cbm_arena_strdup(ctx->arena, method_name);
ctx->pending_template_calls[i].func_qn = func_qn_copy;
ctx->pending_template_calls[i].type_param = type_param_copy;
ctx->pending_template_calls[i].method_name = method_name_copy;
ctx->pending_template_calls[i].arg_count = arg_count;
}

Expand All @@ -298,10 +303,16 @@ static void c_resolve_pending_template_calls(CLSPContext* ctx,
int tpn_count = 0;
while (tpn[tpn_count] && tpn_count < 8) tpn_count++;

// Match call arg types against function param types to deduce type params
// Match call arg types against function param types to deduce type params.
// The call site may contain more arguments than the parsed function signature
// knows about (invalid code, macros, variadic calls, or parser recovery). The
// signature arrays are NULL-terminated, so never index past the sentinel.
if (callee->signature && callee->signature->kind == CBM_TYPE_FUNC &&
callee->signature->data.func.param_types) {
for (int i = 0; i < call_arg_count; i++) {
int formal_count = 0;
while (callee->signature->data.func.param_types[formal_count]) formal_count++;
int limit = call_arg_count < formal_count ? call_arg_count : formal_count;
for (int i = 0; i < limit; i++) {
const CBMType* formal = callee->signature->data.func.param_types[i];
if (!formal || !call_arg_types[i]) continue;
// Unwrap references/pointers
Expand All @@ -327,6 +338,10 @@ static void c_resolve_pending_template_calls(CLSPContext* ctx,
const char* saved_func_qn = ctx->enclosing_func_qn;
ctx->enclosing_func_qn = callee->qualified_name;
for (int i = 0; i < ctx->pending_tc_count; i++) {
if (!ctx->pending_template_calls[i].func_qn ||
!ctx->pending_template_calls[i].type_param ||
!ctx->pending_template_calls[i].method_name)
continue;
if (strcmp(ctx->pending_template_calls[i].func_qn, callee->qualified_name) != 0)
continue;
const char* tp = ctx->pending_template_calls[i].type_param;
Expand Down
23 changes: 23 additions & 0 deletions tests/test_c_lsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,28 @@ TEST(clsp_nocrash_template_expression) {
PASS();
}

TEST(clsp_nocrash_template_extra_call_args) {
CBMFileResult *r = extract_cpp("\n"
"class Widget {\n"
"public:\n"
" void render() {}\n"
"};\n"
"\n"
"template<class T>\n"
"void invoke(T item) {\n"
" item.render();\n"
"}\n"
"\n"
"void test() {\n"
" Widget w;\n"
" invoke(w, 1, 2);\n"
"}\n"
"");
ASSERT_NOT_NULL(r);
cbm_free_result(r);
PASS();
}

TEST(clsp_nocrash_lambda) {
CBMFileResult *r = extract_cpp("\n"
"void test() {\n"
Expand Down Expand Up @@ -15101,6 +15123,7 @@ SUITE(c_lsp) {
RUN_TEST(clsp_operator_stream);
RUN_TEST(clsp_cross_file);
RUN_TEST(clsp_nocrash_template_expression);
RUN_TEST(clsp_nocrash_template_extra_call_args);
RUN_TEST(clsp_nocrash_lambda);
RUN_TEST(clsp_nocrash_nested_namespace);
RUN_TEST(clsp_nocrash_empty_source);
Expand Down