Skip to content

Commit 6058224

Browse files
authored
Fixed empty destructuring declarations/assignments in for...of loops (#658)
* Fixed empty destructuring declarations/assignments in for...of loops fixes #607 * consolidated tests * removing erroneous tab * re-ran prettier
1 parent 8626036 commit 6058224

File tree

2 files changed

+124
-23
lines changed

2 files changed

+124
-23
lines changed

src/LuaTransformer.ts

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2260,11 +2260,18 @@ export class LuaTransformer {
22602260
return tstl.createDoStatement(result, statement);
22612261
}
22622262

2263-
protected transformForOfInitializer(initializer: ts.ForInitializer, expression: tstl.Expression): tstl.Statement {
2263+
protected transformForOfInitializer(
2264+
initializer: ts.ForInitializer,
2265+
expression: tstl.Expression
2266+
): tstl.Statement | undefined {
22642267
if (ts.isVariableDeclarationList(initializer)) {
22652268
// Declaration of new variable
22662269
const variableDeclarations = this.transformVariableDeclaration(initializer.declarations[0]);
22672270
if (ts.isArrayBindingPattern(initializer.declarations[0].name)) {
2271+
if (initializer.declarations[0].name.elements.length === 0) {
2272+
// Ignore empty destructuring assignment
2273+
return undefined;
2274+
}
22682275
expression = this.createUnpackCall(expression, initializer);
22692276
} else if (ts.isObjectBindingPattern(initializer.declarations[0].name)) {
22702277
throw TSTLErrors.UnsupportedObjectDestructuringInForOf(initializer);
@@ -2284,10 +2291,15 @@ export class LuaTransformer {
22842291
// Assignment to existing variable
22852292
let variables: tstl.AssignmentLeftHandSideExpression | tstl.AssignmentLeftHandSideExpression[];
22862293
if (ts.isArrayLiteralExpression(initializer)) {
2287-
expression = this.createUnpackCall(expression, initializer);
2288-
variables = initializer.elements.map(e =>
2289-
this.transformExpression(e)
2290-
) as tstl.AssignmentLeftHandSideExpression[];
2294+
if (initializer.elements.length > 0) {
2295+
expression = this.createUnpackCall(expression, initializer);
2296+
variables = initializer.elements.map(e =>
2297+
this.transformExpression(e)
2298+
) as tstl.AssignmentLeftHandSideExpression[];
2299+
} else {
2300+
// Ignore empty destructring assignment
2301+
return undefined;
2302+
}
22912303
} else if (ts.isObjectLiteralExpression(initializer)) {
22922304
throw TSTLErrors.UnsupportedObjectDestructuringInForOf(initializer);
22932305
} else {
@@ -2329,14 +2341,20 @@ export class LuaTransformer {
23292341
const variables = statement.initializer.declarations[0].name;
23302342
if (ts.isArrayBindingPattern(variables) || ts.isObjectBindingPattern(variables)) {
23312343
valueVariable = tstl.createIdentifier("____values");
2332-
block.statements.unshift(this.transformForOfInitializer(statement.initializer, valueVariable));
2344+
const initializer = this.transformForOfInitializer(statement.initializer, valueVariable);
2345+
if (initializer) {
2346+
block.statements.unshift(initializer);
2347+
}
23332348
} else {
23342349
valueVariable = this.transformIdentifier(variables);
23352350
}
23362351
} else {
23372352
// Assignment to existing variable
23382353
valueVariable = tstl.createIdentifier("____value");
2339-
block.statements.unshift(this.transformForOfInitializer(statement.initializer, valueVariable));
2354+
const initializer = this.transformForOfInitializer(statement.initializer, valueVariable);
2355+
if (initializer) {
2356+
block.statements.unshift(initializer);
2357+
}
23402358
}
23412359

23422360
const ipairsCall = tstl.createCallExpression(tstl.createIdentifier("ipairs"), [
@@ -2365,14 +2383,14 @@ export class LuaTransformer {
23652383
// for ${initializer} in ${iterable} do
23662384
const initializerVariable = statement.initializer.declarations[0].name;
23672385
if (ts.isArrayBindingPattern(initializerVariable)) {
2368-
return tstl.createForInStatement(
2369-
block,
2370-
this.filterUndefinedAndCast(
2371-
initializerVariable.elements.map(e => this.transformArrayBindingElement(e)),
2372-
tstl.isIdentifier
2373-
),
2374-
[luaIterator]
2386+
const identifiers = this.filterUndefinedAndCast(
2387+
initializerVariable.elements.map(e => this.transformArrayBindingElement(e)),
2388+
tstl.isIdentifier
23752389
);
2390+
if (identifiers.length === 0) {
2391+
identifiers.push(tstl.createAnonymousIdentifier());
2392+
}
2393+
return tstl.createForInStatement(block, identifiers, [luaIterator]);
23762394
} else {
23772395
// Single variable is not allowed
23782396
throw TSTLErrors.UnsupportedNonDestructuringLuaIterator(statement.initializer);
@@ -2383,13 +2401,17 @@ export class LuaTransformer {
23832401
// ${initializer} = ____value0
23842402
if (ts.isArrayLiteralExpression(statement.initializer)) {
23852403
const tmps = statement.initializer.elements.map((_, i) => tstl.createIdentifier(`____value${i}`));
2386-
const assign = tstl.createAssignmentStatement(
2387-
statement.initializer.elements.map(
2388-
e => this.transformExpression(e) as tstl.AssignmentLeftHandSideExpression
2389-
),
2390-
tmps
2391-
);
2392-
block.statements.splice(0, 0, assign);
2404+
if (tmps.length > 0) {
2405+
const assign = tstl.createAssignmentStatement(
2406+
statement.initializer.elements.map(
2407+
e => this.transformExpression(e) as tstl.AssignmentLeftHandSideExpression
2408+
),
2409+
tmps
2410+
);
2411+
block.statements.splice(0, 0, assign);
2412+
} else {
2413+
tmps.push(tstl.createAnonymousIdentifier());
2414+
}
23932415
return tstl.createForInStatement(block, tmps, [luaIterator]);
23942416
} else {
23952417
// Single variable is not allowed
@@ -2415,7 +2437,9 @@ export class LuaTransformer {
24152437
// local ${initializer} = unpack(____value)
24162438
const valueVariable = tstl.createIdentifier("____value");
24172439
const initializer = this.transformForOfInitializer(statement.initializer, valueVariable);
2418-
block.statements.splice(0, 0, initializer);
2440+
if (initializer) {
2441+
block.statements.splice(0, 0, initializer);
2442+
}
24192443
return tstl.createForInStatement(block, [valueVariable], [luaIterator]);
24202444
}
24212445
}
@@ -2440,7 +2464,9 @@ export class LuaTransformer {
24402464
// local ${initializer} = ____value
24412465
const valueVariable = tstl.createIdentifier("____value");
24422466
const initializer = this.transformForOfInitializer(statement.initializer, valueVariable);
2443-
block.statements.splice(0, 0, initializer);
2467+
if (initializer) {
2468+
block.statements.splice(0, 0, initializer);
2469+
}
24442470
return tstl.createForInStatement(
24452471
block,
24462472
[valueVariable],

test/unit/loops.spec.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,81 @@ test("forof forwarded lua iterator with tupleReturn", () => {
754754
expect(result).toBe("0a1b2c");
755755
});
756756

757+
describe("for...of empty destructuring", () => {
758+
const declareTests = (destructuringPrefix: string) => {
759+
test("array", () => {
760+
const code = `
761+
const arr = [["a"], ["b"], ["c"]];
762+
let i = 0;
763+
for (${destructuringPrefix}[] of arr) {
764+
++i;
765+
}
766+
return i;
767+
`;
768+
expect(util.transpileAndExecute(code)).toBe(3);
769+
});
770+
771+
test("iterable", () => {
772+
const code = `
773+
const iter: Iterable<string[]> = [["a"], ["b"], ["c"]];
774+
let i = 0;
775+
for (${destructuringPrefix}[] of iter) {
776+
++i;
777+
}
778+
return i;
779+
`;
780+
expect(util.transpileAndExecute(code)).toBe(3);
781+
});
782+
783+
test("luaIterator", () => {
784+
const code = `
785+
const arr = [["a"], ["b"], ["c"]];
786+
/** @luaIterator */
787+
interface Iter extends Iterable<string[]> {}
788+
function luaIter(): Iter {
789+
let it = 0;
790+
return (() => arr[it++]) as any;
791+
}
792+
let i = 0;
793+
for (${destructuringPrefix}[] of luaIter()) {
794+
++i;
795+
}
796+
return i;
797+
`;
798+
expect(util.transpileAndExecute(code)).toBe(3);
799+
});
800+
801+
test("luaIterator+tupleReturn", () => {
802+
const code = `
803+
const arr = [["a", "b"], ["c", "d"], ["e", "f"]];
804+
/** @luaIterator */
805+
/** @tupleReturn */
806+
interface Iter extends Iterable<[string, string]> {}
807+
function luaIter(): Iter {
808+
let it = 0;
809+
/** @tupleReturn */
810+
function iter() {
811+
const e = arr[it++];
812+
if (e) {
813+
return e;
814+
}
815+
}
816+
return iter as any;
817+
}
818+
let i = 0;
819+
for (${destructuringPrefix}[] of luaIter()) {
820+
++i;
821+
}
822+
return i;
823+
`;
824+
expect(util.transpileAndExecute(code)).toBe(3);
825+
});
826+
};
827+
828+
describe("declaration", () => declareTests("const "));
829+
describe("assignment", () => declareTests(""));
830+
});
831+
757832
test.each([
758833
"while (a < b) { i++; continue; }",
759834
"do { i++; continue; } while (a < b)",

0 commit comments

Comments
 (0)