mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-06 08:16:02 +00:00
13 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
72876ab69a
|
fix(cpp): rank homogeneous braced-init overloads (#2214)
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Gitleaks / gitleaks (push) Has been cancelled
Publish / Classify release event (push) Has been cancelled
Scorecard / Scorecard analysis (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-cli) (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-web) (push) Has been cancelled
Publish / RC guard (marker + release-PR skip) (push) Has been cancelled
Publish / ci (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
Publish / Build & Push RC Docker images (push) Has been cancelled
|
||
|
|
e26002c37a
|
fix(cpp): suppress deleted overload winners (#2094)
* fix(cpp): suppress deleted overload winners * test(cpp): update scope capture fingerprint --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
3a4247ec36
|
feat(cpp): resolve inheritance-lattice member lookup (#2077)
* feat(cpp): resolve inheritance-lattice member lookup * fix(cpp): harden inheritance-lattice lookup --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
f1b8438388
|
perf(cpp): index ADL candidates once instead of per-site rescans (#1990)
* perf(cpp): index ADL candidates once instead of per-site rescans C++ scope-resolution `emit` dominated large-repo analysis (~6.76h on a 5,969-file repo — ~70% of the total run). `pickCppAdlCandidates` ran once per unresolved ADL-eligible call site and each time: - rescanned every parsed file (rebuilding a per-file scope map per call), - scanned every workspace def (`findCppClassDefBySimpleName`), and - used an O(scopes²) child-scope walk for hidden friends. That is O(unresolved sites × files); with hundreds of thousands of unresolved C++ sites the emit phase went super-linear. `resolve` (registry lookup) was only 3.5s — the cost was entirely in fallback edge emission. Build an `AdlCandidateIndex` once per run (lazy, guarded by `parsedFiles` identity, reset in `clearCppAdlState`) and query it per site: - `classDefsBySimple` — preserves `defs.byId` order so first-match / ambiguous semantics are identical to the legacy linear scan. - `nsCandidates` — namespace-owned callables, with inline-namespace transparency. - `friendCandidates` — hidden-friend + class-member callables; a parent→children scope index replaces the O(scopes²) walk. - `nsFunctionsByQName` / `nsFunctionsBySimple` — function-reference ADL path. A monotonic `seqByNodeId` (file-major; namespace defs before friend/member defs within a file) lets the per-site query merge candidates across associated namespaces, dedup by nodeId, and sort — reproducing the exact legacy candidate set and order. Per-site cost drops from O(sites × files) to O(associated namespaces); the emit phase goes from linear-in-sites to flat. Benchmark (files=80): emit at 1000 sites 232ms → 9ms, 2000 sites flat at 17ms; the eliminated term scales with file count, so the speedup is ~1000×+ on the real 5,969-file repo. Behavior is unchanged: synthetic candidate output is byte-identical before/after, all 270 C++ integration resolver tests and 4/4 resolver-parity-expected-failures pass, and tsc + eslint are clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(cpp): correct ADL state-lifecycle and cache-guard comments The header lifecycle block listed three module-level maps and named clearFileLocalNames as the reset caller; both became inaccurate when the candidate index was added. Enumerate all five state pieces, name the real caller (loadResolutionConfig), and document that ensureAdlIndex's staleness guard keys on parsedFiles identity while the index also depends on scopes and classToNamespaceQualifiedName. Addresses PR #1990 tri-review (U1, U3). Doc-only; no behavior change. * test(cpp): guard the ADL seq-coverage invariant in dev/test pickCppAdlCandidates sorts merged candidates by seqByNodeId with a `?? 0` fallback. That fallback is unreachable today (every bucketed def is seq-assigned in the same build block), but a future regression could break it and silently collapse two seq-0 candidates, dropping a CALLS edge with no error. Add validateAdlSeqCoverage and run it from buildAdlIndex under the resolver's opt-in validation gate (NODE_ENV!=production && VALIDATE_SEMANTIC_MODEL!=0), so a broken invariant throws loudly in dev/CI instead. Production behavior and the hot path are unchanged. Unit-tested; 270/270 cpp integration tests pass with the guard active. Addresses PR #1990 tri-review (U2). * test(cpp): parity fixture for ADL hidden-friend + namespace-callable merge pickCppAdlCandidates merges friendCandidates (hidden friends of associated classes) and nsCandidates (namespace-owned callables) for a single associated namespace. The byte-identical-parity claim rested only on an uncommitted harness. Add a fixture that reaches one callable through each bucket — combine only via a hidden friend, process only via a namespace member — so dropping either bucket from the merge fails the suite. Candidate order is not observable (narrowing resolves a unique survivor or suppresses), so the guard is on the set. Addresses PR #1990 tri-review (U4). * test(cpp): add ADL emit-scaling benchmark Guards the PR #1990 optimization against reintroducing the O(sites x files) ADL candidate scan. Generates many UNRESOLVED ADL sites (class-typed arg + a callee declared nowhere) and co-scales files and sites with N, so the old cost is O(N^2) and the new cost O(N). Isolates the scope-resolution emit ms from parse-dominated wall time via the logger test destination (capture verified) and asserts the end-to-end emit ratio stays under fileRatio^1.5. Gated by GITNEXUS_BENCH=1; runs build-free (workerPoolSize: 0). Addresses the benchmark request alongside PR #1990 (U5). * test(cpp): add cpp pipeline file-count benchmark Fills the one missing per-language pipeline benchmark (cobol/csharp/go/php/ ruby/rust already have one); modeled on cobol-pipeline-benchmark.test.ts. Generates synthetic C++ with constant per-file work and constant header fan-out, sweeps file count through the full pipeline, and guards linearity with a coarse time-ratio bound plus a deterministic node-ratio bound (the non-flaky guard against reintroducing O(fileCount^2) work). Gated by GITNEXUS_BENCH=1; runs build-free (workerPoolSize: 0). Addresses the benchmark request alongside PR #1990 (U6). * style(cpp): prettier-format adl benchmark * test(cpp): rebaseline scope-capture fingerprint for new ADL fixture The U4 parity fixture (cpp-adl-ns-plus-hidden-friend-same-name) lives under test/fixtures/lang-resolution/cpp-*, so its lib.h + app.cpp join the cpp scope-capture bench corpus (bench/scope-capture/measure.mjs). That is pure fixture-corpus growth — no scope-extractor change, existing fixtures' captures byte-identical — so the cpp fingerprint legitimately drifts (fixture_count 265->267). Rebaseline cpp to match, as #1965/#1975 did for earlier fixture additions. Verified: --check PASS for all 14 languages. Addresses PR #1990 tri-review (U4 follow-on). --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
0a612a31c6
|
fix(cpp): capture uninitialized multi-declarators (#1965) | ||
|
|
2f5fd90947
|
fix(c/cpp): capture typedef enum and anonymous struct declarations (#1941)
* fix(cpp): capture typedef enums and anonymous structs * fix(cpp): suppress duplicate typedef symbols --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
e234dac849
|
feat(cpp): add template partial ordering (#1885)
* feat(cpp): add template partial ordering * fix(cpp): harden template partial ordering --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
b1445daf04
|
feat(cpp): rank user-defined conversions (#1829) | ||
|
|
dae70a26ea
|
feat(cpp): Add pointer nullptr ellipsis conversion ranks (#1708)
* Add C++ pointer null ellipsis ranks * test(cpp): Strengthen pointer overload assertions --------- Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
5f0c0eba0e
|
feat(cpp): Expand type_traits constraint registry (#1648) | ||
|
|
2376912ca7
|
feat(ingestion): Add C++ parameter type class sidecar (#1642)
Some checks failed
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Gitleaks / gitleaks (push) Has been cancelled
Publish / Classify release event (push) Has been cancelled
Scorecard / Scorecard analysis (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-cli) (push) Has been cancelled
Trivy Image Scan / Trivy (gitnexus-web) (push) Has been cancelled
Publish / RC guard (marker + release-PR skip) (push) Has been cancelled
Publish / ci (push) Has been cancelled
Publish / Publish to npm (push) Has been cancelled
Publish / Build & Push RC Docker images (push) Has been cancelled
Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|
|
a4dfebd073
|
feat(cpp): sfinae filter (#1623)
* feat(cpp): SFINAE-aware overload filter — drops candidates whose enable_if_t / requires constraints fail (#1579) * fix(cpp): SFINAE follow-ups for is_integral_v/is_arithmetic_v bool and char support, an unqualified F1 test fixture, and parameter-lookup gap documentation (#1579) -> claude feedback * revert: reverting all changes to .md files |
||
|
|
e01f0912bc
|
feat(cpp): migrate C++ to scope-based resolution model (#938) (#1520)
* fix(cpp): complete scope-resolution parity * fix(ci): resolve formatting, lint errors for PR #1520 - prettier: format arity-metadata.ts, captures.ts, index.ts - eslint: rename unused HEADER_GLOB to _HEADER_GLOB - eslint: replace unsafe parser.parse() with parseSourceSafe() - eslint: suppress intentional console.warn/log in sync.ts - eslint: remove unused _it import alias in cpp.test.ts * fix(ci): complete formatting, lint, and typecheck fixes - prettier: format call-processor.ts, imported-return-types.ts, include-extractor.test.ts, cpp-captures.test.ts, cpp-imports.test.ts - eslint: suppress intentional console.warn in manifest-extractor.ts - typecheck: restore 'thrift' in ContractType union (was accidentally removed) and add thrift case to exhaustive switch in manifest-extractor * fix(ci): revert unintended group module changes that broke tests Restore types.ts, config-parser.ts, matching.ts, sync.ts, and manifest-extractor.ts to upstream/main versions. The original commit accidentally removed fields (thrift, workspace_deps, exclude_links_paths, exclude_links_param_only_paths) from DetectConfig/MatchingConfig/ContractType which are still referenced by matching.test.ts, config-parser.test.ts, sync.test.ts and other integration tests. This PR's scope is C++ scope-resolution parity only — group module type definitions and logic should remain unchanged. * fix(codeql): address security and quality alerts - arity-metadata.ts, interpret.ts: replace single-pass template strip regex (/<[^>]*>/g) with a while-loop to fully handle nested templates like Map<List<int>> — resolves 'Incomplete multi-character sanitization' - cpp.test.ts: remove unused vitest 'it' import since the file defines its own 'it' via createResolverParityIt — resolves 'Assignment to constant' - include-extractor.test.ts: use fs.mkdtempSync() instead of predictable os.tmpdir()+Date.now() paths — resolves 'Insecure temporary file' - interpret.ts: remove redundant 'name !== undefined' check (already guaranteed by early return) — resolves 'Comparison between inconvertible types' * review: address Claude review findings on PR #1520 - Findings 1-3 (BLOCKERS): restore include-extractor.ts and its test to the main baseline. Block-comment fallback regression, suffix-resolve false-positive suppression, and the four deleted regression tests (#3-#6) are now back. These changes were unrelated to C++ scope parity and should not have been in this PR. - Finding 4 (MAJOR, partial): revert COMPOUND_RECEIVER_MAX_DEPTH 6 to 4. No C++ test exercises depth > 4 (cpp-chain-call uses a 2-hop chain), so the bump risked silent regressions on other migrated languages without justification. The wildcard-origin propagation in imported-return-types.ts is retained — C++ #include and using namespace both emit wildcard-origin bindings (cpp/import-decomposer .ts:40,90), so wildcard propagation is causal to C++ parity. - Finding 6: tighten write-access dedup test with exact per-field counts (nameWrites = 2, addrWrites = 1) instead of total-count + sub string containment, so a regression in one of the two name writes can no longer be masked. - Finding 8: skipped. Box-drawing characters in cpp/query.ts comments match the established convention used in csharp/java/php query files. Finding 5 (int/long normalization tie-breaker) left as documented follow-up — proper fix requires resolver-level tie-breaker logic and risks regressing other arity-matching tests. * fix(cpp): stop #include from leaking class methods and namespace members (U1) The C++ registry-primary resolver was emitting impossible CALLS edges for ordinary headers: an including file's unqualified save() resolved to User::save and unqualified foo() resolved to ns::foo. Two leak paths converged on localDefs: 1. expandCppWildcardNames (file-local-linkage.ts) iterated the flattened localDefs and exported every simple tail, including class-owned methods and namespace-contained symbols. Replaced with a scope-aware filter: build nodeId -> owning Scope from Scope.ownedDefs and skip defs whose owning scope is Namespace or Class. 2. The shared global free-call fallback's pickUniqueGlobalCallable walks the workspace registry by simple name and would still hit class methods / namespace members even with wildcard expansion fixed. Plugged the gap via the existing isFileLocalDef hook — semantically 'logically invisible cross-file' — by tracking per- file non-globally-visible nodeIds (populateCppNonGloballyVisible, called from populateOwners) and adding an ownerId !== undefined fast-path for class-owned defs. Side fix in shared finalize-algorithm.ts: when wildcard expansion resolves to a real target but produces zero propagating names, the edge was dropped, taking the file-level IMPORTS edge with it. Preserve the original wildcard edge so #include dependencies survive even when the header exposes no unqualified bindings. Tests: cpp-include-no-class-leak, cpp-include-no-namespace-leak, and cpp-anon-ns-same-file-visible fixtures. Negative tests mode-gated to REGISTRY_PRIMARY_CPP=1 via the expected-failures registry — legacy DAG has no scope-aware filtering on the global fallback; backporting is out of scope. All 2104 resolver integration tests pass under registry-primary mode. * fix(cpp): suppress receiver-bound CALLS when integer-width overloads collide (U2) C++ arity-metadata normalizes int, long, short, unsigned, size_t to 'int' so single-candidate flows like 'process(42L)' match a 'long'- typed parameter via loose matching. But when both 'process(int)' and 'process(long)' coexist as method overloads, they both end up with parameterTypes=['int'] in the registry, and pickOverload's narrowing returns 2 candidates with no way to disambiguate. The previous code picked candidates[0] arbitrarily, emitting a CALLS edge to the wrong overload roughly half the time. Fix: - Add isOverloadAmbiguousAfterNormalization in overload-narrowing.ts that detects >1 candidate sharing identical parameterTypes sequences. - Have pickOverload return a new OVERLOAD_AMBIGUOUS sentinel when this fires. - In the receiver-bound-calls loop, when pickOverload signals ambiguity, suppress the edge AND add the site to handledSites so the late-stage emitReferencesViaLookup pass does not re-emit the pre-resolved reference. Without the handled-mark, the reference index still carries a toDef and emits the same wrong edge. Graph schema has no ambiguous-target edge model, so emitting two edges (one per candidate) would require a separate schema change. Zero-edge is the only safe outcome. Other languages: the ambiguity check is a precondition gate, not a behavior change for normal narrowing. Languages whose normalizers do not collapse distinct types into a single token (verified by grep over *-arity-metadata.ts) will never produce >1 candidate with identical parameterTypes from genuinely distinct declarations, so the branch is effectively C++-only in practice. Test: cpp-overload-int-long fixture asserts exactly .toBe(0) CALLS edges. Count=1 = arbitrary pick (the bug); count>1 = unsupported ambiguous-edge model. Mode-gated to REGISTRY_PRIMARY_CPP=1 — legacy DAG has no OVERLOAD_AMBIGUOUS wiring; backporting is out of scope. All 2105 resolver integration tests pass under registry-primary; all 139 cpp tests pass under both modes (3 negative tests skipped in legacy as documented). * test(cpp): add integration coverage for anonymous-namespace, using-namespace conflict, and std-shim leakage (U3+U4+U5) Three new end-to-end fixtures exercise the resolver pipeline against scenarios that previously had only unit-level coverage or no coverage at all (Claude review Finding 7): U3 — cpp-anon-ns-cross-file: helper.cpp declares 'namespace { void worker(); }' and calls it internally. caller.cpp declares a separate 'void worker()' and calls it. Asserts (a) the cross-file CALLS edge from caller's run() does not target helper.cpp's anonymous-namespace worker, and (b) the same-file edge from helper_entry() to its own worker still resolves (positive guard against a 'no edges at all' regression making the negative check vacuously pass). Includes a state-isolation guard that re-runs the same fixture and asserts identical results, proving clearFileLocalNames() is called by the pipeline entry. U4 — cpp-using-namespace-conflict: Two headers each declaring 'namespace a { foo() }' and 'namespace b { foo() }' respectively, plus a caller doing 'using namespace a; using namespace b; foo()'. Asserts exactly zero CALLS edges. One edge = arbitrary pick (the bug); two edges would require an ambiguous-target edge model GitNexus does not have. Depends on U1 — without scope-aware filtering, both foo()s would already be in the importer's wildcard binding set as simple 'foo', so the test would pass for the wrong reason. U5 — cpp-using-namespace-std-smoke: Fixture-local 'namespace std { void cout_write(); void println(); }' shim rather than real <iostream> — captures the wildcard-leak shape deterministically without depending on system-header modeling stability (out of scope per plan). Asserts (a) the project-local call resolves correctly, (b) no leak to shim STL symbols, and (c) no CALLS/ACCESSES edges from the caller into std-shim.h at all. Negative tests for U2/U4 mode-gated to REGISTRY_PRIMARY_CPP=1 via the expected-failures registry; legacy DAG lacks the OVERLOAD_AMBIGUOUS suppression and the namespace-aware filtering, so the leaks persist there. All 2112 resolver integration tests pass under registry-primary; all 146 cpp tests pass under both modes (4 negative tests skipped in legacy as documented). * chore(autofix): apply prettier + eslint fixes via /autofix command * fix(cpp): scope-aware isSuperReceiver classification (U1) The C++ isSuperReceiver hook used a regex `/^[A-Z]\w*::/` that misclassified any uppercase-qualified call as a super-receiver call. Singleton::getInstance(), std::Foo::bar(), and PascalCase namespace calls all entered the super branch, where the absence of an enclosing class (or wrong MRO context) dropped the resolution entirely. Fix: - New optional ScopeResolver hook isSuperReceiverInContext(text, callerScope, scopes). Languages where super classification depends on caller context define it; receiver-bound-calls.ts prefers it when defined and falls back to the simple isSuperReceiver(text) otherwise. Other migrated languages (Python, Java, C#, PHP, Go, TypeScript) are unchanged. - C++ implementation: parse the LHS of '::' from the receiver text, resolve via findClassBindingInScope, and return true only when the LHS is a class-like def in the caller's enclosing class's MRO. Returns false for namespace LHS, unresolved LHS, self-class LHS (qualified self-calls aren't super), and any non-'::' form. - Extended the C++ tree-sitter query to capture the LHS of qualified_identifier as @reference.receiver so qualified static member calls (Singleton::getInstance()) reach the receiver-bound Case 2 (class-name receiver) path. Without the receiver capture, qualified calls had no explicit receiver and could not resolve through any receiver-bound branch. Test: cpp-namespace-qualified-not-super fixture. Singleton::getInstance() from a free function asserts exactly 1 CALLS edge through the qualified-call path. Passes under both REGISTRY_PRIMARY_CPP=1 and =0. All 2113 resolver integration tests pass; all 147 cpp tests pass under both modes. * fix(cpp): suppress receiver-bound CALLS when default-arg overloads collide (U4) ISO C++ rejects 's.f(1)' as ambiguous when both 'void f(int)' and 'void f(int, int = 0)' are declared on S. The previous resolver returned the first viable candidate via pickOverload's fallback. Extended isOverloadAmbiguousAfterNormalization to take an optional argCount: when provided, the predicate compares only the first argCount slots of each candidate's parameterTypes. Candidates whose declared-prefix matches up to argCount are treated as ambiguous because default arguments make all of them equally viable for the call. Without argCount, behavior is unchanged (the original int/long normalization-collapse contract, full-length equality required). pickOverload now passes site.arity so default-arg ambiguity fires. Test: cpp-overload-default-arg-ambiguous fixture. s.f(1) where S has f(int) and f(int, int = 0) asserts exactly .toBe(0) CALLS edges. Passes under both REGISTRY_PRIMARY_CPP=1 and =0. All 2114 resolver integration tests pass; all 148 cpp tests pass under both modes. * fix(cpp): two-phase template lookup suppresses dependent-base members (U3) ISO C++ two-phase name lookup: inside a class template body, unqualified calls MUST NOT bind to members of a dependent base class. Only this->name or Base<T>::name forms make the lookup dependent. GCC and Clang both reject the unqualified form with 'declaration of f must be available'. Before this fix, GitNexus's global free-call fallback walked the workspace registry by simple name and bound unqualified calls inside template bodies to dependent-base members, producing CALLS edges the compiler would reject. Implementation: - New languages/cpp/two-phase-lookup.ts module: per-pipeline state recording (className, dependentBaseName) pairs at capture time and resolving them to nodeId sets during populateOwners. - captures.ts detectCppDependentBases walks the AST once finding every template_declaration containing a class/struct definition. For each, it collects template-parameter names (typename T, class T, non-type int N, template-template parameters) and walks each base in the base_class_clause checking whether any inner type_identifier matches a template parameter. Conservative bias: typename T::U, decltype, and template-template-parameter shapes also classified as dependent. - Extended scope-resolution contract's isCallableVisibleFromCaller hook with optional callerScope and scopes fields. C++ implements the hook to consult isCppDependentBaseMember: when the candidate is a member of a dependent base of the caller's enclosing class, the hook returns false and pickUniqueGlobalCallable skips the candidate. - clearFileLocalNames also clears the dependent-base state per pipeline run. Fixtures: - cpp-two-phase-dependent-base: Derived<T> deriving from Base<T>, unqualified f() and i inside Derived's body. Asserts zero CALLS edges and zero ACCESSES edges respectively. - cpp-two-phase-this-qualified, cpp-two-phase-non-dependent-base, cpp-two-phase-namespace-free-call-inside-template: positive fixtures left as documented gaps (this-> and qualified-name resolution inside template bodies are pre-existing resolver weaknesses independent of U3). Tracked separately. Negative test mode-gated to REGISTRY_PRIMARY_CPP=1 via the expected- failures registry; legacy DAG has no two-phase lookup. All 2116 resolver integration tests pass under registry-primary; all 150 cpp tests pass under both modes (5 negative tests skipped in legacy as documented). * fix(cpp): implement V1 ADL (Koenig lookup) for free-function calls (U2) Plan 2026-05-13-001 U2. Adds argument-dependent lookup as a new candidate-generating tier in `emitFreeCallFallback`: when ordinary unqualified lookup is empty, ADL surfaces candidates from each value-class-typed argument's enclosing namespace. V1 boundary (locked by cpp-adl-pointer-arg-boundary fixture): - only direct enclosing-namespace closure - only directly-named class-type values (pointer / reference / template- spec args excluded; closure rules deferred to V2) - ADL fires ONLY when ordinary lookup is empty (no union-and-resolve) Parenthesized name `(f)(s)` suppresses ADL per ISO C++ [basic.lookup.argdep]/3.1. Multi-candidate ambiguity (e.g. `process(int)` vs `process(long)` after C++ int-width normalization) returns the ADL_AMBIGUOUS sentinel — caller suppresses entirely, mirroring the OVERLOAD_AMBIGUOUS contract from plan 2026-05-12-002 U2. Implementation: - `cpp/adl.ts` — new module: per-pipeline argInfoBySite + noAdlSites Maps populated at capture time, classToNamespaceQualifiedName Map populated during populateOwners; `pickCppAdlCandidates` returns SymbolDefinition | ADL_AMBIGUOUS | undefined - `scope-resolution/contract/scope-resolver.ts` — adds optional `resolveAdlCandidates` hook - `scope-resolution/passes/free-call-fallback.ts` — invokes ADL hook between `findCallableBindingInScope` and `pickUniqueGlobalCallable`; marks site handled on `'ambiguous'` so emit-references doesn't retry - `cpp/captures.ts` — detects `parenthesized_expression` function wrap; per-arg classification (pointer/reference/value class) preserving the shape info the existing arity-narrowing normalizer strips - `cpp/scope-resolver.ts` — registers hook, populates associated namespaces, clears state in loadResolutionConfig Negative tests (parens, pointer-boundary, ambiguous) gated under LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES.cpp — legacy DAG has no V1/V2 ADL boundary or ADL_AMBIGUOUS suppression. 154/154 cpp integration tests pass under REGISTRY_PRIMARY_CPP=1; 147 pass + 7 skipped under =0 (legacy parity baseline). * fix(cpp): inline namespace transitive walking + qualified namespace resolution (U5) Plan 2026-05-13-001 U5. Two ISO C++ inline-namespace semantics: 1. Unqualified-lookup transitive visibility: inline-namespace members reach the enclosing namespace's scope as if declared there. The `populateCppNonGloballyVisible` exemption keeps them globally visible so cross-file unqualified lookup finds them. 2. Qualified-receiver transitive visibility: `outer::foo()` resolves to `outer::v1::foo()` when `v1` is inline (and through arbitrarily-deep nesting like `outer::v1::experimental::foo`, matching libc++ `__1` / libstdc++ `__cxx11`). The second behavior required a new resolver case in `receiver-bound-calls.ts` (Case 1.5: language-specific qualified-receiver member lookup) because C++ qualified-namespace member calls had no prior resolution path — receiver-bound Case 1 only handled `ParsedImport.kind === 'namespace'` (Python/JS-style) and Case 2 handles class receivers, neither of which fired for `outer::foo()`. The new hook `resolveQualifiedReceiverMember` is opt-in; languages without C++-style qualified-name semantics omit it. Implementation: - `cpp/inline-namespaces.ts` — new module: per-pipeline `inlineNamespaceRangesByFile` + `inlineNamespaceScopeIds` Sets; `markCppInlineNamespaceRange` at capture time; `populateCppInlineNamespaceScopes` resolves ranges → scope IDs; `resolveCppQualifiedNamespaceMember` walks namespace scopes by simple name and descends transitively through inline children only. - `scope-resolution/contract/scope-resolver.ts` — adds optional `resolveQualifiedReceiverMember` hook to the contract. - `scope-resolution/passes/receiver-bound-calls.ts` — Case 1.5 invokes the hook between Case 1 (namespace imports) and Case 2 (class-name receiver). Returns undefined for non-namespace receivers so Case 2 still resolves class-qualified calls. - `cpp/captures.ts` — detects `inline` keyword child on `namespace_definition`; records 1-based range to match Scope.range. - `cpp/file-local-linkage.ts` — `populateCppNonGloballyVisible` exempts inline-namespace scopes so cross-file unqualified lookup keeps their members visible. - `cpp/scope-resolver.ts` — wires `populateCppInlineNamespaceScopes` into populateOwners (BEFORE `populateCppNonGloballyVisible` so the exemption sees populated state); registers `resolveQualifiedReceiverMember` hook. 4 fixtures: `cpp-inline-namespace-unqualified`, `-versioned`, `-nested` (two transitive inline hops, STL `__1` shape), and `-adl-participation` (composes with U2 — ADL surfaces records declared inside inline child namespaces). All 4 assert exactly 1 CALLS edge with correct target file. Versioned fixture gated under LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES.cpp — legacy DAG can't disambiguate two same-name foos without inline awareness. Other 3 coincidentally resolve in legacy. 158/158 cpp integration tests pass under REGISTRY_PRIMARY_CPP=1; 150 pass + 8 skipped under =0 (legacy parity baseline). * test(cpp): Phase 5 cross-unit composition tests for U1/U2/U3/U5 Plan 2026-05-13-001 Phase 5. Locks in correct behavior at the intersections between the previously-shipped scope-resolver units. Enhancement to U1: `isSuperReceiverInContext` strips template-argument lists (`Base<T>` → `Base`) and namespace prefixes (`outer::v1::Base` → `Base`) before resolving the receiver in the caller's scope chain. This makes the super-receiver classification work for template-class heritage shapes like `Base<T>::method()` and `outer::v1::Base<T>::f()`. Three fixtures + four tests: - `cpp-phase5-u1-u3-qualified-base-call`: `template<class T> struct Derived : Base<T>` with `Base<T>::method()` inside a template body. Asserts NO mis-routing (count = 0) — documents the V1 gap that template-class inheritance isn't captured as EXTENDS by the legacy DAG, so MRO walks are empty and the super branch can't dispatch. The composition still works correctly: U1's template-arg-stripping classifies `Base<T>` as a super candidate, but the empty-MRO terminates without false edges. - `cpp-phase5-u2-u3-adl-from-derived`: `Derived : Base<T>` where `Base::record` shadows `audit::record`. Unqualified `record(e)` inside the template body should resolve via ADL to `audit::record` (because U3 + the `isFileLocalDef` class- owned filter suppress `Base::record`). Asserts 1 edge to audit.h and 0 edges to base.h. - `cpp-phase5-u3-u5-inline-base`: `template<class T> struct Derived : outer::v1::Base<T>` where `v1` is inline. Unqualified `f()` inside `Derived<T>::g()` should NOT bind to Base::f (dependent-base suppression even across inline namespace prefix). Asserts count = 0. Phase 5 tests asserting no-false-positives are gated under LEGACY_RESOLVER_PARITY_EXPECTED_FAILURES.cpp — legacy DAG over- resolves without the template-arg-stripping qualified-receiver path and without two-phase dependent-base suppression. 162/162 cpp integration tests pass under REGISTRY_PRIMARY_CPP=1; 152 pass + 10 skipped under =0 (legacy parity baseline). --------- Co-authored-by: HuangWenjie <zhoudeng.hwj@alibaba-inc.com> Co-authored-by: Gergo Magyar <gergomagyar@icloud.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> |