mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-02 01:37:32 +08:00
A lone project method now stands for an untyped receiver's member value only when the receiver is named after the method's class: netbox's `device=self.parent.device` model-field reads no longer reach a GraphQL filter's `device` method. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
2.1 KiB
TypeScript
62 lines
2.1 KiB
TypeScript
/**
|
|
* A member passed as a value through a receiver nothing types —
|
|
* `device=self.parent.device`, `self.check.last_ping` — is only the
|
|
* project's one method of that name when the receiver is named after its
|
|
* owner: netbox's model-field reads bound to a GraphQL filter's `device`
|
|
* method, healthchecks' `check.last_ping` to a transport's.
|
|
*/
|
|
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-member-fnref-'));
|
|
const files: Record<string, string> = {
|
|
'app/filters.py': `class FHRPGroupAssignmentFilter:
|
|
def device(self, queryset):
|
|
return queryset
|
|
`,
|
|
'app/components.py': `def describe(parent):
|
|
return "{interface}".format(interface=parent, device=parent.device)
|
|
`,
|
|
'app/recorder.py': `class HookRecorder:
|
|
def finish_recording(self):
|
|
return None
|
|
|
|
|
|
def register(request, hook_recorder):
|
|
request.addfinalizer(hook_recorder.finish_recording)
|
|
`,
|
|
};
|
|
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 refsFrom = (file: string) => {
|
|
const ids = cg.getNodesInFile(file).map((n) => n.id);
|
|
return cg.getOutgoingEdgesFrom(ids).filter((e) => e.kind === 'references').map((e) => cg.getNode(e.target)!.qualifiedName);
|
|
};
|
|
|
|
describe('member values through an untyped receiver', () => {
|
|
it('are not a method whose owner the receiver is not named after', () => {
|
|
expect(refsFrom('app/components.py')).not.toContain('FHRPGroupAssignmentFilter::device');
|
|
});
|
|
|
|
it('are a method whose owner the receiver is named after', () => {
|
|
expect(refsFrom('app/recorder.py')).toContain('HookRecorder::finish_recording');
|
|
});
|
|
});
|