Skip to content

fix(resolution): resolve this.#field.method() on the field's type (#1987) - #1990

Closed
danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:fix/1987-private-field-receiver
Closed

danusha2345 wants to merge 1 commit into
colbymchenry:mainfrom
danusha2345:fix/1987-private-field-receiver

Conversation

@danusha2345

@danusha2345 danusha2345 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Covers the ES private field case from #1987. The untyped list.map case in that issue is #1941.

Problem

this.<field>.<method>() (#1496) resolves on the field's declared type, but only for a public field. The extractor checks that the member is a property_identifier. An ES private field is a private_property_identifier, so this.#items.add(x) was emitted as the bare add. That name exact-matched whichever project method is called add, often the calling method itself.

Fix

  • Extraction. The this.<field> branch accepts a private field and keeps the #, emitting this.#items.add. The kernel's this_field_of mirrors this.
  • Resolution. matchMethodCall matches this.#field.method before its generic dotted pattern, whose receiver class [\w.]+ has no #, and hands it to matchTsThisFieldCall. That function's field regexes now open with a lookbehind instead of \b for a # name, because \b never matches before #.

The declared-type and new initializer patterns are unchanged, and a builtin or external field type (Set, Map, an array) still yields no edge.

Measured (main ba3c21e vs this branch, kernel on)

Calls edges on lines containing this.#field.method(:

repo before after removed (all verified wrong) added (all verified right)
node-lru-cache 24 5 24, e.g. #keyMap.get → LRUCache::get, #perf.now → Status::now 5, #perf.now → Perf::now
path-scurry 5 0 5 self-edges, e.g. #fs.lstatSync → PathBase::lstatSync 0
hono 9 7 6, e.g. #var.get → Context::get (self-edge) 4, e.g. #root.insert → Node::insert, #init.send → WSContextInit::send

lru-cache also loses 5 wrong this.#disposed?.push(…) → Stack::push edges, where #disposed is an array. Total edge counts move by exactly these amounts (lru-cache −24, path-scurry −5, hono −2).

Known limit, shared with public fields: a field typed as a type alias to a union isn't followed. lru-cache's #free: StackLike (Stack | Index[]) loses two plausible Stack::push edges, the same result free: StackLike gets today.

Tests

  • ts-this-field-call.test.ts: a #field: Mailer and a #field = new Mailer() resolve to Mailer::send over a same-file decoy Outbox::send, and this.#items.add() on a Set stays unresolved over a same-file Cart::add. Both cases fail on main.
  • kernel-tsjs-parity: the torture fixture gains a private-field delegator. Parity fails if the kernel mirror is reverted.

Full suite on this branch (Linux, Node 22, native kernel): all tests pass except the known intermittent extraction.test.ts pool-worker crash from #1779 (fixed by #1883), which also occurs on main; that file alone passes 655/655.

🤖 Generated with Claude Code

…lbymchenry#1987)

A call through an ES private field fell outside the `this.<field>` shape
(colbymchenry#1496): the extractor accepted only a property_identifier, so
`this.#items.add(x)` was emitted as the bare `add` and exact-matched
whichever project method shared the name, often the calling method itself.
The extractor and its kernel mirror now keep `this.#items.add`, and the
resolver matches that shape and reads the field's type off the class
declaration. The field regexes open with a lookbehind instead of `\b`,
which never matches before `#`. A builtin or external field type
(`new Set()`, a `Map`, an array) now yields no edge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry added a commit that referenced this pull request Sep 27, 2026
…1987) (#2028)

* fix(resolution): resolve this.#field.method() on the field's type (#1987)

A call through an ES private field fell outside the `this.<field>` shape
(#1496): the extractor accepted only a property_identifier, so
`this.#items.add(x)` was emitted as the bare `add` and exact-matched
whichever project method shared the name, often the calling method itself.
The extractor and its kernel mirror now keep `this.#items.add`, and the
resolver matches that shape and reads the field's type off the class
declaration. The field regexes open with a lookbehind instead of `\b`,
which never matches before `#`. A builtin or external field type
(`new Set()`, a `Map`, an array) now yields no edge.

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

* fix(resolution): require receiver evidence for built-in method calls (#1987)

Unknown JavaScript receivers could match unrelated methods by name or class-name similarity.
Require validated receiver evidence for built-in method names while retaining typed, imported, object-literal and class resolution.
Preserve private-field receivers in TypeScript and Rust extraction and keep public/private field declarations distinct.
Build on PR #1990 with regression coverage across all four JS/TS variants and kernel/WASM parity.

Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: danusha2345 <ewidusoc498@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@colbymchenry

Copy link
Copy Markdown
Owner

Thanks @danusha2345! Your commits here were carried into #2028 (authorship preserved), with a few follow-up changes from review, and that is now merged. Closing this one in favour of it. It will be in the next release.

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.

2 participants