fix(extract): port cross-language isolation fixes from upstream PR 2503 (#24, #33) - #34
Merged
Merged
Conversation
…sources (#24) The C++, Swift, TypeScript and Python member-call resolvers built their receiver-type index from every type-like node in the corpus, so a receiver's declared type name was matched against class definitions written in ANY language. #8 (PHP/ObjC) and #10 (Java/C#) closed the same defect on the other six; these were the copies left over. The defect cuts both ways, so it is two fixes per resolver: * a foreign class TYPES the receiver, minting a cross-language edge that should not exist — `Lead lead; lead.search()` in C++ bound to a Python `class Lead` at INFERRED; * a foreign class merely SHARING the short name pushes the single-definition god-node guard to 2 and silently suppresses the correct same-language edge. Python needed more than a copy-paste on both arms. Its class index is built from the SOURCES of `method` edges rather than from a scan over `all_nodes`, so the suffix test goes on the looked-up owner node; and its `module.func()` arm matched any corpus file whose stem equalled the receiver, so `import lead` beside a `lead.ts` bound the call to a TypeScript function at EXTRACTED. C++ had no suffix tuple yet. The new `_CPP_RESOLVER_SUFFIXES` includes `.h`, because a C++ class is routinely declared in a header — `.h` routes to extract_cpp or extract_objc by content, so it sits in both the C++ and the ObjC definition-index sets. That is the accepted compromise: the two are isolated from every other language but not from each other, which no suffix can fix. Raw-call ownership for C++/ObjC is unaffected and stays on the extractor- stamped `lang`, for exactly that reason. The tuple also now feeds the `cpp_member_calls` registration, so registration and scoping cannot drift. All ten indexes now go through one `_is_owned_definition` predicate, the definition-index twin of `_raw_call_is_owned`. Tests: ten new cases in tests/test_mixed_corpus_member_calls.py, both defect directions for C++, Swift, TypeScript and the Python class arm, plus the Python module arm's leak direction and its positive control. Nine were red before this change; the tenth is the module-arm positive control. The module arm's suppression direction is NOT covered here — two same-stem files salt the FILE node ids apart while the `imports` edge target stays the bare alias, so the arm sees zero candidates rather than an ambiguous two. That is a separate defect one layer down, tracked as #33. The existing ObjC defect-1 case is widened to assert through `_cross_language_targets`, so a leak landing as a `references` edge onto the foreign TYPE (rather than a `calls` edge onto its method) is caught too. It was already green — #8 scoped that index — and stands as a regression guard. Suite: 4097 passed / 36 skipped, up from 4087 / 36 by exactly the ten new tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ibling (#33) Python, Rust, Zig, Elixir, PowerShell, Pascal and Bash all name an import's target by the imported file's bare stem id (`import lead` -> `lead`). That resolves only while the id is unique: add ANY same-stem file — a `lead.md` will do — and the two file nodes collide, so `_disambiguate_colliding_node_ids` salts them apart into `lead_py_lead` and `lead_md_lead`. The edge's target salt is keyed by the IMPORTER's source_file, which matches neither, so the edge was left pointing at an id that no longer named anything: silently dropped, along with everything downstream of it — the Python resolver's `module.func()` arm among them, which is the loose end #24 recorded and deferred here. `_hint_import_targets` stamps the `target_file` hint the disambiguator already accepts for this shape (Graphify-Labs#1814), keying the target salt by that file instead. Stamped centrally off one `_IMPORT_STEM_LANGUAGES` table rather than in each extractor, because an extractor sees one file and cannot know which of the corpus's same-stem candidates the id will end up naming. Bash needed a second fix. Its `resolve_bash_source_edges` pass runs AFTER disambiguation but re-derives file ids from the path formula, so under a collision it emitted the edge twice — once correct, once dangling — and its source-backed `calls` edges named nodes that did not exist. It now receives the ids as they actually stand, via `_file_nids_by_path`; the formula remains the fallback for paths the caller did not resolve and for direct callers that pass nothing, so existing behavior is untouched. Audited and NOT vulnerable, so untouched: TS/JS and C/C++/ObjC already stamp their own equivalent protection; Julia, Fortran and Verilog target an importer-scoped node; Dart mints its own stub nodes; Ruby/PHP emit no file-targeting import edges. Tests: ten new cases. tests/test_import_alias_disambiguation.py parametrizes control-vs-collided corpora over the six non-Python languages (seven cases — PowerShell has two import spellings), each asserting BOTH directions so an inert fixture cannot pass by accident, plus one case pinning that the transient `target_file` hint never reaches the graph. tests/test_mixed_corpus_member_calls .py adds the Python pair: the `module.func()` call edge and the `imports` edge one layer under it, asserted separately because the module arm is only one consumer. Nine were red before this change; the hint-leak case passed vacuously beforehand (nothing stamped the hint yet) and is a real guard only now. Parity evidence for the no-collision case, on a 14-file corpus covering all seven languages with every stem unique: the serialized node+edge set is bit-identical across this change, md5 4c6281a76937fe051d641673f486c89a both sides. Adding a same-stem `.md` beside each of the seven targets goes from 8 import edges / 7 dangling to 7 / 0 — the extra edge before being exactly the Bash duplicate. Suite: 4107 passed / 36 skipped, up from 4097 / 36 by exactly the ten new tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #24. Fixes #33.
What
Port of upstream PR Graphify-Labs/graphify#2503 commits 2–5 to the fork's diverged
v8, adapted rather than cherry-picked (the fork already had ObjC index scoping from2e68ec6and the raw-call ownership filter from #28's fix; the PHP feature surrounds the resolver code here).49c25ff(closes Swift, Python, TypeScript and C++ member-call resolvers still build unscoped receiver-type indexes #24) — scopes the last four receiver-type indexes (Swift, TypeScript, C++, and Python's class + module arms) to their own sources, and unifies all ten language indexes behind one_is_owned_definitionpredicate so index and registration cannot drift..hstays in both the C++ and ObjC sets — those two are isolated from everyone else but not from each other (needs extractor-tagged definition nodes; recorded on the issue).10cdee2(closes Import edges die when a same-stem foreign sibling exists (Python, PowerShell, Zig, Rust, Pascal, Elixir, bash) #33) — a same-stem foreign sibling (lead.mdnext tolead.rs) no longer kills import edges: thetarget_filehint pass is generalized to a language table (Python, PowerShell, Zig, Rust, Pascal, Elixir), and bash'ssymbol_resolution.pyconsults post-disambiguation ids instead of re-deriving them by path formula, deduping its duplicate dangling edge.Test evidence
cec9cdd: 4087/36 → after49c25ff: 4097/36 → after10cdee2: 4107 passed / 36 skipped / 0 failed (+10 each, zero pre-existing tests changed status).Deliberately untouched (recorded on the issues)
Python's missing builtin-globals guard (out of #24's scope), the
.hC++/ObjC ambiguity, and the Go import dangle that reproduces without any collision (not this defect family; unasserted pending its own investigation).🤖 Generated with Claude Code