Skip to content

bug(parity): WASM/native diverge by ~150 calls edges on repo's own src/ (CHA/interface-dispatch fan-out) #2537

Description

@carlos-alm

Summary

While fixing #2413 (a narrow 3-edge WASM-vs-native divergence on this repo's own src/), a much larger divergence surfaced once the repo/corpus grew: a full build now shows 167 wasm-only and 14 native-only calls edges (not the 3 originally reported) — same node counts (15370) on both engines, edge counts diverging (wasm 6989 vs native 6836 before #2413's fix, 6838 after).

This is unrelated to #2413's root cause (already fixed: a Rust flag-only-dynamic-kinds list omitted reflection). The remaining divergence is a completely different shape, concentrated around interface/base-typed method dispatch and CHA (class-hierarchy-analysis) fan-out.

Reproduction

node dist/cli.js build src --engine wasm --no-incremental    # save src/.codegraph/graph.db
node dist/cli.js build src --engine native --no-incremental  # compare against the saved copy

Compare calls edges by (source file:line:name -> target file:line:name, dynamic).

Sample divergent edges

wasm-only (a representative sample; ~167 total):

ast-analysis/rules/csharp.ts:191:extractParamName -> types.ts:1075:TreeSitterNode.childForFieldName
ast-analysis/visitor.ts:48:initVisitors -> types.ts:1159:Visitor.init
ast-analysis/visitor.ts:67:dispatchEnterFunction -> types.ts:1162:Visitor.enterFunction
db/connection.ts:190:flushDeferredClose -> types.ts:2685:BetterSqlite3Database.close
db/connection.ts:524:openRepoNative -> types.ts:3040:NativeDatabase.getBuildMeta

native-only (~14 total) — notably, native resolves to MULTIPLE concrete close overloads where WASM resolves to the interface-typed types.ts declaration only:

db/connection.ts:190:flushDeferredClose -> db/connection.ts:542:close
db/connection.ts:190:flushDeferredClose -> db/connection.ts:570:close
db/connection.ts:190:flushDeferredClose -> db/connection.ts:610:close
db/connection.ts:190:flushDeferredClose -> db/connection.ts:694:close
db/connection.ts:524:openRepoNative -> db/connection.ts:542:close   (+ 3 more overloads)
db/connection.ts:542:close -> db/connection.ts:570:close            (cross-overload edges)

The pattern: for a call through a value typed as an interface/type alias declared in types.ts (e.g. TreeSitterNode, Visitor, BetterSqlite3Database, NativeDatabase), WASM appears to resolve (or fall back) to the interface's own declaration site in types.ts, while native's CHA resolves to every concrete implementor it can find (sometimes several, including what look like unrelated close functions cross-referencing each other). Neither behavior is obviously "the accepted one" without deeper investigation — this needs a look at how each engine's method-hierarchy/CHA resolution handles a receiver typed as an interface declared in a .d.ts-style ambient/shared types file versus a receiver typed as a concrete class.

Scope

Per CLAUDE.md, a divergence means one engine has a bug — this is not a "parity gap" to document. Given the scale (~180 edges across dozens of call sites) and that it clusters around one specific mechanism (interface-typed receiver dispatch via types.ts), this is likely 1-3 root causes rather than 180 separate bugs, but needs investigation to confirm which engine is right (or whether both are partially wrong) before scoping a fix.

How it surfaced

Found while verifying the fix for #2413 against the current src/ tree — the original issue's exact 3-edge repro no longer matched (grew from 3 to ~180 edges), and after fixing #2413's actual root cause (a missing reflection entry in native's flag-only dynamic-kind list), this much larger, structurally different divergence remained.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions