mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-12 09:47:20 +08:00
fix(extract): source calls to the enclosing callable for all grammars
The unified/calls extractor used a private 4-case name resolver (resolve_func_name_node) that returned NULL for the ~130 grammars whose function node has no `name` field — Fortran subroutine, SCSS mixin, SQL create_function, Julia short-form, the Lisp/FP family, and more. On NULL, push_boundary_scopes never pushed a SCOPE_FUNC, so every call inside such a function was attributed to the enclosing Module instead of the callable (QUALITY_ANALYSIS gap #3). Export the rich resolver from extract_defs (cbm_resolve_func_name) — a strict superset that already drives definition extraction (generic name field, arrow/declarator, C/C++ declarator chain, plus the per-language quirks) — and route the calls scope path (compute_func_qn) through it, preserving the enclosing-class QN join. Definition extraction is unchanged (it already called this resolver); the two paths now share one source of truth, the same de-drift pattern used for cbm_resolve_c_declarator_name_node (#438). Reproduced by repro_invariant_enclosing_parity (Fortran, SCSS, SQL, Verilog, Julia, Nix, CommonLisp, EmacsLisp, Dart, COBOL): each asserts no CALLS edge is Module-sourced and was RED; the fix makes them callable-sourced. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
@@ -558,8 +558,9 @@ static TSNode find_first_descendant_by_kind(TSNode node,
|
||||
return null_node;
|
||||
}
|
||||
|
||||
// Forward declaration for mutual recursion.
|
||||
static TSNode resolve_func_name(TSNode node, CBMLanguage lang);
|
||||
// Forward declaration for mutual recursion. Exported (see helpers.h) so the
|
||||
// unified/calls extractor shares this one resolver — see cbm_resolve_func_name.
|
||||
TSNode cbm_resolve_func_name(TSNode node, CBMLanguage lang);
|
||||
|
||||
static bool is_cpp_template_inner_kind(const char *kind) {
|
||||
return strcmp(kind, "function_definition") == 0 || strcmp(kind, "declaration") == 0 ||
|
||||
@@ -639,7 +640,7 @@ static TSNode resolve_func_name_c_family(TSNode *node_ptr, CBMLanguage lang, con
|
||||
|
||||
// Resolve the name node for a function, handling language-specific quirks.
|
||||
// Uses a loop to handle template_declaration unwrapping (avoids recursion).
|
||||
static TSNode resolve_func_name(TSNode node, CBMLanguage lang) {
|
||||
TSNode cbm_resolve_func_name(TSNode node, CBMLanguage lang) {
|
||||
enum { MAX_TEMPLATE_DEPTH = 2 };
|
||||
for (int tmpl_depth = 0; tmpl_depth < MAX_TEMPLATE_DEPTH; tmpl_depth++) {
|
||||
const char *kind = ts_node_type(node);
|
||||
@@ -2671,7 +2672,7 @@ static char *go_receiver_type_name(CBMArena *a, TSNode recv, const char *source)
|
||||
static void extract_func_def(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec) {
|
||||
CBMArena *a = ctx->arena;
|
||||
|
||||
TSNode name_node = resolve_func_name(node, ctx->language);
|
||||
TSNode name_node = cbm_resolve_func_name(node, ctx->language);
|
||||
if (ts_node_is_null(name_node)) {
|
||||
return;
|
||||
}
|
||||
@@ -3457,7 +3458,7 @@ static TSNode resolve_method_name(TSNode child, CBMLanguage lang) {
|
||||
if ((lang == CBM_LANG_C || lang == CBM_LANG_CPP || lang == CBM_LANG_CUDA ||
|
||||
lang == CBM_LANG_GLSL) &&
|
||||
strcmp(ck, "function_definition") == 0) {
|
||||
return resolve_func_name(child, lang);
|
||||
return cbm_resolve_func_name(child, lang);
|
||||
}
|
||||
|
||||
if (lang == CBM_LANG_GROOVY && strcmp(ck, "function_definition") == 0) {
|
||||
|
||||
@@ -87,27 +87,6 @@ static const char *compute_wolfram_func_qn(CBMExtractCtx *ctx, TSNode node) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Resolve the name node for a function, handling arrow functions.
|
||||
static TSNode resolve_func_name_node(TSNode node) {
|
||||
TSNode name_node = ts_node_child_by_field_name(node, TS_FIELD("name"));
|
||||
if (ts_node_is_null(name_node) && strcmp(ts_node_type(node), "arrow_function") == 0) {
|
||||
TSNode parent = ts_node_parent(node);
|
||||
if (!ts_node_is_null(parent) && strcmp(ts_node_type(parent), "variable_declarator") == 0) {
|
||||
name_node = ts_node_child_by_field_name(parent, TS_FIELD("name"));
|
||||
}
|
||||
}
|
||||
/* Grammars without a `name` field (e.g. newer tree-sitter-kotlin): the
|
||||
* function name is a simple_identifier child of function_declaration. */
|
||||
if (ts_node_is_null(name_node) && strcmp(ts_node_type(node), "function_declaration") == 0) {
|
||||
name_node = cbm_find_child_by_kind(node, "simple_identifier");
|
||||
}
|
||||
/* C/C++/CUDA/GLSL: function_definition name lives in the declarator chain. */
|
||||
if (ts_node_is_null(name_node) && strcmp(ts_node_type(node), "function_definition") == 0) {
|
||||
name_node = cbm_resolve_c_declarator_name_node(node);
|
||||
}
|
||||
return name_node;
|
||||
}
|
||||
|
||||
// Compute function QN for scope tracking (mirrors cbm_enclosing_func_qn logic).
|
||||
static const char *compute_func_qn(CBMExtractCtx *ctx, TSNode node, const CBMLangSpec *spec,
|
||||
WalkState *state) {
|
||||
@@ -116,7 +95,13 @@ static const char *compute_func_qn(CBMExtractCtx *ctx, TSNode node, const CBMLan
|
||||
return compute_wolfram_func_qn(ctx, node);
|
||||
}
|
||||
|
||||
TSNode name_node = resolve_func_name_node(node);
|
||||
/* 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,
|
||||
* SCSS mixin, SQL create_function, Julia short-form, etc., so
|
||||
* push_boundary_scopes never pushed a SCOPE_FUNC and the calls inside were
|
||||
* mis-attributed to the enclosing Module (QUALITY_ANALYSIS gap #3). */
|
||||
TSNode name_node = cbm_resolve_func_name(node, ctx->language);
|
||||
if (ts_node_is_null(name_node)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -50,6 +50,15 @@ const char *cbm_enclosing_func_qn_cached(CBMExtractCtx *ctx, TSNode node);
|
||||
// enclosing-function attribution — drift between private copies caused #438.
|
||||
TSNode cbm_resolve_c_declarator_name_node(TSNode func_node);
|
||||
|
||||
// Resolve a function/method definition node's NAME node across all ~130 grammars
|
||||
// (generic `name` field, arrow→declarator, C/C++ declarator chain, plus the many
|
||||
// per-language quirks: Fortran subroutine, SCSS mixin, SQL create_function, R,
|
||||
// PowerShell, Ada, the Lisp/FP family, etc.). Defined in extract_defs.c. Shared by
|
||||
// the defs, calls, and unified extractors so all three agree on enclosing-function
|
||||
// naming — drift between private copies caused the Module-mis-attribution of
|
||||
// gap #3 (and #438 for the C-declarator case).
|
||||
TSNode cbm_resolve_func_name(TSNode node, CBMLanguage lang);
|
||||
|
||||
// Find a child node by kind string.
|
||||
TSNode cbm_find_child_by_kind(TSNode parent, const char *kind);
|
||||
|
||||
|
||||
@@ -507,7 +507,7 @@ static const char *elixir_var_types[] = {"binary_operator", NULL};
|
||||
|
||||
// ==================== HASKELL ====================
|
||||
/* "bind" = a nullary value binding (`foo = 1`); has a `name` field like `function`.
|
||||
* `signature` (type annotations) is suppressed in resolve_func_name so it never doubles. */
|
||||
* `signature` (type annotations) is suppressed in cbm_resolve_func_name so it never doubles. */
|
||||
static const char *haskell_func_types[] = {"function", "signature", "bind", NULL};
|
||||
static const char *haskell_class_types[] = {"class", "data_type", "newtype", NULL};
|
||||
static const char *haskell_module_types[] = {"haskell", NULL};
|
||||
@@ -701,7 +701,7 @@ static const char *clojure_call_types[] = {"list_lit", NULL};
|
||||
/* Top-level `let f () = ...` parses to function_or_value_defn (module-level
|
||||
* value_declaration is aliased to declaration_expression, which wraps it). The
|
||||
* name lives on a function_declaration_left/value_declaration_left child — see
|
||||
* the CBM_LANG_FSHARP branch in resolve_func_name. */
|
||||
* the CBM_LANG_FSHARP branch in cbm_resolve_func_name. */
|
||||
static const char *fsharp_func_types[] = {"function_declaration", "value_declaration",
|
||||
"function_or_value_defn", NULL};
|
||||
static const char *fsharp_class_types[] = {"type_definition", "exception_definition", NULL};
|
||||
|
||||
Reference in New Issue
Block a user