Skip to content

fix(cpp): eliminate remaining dangling edges via pass-2 location reuse and deferred INHERITS resolution#663

Merged
vitali87 merged 10 commits into
mainfrom
fix/cpp-dangling-callers-inherits
Jul 8, 2026
Merged

fix(cpp): eliminate remaining dangling edges via pass-2 location reuse and deferred INHERITS resolution#663
vitali87 merged 10 commits into
mainfrom
fix/cpp-dangling-callers-inherits

Conversation

@vitali87

@vitali87 vitali87 commented Jul 8, 2026

Copy link
Copy Markdown
Owner

Iteration 2 of #652. Eliminates every remaining dangling edge on the souffle corpus: 4,261 after #657, now 0 (campaign total 18,175 -> 0). Zero orphans preserved, suite 4593 passed / 0 failed, ty and ruff clean.

Root causes fixed (each red -> green in commit history)

1. Residual phantom callers (3,683 CALLS FROM + 157 REFERENCES FROM + 21 INSTANTIATES FROM)

Pass 3 re-derived caller qns structurally and diverged from Pass 2 on preprocessor-distorted class bodies. souffle's BTreeDelete.h declares methods inside #ifdef IS_PARALLEL / #else blocks inside a template class; tree-sitter's recovery drops parts of the ancestry, so Pass 2 registered ...engine.node.split while Pass 3 attributed the same body's calls to ...node.split or ...split.

Fix: Pass 2 now records every C++ Function/Method node it registers in cpp_function_locations keyed by (module_qn, start_line) (value: label, qn, container qn). Pass 3 consults the record in both walk paths (_process_calls_in_functions, _process_methods_in_class) before deriving anything. This is the record-and-reuse pattern from #657's out-of-class method fix, generalized to all C++ callers. ingest_method now returns the final method qn so call sites can record it.

2. Cross-file INHERITS bases (398 TO-missing)

parse_cpp_base_classes anchored every base to the child's own module qn at parse time with no resolution, so class Aggregator : public Argument (Argument defined in Argument.h) pointed at a phantom under Aggregator.h's module.

Fix: C++ INHERITS emission is deferred (DeferredCppInherit) until every class is registered, then resolved scope-first across files via _resolve_cpp_class_qn (new exclude_qn parameter prevents class Type : public other::Type from self-inheriting). Resolved qns replace the parse-time guesses in class_inheritance in place, so Pass-3 method resolution and override detection walk the real hierarchy - the run now finds cross-file overrides it previously missed. A base that resolves nowhere (std::exception) emits no edge rather than one the database silently drops.

3. Template function duplicate nodes

The C++ functions query captures a templated free function twice: the template_declaration wrapper and its inner function_definition. Both registered, minting a qn@line duplicate node per template function; with fix 1 in place, call attribution faithfully bound bodies to the duplicate. The inner definition no longer registers (mirroring the existing class-specifier rule); the wrapper's location is also recorded at the inner definition's start line so Pass 3's walk still hits it.

4. OVERRIDES onto a same-named nested class (final 4)

