Skip to content

Commit cd9f032

Browse files
Perryvwark120202
andauthored
Fix broken for-in declaration (#711)
* Fix busted for-in declaration * Fixed scoping issue once the for-in loop is finished * replaced splice with unshift Co-Authored-By: ark120202 <ark120202@gmail.com>
1 parent 0c65c0c commit cd9f032

3 files changed

Lines changed: 51 additions & 5 deletions

File tree

src/LuaTransformer.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2628,10 +2628,6 @@ export class LuaTransformer {
26282628
}
26292629

26302630
public transformForInStatement(statement: ts.ForInStatement): StatementVisitResult {
2631-
// Get variable identifier
2632-
const variable = (statement.initializer as ts.VariableDeclarationList).declarations[0];
2633-
const identifier = variable.name as ts.Identifier;
2634-
26352631
// Transpile expression
26362632
const pairsIdentifier = tstl.createIdentifier("pairs");
26372633
const expression = this.transformExpression(statement.expression);
@@ -2643,7 +2639,30 @@ export class LuaTransformer {
26432639

26442640
const body = tstl.createBlock(this.transformLoopBody(statement));
26452641

2646-
return tstl.createForInStatement(body, [this.transformIdentifier(identifier)], [pairsCall], statement);
2642+
// Transform iteration variable
2643+
// TODO: After the transformation pipeline refactor we should look at refactoring this together with the
2644+
// for-of initializer transformation.
2645+
let iterationVariable: tstl.Identifier;
2646+
if (
2647+
ts.isVariableDeclarationList(statement.initializer) &&
2648+
ts.isIdentifier(statement.initializer.declarations[0].name)
2649+
) {
2650+
iterationVariable = this.transformIdentifier(statement.initializer.declarations[0].name);
2651+
} else if (ts.isIdentifier(statement.initializer)) {
2652+
// Iteration variable becomes ____key
2653+
iterationVariable = tstl.createIdentifier("____key");
2654+
// Push variable = ____key to the start of the loop body to match TS scoping
2655+
const initializer = tstl.createAssignmentStatement(
2656+
this.transformIdentifier(statement.initializer),
2657+
iterationVariable
2658+
);
2659+
body.statements.unshift(initializer);
2660+
} else {
2661+
// This should never occur
2662+
throw TSTLErrors.UnsupportedForInVariable(statement.initializer);
2663+
}
2664+
2665+
return tstl.createForInStatement(body, [iterationVariable], [pairsCall], statement);
26472666
}
26482667

26492668
public transformSwitchStatement(statement: ts.SwitchStatement): StatementVisitResult {

src/TSTLErrors.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ export const NonFlattenableDestructure = (node: ts.Node) =>
7777
export const UndefinedFunctionDefinition = (functionSymbolId: number) =>
7878
new Error(`Function definition for function symbol ${functionSymbolId} is undefined.`);
7979

80+
export const UnsupportedForInVariable = (initializer: ts.Node) =>
81+
new TranspileError(`Unsuppored for-in variable kind.`, initializer);
82+
8083
export const UndefinedScope = () => new Error("Expected to pop a scope, but found undefined.");
8184

8285
export const UndefinedTypeNode = (node: ts.Node) => new TranspileError("Failed to resolve required type node.", node);

test/unit/loops.spec.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,3 +630,27 @@ test("do...while double-negation", () => {
630630
expect(util.transpileString(code)).not.toMatch("not");
631631
expect(util.transpileAndExecute(code)).toBe(2);
632632
});
633+
634+
test("for...in with pre-defined variable", () => {
635+
util.testFunction`
636+
const obj = { x: "y", foo: "bar" };
637+
638+
let x = "";
639+
let result = [];
640+
for (x in obj) {
641+
result.push(x);
642+
}
643+
return result;
644+
`.expectToMatchJsResult();
645+
});
646+
647+
test("for...in with pre-defined variable keeps last value", () => {
648+
util.testFunction`
649+
const obj = { x: "y", foo: "bar" };
650+
651+
let x = "";
652+
for (x in obj) {
653+
}
654+
return x;
655+
`.expectToMatchJsResult();
656+
});

0 commit comments

Comments
 (0)