Skip to content

C++ CALLS edges attributed to Module node instead of Function/Method nodes #220

@nishitpatel92

Description

@nishitpatel92

Bug

In C++ codebases, all CALLS edges originate from the Module (file) node rather than from individual Function/Method nodes. This makes trace_path unable to traverse C++ call chains at function-level granularity.

Version: 0.6.0 (macOS arm64)

Evidence

Querying CALLS edge source labels across languages in the same repository:

-- C++ (hbmx/marcopolo/src/): 100% of CALLS from Module
SELECT n1.label AS source_label, count(*) AS call_count
FROM edges e JOIN nodes n1 ON e.source_id = n1.id
WHERE e.type = 'CALLS' AND n1.file_path LIKE 'hbmx/marcopolo/src/%'
GROUP BY n1.label ORDER BY call_count DESC;

-- Result: Module|1706  (no Method or Function entries)

-- Swift (ios/MarcoPolo/): 82% from Method/Function
SELECT n1.label AS source_label, count(*) AS call_count
FROM edges e JOIN nodes n1 ON e.source_id = n1.id
WHERE e.type = 'CALLS' AND n1.file_path LIKE 'ios/MarcoPolo/%'
GROUP BY n1.label ORDER BY call_count DESC;

-- Result: Method|20160, Module|4417, File|611, Function|261

Concrete example — AutoplayController.cpp has 29 Method nodes but zero outbound CALLS edges. All 23 CALLS are attributed to the .hpp Module node:

-- Zero CALLS from .cpp
SELECT count(*) FROM edges e JOIN nodes n1 ON e.source_id = n1.id
WHERE e.type = 'CALLS'
AND n1.file_path = 'hbmx/marcopolo/src/conversation/AutoplayController.cpp';
-- Result: 0

-- 23 CALLS from .hpp Module node
SELECT count(*) FROM edges e JOIN nodes n1 ON e.source_id = n1.id
WHERE e.type = 'CALLS'
AND n1.file_path = 'hbmx/marcopolo/src/conversation/AutoplayController.hpp';
-- Result: 23

-- All from the Module node specifically
SELECT DISTINCT n1.id, n1.name, n1.label FROM edges e
JOIN nodes n1 ON e.source_id = n1.id
WHERE e.type = 'CALLS'
AND n1.file_path = 'hbmx/marcopolo/src/conversation/AutoplayController.hpp';
-- Result: 23296|hbmx/marcopolo/src/conversation/AutoplayController.hpp|Module

Impact

trace_path(function_name="configureAutoplay", direction="inbound") returns only 1 caller (the Module node) instead of the actual function-level callers. Effectively, trace_path is unusable for C++ code.

Root Cause Analysis

Traced through the source code:

1. func_kinds_cpp is too narrow (helpers.c:361):

static const char *func_kinds_cpp[] = {"function_definition", NULL};

Compare with the generic/Swift fallback which works correctly:

static const char *func_kinds_generic[] = {
    "function_declaration", "function_definition",
    "method_declaration", "method_definition", NULL
};

C++ tree-sitter uses function_definition for .cpp implementations, but .hpp inline methods and other constructs may use different node types. The narrow list means cbm_find_enclosing_func often fails to find the enclosing function in C++.

2. Module QN collision between .hpp and .cpp — When cbm_find_enclosing_func returns null, cbm_enclosing_func_qn falls back to module_qn (helpers.c:480):

TSNode func_node = cbm_find_enclosing_func(node, lang);
if (ts_node_is_null(func_node)) {
    return module_qn;  // fallback to file-level
}

Since AutoplayController.hpp and AutoplayController.cpp produce the same base Module qualified name (...AutoplayController), calls extracted from the .cpp are attributed to the .hpp Module node (which was created first). The .cpp ends up with zero CALLS edges.

Suggested Fix

  1. Expand func_kinds_cpp to include "declaration" or the specific tree-sitter node types used for C++ method declarations in class bodies
  2. Ensure .hpp and .cpp files get distinct Module qualified names (e.g., include the extension) so calls aren't cross-attributed

Reproduction

Any C++ codebase with the standard .hpp/.cpp split pattern will exhibit this. Index the repository, then compare CALLS edge source labels for C++ files vs Swift/Kotlin files — C++ will show 100% Module-attributed CALLS.

Environment

  • codebase-memory-mcp 0.6.0
  • macOS arm64 (Apple Silicon)
  • Repository: ~14K files, mixed Swift/Kotlin/C++/TypeScript
  • Clean index (deleted project + removed .db file before reindexing)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions