fix(resolution): calls through an imported singleton resolve to the method (#1315)
reproStore.notifyJoinGuildStatus() after `import { reproStore }`
resolved its calls edge to the exported CONSTANT (resolvedBy:'import'),
while the identical same-file call resolved to the method via
local-variable receiver inference (#1108) — so `callers <method>`
missed every cross-file use and a widely-used method could look
unused (#1292).
resolveViaImport's member-descend now handles imported VALUES alongside
the #825 static-member case: when the base resolves to a
constant/variable, the value's type is inferred from ITS OWN
declaration lines in the exporting file (the shared #1108 pattern
table: `= new T(...)` initializers and type annotations) and the member
is resolved AND VALIDATED on that type via resolveMethodOnType. A
failed inference or validation keeps the existing constant edge —
never a fabricated one. Calls only; plain member reads still reference
the value.
excalidraw control: byte-identical graph (10,653 nodes / 19,483 calls
edges before and after).
Fixes #1292
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
ce983a08fe
commit
2ec877b08c
@@ -2541,6 +2541,61 @@ func main() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Imported singleton instance-method calls (#1292)', () => {
|
||||
// `reproStore.notifyJoinGuildStatus()` after `import { reproStore }` used
|
||||
// to emit its calls edge to the CONSTANT (resolvedBy:'import'), while the
|
||||
// identical call in the defining file resolved to the method — so callers
|
||||
// of the method missed every cross-file use. The import path now infers
|
||||
// the value's type from its own declaration and resolves the member on it.
|
||||
it('cross-file call through an imported singleton resolves to the class method', async () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-1292-'));
|
||||
try {
|
||||
fs.mkdirSync(path.join(tmpDir, 'src'));
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, 'src', 'store.ts'),
|
||||
`export class ReproStore {
|
||||
notifyJoinGuildStatus(): void {
|
||||
console.log('notified');
|
||||
}
|
||||
}
|
||||
|
||||
export const reproStore = new ReproStore();
|
||||
|
||||
export function callInDefinitionFile(): void {
|
||||
reproStore.notifyJoinGuildStatus();
|
||||
}
|
||||
`
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(tmpDir, 'src', 'caller.ts'),
|
||||
`import { reproStore } from './store';
|
||||
|
||||
export function callFromImportedFile(): void {
|
||||
reproStore.notifyJoinGuildStatus();
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
const cg = CodeGraph.initSync(tmpDir);
|
||||
await cg.indexAll();
|
||||
|
||||
const method = (await cg.searchNodes('notifyJoinGuildStatus', { limit: 5 })).find(
|
||||
(r) => r.node.kind === 'method'
|
||||
);
|
||||
expect(method).toBeDefined();
|
||||
|
||||
// BOTH functions call the method — the cross-file one included.
|
||||
const callers = await cg.getCallers(method!.node.id);
|
||||
const callerNames = callers.map((c) => c.node.name).sort();
|
||||
expect(callerNames).toContain('callInDefinitionFile');
|
||||
expect(callerNames).toContain('callFromImportedFile');
|
||||
cg.close();
|
||||
} finally {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
describe('C++ namespace-qualified static method calls to out-of-line definitions (#1291)', () => {
|
||||
// The issue's exact shape: nested types + out-of-line static method
|
||||
// definition inside `namespace simulator { }` in the .cpp, called via the
|
||||
|
||||
Reference in New Issue
Block a user