feat(cpp): thread base-specifier qualifier through dependent-base lookup (#1815)#1819
Conversation
…kup (abhigyanpatwari#1815) captures.ts: add extractBaseLookupQualifier, fix isBaseDependent for qualified_identifier bases. two-phase-lookup.ts: qualifier storage, markCppDependentBase accepts qualifier, dedup index by nodeId (last-wins), V3 qualifier targeting (dormant). Infrastructure delivered: qualifier extraction, storage, dedup, isBaseDependent fix. V3 targeting dormant until qualifiedName computation fix reaches localDefs. Part of abhigyanpatwari#1564. Infrastructure for abhigyanpatwari#1815.
|
@prajapatisparsh is attempting to deploy a commit to the NexusCore Team on Vercel. A member of the Team first needs to authorize it. |
|
hey @magyargergo can u give me the claude's insight by reviewing i am not sure about this implementation no need to run the CI till then |
CI Report✅ All checks passed Pipeline Status
Test Results
✅ All 9854 tests passed 3 test(s) skipped — expand for details
Code CoverageTests
📋 View full run · Generated by CI |
|
Claude finished @magyargergo's task in 5m 17s —— View job PR #1819 Review —
|
| Fixture | Before PR | After PR | Expected by #1815 |
|---|---|---|---|
cpp-two-phase-dependent-base-cross-ns-sibling-suppress |
0 edges | 0 edges (unchanged) | 1 edge |
cpp-two-phase-dependent-base-cross-ns-pos |
1 edge | 1 edge | 1 edge |
cpp-two-phase-dependent-base-cross-ns-neg |
0 edges | 0 edges | 0 edges |
cpp-two-phase-dependent-base-cross-ns-inline |
1 edge | 1 edge | 1 edge |
cpp-two-phase-dependent-base-cross-ns-deep |
0 edges | 0 edges | 0 edges |
CI is green because the feature is dormant — the test expectation was never updated. No test proves the new runtime path is exercised.
Hidden Unicode and hygiene
git grepscan for bidi/hidden Unicode in both changed files: No hidden/bidi characters found.- The GitHub UI warning banner may be triggered by non-ASCII in adjacent unrelated files pulled in by the merge commit; the changed files themselves are clean.
- The diff removes one blank line between
populateCppDependentBasesandisCppDependentBaseMember(cosmetic, harmless).
Back-and-forth avoided by verifying
- Confirmed via diff that
isBaseDependentstack walk is not over-classifying concrete qualified bases: the walk checks fortype_identifiertext intemplateParamsset, not for arbitrary identifiers. Adetail::ConcreteBasewith no template arguments will have notype_identifierchild matching a template parameter in the argument list. - Confirmed the
cross-ns-posfixture still expects 1 edge (V2 behavior preserved for nested namespace case with single candidate). - Confirmed the merge commit's unrelated files are from main branch sync, not introduced by the PR author.
Open questions (unavoidable)
-
Does the class extractor's
buildQualifiedNamereliably produce the fully-qualified name in practice? The PR body says it does, but thequalifiedNamedoesn't reachparsed.localDefs. Is this a parsing-processor contract gap or a per-file extractor ordering issue? The answer determines whether fixing V3 is a one-file change or a multi-phase refactor. -
Is the
||inc.nsPrefix === expectedNs || c.nsPrefix === normalizedQualifierintentional? Matching barenormalizedQualifierallows a global-scopedetail::Innerto match when the base specifier saysdetail::Inner<T>insidemylib::Derived. Is this the intended fallback or a latent false-positive?
Final verdict
not production-ready
This PR introduces useful infrastructure — qualifier extraction, storage threading, and isBaseDependent fix for qualified-identifier bases — but it does not satisfy #1815's stated DoD. The V3 qualifier-targeting path is dormant because qualifiedName does not reach parsed.localDefs, so the key fixture (cpp-two-phase-dependent-base-cross-ns-sibling-suppress) still expects 0 edges instead of the required 1. Beyond the dormant feature, there are two structural bugs that block merge independently: (1) Map<baseName, qualifier> silently collapses multiple same-name bases from different qualifiers, and (2) the single-candidate path accepts a mis-qualified candidate in violation of #1564's suppress-over-guess policy. No new tests prove any of the changed code paths are exercised. The smallest path to production-ready is to either complete the parsing-processor.ts plumbing and add qualifying fixtures with exact positive/negative edge assertions, or to explicitly re-scope as infrastructure-only (retitle, remove the #1815 close claim) while fixing the Map-collision bug and the false-positive single-candidate bypass.
There was a problem hiding this comment.
Pull request overview
This PR adds plumbing to carry a dependent base-specifier’s syntactic namespace qualifier (e.g., detail in detail::Inner<T>) from C++ AST capture through storage into the dependent-base resolver, enabling more ISO-correct disambiguation among same-name candidates during two-phase lookup.
Changes:
- Extract and record base-specifier qualifiers during capture (
qualified_identifierscope handling + dependent-base detection fix). - Change dependent-base storage from
Set<baseName>toMap<baseName, qualifier>and thread the qualifier throughmarkCppDependentBase. - Adjust workspace class indexing to deduplicate entries by
nodeId, and add a qualifier-based exact-match path (with V2 heuristic fallback).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
gitnexus/src/core/ingestion/languages/cpp/two-phase-lookup.ts |
Stores dependent base qualifiers and uses them (when available) to disambiguate dependent base resolution; deduplicates class index by nodeId. |
gitnexus/src/core/ingestion/languages/cpp/captures.ts |
Extracts base-specifier qualifiers and fixes dependent-base detection to handle qualified_identifier bases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (bases === undefined) { | ||
| bases = new Set(); | ||
| bases = new Map(); | ||
| perFile.set(className, bases); | ||
| } | ||
| bases.add(baseName); | ||
| bases.set(baseName, qualifier); |
| // nsPrefix. Dedup by nodeId removes broken entries from the | ||
| // classesBySimpleName index, making the surviving nsPrefix reliable. | ||
| if (baseQualifier) { | ||
| const qualifierMatch = candidates.find( | ||
| (c) => c.nsPrefix === expectedNs || c.nsPrefix === normalizedQualifier, | ||
| ); | ||
| if (qualifierMatch !== undefined) { | ||
| bases.add(qualifierMatch.nodeId); | ||
| continue; | ||
| } | ||
| // Exact qualifier match failed — fall through to V2 prefix-heuristic. |
|
@prajapatisparsh could you please look into claude's findings? 🙏 |
yes yes .. i was just reading them only |
Fix 1 — Map collision in markCppDependentBase (line 83): Change innermost storage from Map<baseName, qualifier> to Map<baseName, Set<qualifier>> so multiple captures of the same dependent base name with different qualifiers don't collide. Fix 2 — Single-candidate bypass (lines 197-206): For qualified bases with only one candidate, verify namespace match before accepting. Unqualified bases still accept the unique candidate. Previously accepted regardless, creating false edges. Fix 3 — V3→V2 fallthrough (line 221): When a syntactic qualifier is present but no exact match is found, suppress rather than falling through to V2 prefix-heuristic. V2 only runs for truly unqualified bases, which is what it was designed for. All three are conservative bug fixes — turn false positives into suppression, not behavior changes. 250/250 tests pass both modes.
Summary
Infrastructure for #1815. Provide the syntactic namespace qualifier (e.g.
detailindetail::Inner<T>) from AST capture through storage to resolution. Part of #1564.Changes
captures.ts:
extractBaseLookupQualifier— reads thescopefield fromqualified_identifierAST nodes (e.g.detailfromdetail::Inner<T>)isBaseDependentfix — now recognizesqualified_identifierbases (previously rejected unconditionally); they descend into children to check for template parameter referencestwo-phase-lookup.ts:
dependentBasesByFilestorage changed fromSet<baseName>toMap<baseName, qualifier>markCppDependentBaseaccepts optionalqualifierparameterclassesBySimpleNameindex deduplicated by nodeId (last-wins Map); removes duplicate entries caused by scope-extractor and class-extractor overlapWhat's proven
"detail"fromdetail::Inner<T>qualified_identifierbases now processedWhat's dormant
The V3 qualifier targeting and V2 prefix-heuristic both rely on
nsPrefixderived fromdef.qualifiedNameonparsed.localDefsentries. The class extractor'sbuildQualifiedNamecorrectly producesmylib.detail.Innerbut this qualifiedName doesn't reachlocalDefs— the scope-extractor creates separate defs with simple-name qualifiedNames like"Inner"and"Inner.Inner". Fixing this requires changes inparsing-processor.tsto thread the class extractor's qualifiedName into@declaration.qualified_namecaptures (tracked separately).Testing