mirror of
https://github.com/Tencent/teamai-cli.git
synced 2026-10-02 03:14:40 +08:00
test(code-knowledge): make the assignment case pin the binding fix (#943)
* test(code-knowledge): make the assignment case pin the binding fix `ast-swift-module-scope.test.ts` asserted 2 edges for `alias = work()`. The name on the right sits in a call callee, a position both walkers skip -- so the case produced the same count before and after the fix it guards, and passed on the base commit as well. The fixture becomes `alias = work` followed by `work()`, and the expectation 1 edge. Measured on the two walkers, one variable: basea504f8afalias = work() 2 edges the old assertion passed here basea504f8afalias = work 0 edges the new assertion fails here mainf73493d4alias = work 1 edge the new assertion passes here Whole file: 2 failed / 36 passed on the base walker (the second failure is the initializer case #928 fixed), 38 passed / 0 failed on main. No source change: the walker on main already reads the position correctly. * test(code-knowledge): pin both sides of the assignment, not just the callee `ast-swift-module-scope.test.ts` asserted 2 edges for `alias = work()`. The name on the right sits in a call callee, a position both walkers skip -- so the case produced the same count before and after the fix it guards, and passed on the base commit as well. The fixture became `alias = work` followed by `work()`, and the expectation 1 edge. The review on this PR then pointed out that this exercises the *value* side of the assignment while the case name describes the *target* side, and that a regression reading assignment targets as declarations would still pass it. That is right, so this revision covers both positions instead of renaming past one of them: `does not let an assignment value mention stand in for a binding` var alias = 0; alias = work; work() -- renamed to match its fixture `does not let an assignment target stand in for a binding` alias = work; alias() -- new, with func alias() sibling Measured on the two walkers, one variable: basea504f8afalias = work() 2 edges the old assertion passed basea504f8afalias = work + work() 0 edges value case fails here basea504f8afalias = work + alias() 0 edges target case fails here mainf73493d4alias = work + work() 1 edge value case passes here mainf73493d4alias = work + alias() 1 edge target case passes here both sides cross-file, alias = work; alias(); work() basea504f8af0 edges mainf73493d42 edges Whole file: 3 failed / 36 passed (39) on the base walker -- the initializer case #928 fixed plus these two -- and 39 passed / 0 failed (39) on main. The target case needs its own fixture rather than a second assertion on the value one: the value fixture's target is a name a real `var` already bound, so both walkers suppress the call there and the assertion cannot distinguish them. No source change: the walker on main already reads the position correctly.
This commit is contained in:
@@ -608,18 +608,38 @@ describe('Swift module-scope resolution yields to enclosing bindings', () => {
|
||||
expect(references[0]?.to).toBe('Sources/App/Worker.swift');
|
||||
});
|
||||
|
||||
it('does not let an assignment target stand in for a binding', async () => {
|
||||
it('does not let an assignment value mention stand in for a binding', async () => {
|
||||
const { result } = await extractFiles([
|
||||
['Sources/App/Worker.swift', 'func work() -> Int { return 1 }\n'],
|
||||
['Sources/App/Run.swift', 'func run() {\n var alias = 0\n alias = work()\n work()\n}\n'],
|
||||
['Sources/App/Run.swift', 'func run() {\n var alias = 0\n alias = work\n work()\n}\n'],
|
||||
]);
|
||||
|
||||
// The same position in an assignment rather than a declaration, and with no
|
||||
// `let` in sight. A rule that had to be told about one statement form after
|
||||
// another would need a third fix here; reading the position needs none.
|
||||
// `alias = work` assigns to `alias`; `work` on the right is a *mention*.
|
||||
// Reading that mention as if the assignment declared it shadowed the
|
||||
// module-level `work`, and the call on the next line then resolved to
|
||||
// nothing. A rule that had to be told about one statement form after another
|
||||
// would need a third fix here; reading the position needs none.
|
||||
const references = result.edges.filter((e) => e.relation === 'REFERENCES');
|
||||
expect(references).toHaveLength(2);
|
||||
for (const reference of references) expect(reference.to).toBe('Sources/App/Worker.swift');
|
||||
expect(references).toHaveLength(1);
|
||||
expect(references[0]?.to).toBe('Sources/App/Worker.swift');
|
||||
});
|
||||
|
||||
it('does not let an assignment target stand in for a binding', async () => {
|
||||
const { result } = await extractFiles([
|
||||
['Sources/App/Alias.swift', 'func alias() -> Int { return 1 }\n'],
|
||||
['Sources/App/Run.swift', 'func run() {\n alias = work\n alias()\n}\n'],
|
||||
]);
|
||||
|
||||
// The same statement read from the other side. `alias` is the assignment
|
||||
// *target* and `work` on the right is a mention, so neither side is a
|
||||
// binding and `alias()` answers to the sibling file. The value-position case
|
||||
// above cannot see this one: its target is a name a real `var` already
|
||||
// bound, so a rule that read assignment targets as declarations too would
|
||||
// leave that test green.
|
||||
const references = result.edges.filter((e) => e.relation === 'REFERENCES');
|
||||
expect(references).toHaveLength(1);
|
||||
expect(references[0]?.from).toBe('Sources/App/Run.swift');
|
||||
expect(references[0]?.to).toBe('Sources/App/Alias.swift');
|
||||
});
|
||||
|
||||
it('resolves a call that passes its own name', async () => {
|
||||
|
||||
Reference in New Issue
Block a user