Summary
TypeScript calls to the built-in Map.get, Map.set, and Map.has methods
resolve to unrelated project methods when the project contains exactly one
same-language method with each name.
Both common receiver shapes fail:
- a simple local receiver such as
values.get(...) is stored as
values.get, then resolves to the sole project get method with confidence
0.7 / instance-method; and
- a nested receiver such as
holder.values.get(...) is extracted as bare
get, then resolves to the project method with confidence 0.9 /
exact-match.
The simple local receiver is the distinct gap demonstrated here. The nested
receiver belongs to the extraction family already reported in #1496 and is
included as a regression boundary rather than as a second new defect.
The resulting callers, callees, impact, and raw graph all report calls that do
not exist. An unresolved external/built-in call would be safer than a confident
wrong project edge.
Minimal reproduction
One dependency-free TypeScript file:
export class LRUCache {
private store = new Map<string, string>();
get(key: string): string | undefined {
return this.store.get(key);
}
set(key: string, value: string): void {
this.store.set(key, value);
}
has(key: string): boolean {
return this.store.has(key);
}
}
export function useLocalMap(): boolean {
const values = new Map<string, string>();
values.set("answer", "42");
values.get("answer");
return values.has("answer");
}
export function useNestedMap(
holder: { values: Map<string, string> },
): string | undefined {
return holder.values.get("answer");
}
$ codegraph init
$ codegraph callees useLocalMap --json
{
"symbol": "useLocalMap",
"callees": [
{ "name": "set", "kind": "method", "filePath": "repro.ts", "startLine": 8 },
{ "name": "get", "kind": "method", "filePath": "repro.ts", "startLine": 4 },
{ "name": "has", "kind": "method", "filePath": "repro.ts", "startLine": 12 }
]
}
$ codegraph callees useNestedMap --json
{
"symbol": "useNestedMap",
"callees": [
{ "name": "get", "kind": "method", "filePath": "repro.ts", "startLine": 4 }
]
}
The stored edge metadata shows two independent fallbacks:
source target confidence resolvedBy refName
useLocalMap LRUCache::set 0.7 instance-method values.set
useLocalMap LRUCache::get 0.7 instance-method values.get
useLocalMap LRUCache::has 0.7 instance-method values.has
useNestedMap LRUCache::get 0.9 exact-match get
Control
Remove only the LRUCache class, leaving both Map-using functions unchanged,
then initialize a fresh index:
$ codegraph callees useLocalMap --json
{ "symbol": "useLocalMap", "callees": [] }
$ codegraph callees useNestedMap --json
{ "symbol": "useNestedMap", "callees": [] }
The control graph contains zero calls edges. The four call sites that remain
unchanged between the two fixtures acquire false edges only when same-named
project methods become available.
Source-level cause
There are two paths to the same bad outcome.
In src/extraction/tree-sitter.ts, TS/JS method-call extraction preserves a
receiver only when it is a simple identifier. A nested member_expression
such as holder.values or this.store falls through as the bare method name.
That bare name then reaches exact-name resolution, where a sole same-language
candidate wins at confidence 0.9.
For the distinct preserved-simple-receiver case, matchMethodCall in
src/resolution/name-matcher.ts gathers every project method named get,
set, or has. When exactly one same-language method exists, it selects that
method at confidence 0.7 even though receiver inference has not established
that the built-in Map receiver has the project class's type.
A guard that only recognizes an already-inferred receiver type of Map would
not fix the nested case. For simple local receivers, once inference identifies
an external/built-in type rather than a project class, failure to find a project
method should remain unresolved instead of falling back. The recall tradeoff
for a genuinely untyped receiver is separate. For multi-segment TS/JS
receivers, the safe choices are to infer the nested property's type or keep the
call unresolved; preserving more receiver text and then applying the same
unique-method guess would retain the false edge.
Suggested regression
Add a TypeScript fixture beside the local receiver-inference tests with:
- one project class defining
get, set, and has;
- a local
Map using all three methods; and
- a nested
holder.values.get() call.
Assert that none of those built-in calls produces a project calls edge. Keep
the existing positive control where a local variable constructed as a project
class resolves to that class's method.
Relationship to existing issues
Environment
Reproduced from fresh indexes with the released CodeGraph 1.5.0 binary on
macOS 26.6 arm64.
Summary
TypeScript calls to the built-in
Map.get,Map.set, andMap.hasmethodsresolve to unrelated project methods when the project contains exactly one
same-language method with each name.
Both common receiver shapes fail:
values.get(...)is stored asvalues.get, then resolves to the sole projectgetmethod with confidence0.7 /
instance-method; andholder.values.get(...)is extracted as bareget, then resolves to the project method with confidence 0.9 /exact-match.The simple local receiver is the distinct gap demonstrated here. The nested
receiver belongs to the extraction family already reported in #1496 and is
included as a regression boundary rather than as a second new defect.
The resulting callers, callees, impact, and raw graph all report calls that do
not exist. An unresolved external/built-in call would be safer than a confident
wrong project edge.
Minimal reproduction
One dependency-free TypeScript file:
The stored edge metadata shows two independent fallbacks:
Control
Remove only the
LRUCacheclass, leaving bothMap-using functions unchanged,then initialize a fresh index:
The control graph contains zero
callsedges. The four call sites that remainunchanged between the two fixtures acquire false edges only when same-named
project methods become available.
Source-level cause
There are two paths to the same bad outcome.
In
src/extraction/tree-sitter.ts, TS/JS method-call extraction preserves areceiver only when it is a simple identifier. A nested
member_expressionsuch as
holder.valuesorthis.storefalls through as the bare method name.That bare name then reaches exact-name resolution, where a sole same-language
candidate wins at confidence 0.9.
For the distinct preserved-simple-receiver case,
matchMethodCallinsrc/resolution/name-matcher.tsgathers every project method namedget,set, orhas. When exactly one same-language method exists, it selects thatmethod at confidence 0.7 even though receiver inference has not established
that the built-in
Mapreceiver has the project class's type.A guard that only recognizes an already-inferred receiver type of
Mapwouldnot fix the nested case. For simple local receivers, once inference identifies
an external/built-in type rather than a project class, failure to find a project
method should remain unresolved instead of falling back. The recall tradeoff
for a genuinely untyped receiver is separate. For multi-segment TS/JS
receivers, the safe choices are to infer the nested property's type or keep the
call unresolved; preserving more receiver text and then applying the same
unique-method guess would retain the false edge.
Suggested regression
Add a TypeScript fixture beside the local receiver-inference tests with:
get,set, andhas;Mapusing all three methods; andholder.values.get()call.Assert that none of those built-in calls produces a project
callsedge. Keepthe existing positive control where a local variable constructed as a project
class resolves to that class's method.
Relationship to existing issues
this.<field>resolves to the ENCLOSING method when the two share a name — silent self-edge, 0% recall on that shape #1496 covers a droppedthis.<field>receiver and its resulting self-edge.The nested control here is from that same extraction family. The new evidence
is the preserved simple local
Mapreceiver still falling into the separateconfidence-0.7 fallback.
variable whose value is a built-in
Mapis neither a literal receiver nor anested-local candidate.
complementary safety property: when receiver typing does not prove a project
class, a sole same-name method must not become a guessed edge.
Environment
Reproduced from fresh indexes with the released CodeGraph 1.5.0 binary on
macOS 26.6 arm64.