fix(extract): restore HDL call edges

Add a shared Verilog/SystemVerilog leaf-identifier resolver for wrapped subroutine and tf_call nodes, and include SystemVerilog subroutine_call nodes exposed by the vendored grammar.

Evidence: AST probes confirmed the wrapper shapes, failed-first canaries produced 252 passed / 2 failed before the fix, and the focused extraction suite now passes 254/254.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
This commit is contained in:
Andrew Hundt
2026-07-01 10:39:06 -04:00
parent 9e5aaa1759
commit 6044e19e7e
3 changed files with 68 additions and 1 deletions
+31
View File
@@ -834,6 +834,33 @@ static char *extract_vhdl_callee(CBMArena *a, TSNode node, const char *source, c
return NULL;
}
static char *extract_first_leaf_identifier(CBMArena *a, TSNode node, const char *source) {
TSNode cur = node;
enum { CALLEE_LEAF_DESCENT_LIMIT = 8 };
for (int depth = 0; depth < CALLEE_LEAF_DESCENT_LIMIT && !ts_node_is_null(cur); depth++) {
const char *k = ts_node_type(cur);
if (strcmp(k, "simple_identifier") == 0 || strcmp(k, "identifier") == 0 ||
strcmp(k, "word") == 0 || strcmp(k, "name") == 0 || strcmp(k, "qid") == 0) {
char *t = cbm_node_text(a, cur, source);
return (t && t[0]) ? t : NULL;
}
if (ts_node_named_child_count(cur) == 0) {
return NULL;
}
cur = ts_node_named_child(cur, 0);
}
return NULL;
}
// HDL callees are often wrapped by subroutine/tf/hierarchical identifier nodes.
static char *extract_hdl_callee(CBMArena *a, TSNode node, const char *source, const char *nk) {
if (strcmp(nk, "function_subroutine_call") != 0 && strcmp(nk, "subroutine_call") != 0 &&
strcmp(nk, "tf_call") != 0 && strcmp(nk, "system_tf_call") != 0) {
return NULL;
}
return extract_first_leaf_identifier(a, node, source);
}
// Make builtins are represented as function_call nodes; $(shell ...) is shell_function.
static char *extract_make_callee(CBMArena *a, TSNode node, const char *source, const char *nk) {
if (strcmp(nk, "shell_function") == 0) {
@@ -943,6 +970,10 @@ static char *extract_callee_lang_specific(CBMArena *a, TSNode node, const char *
char *c = extract_vhdl_callee(a, node, source, nk);
return c ? c : extract_scripting_callee(a, node, source, lang, nk);
}
if (lang == CBM_LANG_VERILOG || lang == CBM_LANG_SYSTEMVERILOG) {
char *c = extract_hdl_callee(a, node, source, nk);
return c ? c : extract_scripting_callee(a, node, source, lang, nk);
}
if (lang == CBM_LANG_CLOJURE || lang == CBM_LANG_COMMONLISP || lang == CBM_LANG_SCHEME ||
lang == CBM_LANG_FENNEL || lang == CBM_LANG_RACKET || lang == CBM_LANG_EMACSLISP) {
+1 -1
View File
@@ -1384,7 +1384,7 @@ static const char *systemverilog_class_types[] = {"class_declaration",
"type_declaration",
NULL};
static const char *systemverilog_call_types[] = {"function_subroutine_call", "system_tf_call",
"method_call", NULL};
"subroutine_call", "method_call", NULL};
static const char *systemverilog_import_types[] = {
"package_import_declaration", "extends", "import", "include",
"include_statement", "instance", "use_clause", NULL};
+36
View File
@@ -1260,6 +1260,40 @@ TEST(vhdl_function_call_edge) {
PASS();
}
TEST(verilog_function_call_edge) {
CBMFileResult *r = extract("module m;\n"
" function integer foo;\n"
" input integer x;\n"
" begin foo = x; end\n"
" endfunction\n"
" integer y;\n"
" initial begin\n"
" y = foo(1);\n"
" end\n"
"endmodule\n",
CBM_LANG_VERILOG, "t", "mod.v");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_call_exact(r, "foo"));
cbm_free_result(r);
PASS();
}
TEST(systemverilog_subroutine_call_edge) {
CBMFileResult *r = extract("module m;\n"
" function int foo(); return 1; endfunction\n"
" initial begin\n"
" foo();\n"
" end\n"
"endmodule\n",
CBM_LANG_SYSTEMVERILOG, "t", "mod.sv");
ASSERT_NOT_NULL(r);
ASSERT_FALSE(r->has_error);
ASSERT(has_call_exact(r, "foo"));
cbm_free_result(r);
PASS();
}
/* --- Fortran --- */
TEST(fortran_function) {
/* Fortran subroutine name extraction is incomplete — just verify no crash */
@@ -3456,6 +3490,8 @@ SUITE(extraction) {
RUN_TEST(nickel_function_application_edge);
RUN_TEST(func_function_application_edge);
RUN_TEST(vhdl_function_call_edge);
RUN_TEST(verilog_function_call_edge);
RUN_TEST(systemverilog_subroutine_call_edge);
RUN_TEST(fortran_function);
/* OOP/Systems variants */