Repository navigation
Builtin-name list is checked by every language, so Go/Rust/Ruby/PHP/C/C++ lose real call edges — but it also hides wrong ones (follows #2296) #2313
Description
When graphify links a call to its definition, it skips any callee whose name is in _LANGUAGE_BUILTIN_GLOBALS. That list mixes builtins from Python, JS, Swift and others, and every language checks it. So a Go method named set gets no incoming calls, because set is a Python builtin.
The obvious fix is to give each language its own list. Doing only that would make things worse: on a real Go repo, most of what the list blocks today are wrong edges that should stay blocked. Details below.
Example
From 3x-ui. One file, one struct, two methods:
// internal/web/job/node_traffic_sync_job.go
type atomicBool struct { mu sync.Mutex; v bool }
func (a *atomicBool) set() { ... } // L63
func (a *atomicBool) takeAndReset() { ... } // L71
j.structural.set() // L147
j.structural.takeAndReset() // L234
j.structural.set() // L379(*atomicBool).set incoming calls = 0 ← called twice
(*atomicBool).takeAndReset incoming calls = 1 ← correct
The two methods are on the same struct, in the same file, and are called the same way. The only difference is that set is on the list.
Where the check runs
graphify/extractors/go.py:395 (Go builtins are checked first since #2296)
graphify/extractors/rust.py:372
graphify/extractors/elixir.py:203
graphify/extractors/engine.py:4158 Ruby, C#, Java, Kotlin, PHP, Python, JS/TS, Swift, C, C++
graphify/extract.py:5305 shared cross-file pass, all languages
At each extraction site the check blocks both the in-file match and the hand-off to cross-file resolution. At extract.py:5305 it runs before import evidence is looked at, so a name that is explicitly imported is still dropped.
How widespread
I made a two-file repo for each language: file a defines a name and file b calls it from another file. I tested 29 common names, plus a made-up control name zqxwidget.
| language | control resolves | names blocked (of 29) |
|---|---|---|
| Python | yes | 0 |
| JS, TS, Rust, Ruby, C, C++ | yes | 29 |
| Go | yes | 27 |
| PHP | yes | 28 |
| Elixir | no, even the control fails | not measured |
Changing one letter makes each name resolve (len → lenx, Error → Errorz), so the list is the cause. Python is not affected because it resolves names earlier through symbol_resolution, even though Python builtins are the biggest part of the list.
Why splitting the list alone is wrong
On a 466-file Go repo, turning the list off adds 45 edges:
| target | edges | definitions in repo | real? |
|---|---|---|---|
Error |
31 | 6 | no |
Int |
5 | 1 (stub, no source file) | no |
Map |
3 | 1 (stub, no source file) | no |
String |
3 | 1 (but fmt.Stringer is everywhere) |
doubtful |
set |
3 | 2 | 2 yes (the example above) |
Six types in that repo define Error(). Without type information graphify can't tell which one err.Error() calls. If those 31 calls were linked by name, one node would collect calls that belong to six types. That is the problem #2296 removed. Today Error is blocked only because JS has a global Error. The reason is wrong but the result is right. Split the list per language and those 31 wrong edges come back in every Go repo.
The TS/JS side of the same repo has 512 blocked edges. Almost all of them are names with several definitions (set 260 edges across 6 definitions, map 158 across 3, filter 39 across 2). Every single-definition case I checked (next, range, iter) was inside a minified vendor bundle.
Suggested order
- When linking a bare name across files, refuse if the repo has more than one definition with that name. This applies whether or not the name is on any list.
disambiguate_ambiguous_candidatesdoes something close for the ambiguous case, and this would be the default. - Then split the list per language.
Errorstays blocked for the right reason andsetcomes back. - Where possible, decide by the shape of the call instead of by name lists. fix(go): filter Go predeclared functions from call-target resolution #2296 checks whether the callee is a bare identifier, which is safe at any stage.
Not checked
- Real-repo numbers exist only for Go and TS/JS. Rust, Ruby, PHP, C, C++, C# and Java were confirmed only on the synthetic repos.
- Elixir was not measured, because the control name doesn't resolve there either.
- In-file matches were spot-checked only for Go and Rust.
- I only checked Go for the opposite case: a language whose builtins are on no list and end up getting wrongly linked.
Measured on upstream/v8 at ecfcd16 plus the #2296 head.
Activity
chore: bump to 0.9.31; changelog for #2308/#2299/#2324/#2309/#2266/#2313
safishamsi commentedon Jul 30, 2026
The Go-specific symptom is addressed in v0.9.31 (#2296): predeclared-function filtering is now scoped to Go bare-identifier callees + a Go raw_calls gate, so it no longer touches the shared behavior. The broader design point you raise here — that
_LANGUAGE_BUILTIN_GLOBALSis applied cross-language at extraction time and doubles as an accidental multi-definition god-node guard — remains open. Your proposed order (multi-definition god-node guard first, then per-language split, then syntactic-shape evidence over name lists) is the right plan; leaving this open to track it. https://github.com/Graphify-Labs/graphify/releases/tag/v0.9.31chore: bump to 0.9.31; changelog for Graphify-Labs#2308/Graphify-Labs…
iakshkhurana commentedon Oct 1, 2026
Hi @safishamsi, I'd like to work on this, following the order you approved:
I'd send step 1 as its own PR with tests, then step 2 as a follow-up, so each one is easy to review. OK to go ahead?
[-]_LANGUAGE_BUILTIN_GLOBALS is applied at extraction time in every language: Python/Swift/JS builtin names suppress genuine Go/Rust/Ruby/PHP/C/C++ edges — but it is load-bearing as an accidental god-node guard (follows #2296)[/-][+]Builtin-name list is checked by every language, so Go/Rust/Ruby/PHP/C/C++ lose real call edges — but it also hides wrong ones (follows #2296)[/+]PathGao commentedon Oct 3, 2026
@iakshkhurana I rewrote the issue body in plainer language, so it should be easier to follow now. The numbers and the suggested order are unchanged.
iakshkhurana commentedon Oct 3, 2026
Can you assign me please?
PathGao commentedon Oct 3, 2026
I am not a part of team so I cant assgin anybody