Skip to content

C++ out-of-line method definitions: CALLS edge source falls back to Module (file-level) instead of the enclosing Method #554

Closed

Description

@spwlyzx

Version

0.8.1

Platform

macOS (Apple Silicon)

Install channel

GitHub release archive / install.sh / install.ps1

Binary variant

standard

What happened, and what did you expect?

For C++ out-of-line method definitions (void Foo::Bar() { ... } in a .cpp file), CALLS edges emitted from call sites inside the method body have source = Module (file-level node) instead of source = the enclosing Method node. This causes trace_path(direction=inbound) to return file paths rather than specific caller method names, and prevents multi-hop method-to-method BFS traversal.

Expected: CALLS edges should have source = Method("Foo::Bar"), matching how in-class definitions are handled.

Actual: CALLS edges have source = Module("path/to/file.cpp"). The calls_find_source() fallback to __file__ fires because call->enclosing_func_qn does not match the graph node's qualified name after PR #428 promoted out-of-line definitions from Function to Method (changing their QN).

Reproduction

Reproduction:

  1. Create a minimal C++ project:
// include/factory.h
#pragma once
class TileDataFactory {
public:
    void CreateTileData(int level);
};

// src/factory.cpp
#include "factory.h"
void TileDataFactory::CreateTileData(int level) {}  // out-of-line

// src/consumer.cpp
#include "factory.h"
class DataProvider {
public:
    void ConvertBlob();   // in-class declaration
};

void DataProvider::ConvertBlob() {   // out-of-line definition
    TileDataFactory f;
    f.CreateTileData(1);   // <-- this call site
}
  1. Index and query:
codebase-memory-mcp cli index_repository '{"repo_path": "/tmp/repro"}'
codebase-memory-mcp cli query_graph '{"query": "MATCH (src)-[:CALLS]->(dst {name: \"CreateTileData\"}) RETURN src.name, src.label, dst.name, dst.label", "project": "..."}'
  1. Actual output: src.name = "src/consumer.cpp", src.label = "Module"
  2. Expected output: src.name = "ConvertBlob", src.label = "Method" (with parent_class = DataProvider)

Root cause (from source inspection):

In src/pipeline/pass_calls.c L320–333, calls_find_source() first tries enclosing_func_qn:

static const cbm_gbuf_node_t *calls_find_source(ctx, rel, enclosing_qn) {
    if (enclosing_qn) {
        src = cbm_gbuf_find_by_qn(ctx->gbuf, enclosing_qn);
    }
    if (!src) {
        // fallback to file-level Module
        file_qn = cbm_pipeline_fqn_compute(ctx->project_name, rel, "__file__");
        src = cbm_gbuf_find_by_qn(ctx->gbuf, file_qn);
    }
}

After PR #428 (commit a50b086), out-of-line method definitions are correctly promoted from Function to Method with a class-qualified QN. However, call->enclosing_func_qn (set during extraction in pass_definitions.c) still carries the old pre-promotion QN (free-function style), so gbuf_find_by_qn fails → fallback fires.

This is visible in production: a 2500-file C++ engine module shows 99.6% of CALLS edges have source.label = Module (5779 Module→Method vs 43 Function/Method→Function/Method).

Logs


Project scale (if relevant)

2543 files, 36,219 nodes, 79,170 edges

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet or a public OSS repository), not proprietary code.

Activity

  1. DeusData commented on Jun 22, 2026

    @DeusData
    Owner

    Hey, thx for reporting, will check ASAP

  2. DeusData commented on Jun 23, 2026

    @DeusData
    Owner

    Related to the general bug #438 (cross-file CALLS edges resolving to the Module node instead of the caller function). Your case is the C++ out-of-line-definition path — the calls_find_source() fallback to __file__ firing because enclosing_func_qn no longer matches the node QN after #428 promoted out-of-line defs to Method. PR #463 targets exactly this by attributing C/C++ CALLS edges to the enclosing function. Linking the two.

  3. DeusData commented on Jun 28, 2026

    @DeusData
    Owner

    Reproduced as a first-class failing test in the cumulative bug-reproduction suite and fixed at the root cause in #667.

    Root cause: for an out-of-line method definition (Class::method in a .cpp), the LSP-resolved caller QN omitted the module prefix, so it never matched the textual call's enclosing-function QN and the resolution was discarded — the CALLS edge fell back to the Module node. The caller QN is now module-qualified so the LSP rescue joins. The reproduction stays as a permanent regression guard.

    (Lands when #667 merges.)

  4. added
    priority/normalStandard review queue; useful PR with ordinary maintainer urgency.
    on Jun 30, 2026
  5. added this to the 0.9.1-rc milestone on Jul 8, 2026
  6. added
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    parsing/qualityGraph extraction bugs, false positives, missing edges
    and removed
    priority/normalStandard review queue; useful PR with ordinary maintainer urgency.
    on Sep 5, 2026
  7. DeusData commented on Sep 26, 2026

    @DeusData
    Owner

    Thank you, @spwlyzx, for the minimal reproduction and for tracing it straight to calls_find_source()!

    The root cause was that the LSP-resolved caller QN for an out-of-line Class::method was missing the module prefix. The resolution was discarded, and the edge fell back to the Module node. Since #667 the caller QN is module-qualified. I re-ran your exact three-file repro on current main and got ConvertBlob (Method) -CALLS-> CreateTileData (Method), with no Module source.

    Closing as fixed (v0.9.0+). Please reopen if your engine module still shows Module-sourced CALLS on v0.11.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edgespriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions