Skip to content

fix(ts-lsp): type imported class receivers for cross-file method calls (#1354) - #2407

Merged
DeusData merged 1 commit into
mainfrom
fix/issue-1354
Oct 1, 2026
Merged

DeusData merged 1 commit into
mainfrom
fix/issue-1354

Conversation

@DeusData

Copy link
Copy Markdown
Owner

A method call on an instance of a class imported from another file got no CALLS edge. The same code in the class's own file resolved. There were two receiver-typing defects in internal/cbm/lsp/ts_lsp.c:

  • new C() used the wrong module. It qualified a bare class name against the current module. It now follows TS scoping: a local class first, then an unambiguous import binding, then the old spelling.
  • Class-named files were typed as the module. An import value ending in .<name> was read as an already-qualified symbol QN. When a file is named after its class (Foo.ts exports Foo), the receiver was typed as the module instead of the class. The registry now decides the ambiguous case.

Both resolve through the existing shared cross-file registry. No name-based fallback is added. The only added cost is hashed lookups during the walk: none during registration, no tail-scan, and the shared registry is never written.

Tests:

  • ts_lsp: tslsp_crossfile_new_expression_receiver_issue1354 and tslsp_crossfile_same_name_file_receiver_issue1354, on the production shared-registry path.
  • pipeline: pipeline_ts_crossfile_new_instance_method_call_issue1354, end to end, with a same-file control.
  • All fail before the fix, pass after it, and fail again when it is reverted.
  • ts_lsp, the C/LSP repro suites, registry, pipeline, parallel and complexity are green (1,626 tests), and make -f Makefile.cbm lint-ci is clean.

Proof on typeorm/typeorm (3,593 .ts files):

before after
lsp_ts_method edges 2,551 3,130
of which cross-file 696 1,275 (+83 %)
edges lost — 0
other strategies — unchanged
index time 6.35 / 6.38 s 6.37 / 6.51 s

A sample of 20 new edges, each checked against the declared receiver type in the source, was 20/20 correct.

No overlap with #2305 (unresolved-call coverage signal). Constructor parameter properties, unannotated field initializers and tsconfig paths alias imports are separate gaps and are not covered here.

Thanks to @artaommahe for the sharp same-file control, @braggintime for the 0.8.1 → 0.9.0 edge census, and @chonorov for confirming on v0.10.8.

Fixes #1354

#1354)

A method call on an instance of a class defined in another file produced
no CALLS edge, while the identical code in the class's own file resolved
via lsp_ts_method. The type-aware tier typed the receiver wrongly in two
places, so the method lookup in the shared TS registry missed and the
call fell to method_not_in_registry:

1. `new C()` always qualified a bare constructor name against the
   CURRENT module. `const t = new ImportedClass(); t.m()` and
   `new ImportedClass().m()` were typed as a phantom
   `<this module>.ImportedClass`. The constructor type now follows TS
   scoping: a class declared in this module, else the class an
   unambiguous import binding names, else the old spelling.

2. ts_import_symbol_qn (and the copy of its heuristic in
   resolve_type_with_imports) treated an import value ending in
   ".<name>" as an already-qualified symbol QN. For the dominant TS
   convention of a file named after its class (`Foo.ts` exports
   `class Foo`, module `x.Foo`, class `x.Foo.Foo`), the receiver was
   typed as the MODULE, for `new`, annotations and parameters alike.
   The registry now decides the ambiguous case: a registered
   `module.name` wins, else the value is the symbol QN as before.

Both are resolved through the existing shared cross-file registry; no
name-based fallback is reintroduced. Cost is one or two hashed lookups
on the finalized overlay / sealed shared registry, per `new` expression
or per ambiguous import spelling during the walk; nothing is looked up
during registration and nothing is written to the shared registry.

typeorm (3,593 .ts files, shallow f279fd13): lsp_ts_method 2,551 ->
3,130, all 579 new edges cross-file (cross-file lsp_ts_method 696 ->
1,275), 0 edges lost, other strategies unchanged, index time
unchanged. 20 sampled new edges checked against source: 20 true
positives.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeScript: cross-file method calls produce no CALLS edge — type-aware tier resolves same-file calls only, so trace_path(inbound) returns 0 callers

1 participant