Skip to content

Commit 1b98e7f

Browse files
committed
test(bench): add JS fixture for bare-call class-scoped lookup regression guard (#1407)
Adds class-scope.js to the JS resolution benchmark. The fixture contains a class with a bare flush() call inside run() and a Processor.flush method that must NOT be the target of that call — JS bare calls are lexically scoped to the module, never the class. The 1.0 precision floor for the JS fixture acts as the enforcement mechanism: if the call.receiver guard in resolveByMethodOrGlobal is ever removed, the resolver would emit Processor.run → Processor.flush as a false positive, immediately failing CI. Closes #1407
1 parent ddfc14c commit 1b98e7f

3 files changed

Lines changed: 30 additions & 1 deletion

File tree

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression guard: bare function calls in JS class methods must NOT resolve
2+
// to same-named class methods. In JS/TS, bare foo() is lexically scoped to
3+
// the module, not the class — there is no implicit this binding on bare calls.
4+
//
5+
// If the call.receiver guard in resolveByMethodOrGlobal (call-resolver.ts) is
6+
// ever removed, the resolver would incorrectly emit Processor.run → Processor.flush
7+
// (a false positive). The 1.0 precision floor on the JS fixture catches that
8+
// regression immediately.
9+
10+
export function processData(x) {
11+
return x * 2;
12+
}
13+
14+
export class Processor {
15+
run(x) {
16+
processData(x); // same-file module-level function — resolves correctly
17+
flush(); // bare call; no module-level 'flush' in scope — must NOT resolve to Processor.flush
18+
}
19+
20+
flush() {} // Processor.flush exists; bare flush() in run() must not target it
21+
}

tests/benchmarks/resolution/fixtures/javascript/expected-edges.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,13 @@
275275
"kind": "calls",
276276
"mode": "receiver-typed",
277277
"notes": "this.service.doB() — receiver-typed via ClassB.service = new ServiceB() (class-scoped typeMap key prevents collision with ClassA.service)"
278+
},
279+
{
280+
"source": { "name": "Processor.run", "file": "class-scope.js" },
281+
"target": { "name": "processData", "file": "class-scope.js" },
282+
"kind": "calls",
283+
"mode": "same-file",
284+
"notes": "Bare call to same-file module-level function — regression guard: bare flush() in run() must NOT resolve to Processor.flush (class-scoped lookup must be receiver-gated)"
278285
}
279286
]
280287
}

tests/benchmarks/resolution/resolution-benchmark.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ const THRESHOLDS: Record<string, { precision: number; recall: number }> = {
125125
// adds bind/call/apply resolution (3 new edges in bind-call-apply.js), total expected now 33.
126126
// Phase 8.3f adds Object.defineProperty accessor this-dispatch (#1335): getter→baz in
127127
// define-property.js and accessorGetter→accessorTarget.accessMethod in define-property-accessor.js,
128-
// total expected now 35.
128+
// total expected now 35. multi-class.js adds 4 class-scoped typeMap edges → 39.
129+
// #1407 adds class-scope.js (bare-call guard), +1 → total 40.
129130
javascript: { precision: 1.0, recall: 0.9 },
130131
// pts-javascript: hand-authored points-to JS fixture (for-of, Set, Array.from, spread) — patterns
131132
// too broad for the main JS fixture. Patterns split per file to prevent intra-fixture FPs.

0 commit comments

Comments
 (0)