feat(js): functions that call a method on an imported constant now appear as its callers

Merged
BO
2d ago
+228-37

callers on an imported constant now lists the functions that call a method on it. LIST.includes(x) after import { LIST } from './constants', store.get(k) through a default import and useStore.setState() through an aliased one get a calls edge from the calling function to the constant, in JavaScript, TypeScript, Vue, Svelte and Astro. Before, the fork linked a call through an import only to a function, method, class or component (or to a store through its getState), so these calls were invisible to callers and impact: on supabase, callers safeLocalStorage lists 29 files and no functions in the fork, against 43 functions in upstream baf19067.

Change

One condition in bound_receiver_claim (codegraph-kernel/src/resolve/method_call.rs, JavaScript/TypeScript branch) accepted a constant or variable only when the method was getState. It now accepts one for any method. The guards stay as they were:

  • A name a parameter or local hides (({ expect }) => expect.getState()) resolves to that binding, not the import, so it links nothing.
  • A name a Svelte markup block declares ({#each rows as count}, {#snippet item(count)}, {#await p then count}, {@const count = row}) does not reach the script's import of that name. Markup has no binding rows, so the template scan in src/extraction/svelte-extractor.ts now tracks the names the enclosing blocks declare and skips calls rooted at them (found by the Greptile review; it applies to imported functions too). A call after the block closes still links.
  • A deeper chain (supabase.auth.signUp()) is refused before the kind check.
  • A receiver whose module never resolves is still not guessed by its method's name.

The seven link expectations in sync-import-member-retry.test.ts go back to upstream's. Four more expectations in js-family-receiver-fixes, kernel-resolve-parity and resolution were written for the old rule and now expect the link to the constant (each stays unlinked to an unrelated same-named function or class method). A new test, imported-constant-member-calls.test.ts, covers a constant, a variable, a shadowing parameter and local, a deeper chain and the Svelte block forms above; it fails on the base kernel and passes here.

Evidence

From a read-only study of the base fork (5b5ac434) against upstream baf19067 on vite, vitest, svelte and supabase:

  • Upstream adds 4,426 links of this kind that the fork lacks. 156 seeded samples read against source: 153 correct, 3 ambiguous (deep chains), none wrong. Screening all 4,426 against the fork's binding rows finds 15 wrong, all in vitest, where a parameter shadows the import; this change makes none of them.
  • This change on the same corpora: vite +1,362 call edges (+4.9%), vitest +2,595 (+3.7%), svelte +135 (+0.2%), supabase +1,662 (+0.5%), none lost. It reproduces 4,006 of upstream's 4,426 and makes 1,748 more (the fork follows ~utils aliases and export * chains that upstream does not); a seeded sample of 49 of those read 49 correct.
  • Most added edges are in test and playground code: vi and expect account for 1,910 of vitest's links, and 1,217 of vite's land on one Playwright page variable.
  • I reproduced the vite, vitest and svelte edge totals on this head (29,238 / 72,769 / 59,520). Supabase and the sampling come from the study.

Checks

On head 178cd22e:

CheckResult
npm run build:kernel (clippy -D warnings)exit 0
npx vitest run (full)751 files, 9,293 passed, 40 skipped, 0 failed
kernel-golden-dumps with UPDATE_GOLDEN=18 passed, no .dump changed
eval:precision vite, vitest, svelteexit 0; known-wrong cases absent 6/6, 1/1, 1/1; controls present 2/2 on vite; svelte edge total 59,519 (59,520 before the markup fix)
npx tsc --noEmit, npm run check:agent-docs, readme-syncexit 0
New test on the base kernelfails ([] against the two expected links)

Index time shows no clear change (vite 4.0 s either way; one run each).

README rows checked

  • Kept-resolution paragraph (line 128): its sentence that a call on an imported constant stays unresolved is replaced with the new behavior.
  • "About this fork" tables and the full-index numbers: unchanged; this adds 0.2-4.9% edges and no measured time.
  • CHANGELOG: new Fixes entry; the fork-only note that these calls stay unlinked after a sync is removed; the Kotlin entry's "7% and 6% more peak memory" now says it holds for that change alone.

Not covered

  • Links to static class members (22 vitest calls such as TestRunner.getCurrentTest(), all correct in the sample) still need property targets and stay unlinked.
  • Whether these should be references rather than calls for receivers that are not functions.
  • Arrow-function parameters inside a markup expression (rows.map((count) => count.set(1))) in Svelte and Astro still resolve to a script import of the same name; the gap already existed for imported functions.
  • Kotlin, Java, Python and the other languages: only the JavaScript/TypeScript branch changes.
  • Promoting the build: separate step.

Summary by cubic

A member call on an imported constant or variable now links to that constant, so callers on one lists the functions that use it, not only the files that import it.

Before, a call through an import linked only to a function, method, class or component, or to a store through its getState. Now LIST.includes(x) after import { LIST } from './constants', store.get(k) through a default import and useStore.setState() through an aliased one give the calling function a calls edge to the constant, in JavaScript, TypeScript, Vue, Svelte and Astro.

  • A name a parameter, a local or a Svelte markup block hides — ({ expect }) => expect.getState(), {#each rows as count} — a deeper chain like config.api.fetch(), and a receiver whose module never resolves still link nothing.
  • Added tests cover a constant, a variable, shadowed names, a deeper chain and Svelte markup blocks; existing expectations are updated to match.
  • Full-index edge totals rise 0.2–4.9% on vite, vitest, svelte and supabase with none lost; most added edges land in test and playground code.
  • Re-index JavaScript and TypeScript projects after upgrading.