mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-28 22:31:53 +08:00
fix(js,ts): a call on a new C() receiver resolves through C
A TypeScript or JavaScript call on a constructed object, new C(args).m(),
reached the resolver as the bare method name in both engines, so exact
matching bound it to any project method called m: new RegExp(p).exec(s)
to a database wrapper's exec, new Array(n).fill(0) to a text class's
fill, new TextEncoder().encode(s) to a project encode function.
Both engines now emit new C().m (new ns.C().m for a namespaced class, and
nothing for a constructor that is not a plain name or member chain). The
resolver looks m up on C with resolveMethodOnType, inherited methods on
the conformance pass once extends edges exist, an aliased named import
mapped back to its class, and a JS built-in (RegExp, Map, Date...) never
a project class unless the file imports or declares one. An ES5
constructor function keeps its prototype method.
Our changes: the lookup passes the file the call's file imports the class
from (tsImportedTypeFile), so of two classes sharing a name the imported
one is the one constructed (the PR as written took the first indexed copy,
at 0.9); new this.constructor() and a static new this() keep the bare
method name, as a call on this does (the PR dropped three.js's clone
idiom, 29 links to a class's own copy); rebased onto PR #2852, which
edits the same keepsBareTsJsReceiver and test lists; resolved the import
list conflict with rustChainedCallByName from PR #2846; CHANGELOG bullet
moved next to the JS/TS receiver bullets.
Validation: the new tests fail 4 of 6 on main 449eefa7 with both engines
and pass here. A/B against main, kernel per arm: flask, gin and vite
byte-identical. codegraph 48 links added (new ToolHandler(cg).execute), 2
retargeted and 14 typed, all right, 58 removed, 57 wrong (RegExp exec and
test, Array fill, Set add) and 1 right one lost to the grammar's parse of
new nodeSqlite!.DatabaseSync(). excalidraw 22 wrong removed (TextEncoder,
TextDecoder, URLSearchParams, Array fill), 3 typed. pixijs 14 retargeted
and 278 typed, all right, 7 wrong removed. pdf.js 5 added, 47 retargeted
and 68 typed, all right, 76 wrong removed (Array keys and fill, typed
arrays, Response). three.js 33 added, 404 retargeted (Capsule::set to
Matrix4::set and Matrix3::set, NURBSCurve::copy to the class's own copy)
and 429 typed, all right; 135 removed, 133 wrong (UINumber onChange, which
three.js attaches at run time, TextDecoder, typed arrays, URL) and 2 right
only by name (Loader methods inherited through a base imported from the
three package). No default-imported class under another name lost an edge.
Kernel parity holds on all six TS/JS repos and this repository, 0 diffs.
Index CPU time unchanged or lower: codegraph 81.5 s to 79.5 s, three.js
72.0 s to 71.2 s (medians of 3, interleaved).
Original PR #1945 by @danusha2345.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Colby Mchenry
co-authored by
Claude Opus 5.5
parent
16f2d357bc
commit
0d46dba08f
@@ -708,13 +708,13 @@ function tsThisSubscriptReceiver(node: SyntaxNode, source: string): string | nul
|
||||
|
||||
/**
|
||||
* Whether a TS/JS receiver still collapses to the bare method name: `this` /
|
||||
* `super` (the resolver reads the owner off the enclosing class), a member
|
||||
* `super` (the resolver reads the owner off the enclosing class) and a member
|
||||
* chain rooted at either or at `window` (the project-global escape of
|
||||
* {@link isUnresolvedTsJsChain}), and `new C()` (its class is written at the
|
||||
* call). A subscript anywhere in the chain does not: the resolver cannot
|
||||
* read the receiver back from it, so the bare name would exact-match any
|
||||
* project method of that name. (An element of a this-field is typed instead,
|
||||
* see {@link tsThisSubscriptReceiver}.)
|
||||
* {@link isUnresolvedTsJsChain}). `new C().m()` has its own encoding. A
|
||||
* subscript anywhere in the chain does not: the resolver cannot read the
|
||||
* receiver back from it, so the bare name would exact-match any project
|
||||
* method of that name. (An element of a this-field is typed instead, see
|
||||
* {@link tsThisSubscriptReceiver}.)
|
||||
*/
|
||||
function keepsBareTsJsReceiver(node: SyntaxNode, source: string): boolean {
|
||||
let cur: SyntaxNode | null = node;
|
||||
@@ -725,8 +725,7 @@ function keepsBareTsJsReceiver(node: SyntaxNode, source: string): boolean {
|
||||
}
|
||||
if (!cur) return false;
|
||||
if (cur.type === 'this' || cur.type === 'super') return true;
|
||||
if (cur.type === 'identifier') return getNodeText(cur, source) === 'window';
|
||||
return cur === node && cur.type === 'new_expression';
|
||||
return cur.type === 'identifier' && getNodeText(cur, source) === 'window';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6309,6 +6308,24 @@ export class TreeSitterExtractor {
|
||||
const chain = getNodeText(func, this.source).replace(/\s+/g, '').replace(/\?\./g, '.');
|
||||
if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*){2,}$/.test(chain)) return;
|
||||
calleeName = chain;
|
||||
} else if (TS_JS_CHAIN_LANGUAGES.has(this.language) && receiver?.type === 'new_expression') {
|
||||
// Constructor receiver — `new Runner().run()`, `new RegExp(p).exec(s)`.
|
||||
// The class is written at the call, so keep it: `new Runner().run`
|
||||
// tells the resolver the receiver is an INSTANCE of `Runner`, and
|
||||
// the method resolves on that class (or a supertype) or nowhere —
|
||||
// a built-in or external class has no project method to bind to.
|
||||
// The bare method name this used to emit exact-matched any
|
||||
// same-named project method (`new RegExp(p).exec()` onto a
|
||||
// database wrapper's `exec`). A constructor that is not a plain
|
||||
// name or member chain (`new (f())().m()`) emits nothing.
|
||||
// Mirrored in the kernel's extract_call (tsjs/extractors.rs).
|
||||
const ctor = getChildByField(receiver, 'constructor');
|
||||
const ctorName = ctor ? getNodeText(ctor, this.source).replace(/\s+/g, '') : '';
|
||||
if (!/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/.test(ctorName)) return;
|
||||
// `new this.constructor().copy(this)`, a static `new this().init()`:
|
||||
// an instance of the enclosing class, which no name is written for,
|
||||
// keeps the bare method name, as a call on `this` does.
|
||||
calleeName = ctorName === 'this' || ctorName === 'this.constructor' ? methodName : `new ${ctorName}().${methodName}`;
|
||||
} else if (
|
||||
TS_JS_CHAIN_LANGUAGES.has(this.language) &&
|
||||
receiver &&
|
||||
|
||||
Reference in New Issue
Block a user