Skip to content

fix(resolution): resolve TS/JS calls on a new C() receiver through C - #1945

Open
danusha2345 wants to merge 2 commits into
colbymchenry:mainfrom
danusha2345:fix/new-expression-receiver
Open

danusha2345 wants to merge 2 commits into
colbymchenry:mainfrom
danusha2345:fix/new-expression-receiver

Conversation

@danusha2345

@danusha2345 danusha2345 commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Problem

A TS/JS call on a constructor receiver, new X(args).m(), reaches the resolver as the bare name m, in both the TS extractor and the kernel. Exact-match then binds it to any project method called m:

  • new RegExp(p).exec(s) → SqliteDatabase::exec
  • new Array(n).fill(0) → TextOptions::fill
  • new TreeSitterExtractor(..).extract() → RazorExtractor::extract

new Runner().run() reached Runner::run only when no other project method was called run.

Fix

  • Encoding. The extractor, TS and kernel alike, now emits new C().m. This form can't be mistaken for a static call (C.m) or a factory call (C().m). new ns.C() keeps its path, and a receiver that isn't a plain name or member chain emits nothing.
  • Resolution. The resolver looks up m on C through resolveMethodOnType, which checks that the method exists there.
    • An inherited method is found on the conformance pass, once extends edges exist.
    • A named-import alias (import { Runner as R }) maps back to Runner.
    • A built-in or external C (RegExp, URL, Map, Date, TextDecoder…) gets no edge.
    • A built-in name counts as a project class only when the file imports or declares it, so a project class that happens to be called Map doesn't take every new Map().get() in the repo.
  • ES5 constructors (second commit). new Params().render() on a constructor function has no class to look the method up on. When no class method matches and Params is one project function, a same-named function assigned onto a prototype in that function's file (prototype.render = function render() {…}) is the target, so the edge main already draws for that shape is kept.

No EXTRACTION_VERSION bump: main already raised it to 28 for this release.

Rebased onto current main. #2363 (functions of a named object literal as Owner::member), #2357 (top-level destructuring) and #2362 (per-file caches in JS resolution) changed the same files, but none of them changes how a new C() receiver is extracted or resolved: on current main new RegExp(p).exec(s) still reaches the resolver as the bare exec.

Measured

Every changed edge was read at its call site. Node counts are unchanged.

corpus lost gained
codegraph (current main → this branch) 61, all wrong: new RegExp(..).exec(..) → SqliteDatabase::exec ×49, new Array(..).fill(..) → TextOptions::fill ×10, new TreeSitterExtractor(..).extract() → RazorExtractor::extract / CfmlExtractor::extract ×2 40, all correct: new ToolHandler(cg).execute(..) → ToolHandler::execute ×38 (no edge on main), new TreeSitterExtractor(..).extract() → TreeSitterExtractor::extract ×2
vite (before the rebase) 1 (new RegExp(context).test(url) → a project test, wrong) 0
excalidraw (before the rebase) 1 (new Array<boolean>(n).fill → DebugPolygon::fill, wrong) 0

Known limit: a default-imported class whose local name differs from its declared name (import Foo from './bar', where the class is Bar) gets no edge. Bare-name matching sometimes guessed it before. That didn't come up in any of the three corpora.

Tests

The new __tests__/new-expression-receiver.test.ts covers:

  • built-ins with no edge;
  • a project class, an inherited method, an alias and a namespaced class;
  • a plain JS file;
  • the extraction shapes.

Three of its four cases fail on current main. The kernel parity test and torture.js are extended, the kernel is rebuilt, and the TS and kernel refs are identical. The TS/JS receiver, object-literal, CommonJS and store suites, kernel-tsjs-parity, resolution.test.ts and extraction.test.ts (28 files, 1193 tests) pass with the native kernel and with CODEGRAPH_KERNEL=0.

🤖 Generated with Claude Code

@danusha2345
danusha2345 force-pushed the fix/new-expression-receiver branch 3 times, most recently from f3a3f7d to ef92763 Compare October 3, 2026 09:01
@danusha2345
danusha2345 force-pushed the fix/new-expression-receiver branch 3 times, most recently from 81bb7a0 to 98273a5 Compare October 7, 2026 16:05
danusha2345 and others added 2 commits October 10, 2026 14:05
`new RegExp(p).exec(s)`, `new Array(n).fill(x)`, `new TextDecoder().decode(b)`
reached the resolver as the bare method name and exact-matched any project
method of that name (in codegraph's own index `new RegExp().exec` onto
`SqliteDatabase::exec`, `new Array().fill` onto `TextOptions::fill`).

Extraction (mirrored in the kernel) now keeps the constructed class:
`new C(args).m()` is encoded `new C().m` (a member-chain constructor keeps its
path, `new ns.C().m`); a constructor that is not a plain name or member chain
emits nothing. The resolver resolves that shape only on C — through a named
import alias to the declared name, and on a supertype in the conformance pass
when C is a project class — so a built-in or external class yields no edge. A
JS built-in name counts as a project class only when the file imports or
declares it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ototype method

With the constructed class kept in the ref, `new Params().render()` resolved
only on a class `Params`; an ES5 constructor function has none, so the call
lost the edge main's colbymchenry#2162 keeps to `prototype.render = function render()`
in the constructor's file. When no class method matches and the constructed
name is one project function, a same-named function assigned onto a
prototype in that function's file is the target.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@danusha2345
danusha2345 force-pushed the fix/new-expression-receiver branch from 98273a5 to 852c074 Compare October 10, 2026 11:22

This branch has not been deployed

No deployments
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.

1 participant