Files
codegraph/__tests__/java-bare-call-scope.test.ts
Colby MchenryandClaude Opus 5.5 8987be4254 fix(resolution): a bare Java call reaches its class, supertypes or static imports (#2126)
Found by the README-wide sweep: halo's most-depended-on symbols were an
`EmailVerificationService.verify` (1,038 — Mockito's `verify(mock)`) and a
builder's `eq` (845 — ArgumentMatchers `eq`); retrofit's a test helper's
`assertThat` (1,357 — Truth); mall's a DTO's `hashCode` (Object's).

A bare Java call (`verify(x)`, `helper()`, `this.x()`, `super.x()`) now has
only method candidates declared on a class around it, on one of that class's
supertypes (read transitively from the declarations' `extends` /
`implements` — the resolved edges don't exist yet on the first pass), or
imported statically (`import static a.B.m;` / `a.B.*`). Memoized per type
and per file.

A/B vs main (edge diffs, every change classified): halo −2,103/+145,
retrofit −1,380/+18, commons-lang −202/+87, jsoup −92/+45, mall −76,
gson −7/+4. Gains are the right target where one exists: halo's `and` /
`equal` / `isNull` through `import static …Queries.*`, jsoup's `attr(…)`
through `LeafNode`, commons-lang's `addExact` on its own class instead of a
nested MathBridge, retrofit's `getRawType` through `CallAdapter.Factory`.

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

75 lines
2.3 KiB
TypeScript

/**
* A bare Java call reaches a method of a class around it, a supertype of that
* class, or a static import — not another class's method that shares the name.
* halo's Mockito `verify(…)` bound 1,038 calls to `EmailVerificationService.verify`.
*/
import { describe, it, expect, afterAll } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { CodeGraph } from '../src';
const roots: string[] = [];
afterAll(() => {
for (const r of roots.splice(0)) fs.rmSync(r, { recursive: true, force: true });
});
const FILES: Record<string, string> = {
'src/main/java/app/EmailVerificationService.java': `package app;
public class EmailVerificationService {
public boolean verify(String token) { return true; }
}
`,
'src/main/java/app/Util.java': `package app;
public class Util {
public static String format(String s) { return s; }
}
`,
'src/main/java/app/Base.java': `package app;
public class Base {
protected void helper() {}
}
`,
'src/main/java/app/Child.java': `package app;
import static app.Util.format;
public class Child extends Base {
public void run() {
helper();
format("x");
own();
}
private void own() {}
}
`,
'src/test/java/app/ChildTest.java': `package app;
import static org.mockito.Mockito.verify;
public class ChildTest {
public void sends(Object mock) {
verify(mock);
}
}
`,
};
describe('Java: a bare call reaches what is in scope', () => {
it('own class, a supertype, a static import — not a same-named method elsewhere', async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-java-bare-'));
roots.push(root);
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);
}
const cg = await CodeGraph.init(root, { index: true });
try {
const callsFrom = (name: string): string[] => {
const from = cg.getNodesByName(name).find((n) => n.kind === 'method')!;
return cg.getOutgoingEdgesFrom([from.id], ['calls']).map((e) => cg.getNode(e.target)!.name).sort();
};
expect(callsFrom('sends')).not.toContain('verify');
expect(callsFrom('run')).toEqual(['format', 'helper', 'own']);
} finally {
cg.close();
}
});
});