Files
codegraph/__tests__/swift-member-receiver.test.ts
Colby Mchenry a329df883a fix(swift): a call on a typed property is a member of its type (#2205)
The declared-member receiver inference #2171 gave C#, Java and Kotlin now
reads Swift too: `var cache: ImageCache!`, `let storage: DiskStorage.Backend<Data>`,
`self.x` as well as `x`, a function's own `let` shadowing the property. The
dotted-call path types the receiver from it, and a chained bare call keeps
only members of that type or its supertypes / protocols.

Kingfisher: `cache.imageCachedType(forKey:)` -> ImageCache's (was a test
subclass's override), `storage.store/value/isCached` -> DiskStorage.Backend,
`manager.retrieveImage` -> KingfisherManager (was a test double), DispatchQueue
`ioQueue.async` no longer a project method. realm-swift: `List::append`,
`RealmCollection::sum/index` (were Objective-C RLMArray selectors); vapor:
`self.storage.write` -> NIOHTTPBodyWriterStorage.

Kingfisher -100 +179, realm-swift -108 +203, Alamofire -16 +54, vapor -15
+10, IceCubesApp -6 +5; Newtonsoft.Json, okhttp unchanged.

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

61 lines
2.0 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 Swift call on a property the enclosing type declares with a type —
* Kingfisher's `var cache: ImageCache!` — is a member of that type (or of
* what it inherits): `cache.imageCachedType(forKey:)` is ImageCache's, not a
* test subclass's override (`EvictingPrefetchCache`) that shared its name.
*/
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-swift-member-'));
const files: Record<string, string> = {
'Sources/Cache/ImageCache.swift': `open class ImageCache {
open func imageCachedType(forKey key: String) -> Int {
return 0
}
}
`,
'Tests/KingfisherTests/ImagePrefetcherTests.swift': `class EvictingPrefetchCache: ImageCache {
override func imageCachedType(forKey key: String) -> Int {
return 1
}
}
`,
'Tests/KingfisherTests/ImageCacheTests.swift': `class ImageCacheTests {
var cache: ImageCache!
func testCachedType() {
_ = cache.imageCachedType(forKey: "a")
_ = self.cache.imageCachedType(forKey: "b")
}
}
`,
};
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 });
});
describe('Swift calls on a typed property', () => {
it('reach the declared type’s method, not a subclass override elsewhere', () => {
const ids = cg.getNodesInFile('Tests/KingfisherTests/ImageCacheTests.swift').map((n) => n.id);
const targets = [...new Set(cg.getOutgoingEdgesFrom(ids).filter((e) => e.kind === 'calls')
.map((e) => cg.getNode(e.target)!.qualifiedName))];
expect(targets).toEqual(['ImageCache::imageCachedType']);
});
});