check_method_overrides matched any registry entry named parent_qn.method_name without checking its node type. A nested class that inherits its encloser (souffle's node::inner_node : node) has a constructor whose name equals the nested class registered at exactly that qn, so the ctor emitted OVERRIDES with a Method label onto a Class node. The parent member must now be NodeType.METHOD.

Test changes

  • New: test_cpp_preproc_caller_attribution.py, test_cpp_cross_file_inherits.py, test_cpp_template_function_single_node.py, test_cpp_ctor_class_override_false_positive.py
  • Adjusted: test_cpp_concepts definitions floor 5 -> 4 (the old floor counted a DEFINES edge onto a now-removed template duplicate node)

Verification

  • souffle-lang/souffle full index: 0 dangling relationship endpoints (was 4,261), 0 orphans
  • Full suite: 4593 passed, 14 skipped, 0 failed (-n auto)
  • ty check: same 352 pre-existing diagnostics as main, none new; ruff check and ruff format clean

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses C++ parsing issues (such as phantom callers and duplicate template function nodes) by deferring C++ inheritance resolution until all classes are registered, tracking function locations during the definition pass, and avoiding duplicate template function registrations. The review feedback highlights two important issues: a missing import of create_inheritance_relationship (or rel) in mixin.py that will cause a runtime NameError, and a potential lookup failure in parent_extraction.py if a C++ base class name contains spaces before template angle brackets, which can be resolved by stripping whitespace.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread codebase_rag/parsers/class_ingest/mixin.py
Comment thread codebase_rag/parsers/class_ingest/parent_extraction.py Outdated
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes several C++ graph ingestion paths that produced dangling edges. The main changes are:

  • Recorded Pass 2 C++ function and method locations for Pass 3 call attribution.
  • Deferred C++ INHERITS edge emission until all classes are registered and cross-file bases can resolve.
  • Removed duplicate inner registrations for templated free functions.
  • Restricted OVERRIDES targets to registered method nodes.
  • Added regression tests for preprocessor-distorted callers, cross-file inheritance, incremental inheritance, template function duplicates, and constructor override false positives.

Confidence Score: 5/5

This PR is safe to merge with low risk.

The changes are focused on C++ graph ingestion edge resolution and include tests for the main clean-index and incremental scenarios reviewed. No blocking or non-blocking issues were identified.

No files require special attention.

T-Rex T-Rex Logs

What T-Rex did

  • The initial test run was attempted and the environment reset caused loguru to be unavailable, as shown by the first pytest invocation.
  • Dependencies were restored using the requested no-project/no-pymgclient uv sync path, and the restoration completed successfully.
  • A subsequent pytest run failed at fixture setup due to a missing mgclient during the import of codebase_rag.cli, blocking progress.
  • A diagnostic log confirmed that codebase_rag.cli import fails because codebase_rag.services.graph_service.py imports mgclient which is not available.
  • A narrow test-only shim (sitecustomize.py) was added to satisfy import-time mgclient references without connecting to Memgraph, and pytest with the shim then passed.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
codebase_rag/graph_updater.py Runs deferred C++ inheritance resolution after rehydration and forward declarations.
codebase_rag/parsers/call_processor.py Reuses recorded C++ function locations for caller attribution.
codebase_rag/parsers/class_ingest/mixin.py Adds deferred C++ inheritance resolution and records ingested C++ method locations.
codebase_rag/parsers/class_ingest/relationships.py Defers C++ inheritance edge emission while preserving base ordering.
codebase_rag/parsers/function_ingest.py Skips duplicate template function registrations and records C++ function locations.
codebase_rag/tests/test_cpp_cross_file_inherits.py Adds tests for cross-header inheritance, unresolved bases, same-file bases, and self-inheritance prevention.
codebase_rag/tests/test_cpp_incremental_inherits.py Adds incremental indexing coverage for cross-header inheritance into unchanged headers.
codebase_rag/tests/test_cpp_preproc_caller_attribution.py Adds tests for preserving full caller chains in preprocessor-distorted C++ class bodies.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant P2 as Pass 2 DefinitionProcessor
participant Reg as Function registry / lookups
participant Defer as Deferred C++ inheritance queue
participant P3 as Pass 3 CallProcessor
participant Graph as Graph ingestor

P2->>Reg: Register C++ classes/functions/methods
P2->>Reg: Store cpp_function_locations by module/start line
P2->>Defer: Queue C++ base names instead of emitting guessed INHERITS
P2->>Reg: Rehydrate unchanged definitions and paths on incremental runs
Defer->>Reg: Resolve base qns scope-first across registered classes
Defer->>Graph: Emit resolved INHERITS edges only for real nodes
P3->>Reg: Lookup recorded caller location by module/start line
P3->>Graph: Emit CALLS from recorded qn/label
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant P2 as Pass 2 DefinitionProcessor
participant Reg as Function registry / lookups
participant Defer as Deferred C++ inheritance queue
participant P3 as Pass 3 CallProcessor
participant Graph as Graph ingestor

P2->>Reg: Register C++ classes/functions/methods
P2->>Reg: Store cpp_function_locations by module/start line
P2->>Defer: Queue C++ base names instead of emitting guessed INHERITS
P2->>Reg: Rehydrate unchanged definitions and paths on incremental runs
Defer->>Reg: Resolve base qns scope-first across registered classes
Defer->>Graph: Emit resolved INHERITS edges only for real nodes
P3->>Reg: Lookup recorded caller location by module/start line
P3->>Graph: Emit CALLS from recorded qn/label
Loading

Reviews (3): Last reviewed commit: "fix(cpp): never emit self-INHERITS from ..." | Re-trigger Greptile

Comment thread codebase_rag/parsers/class_ingest/mixin.py
@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 97.60766% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
codebase_rag/parsers/call_processor.py 93.75% 1 Missing ⚠️
codebase_rag/parsers/class_ingest/mixin.py 97.05% 1 Missing ⚠️
...base_rag/parsers/class_ingest/parent_extraction.py 88.88% 1 Missing ⚠️
codebase_rag/parsers/function_ingest.py 95.00% 1 Missing ⚠️
codebase_rag/parsers/utils.py 80.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@vitali87

vitali87 commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@greptile review

Comment thread codebase_rag/parsers/class_ingest/mixin.py Outdated
@vitali87

vitali87 commented Jul 8, 2026

Copy link
Copy Markdown
Owner Author

@greptile review

@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
E Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@vitali87
vitali87 merged commit 0663141 into main Jul 8, 2026
21 of 22 checks passed
@vitali87
vitali87 deleted the fix/cpp-dangling-callers-inherits branch July 8, 2026 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants