Skip to content

Commit db9a15e

Browse files
author
Your Name
committed
fix(extraction): C/C++ CALLS edge attribution to enclosing function scope
C/C++ function_definition nodes have no 'name' field — the name is buried in a declarator chain (function_definition → declarator → function_declarator → declarator → identifier). Both compute_func_qn() in extract_unified.c and func_node_name() in helpers.c used ts_node_child_by_field_name('name') which returns NULL for C/C++, causing all CALLS edges to be attributed to the File node instead of the containing Function. Fix: walk the C/C++ declarator chain (up to 8 levels) to find the identifier. Handles: identifier, field_identifier, qualified_identifier, scoped_identifier. Also unwraps template_declaration → function_definition for C++ templates. Fixes C, C++, CUDA, and GLSL function scope resolution. Tested: C++ desktop app went from 0 Function→Function CALLS edges to 10, enabling process detection from entry points for the first time.
1 parent 4ece8db commit db9a15e

2 files changed

Lines changed: 87 additions & 0 deletions

File tree

internal/cbm/extract_unified.c

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,65 @@ static const char *compute_func_qn(CBMExtractCtx *ctx, TSNode node, const CBMLan
7979
}
8080
}
8181

82+
/* C/C++/CUDA/GLSL: function_definition has no "name" field.
83+
* Name is buried in declarator chain: function_definition → declarator →
84+
* function_declarator → declarator → identifier. Walk the chain. */
85+
if ((ctx->language == CBM_LANG_C || ctx->language == CBM_LANG_CPP ||
86+
ctx->language == CBM_LANG_CUDA || ctx->language == CBM_LANG_GLSL)) {
87+
const char *nk = ts_node_type(node);
88+
bool is_func_def = (strcmp(nk, "function_definition") == 0);
89+
/* Template declarations wrap the function_definition */
90+
TSNode inner_func = node;
91+
if (strcmp(nk, "template_declaration") == 0) {
92+
for (uint32_t i = 0; i < ts_node_named_child_count(node); i++) {
93+
TSNode ch = ts_node_named_child(node, i);
94+
if (strcmp(ts_node_type(ch), "function_definition") == 0) {
95+
inner_func = ch;
96+
is_func_def = true;
97+
break;
98+
}
99+
}
100+
}
101+
if (is_func_def) {
102+
TSNode decl = ts_node_child_by_field_name(inner_func, "declarator", 10);
103+
for (int depth = 0; depth < 8 && !ts_node_is_null(decl); depth++) {
104+
const char *dk = ts_node_type(decl);
105+
if (strcmp(dk, "identifier") == 0 || strcmp(dk, "field_identifier") == 0) {
106+
char *name = cbm_node_text(ctx->arena, decl, ctx->source);
107+
if (name && name[0]) {
108+
if (state->enclosing_class_qn) {
109+
return cbm_arena_sprintf(ctx->arena, "%s.%s",
110+
state->enclosing_class_qn, name);
111+
}
112+
return cbm_fqn_compute(ctx->arena, ctx->project, ctx->rel_path, name);
113+
}
114+
return NULL;
115+
}
116+
if (strcmp(dk, "qualified_identifier") == 0 ||
117+
strcmp(dk, "scoped_identifier") == 0) {
118+
TSNode id = cbm_find_child_by_kind(decl, "identifier");
119+
if (ts_node_is_null(id))
120+
id = cbm_find_child_by_kind(decl, "field_identifier");
121+
if (!ts_node_is_null(id)) {
122+
char *name = cbm_node_text(ctx->arena, id, ctx->source);
123+
if (name && name[0]) {
124+
return cbm_fqn_compute(ctx->arena, ctx->project,
125+
ctx->rel_path, name);
126+
}
127+
}
128+
return NULL;
129+
}
130+
/* Unwrap: function_declarator → inner declarator */
131+
TSNode inner = ts_node_child_by_field_name(decl, "declarator", 10);
132+
if (ts_node_is_null(inner) && ts_node_named_child_count(decl) > 0) {
133+
inner = ts_node_named_child(decl, 0);
134+
}
135+
decl = inner;
136+
}
137+
return NULL; /* couldn't resolve C/C++ function name */
138+
}
139+
}
140+
82141
TSNode name_node = ts_node_child_by_field_name(node, "name", 4);
83142

84143
// Arrow function: name from parent variable_declarator

internal/cbm/helpers.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,34 @@ static const char *func_node_name(CBMArena *a, TSNode func_node, const char *sou
444444
}
445445
}
446446

447+
/* C/C++/CUDA/GLSL: function_definition has no "name" field.
448+
* Name is inside declarator chain: function_definition → declarator →
449+
* function_declarator → declarator → identifier. */
450+
if ((lang == CBM_LANG_C || lang == CBM_LANG_CPP ||
451+
lang == CBM_LANG_CUDA || lang == CBM_LANG_GLSL) &&
452+
strcmp(ts_node_type(func_node), "function_definition") == 0) {
453+
TSNode decl = ts_node_child_by_field_name(func_node, "declarator", 10);
454+
for (int depth = 0; depth < 8 && !ts_node_is_null(decl); depth++) {
455+
const char *dk = ts_node_type(decl);
456+
if (strcmp(dk, "identifier") == 0 || strcmp(dk, "field_identifier") == 0) {
457+
return cbm_node_text(a, decl, source);
458+
}
459+
if (strcmp(dk, "qualified_identifier") == 0 ||
460+
strcmp(dk, "scoped_identifier") == 0) {
461+
TSNode id = cbm_find_child_by_kind(decl, "identifier");
462+
if (ts_node_is_null(id))
463+
id = cbm_find_child_by_kind(decl, "field_identifier");
464+
if (!ts_node_is_null(id)) return cbm_node_text(a, id, source);
465+
return NULL;
466+
}
467+
TSNode inner = ts_node_child_by_field_name(decl, "declarator", 10);
468+
if (ts_node_is_null(inner) && ts_node_named_child_count(decl) > 0)
469+
inner = ts_node_named_child(decl, 0);
470+
decl = inner;
471+
}
472+
return NULL;
473+
}
474+
447475
TSNode name_node = ts_node_child_by_field_name(func_node, "name", 4);
448476
if (!ts_node_is_null(name_node)) {
449477
return cbm_node_text(a, name_node, source);

0 commit comments

Comments
 (0)