fix(extract): restore LLVM IR call edges

Use the LLVM grammar's instruction_call, instruction_invoke, and instruction_callbr node names instead of dead call/invoke token names, so call extraction reaches real instruction nodes.

Teach the shared field-based callee resolver to read a callee field and unwrap simple value/var/expression wrappers, which keeps the fix reusable for grammars that expose a semantic callee field.

Add an exact LLVM IR canary for call void @callee(). Failed-first validation produced 246 passed / 1 failed before the fix; after the fix, extraction passes 247/247. Also passed git diff --check, scripts/check-source-safety.sh, and make -j8 -f Makefile.cbm build/c/test-runner.

Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
This commit is contained in:
Andrew Hundt
2026-07-01 09:45:48 -04:00
parent d5d5d3b7f4
commit f225dcbfe4
3 changed files with 31 additions and 1 deletions
+13
View File
@@ -312,6 +312,19 @@ static char *extract_callee_from_fields(CBMArena *a, TSNode node, const char *so
return method;
}
TSNode callee_node = ts_node_child_by_field_name(node, TS_FIELD("callee"));
if (!ts_node_is_null(callee_node)) {
while (ts_node_named_child_count(callee_node) == 1) {
const char *ck = ts_node_type(callee_node);
if (strcmp(ck, "value") != 0 && strcmp(ck, "var") != 0 &&
strcmp(ck, "expression") != 0) {
break;
}
callee_node = ts_node_named_child(callee_node, 0);
}
return cbm_node_text(a, callee_node, source);
}
return NULL;
}
+2 -1
View File
@@ -973,7 +973,8 @@ static const char *d_throw_types[] = {"throw_expression", NULL};
// ==================== LLVM IR ====================
static const char *llvm_func_types[] = {"function_header", NULL};
static const char *llvm_call_types[] = {"call", "invoke", NULL};
static const char *llvm_call_types[] = {"instruction_call", "instruction_invoke",
"instruction_callbr", NULL};
static const char *llvm_branch_types[] = {"br", "switch", NULL};
static const char *llvm_var_types[] = {"local_var", "global_var", NULL};
+16
View File
@@ -2207,6 +2207,21 @@ TEST(just_function_call_edge) {
PASS();
}
TEST(llvm_call_edge) {
CBMFileResult *r = extract("declare void @callee()\n"
"define void @caller() {\n"
"entry:\n"
" call void @callee()\n"
" ret void\n"
"}\n",
CBM_LANG_LLVM_IR, "test", "calls.ll");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_call_exact(r, "@callee"));
cbm_free_result(r);
PASS();
}
TEST(puppet_function_call_edge) {
CBMFileResult *r = extract("notice('ready')\n", CBM_LANG_PUPPET, "test", "site.pp");
ASSERT_NOT_NULL(r);
@@ -3445,6 +3460,7 @@ SUITE(extraction) {
RUN_TEST(makefile_variable_extraction);
RUN_TEST(makefile_builtin_call_edges);
RUN_TEST(just_function_call_edge);
RUN_TEST(llvm_call_edge);
RUN_TEST(puppet_function_call_edge);
RUN_TEST(vimscript_function_extraction);
RUN_TEST(vimscript_function_without_bang);