fix(dart): attribute in-body calls to the enclosing function (signature/body siblings)

Dart models a function as a function_signature plus a SIBLING function_body
(both children of program/class_body), not a single node containing the body.
So a scope pushed at the signature never covers the body, and the signature's
name was also unresolved by the shared resolver — in-body calls sourced to the
Module (enclosing-func drift).

Two parts in the unified walk:
- compute_func_qn resolves a dart function_signature/method_signature name from
  its identifier child (method_signature wraps a function_signature).
- push_boundary_scopes pushes the SCOPE_FUNC at the function_body, computing the
  QN from the preceding signature sibling, so the body's children attribute to
  the function. grammar_scripting and enclosing_parity dart cases now green.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-06-28 07:36:06 +02:00
parent 3f76096b74
commit e01e77fc0b
+44
View File
@@ -322,6 +322,32 @@ static const char *compute_func_qn(CBMExtractCtx *ctx, TSNode node, const CBMLan
}
}
/* Dart: function_signature / method_signature have no `name` field; the name
* is an `identifier` child (method_signature wraps a function_signature). The
* shared resolver doesn't cover them, so resolve here for call-scope so an
* in-body call sources to the function, not the Module. */
if (ctx->language == CBM_LANG_DART && (strcmp(ts_node_type(node), "function_signature") == 0 ||
strcmp(ts_node_type(node), "method_signature") == 0)) {
TSNode sig = node;
if (strcmp(ts_node_type(node), "method_signature") == 0) {
TSNode fs = cbm_find_child_by_kind(node, "function_signature");
if (!ts_node_is_null(fs)) {
sig = fs;
}
}
TSNode id = cbm_find_child_by_kind(sig, "identifier");
if (!ts_node_is_null(id)) {
char *nm = cbm_node_text(ctx->arena, id, ctx->source);
if (nm && nm[0]) {
if (state->enclosing_class_qn) {
return cbm_arena_sprintf(ctx->arena, "%s.%s", state->enclosing_class_qn, nm);
}
return cbm_fqn_compute_source_lang(ctx->arena, ctx->project, ctx->rel_path, nm,
ctx->language);
}
}
}
/* Resolve the function name via the single shared resolver (extract_defs) so
* call-scope attribution agrees with definition extraction across all ~130
* grammars. The old private 4-case copy returned NULL for Fortran subroutine,
@@ -1071,6 +1097,24 @@ static void push_boundary_scopes(CBMExtractCtx *ctx, TSNode node, const CBMLangS
push_scope(state, SCOPE_CLASS, depth, tqn);
}
}
} else if (ctx->language == CBM_LANG_DART && strcmp(ts_node_type(node), "function_body") == 0) {
/* Dart models a function as `function_signature` + `function_body` SIBLINGS
* (the signature node does not contain the body). A scope pushed at the
* signature never covers the body, so in-body calls source to the Module.
* Push the function scope at the BODY using the preceding signature
* sibling's QN, so the body's children attribute to the function. */
TSNode prev = ts_node_prev_sibling(node);
while (!ts_node_is_null(prev) &&
strcmp(ts_node_type(prev), "function_signature") != 0 &&
strcmp(ts_node_type(prev), "method_signature") != 0) {
prev = ts_node_prev_sibling(prev);
}
if (!ts_node_is_null(prev)) {
const char *fqn = compute_func_qn(ctx, prev, spec, state);
if (fqn) {
push_scope(state, SCOPE_FUNC, depth, fqn);
}
}
}
if (spec->call_node_types && cbm_kind_in_set(node, spec->call_node_types)) {