Files
codegraph/__tests__/external-declared-receiver.test.ts
Colby Mchenry 791ae397e5 fix(resolution): a receiver declared with an outside type calls that type (#2169)
When a receiver's type is inferred from its declaration or initializer and
the method isn't on it, the matcher used to fall through to its
capitalized-name and name-overlap guesses. If the inferred name is a type
(capitalized last segment) that nothing in the project declares — JDK /
BCL / okhttp / Django types — it now stops: the method is that outside
type's. Python's initializer pattern no longer reads a keyword argument
(`prefix=IPNetwork(…),` inside a call) as a binding of `prefix`.

A/B (edges removed): commons-lang -1296 (`append` / `length` / `charAt` on
JDK types), Newtonsoft.Json -496 (`MemoryStream.ToArray`, `ToString`),
gson -367 (`List.add`, `Map.put`), retrofit -321 (okhttp `Request.headers()`
/ `method()`), jsoup -130, okio -26, pytest -44 (pathlib / io), netbox -25,
healthchecks -9 (`TimestampSigner.sign`), flask -9, zod -1.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 13:18:33 +00:00

77 lines
2.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* A receiver whose declared type is one the project doesn't declare —
* `List<String> list`, `var sb = new StringBuilder()` — calls that outside
* type's method, never a project type's same-named one: gson's `list.add(…)`
* went to a project list wrapper's `add`, commons-lang's `s.length()` to a
* writer's. A Python keyword argument (`prefix=IPNetwork(…),` inside a call)
* is not a binding of `prefix`.
*/
import { describe, it, expect, afterAll, beforeAll } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { CodeGraph } from '../src';
let root = '';
let cg: CodeGraph;
beforeAll(async () => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-external-declared-'));
const files: Record<string, string> = {
'src/main/java/app/NonNullElementWrapperList.java': `package app;
public class NonNullElementWrapperList {
public boolean add(Object o) { return true; }
}
`,
'src/main/java/app/Use.java': `package app;
import java.util.ArrayList;
import java.util.List;
public class Use {
public void run() {
List<String> list = new ArrayList<>();
list.add("x");
}
}
`,
'app/models.py': `class Prefix:
def get_available_ip_count(self):
return 0
`,
'app/tests.py': `from app.models import Prefix
from netaddr import IPNetwork
def test_count():
prefix = Prefix(
prefix=IPNetwork('192.0.2.0/24'),
)
return prefix.get_available_ip_count()
`,
};
for (const [rel, content] of Object.entries(files)) {
fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true });
fs.writeFileSync(path.join(root, rel), content);
}
cg = await CodeGraph.init(root, { index: true });
});
afterAll(() => {
cg?.close();
if (root) fs.rmSync(root, { recursive: true, force: true });
});
const callsIn = (file: string, fn: string): string[] => {
const node = cg.getNodesInFile(file).find((n) => n.name === fn)!;
return cg.getOutgoingEdges(node.id).filter((e) => e.kind === 'calls').map((e) => cg.getNode(e.target)!.qualifiedName);
};
describe('a receiver declared with an outside type', () => {
it('Java: `List<String> list` calls List.add, not a project class’s add', () => {
expect(callsIn('src/main/java/app/Use.java', 'run').some((t) => t.endsWith('NonNullElementWrapperList::add'))).toBe(false);
});
it('Python: a keyword argument is not the receiver’s binding', () => {
expect(callsIn('app/tests.py', 'test_count').some((t) => t.endsWith('Prefix::get_available_ip_count'))).toBe(true);
});
});