Skip to content

fix(extraction): skip bodiless C++ forward declarations (#1093)#1095

Merged
colbymchenry merged 1 commit into
mainfrom
fix/cpp-forward-decl-phantom-1093
Jul 1, 2026
Merged

fix(extraction): skip bodiless C++ forward declarations (#1093)#1095
colbymchenry merged 1 commit into
mainfrom
fix/cpp-forward-decl-phantom-1093

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Closes #1093.

Problem

In a large C++/Unreal-Engine codebase, codegraph_explore for a heavily used class (e.g. APXCharacter) returned mostly forward-declaration sites and even picked one as the blast-radius representative, while the real definition (with members and callers) was crowded out.

A class Foo; forward declaration parses as a bodiless class_specifier. extractStruct (#831) and extractEnum already skip their bodiless forms, but extractClass did not — so every forward decl (repeated across dozens of headers) minted a phantom bodiless class node competing with the single real definition. A probe confirmed it: a class forward-declared twice then defined produced 3 class nodes instead of 1.

Fix

  • New opt-in skipBodilessClass?: boolean on LanguageExtractor, set only on cppExtractor.
  • extractClass resolves the body once at the top and returns early when the flag is set and the node is bodiless — mirroring the existing bodiless struct/enum skip — then reuses that resolved body for the member walk.
  • The flag keeps this C/C++-scoped. Languages where a bodiless class is a complete definition (Kotlin class Empty, Scala case object / trait) leave it unset and are unaffected — verified they funnel through the same extractClass path.

Validation

  • Post-fix probe: C++ collapses 3 → 1 (the real definition, with its takeDamage method, survives); bodiless-struct control unchanged; Kotlin Empty/Full and Scala trait Marker/case object Red/class Foo all still indexed.
  • 3 new regression tests under #1093 in extraction.test.ts (collapse to single definition, elaborated-type reference → no phantom, Kotlin/Scala unaffected).
  • npm run build (tsc) clean.
  • Full suite: 1863 passed, 4 skipped — the only failure was the known-flaky #662 daemon test (unrelated; passes on retry).

🤖 Generated with Claude Code

A `class Foo;` forward declaration parses as a bodiless class_specifier.
extractStruct (#831) and extractEnum already skip their bodiless forms,
but extractClass did not — so every forward decl across dozens of headers
minted a phantom bodiless `class` node that competed with, and could be
picked as the blast-radius representative over, the single real definition.

Add an opt-in `skipBodilessClass` extractor flag (set only on cppExtractor)
and skip a bodiless class node when it's set, mirroring the struct/enum
skip. The flag keeps this C/C++-scoped: languages where a bodiless class is
a complete definition (Kotlin `class Empty`, Scala `case object`/`trait`)
leave it unset and are unaffected. The body is now resolved once at the top
of extractClass and reused for the member walk.

Regression tests cover the collapse to a single definition, elaborated-type
references creating no phantom, and Kotlin/Scala staying indexed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

C++ forward declarations (class Foo;) index as phantom class nodes, masking the real definition

1 participant