-
-
Notifications
You must be signed in to change notification settings - Fork 11.2k
Let builtin named member calls through to cross file resolution - #3392
Let builtin named member calls through to cross file resolution #3392ayushcodes10 wants to merge 1 commit into
Conversation
_LANGUAGE_BUILTIN_GLOBALS is one union across every language, right for a BARE call (String(x) really would become a god node) but wrong for a MEMBER call. open is a Python builtin and Set a JavaScript one, so session.open() in Swift or _server.Set() in C# named after another language's builtin was silently discarded outright: no same file edge, but also no raw_calls entry, so cross file resolution never got a chance to try it. A member call carries a receiver, so it is not the ambiguous case the union guards against. Lets a builtin named member call reach the existing tgt_nid = None deferral every receiver typed resolver already relies on for other cases (a capitalized Python receiver, any Java member call, and so on), so it can only ever reach an edge through a guarded, receiver typed resolver, never the unguarded bare name path a real god node would need. Verified against real cross file setups for Python (class qualified call) and Swift (field typed receiver, reproducing the reporter's own AppleReadyStreamFinder.find -> RemuxPlayback.open example), plus that a same file member call still cannot bind to an unrelated bare function sharing the builtin name. Scoped to the shared engine only. go.py and rust.py carry the same outer filter, and the reported issue asks for the same change there too, but tracing where their raw_calls entries actually go shows the shared cross file resolver skips every is_member_call entry unconditionally (extract.py, the "obj.log() has no import evidence" guard) regardless of builtin status, and neither language has a dedicated receiver typed resolver the way Python, Java, C#, Swift, TypeScript, C++, and Objective-C do. Making the same change there would be safe but currently inert, so left out rather than shipping a change with no verifiable effect; worth revisiting once either language gets its own receiver typed cross file resolver. Fixes Graphify-Labs#3381.
ayushcodes10
commented
Sep 7, 2026
@safishamsi opened this against #3381 (a member call named after another language's builtin, e.g. Swift's session.open() colliding with Python's open, was silently discarded with no raw_calls entry at all). Verified against real cross-file Python and Swift setups, including the reporter's own AppleReadyStreamFinder.find -> RemuxPlayback.open example. Scoped to the shared engine only; the PR description explains why I left go.py/rust.py out (traced their raw_calls path and it's currently inert there, no receiver-typed resolver to consume it). Happy to address any feedback.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Fixes member calls named after another language's builtin (Session.open(), _server.Set()) being discarded entirely — they now emit a raw_calls entry so cross-file resolution can bind them, while still forcing tgt_nid = None so they only ever resolve through the guarded, receiver-typed path and never fall back to an unrelated bare function of the same name. Bare calls remain filtered against _LANGUAGE_BUILTIN_GLOBALS to avoid god nodes. Adds tests covering both the cross-file resolution and the god-node guard.
No blocking issues surfaced. 3 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1008 functions depend on the 607 functions this change touches.
Health — this change adds coupling hotspots:
- new:
_extract_generic()— 18 callers, 26 callees - new:
extract_js()— 85 callers, 4 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
extract_objc()— 27 callers, 9 callees - new:
extract_julia()— 17 callers, 7 callees - new:
extract_cpp()— 27 callers, 3 callees - new:
extract_vue()— 10 callers, 7 callees - new:
walk()— 1 callers, 58 callees - ...and 8 more — each is listed as a finding
Verification — 1008 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 948 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 16 more finding(s) on lines outside this diff (see the check run).
What
_LANGUAGE_BUILTIN_GLOBALSis one union across every language, right for a bare call (String(x)really would become a god node) but wrong applied to a member call:openis a Python builtin andSeta JavaScript one, sosession.open()in Swift or_server.Set()in C# was silently discarded outright — no same-file edge, but also noraw_callsentry, so cross-file resolution never got a chance to try it.A member call carries a receiver, so it isn't the ambiguous case the union guards against. Lets it reach the existing
tgt_nid = Nonedeferral every receiver-typed resolver already uses for other cases, so it can only ever reach an edge through a guarded, receiver-typed resolver — never the unguarded bare-name path a real god node would need.Verified
Session.open()cross-file class-qualified call now resolves (EXTRACTED).AppleReadyStreamFinder.find -> RemuxPlayback.opennow resolves (INFERRED, via the field-typed receiver resolver).other.open()does not bind to an unrelated same-file bareopen()function, and a bare call to the actual builtinopen(...)still produces no phantom edge.Scope:
go.py/rust.pyintentionally not touchedThe issue asks for the identical change in
extractors/go.py(~L480) andextractors/rust.py(~L372) too. I traced where theirraw_callsentries for member calls actually go:extract.py's shared cross-file resolver skips everyis_member_callentry unconditionally —— regardless of builtin status, and neither Go nor Rust has a dedicated receiver-typed resolver the way Python (
_resolve_python_member_calls), Java, C#, Swift, TypeScript, C++, and Objective-C do. Making the same edit there would be safe (it wouldn't reintroduce the god-node risk) but currently has zero observable effect on the final graph, since nothing downstream would ever consume the preservedraw_callsentry. Rather than ship an inert change, I left it out — worth revisiting once either language gets its own receiver-typed cross-file resolver.Fixes #3381.