Repository navigation
Description
On current main (847f1df), the override-dispatch edges (synthesizedBy: interface-impl) link a base-class method to a subclass method of the same name even when one is static and the other is not. A static method doesn't override an instance method (or the reverse), so a call to one never runs the other.
TypeScript, a.ts:
class B { s(): void {} }
class A extends B {
static s(): void {}
}Result: B::s -> A::s (calls, provenance: heuristic, synthesizedBy: interface-impl). Expected: no edge. The nodes do record the difference (B::s has is_static = 0, A::s has is_static = 1); the synthesizer matches by name only (
codegraph/src/resolution/callback-synthesizer.ts
Lines 1662 to 1680 in 847f1df
| const impls = implsByKey.get(keyOf(bm)); | |
| if (!impls) { | |
| if (promotes) unmatched.push(bm); | |
| continue; | |
| } | |
| for (const m of impls) { | |
| if (added >= MAX_CALLBACKS_PER_CHANNEL) break; | |
| if (bm.id === m.id) continue; | |
| const key = `${bm.id}>${m.id}`; | |
| if (seen.has(key)) continue; | |
| seen.add(key); | |
| edges.push({ | |
| source: bm.id, | |
| target: m.id, | |
| kind: 'calls', | |
| line: bm.startLine, | |
| provenance: 'heuristic', | |
| metadata: { synthesizedBy: 'interface-impl', via: m.name, registeredAt: `${m.filePath}:${m.startLine}` }, | |
| }); |
JavaScript has the same edge, plus a second cause: JS methods never get isStatic at all. javascriptExtractor has no isStatic hook (
| export const javascriptExtractor: LanguageExtractor = { |
codegraph/src/extraction/languages/typescript.ts
Lines 233 to 239 in 847f1df
| isStatic: (node) => { | |
| for (let i = 0; i < node.childCount; i++) { | |
| const child = node.child(i); | |
| if (child?.type === 'static') return true; | |
| } | |
| return false; | |
| }, |
is_static returns None for the JS variants (codegraph/codegraph-kernel/src/tsjs/mod.rs
Lines 1074 to 1081 in 847f1df
| /// TS has an isStatic hook; JS does not (None = field absent). | |
| fn is_static(&self, node: Node) -> Option<bool> { | |
| if self.variant.is_ts() { | |
| Some(self.has_keyword_child(node, "static")) | |
| } else { | |
| None | |
| } |
class B { static s() {} s() {} }
class A extends B { static s() {} }both B::s nodes are stored with is_static = 0, and both get an edge to A::s. So a check in the synthesizer alone wouldn't cover JS.
Likely fix (inferred, not tried): give the JS extractor the same isStatic hook as TypeScript (and the kernel's JS path the same answer, for parity), then skip a base/override pair in the interface-impl pass when their isStatic differs. JS isStatic would also reach the other places that read it, which may change some JS resolution.
Searched open issues and PRs for interface-impl, static and override: none cover this. #2505 handles super.m() calls and static/instance in call resolution, not these synthesized edges.
Repro: index either file with codegraph init, then list calls edges from the DB (Linux / WSL2, Node 22).
Activity
yang0228 commentedon Oct 22, 2026
Reproduced on
mainat0de94978and opened #2775 with a fix covering both causes.JavaScript now records static modifiers through both the WASM extractor and the native JS/JSX kernel, using the same check as TypeScript. The JS/TS override-dispatch pass skips pairs whose static flags differ, while preserving same-kind matches. Regression coverage includes both mismatch directions, classes with static and instance methods sharing a name, callable class fields, and a Java control to keep other languages' matching unchanged.
Validation: the full suite passed 8,447 tests with 27 conditional/platform skips; the actual native kernel was rebuilt and tested for extraction parity, and the WASM path was tested separately. All 15 new regression/control cases also passed on Node 22, and the production build passed. Details are in the PR.
Re-index JavaScript and TypeScript projects after upgrading. Validation was on macOS with Node 22 and 24; Linux/WSL was not exercised directly.
fix(js,ts): a static and an instance method of one name are not an ov…
colbymchenry commentedon Oct 22, 2026
Thanks for the clear report and the two repros, @inth3shadows. Both causes were as you described: the override pass matched methods by name alone, and JavaScript members never recorded
static, in either the extractor or the native kernel.#2813 is merged to
mainand fixes both. In JavaScript and TypeScript, a static method now pairs only with a static one, and an instance method only with an instance one. On webpack's source it removes 15 such links, likeModule'sdeserialize(context)reaching the subclasses'static deserialize(context)factories, and changes no other edges.Thanks also to @yang0228, whose #2775 took the same approach. It ships in the next release; re-index JavaScript and TypeScript projects after upgrading.
fix(js,ts): static and instance methods override only their own kind (c…
fix(js,ts): .tsx and .jsx classes link their overrides as .ts and .js…
fix(rust): JS/TS static and instance methods override only their own …