Skip to content

fix(cpp): distinct nodes for union- and anonymous-namespace-nested same-tail types (#1995)#1998

Closed
magyargergo wants to merge 3 commits into
abhigyanpatwari:mainfrom
magyargergo:fix/1995-cpp-union-anon-ns-collision
Closed

fix(cpp): distinct nodes for union- and anonymous-namespace-nested same-tail types (#1995)#1998
magyargergo wants to merge 3 commits into
abhigyanpatwari:mainfrom
magyargergo:fix/1995-cpp-union-anon-ns-collision

Conversation

@magyargergo

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #1981 (#1978). PR #1981 made namespace/class/struct-nested same-tail C++ types distinct via qualifiedNodeId. Two scope kinds were left out and still merged: types nested in sibling named unions and in anonymous namespaces. This ships both as two commits.

Named unions (commit 1)

union_specifier was missing from cppClassConfig.ancestorScopeNodeTypes, so a struct in union U1 and one in union U2 both qualified to the bare Inner and merged onto one Struct:...:Inner node (from_u1 / from_u2 cross-wired, invisible to findDanglingEdges). Adding union_specifier lets buildQualifiedName pick up the named union's name segment → distinct U1.Inner / U2.Inner. Anonymous unions have no name child and correctly contribute nothing (members inject into the enclosing scope). The separate C config is untouched.

Anonymous namespaces (commit 2)

An anonymous namespace { } has no name child, so the scope walker dropped it (empty segment) and two namespace { struct Inner {} } blocks collapsed. A C++ extractScopeSegments override — the first consumer of the existing, unused config hook (generic.ts:98) — gives each anonymous namespace a deterministic per-block discriminator from its start byte. Named scopes (incl. inline namespace) and anonymous unions fall through unchanged. The discriminator is deterministic across the sequential and worker full-file parses, so the tests assert node distinctness (count==2 / distinct owner ids), never the non-portable discriminator value.

Both fixes are C++-config-only — no shared-walker change, no language leak; cClassConfig (C) is untouched.

Tests

New cpp-union-nested-tail-collision and cpp-anon-ns-tail-collision fixtures + tests mirroring the #1978 template:

  • Named union: U1.Inner / U2.Inner materialize as two distinct Struct nodes; from_u1 / from_u2 own through their own node.
  • Anon ns: two distinct Inner nodes (asserted by count==2 / distinct owner ids); from_anon_a / from_anon_b own through distinct nodes.
  • Worker-path parity block for each.

Verification

  • cpp resolver suite: registry-primary 294/294, legacy 217 + 77 by-design skips — both green, including the worker path; the 8 new tests pass on both legs.
  • tsc --noEmit clean; prettier clean.
  • No cpp captures-golden / scope-capture tripwire exists, so none to re-baseline; brace-init / UDC / two-phase / file-local-linkage / inline-namespace fixtures all stay green.

Closes #1995.

🤖 Generated with Claude Code

…d-qualified Impl node (abhigyanpatwari#1992)

A generic inherent-impl target (`impl<T> Inner<T>`) is a `generic_type` node, which the inherent-impl owner walk (findEnclosingClassInfo) did not match — so the walk returned null and the method got `File -> DEFINES` with NO HAS_METHOD edge (orphaned, and invisible to findDanglingEdges). The Impl node was already correctly mod-qualified (the @name capture drills into the inner type_identifier, tree-sitter-queries.ts), so this is an owner-walk-only fix: drill into the generic base and mirror the node gate so the owner id == the node id byte-for-byte. A scoped-generic target (`impl<T> a::Inner<T>`) materializes no Impl node and is left orphaned (deferred) rather than minting a phantom owner.

The owner walk is shared by the sequential and worker paths. New fixture + tests assert positive HAS_METHOD ownership through distinct `a.Inner` / `b.Inner` nodes on both resolver legs and the worker path, plus a negative scoped-generic guard. rust-captures-golden regenerated additively for the new fixture.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 3, 2026

Copy link
Copy Markdown

@magyargergo is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

CI Report

Some checks failed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
❌ Tests failure unit tests, 3 platforms
✅ E2E success gitnexus-web changes only

Test Results

Tests Passed Failed Skipped Duration
11068 11052 0 16 692s

✅ All 11052 tests passed

16 test(s) skipped — expand for details
  • COBOL pipeline benchmark > scales with file count
  • C++ ADL emit benchmark > emit phase scales sub-quadratically with co-scaled files and sites
  • C++ pipeline benchmark > scales with file count
  • C# pipeline benchmark > scales with file count — namespaces spread across the solution
  • C# pipeline benchmark > scales with file count — all types in one (global) namespace bucket
  • C# pipeline benchmark > scales with file count — all types in one (named) namespace bucket
  • Go pipeline benchmark > scales with file count (workers enabled)
  • Go pipeline benchmark — worker pool (issue Worker idle timeout kills long Go scope extraction and surfaces as Napi::Error during analyze #1848) > does not quarantine the large generated Go file on sub-batch idle timeout
  • Go structural interface detection benchmark > scales linearly with interface × struct count
  • Go structural interface detection split-phase benchmark > separates index-build and detection time
  • PHP pipeline benchmark > scales with file count (workers enabled)
  • Ruby pipeline benchmark > scales with file count (workers enabled)
  • Rust pipeline benchmark > scales with file count (workers enabled)
  • Vue pipeline benchmark > scales with component count
  • run.cjs direct-exec entrypoint (fix(cli): steer docs, skills, and hooks through a CLI-neutral project-local runner (#1939) #1945) > resolves a .cmd shim via the Windows shell branch, passing args and exit code
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Tests

Metric Coverage Covered Base Delta Status
Statements 80.33% 38700/48176 79.84% 📈 +0.5 🟢 ████████████████░░░░
Branches 68.88% 24601/35713 68.5% 📈 +0.4 🟢 █████████████░░░░░░░
Functions 85.55% 4034/4715 84.94% 📈 +0.6 🟢 █████████████████░░░
Lines 83.93% 34814/41475 83.36% 📈 +0.6 🟢 ████████████████░░░░

📋 View full run · Generated by CI

magyargergo and others added 2 commits June 3, 2026 21:00
…bhigyanpatwari#1995)

`union_specifier` was missing from cppClassConfig.ancestorScopeNodeTypes, so a struct nested in `union U1` and one in `union U2` both qualified to the bare `Inner` and merged onto one Struct:...:Inner node — from_u1/from_u2 cross-wired (invisible to findDanglingEdges). Adding `union_specifier` lets buildQualifiedName pick up the named union's `name` segment, materializing distinct `U1.Inner` / `U2.Inner` nodes. Anonymous unions have no `name` child and correctly contribute nothing (members inject into the enclosing scope); the separate C config is untouched. New fixture + positive-identity tests (sequential + worker, both legs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…es (abhigyanpatwari#1995)

An anonymous `namespace { }` is a namespace_definition with no `name` child, so the scope walker dropped it (empty segment) and two `namespace { struct Inner {} }` blocks in one TU collapsed onto a single `Inner` node — from_anon_a/from_anon_b cross-wired. A C++ `extractScopeSegments` override (the first consumer of the existing config hook) gives each anonymous namespace a deterministic per-block discriminator from its start byte, keeping the nested types distinct. Named scopes (incl. `inline namespace`) and anonymous unions are unaffected. Deterministic across the sequential and worker full-file parses. New fixture + tests assert node DISTINCTNESS (count==2 / distinct owners), not the non-portable discriminator value.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@magyargergo magyargergo force-pushed the fix/1995-cpp-union-anon-ns-collision branch from dd91c44 to 059e562 Compare June 3, 2026 21:05
@magyargergo

Copy link
Copy Markdown
Collaborator Author

Superseded by same-repo stacked PR #2004.

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.

fix(ingestion): C++ same-tail nested types in sibling unions / anonymous namespaces still merge into one node (#1978 fan-out U1 retro-fix)

1 participant