You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
TS/JS: a call through a chained receiver (chrome.storage.local.get, this.map.get, a.b.text()) reaches the resolver as the bare method name and exact-matches any project symbol with that name #1707
In the TS/JS extractor, a call_expression whose callee is a member_expression keeps the receiver only when the receiver is a plain identifier (recv.method, codegraph-kernel/src/tsjs/extractors.rsextract_call, and the same branch in TreeSitterExtractor.extractCall). Any other receiver, a nested member expression, a subscript, a call, collapses to the bare method name:
source
reference recorded
chrome.storage.local.get([key])
get
this._espnPlayerById.get(id) (a Map)
get
settled.value.text() (a Response)
text
thHeaders[0].text()
text
document.querySelector(sel)
document.querySelector (identifier receiver, kept, and JS_BUILT_INS drops it)
The bare name then goes through matchByExactName, where a single same-named project node wins at confidence 0.9. Nothing downstream can tell that the receiver was a runtime global or a builtin container, because that information is gone before resolution starts.
119 calls edges named get and 117 named set land on an object-literal storage wrapper's get / set methods. Every one of them is a Map#get, Map#set, chrome.storage.local.get/set or URLSearchParams#get call. Two of them are the wrapper's own methods calling chrome.storage.local.get/set, so the wrapper calls itself.
8 calls edges named querySelector land on an interface member AutopickDoc::querySelector (with fix(extraction): index TypeScript interface members (#1638) #1686's interface members; without it they would land on any function so named). The calls are el.shadowRoot.querySelector(...) and this.root.querySelector(...).
4 calls edges named observe / disconnect from MutationObserver instances land on two helper functions in a test file.
#1691 (#1496) fixes the this.<field>.<method> subset by keeping the field and reading its declared type, and declines when the type is not a project class. The non-this shapes above (chrome.…, x.value.text(), arr[0].text()) are outside its scope and still collapse.
Proposed fix
In the extractor, never drop a chained receiver on the floor. For a nested member_expression / subscript_expression / call_expression receiver, record the chain head plus the method, e.g. chrome.storage.local.get → chrome.….get or the full dotted text (the resolver already handles dotted names in matchByQualifiedName / matchMethodCall, and Java, C++ and Go already encode chains). Then:
isBuiltInOrExternal checks the chain head against JS_BUILT_INS, extended with the extension and browser globals that are missing today: chrome, browser, globalThis, navigator, location, history, localStorage, sessionStorage, performance, crypto.
A chained call whose head is a local or a parameter with no inferable type declines, the way matchTsThisFieldCall declines, instead of falling through to bare-name matching. A calls edge that is a guess between Map#get and a project get is worse than no edge: it hides the real callee and inflates every caller list for common method names (get, set, text, push, clear, close).
Happy to open a PR if the direction is right; the head-plus-method encoding is the part I would want a maintainer's view on, since it changes the reference names other resolvers see.
What happens
In the TS/JS extractor, a
call_expressionwhose callee is amember_expressionkeeps the receiver only when the receiver is a plain identifier (recv.method,codegraph-kernel/src/tsjs/extractors.rsextract_call, and the same branch inTreeSitterExtractor.extractCall). Any other receiver, a nested member expression, a subscript, a call, collapses to the bare method name:chrome.storage.local.get([key])getthis._espnPlayerById.get(id)(aMap)getsettled.value.text()(aResponse)textthHeaders[0].text()textdocument.querySelector(sel)document.querySelector(identifier receiver, kept, andJS_BUILT_INSdrops it)The bare name then goes through
matchByExactName, where a single same-named project node wins at confidence 0.9. Nothing downstream can tell that the receiver was a runtime global or a builtin container, because that information is gone before resolution starts.Measured
Real TS repo, 584 files, indexed on
main+ #1706:callsedges namedgetand 117 namedsetland on an object-literal storage wrapper'sget/setmethods. Every one of them is aMap#get,Map#set,chrome.storage.local.get/setorURLSearchParams#getcall. Two of them are the wrapper's own methods callingchrome.storage.local.get/set, so the wrapper calls itself.callsedges namedquerySelectorland on an interface memberAutopickDoc::querySelector(with fix(extraction): index TypeScript interface members (#1638) #1686's interface members; without it they would land on any function so named). The calls areel.shadowRoot.querySelector(...)andthis.root.querySelector(...).callsedges namedobserve/disconnectfromMutationObserverinstances land on two helper functions in a test file.#1691 (#1496) fixes the
this.<field>.<method>subset by keeping the field and reading its declared type, and declines when the type is not a project class. The non-thisshapes above (chrome.…,x.value.text(),arr[0].text()) are outside its scope and still collapse.Proposed fix
In the extractor, never drop a chained receiver on the floor. For a nested
member_expression/subscript_expression/call_expressionreceiver, record the chain head plus the method, e.g.chrome.storage.local.get→chrome.….getor the full dotted text (the resolver already handles dotted names inmatchByQualifiedName/matchMethodCall, and Java, C++ and Go already encode chains). Then:isBuiltInOrExternalchecks the chain head againstJS_BUILT_INS, extended with the extension and browser globals that are missing today:chrome,browser,globalThis,navigator,location,history,localStorage,sessionStorage,performance,crypto.matchTsThisFieldCalldeclines, instead of falling through to bare-name matching. Acallsedge that is a guess betweenMap#getand a projectgetis worse than no edge: it hides the real callee and inflates every caller list for common method names (get,set,text,push,clear,close).Happy to open a PR if the direction is right; the head-plus-method encoding is the part I would want a maintainer's view on, since it changes the reference names other resolvers see.