fix(extract): stop a built-in base class from inheriting across languages (#2812) - #2939
fix(extract): stop a built-in base class from inheriting across languages (#2812)#2939ousamabenyounes wants to merge 1 commit into
Conversation
…ages (Graphify-Labs#2812) A class that extends one of its own language's built-in exception types — `class FooApiException extends \Exception` in PHP — mints a sourceless `Exception` stub, and `_rewire_unique_stub_nodes` bound that stub to the one same-labelled real definition anywhere in the corpus. In a PHP + TypeScript monorepo that definition was an unrelated TypeScript `Exception` class, so PHP exception classes inherited from a TS one and inflated it into a god node. The rewire now refuses a supertype edge whose referring file names its own language's built-in throwable while the resolved target lives in another language family. The decision is per edge, next to the existing C#-scoped carve-out, so one shared stub still resolves for referrers whose language does own that class, and PHP's case-insensitive identifiers are folded. Legitimate cross-language type rewires are unchanged.
There was a problem hiding this comment.
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
Blocks cross-language stub rewiring for built-in exception types in _rewire_unique_stub_nodes: a PHP class FooApiException extends \Exception no longer fuses onto a same-named TypeScript Exception class. Adds per-family built-in throwable tables (_LANGUAGE_BUILTIN_BASE_CLASSES plus a case-folded companion) and a per-edge _names_own_builtin_base guard that keeps the stub as the external base only when the referring and target families differ, leaving same-language rewires intact.
No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2038 functions depend on the 860 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 475 callers, 42 callees - new:
_rebuild_code()— 98 callers, 50 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
extract_js()— 80 callers, 3 callees - new:
dispatch_command()— 2 callers, 119 callees - new:
_get_extractor()— 26 callers, 6 callees - new:
run_pipeline()— 8 callers, 13 callees - new:
collect_files()— 17 callers, 6 callees - …and 23 more — each is listed as a finding
Verification — 2038 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: 1890 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify \_rewire\_unique\_stub\_nodes.
The verifier did not have enough to check \_rewire\_unique\_stub\_nodes, 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: not verifiable: the input domain has 81 values but only 9 distinct were tested — a small finite domain must be EXHAUSTED, not sampled (an untested input could invert the result)
· 31 more finding(s) on lines outside this diff (see the check run).
Fixes #2812.
The bug
class FooApiException extends \Exceptionin PHP mints a sourcelessExceptionstub, because the base class is not defined in the corpus.
_rewire_unique_stub_nodesthen binds that stub to the one same-labelled real definition found anywhere in
the corpus — and in a PHP + TypeScript monorepo that definition is an unrelated
TypeScript
class Exception. The reporter's ~6k-file repo got 12 phantominheritsedges from first-party PHP exception classes onto one TS class, whichpushed it into the top-10 god nodes and produced a false cross-community bridge
question.
The fix
The rewire now refuses a supertype edge when the referring file's language names
its own built-in throwable and the resolved target lives in a different language
family. Three deliberate choices:
Exceptionstub collects referrersfrom every language that names it. Deciding on the stub's referrer families as a
set would let a single TypeScript referrer re-open the cross-language bind for
the PHP ones. The check reads
edge["source_file"], and sits next to theexisting C#-scoped carve-out in the same loop.
really can declare
BookStorein one language and subclass it from another —test_extract_rewires_unique_inheritance_stub_to_real_definitionpins exactlythat, and this PR leaves it passing untouched. But
Exceptionnamed from a PHPfile is always PHP's own, so a same-named class in another language cannot be
what it refers to.
case-insensitively, so
extends \exceptionnames the same built-in(mirrors the existing
real_by_label/real_by_label_cipair, Case-folded cross-file resolution collides distinct identifiers (Path/PATH) → false super-hubs in case-sensitive languages #1581)._LANGUAGE_BUILTIN_GLOBALSwas not reused: it is a flat set consulted at callsites and has no per-language scoping, which is what this path needs.
Relationship to #2365
#2365 proposes hoisting the interop family gate to every unique match, which
would also close this issue but changes existing behaviour — it has to rewrite
test_extract_rewires_unique_inheritance_stub_to_real_definition, whosePython↔C# rewire is currently intended. This PR is additive and takes the
narrower position that only a language's own built-ins can never be foreign.
If you prefer the blanket gate, #2365 supersedes this and I'll close it.
Test verification (RED → GREEN)
RED — new tests against unmodified
v8(b2cd362), production file reverted:The failure is the reported leak verbatim:
GREEN — same tests with the fix:
Full suite, unchanged against the
v8baseline (the 4 failures are pre-existingin my local environment — 3
test_ollamabackend-detection tests read env vars Ihave set, and
test_collect_files_skips_hidden— all 4 fail identically on cleanv8):ruff checkclean on the touched files, and the fivetools.skillgenvalidators (
--check,--audit-coverage,--schema-singleton,--monolith-roundtrip,--always-on-roundtrip) all pass.