Problem
In call-resolver.ts, the same-class method lookup (lines 200–213) runs for all bare calls when no exact same-file match is found, regardless of language. This is correct for C#/Java static siblings (e.g., IsValidEmail() inside Validators.ValidateUser → Validators.IsValidEmail), but incorrect for JavaScript/TypeScript where bare calls are lexically scoped to the module, not the class.
Repro
Given class-scope.js (from PR #1422):
export class Processor {
run(x) {
flush(); // bare call; no module-level 'flush' in scope
}
flush() {}
}
The resolver emits Processor.run → Processor.flush (false positive) because:
flush() has no receiver (call.receiver = null)
- Exact same-file lookup returns no match (no module-level
flush)
- Same-class fallback matches
Processor.flush — incorrectly
Expected behavior
For JS/TS, bare calls with no same-file/imported match should not fall back to same-class method lookup. The JS scoping rule is: bare foo() is module-scoped, not class-scoped.
Root cause
resolveByMethodOrGlobal in src/domain/graph/builder/call-resolver.ts at lines 195–213 does not gate the same-class fallback on language. It was introduced to support C#/Java static sibling resolution (#1395) but is applied unconditionally.
Fix
Gate the same-class fallback on language identifier (e.g., skip for js/ts/tsx/jsx) or add a caller-language parameter to resolveByMethodOrGlobal.
Also applies to the native Rust engine (crates/codegraph-core/) which mirrors this logic.
Detection
The class-scope.js fixture added in PR #1422 exposes this regression. The 1.0 precision floor on the JS benchmark will fail CI until this is fixed.
Deferred from PR #1422 to keep the fixture PR focused on test infrastructure.
Problem
In
call-resolver.ts, the same-class method lookup (lines 200–213) runs for all bare calls when no exact same-file match is found, regardless of language. This is correct for C#/Java static siblings (e.g.,IsValidEmail()insideValidators.ValidateUser→Validators.IsValidEmail), but incorrect for JavaScript/TypeScript where bare calls are lexically scoped to the module, not the class.Repro
Given
class-scope.js(from PR #1422):The resolver emits
Processor.run → Processor.flush(false positive) because:flush()has no receiver (call.receiver = null)flush)Processor.flush— incorrectlyExpected behavior
For JS/TS, bare calls with no same-file/imported match should not fall back to same-class method lookup. The JS scoping rule is: bare
foo()is module-scoped, not class-scoped.Root cause
resolveByMethodOrGlobalinsrc/domain/graph/builder/call-resolver.tsat lines 195–213 does not gate the same-class fallback on language. It was introduced to support C#/Java static sibling resolution (#1395) but is applied unconditionally.Fix
Gate the same-class fallback on language identifier (e.g., skip for
js/ts/tsx/jsx) or add a caller-language parameter toresolveByMethodOrGlobal.Also applies to the native Rust engine (
crates/codegraph-core/) which mirrors this logic.Detection
The
class-scope.jsfixture added in PR #1422 exposes this regression. The 1.0 precision floor on the JS benchmark will fail CI until this is fixed.Deferred from PR #1422 to keep the fixture PR focused on test infrastructure.