Skip to content

Commit a08e239

Browse files
committed
Make unsupported luaIterator usage error a diagnostic
1 parent 05f9f50 commit a08e239

File tree

5 files changed

+63
-120
lines changed

5 files changed

+63
-120
lines changed

src/transformation/utils/diagnostics.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,9 @@ export const luaTableInvalidInstanceOf = createDiagnosticFactory(
6161
export const luaTableForbiddenUsage = createDiagnosticFactory(
6262
(description: string) => `Invalid @luaTable usage: ${description}.`
6363
);
64+
65+
export const luaIteratorForbiddenUsage = createDiagnosticFactory(
66+
"Unsupported use of lua iterator with '@tupleReturn' annotation in for...of statement. " +
67+
"You must use a destructuring statement to catch results from a lua iterator with " +
68+
"the '@tupleReturn' annotation."
69+
);

src/transformation/utils/errors.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,6 @@ export const UnsupportedOverloadAssignment = (node: ts.Node, name?: string) => {
5757
);
5858
};
5959

60-
export const UnsupportedNonDestructuringLuaIterator = (node: ts.Node) =>
61-
new TranspileError(
62-
"Unsupported use of lua iterator with '@tupleReturn' annotation in for...of statement. " +
63-
"You must use a destructuring statement to catch results from a lua iterator with " +
64-
"the '@tupleReturn' annotation.",
65-
node
66-
);
67-
6860
export const UnresolvableRequirePath = (node: ts.Node, reason: string, path?: string) =>
6961
new TranspileError(`${reason}. TypeScript path: ${path}.`, node);
7062

src/transformation/visitors/loops/for-of.ts

Lines changed: 28 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,8 @@ import * as lua from "../../../LuaAST";
33
import { assert, cast, castEach } from "../../../utils";
44
import { FunctionVisitor, TransformationContext } from "../../context";
55
import { AnnotationKind, getTypeAnnotations, isForRangeType, isLuaIteratorType } from "../../utils/annotations";
6-
import { invalidForRangeCall } from "../../utils/diagnostics";
7-
import {
8-
MissingForOfVariables,
9-
UnsupportedNonDestructuringLuaIterator,
10-
UnsupportedObjectDestructuringInForOf,
11-
} from "../../utils/errors";
6+
import { invalidForRangeCall, luaIteratorForbiddenUsage } from "../../utils/diagnostics";
7+
import { MissingForOfVariables, UnsupportedObjectDestructuringInForOf } from "../../utils/errors";
128
import { createUnpackCall } from "../../utils/lua-ast";
139
import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib";
1410
import { isArrayType, isNumberType } from "../../utils/typescript";
@@ -128,47 +124,42 @@ function transformForOfLuaIteratorStatement(
128124
const luaIterator = context.transformExpression(statement.expression);
129125
const type = context.checker.getTypeAtLocation(statement.expression);
130126
const tupleReturn = getTypeAnnotations(context, type).has(AnnotationKind.TupleReturn);
127+
let identifiers: lua.Identifier[] = [];
128+
131129
if (tupleReturn) {
132130
// LuaIterator + TupleReturn
133131
if (ts.isVariableDeclarationList(statement.initializer)) {
134132
// Variables declared in for loop
135133
// for ${initializer} in ${iterable} do
136134
const initializerVariable = statement.initializer.declarations[0].name;
135+
137136
if (ts.isArrayBindingPattern(initializerVariable)) {
138-
const identifiers = castEach(
137+
identifiers = castEach(
139138
initializerVariable.elements.map(e => transformArrayBindingElement(context, e)),
140139
lua.isIdentifier
141140
);
142-
if (identifiers.length === 0) {
143-
identifiers.push(lua.createAnonymousIdentifier());
144-
}
145-
return lua.createForInStatement(block, identifiers, [luaIterator]);
146141
} else {
147-
// Single variable is not allowed
148-
throw UnsupportedNonDestructuringLuaIterator(statement.initializer);
142+
context.diagnostics.push(luaIteratorForbiddenUsage(initializerVariable));
149143
}
150144
} else {
151145
// Variables NOT declared in for loop - catch iterator values in temps and assign
152146
// for ____value0 in ${iterable} do
153147
// ${initializer} = ____value0
154148
if (ts.isArrayLiteralExpression(statement.initializer)) {
155-
const tmps = statement.initializer.elements.map((_, i) => lua.createIdentifier(`____value${i}`));
156-
if (tmps.length > 0) {
157-
const assign = lua.createAssignmentStatement(
158-
castEach(
159-
statement.initializer.elements.map(e => context.transformExpression(e)),
160-
lua.isAssignmentLeftHandSideExpression
161-
),
162-
tmps
149+
identifiers = statement.initializer.elements.map((_, i) => lua.createIdentifier(`____value${i}`));
150+
if (identifiers.length > 0) {
151+
block.statements.unshift(
152+
lua.createAssignmentStatement(
153+
castEach(
154+
statement.initializer.elements.map(e => context.transformExpression(e)),
155+
lua.isAssignmentLeftHandSideExpression
156+
),
157+
identifiers
158+
)
163159
);
164-
block.statements.splice(0, 0, assign);
165-
} else {
166-
tmps.push(lua.createAnonymousIdentifier());
167160
}
168-
return lua.createForInStatement(block, tmps, [luaIterator]);
169161
} else {
170-
// Single variable is not allowed
171-
throw UnsupportedNonDestructuringLuaIterator(statement.initializer);
162+
context.diagnostics.push(luaIteratorForbiddenUsage(statement.initializer));
172163
}
173164
}
174165
} else {
@@ -179,23 +170,25 @@ function transformForOfLuaIteratorStatement(
179170
) {
180171
// Single variable declared in for loop
181172
// for ${initializer} in ${iterator} do
182-
return lua.createForInStatement(
183-
block,
184-
[transformIdentifier(context, statement.initializer.declarations[0].name)],
185-
[luaIterator]
186-
);
173+
identifiers.push(transformIdentifier(context, statement.initializer.declarations[0].name));
187174
} else {
188175
// Destructuring or variable NOT declared in for loop
189176
// for ____value in ${iterator} do
190-
// local ${initializer} = unpack(____value)
177+
// local ${initializer} = ____value
191178
const valueVariable = lua.createIdentifier("____value");
192179
const initializer = transformForOfInitializer(context, statement.initializer, valueVariable);
193180
if (initializer) {
194-
block.statements.splice(0, 0, initializer);
181+
identifiers.push(valueVariable);
182+
block.statements.unshift(initializer);
195183
}
196-
return lua.createForInStatement(block, [valueVariable], [luaIterator]);
197184
}
198185
}
186+
187+
if (identifiers.length === 0) {
188+
identifiers.push(lua.createAnonymousIdentifier());
189+
}
190+
191+
return lua.createForInStatement(block, identifiers, [luaIterator]);
199192
}
200193

201194
function transformForOfArrayStatement(
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`forof lua iterator tuple-return single existing variable: code 1`] = `
4+
"local x
5+
for ____ in luaIter(_G) do
6+
end"
7+
`;
8+
9+
exports[`forof lua iterator tuple-return single existing variable: diagnostics 1`] = `"main.ts(9,14): error TSTL: Unsupported use of lua iterator with '@tupleReturn' annotation in for...of statement. You must use a destructuring statement to catch results from a lua iterator with the '@tupleReturn' annotation."`;
10+
11+
exports[`forof lua iterator tuple-return single variable: code 1`] = `
12+
"for ____ in luaIter(_G) do
13+
end"
14+
`;
15+
16+
exports[`forof lua iterator tuple-return single variable: diagnostics 1`] = `"main.ts(8,18): error TSTL: Unsupported use of lua iterator with '@tupleReturn' annotation in for...of statement. You must use a destructuring statement to catch results from a lua iterator with the '@tupleReturn' annotation."`;

test/unit/decorators/luaIterator.spec.ts

Lines changed: 13 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
import * as ts from "typescript";
2-
import * as tstl from "../../../src";
3-
import { UnsupportedNonDestructuringLuaIterator } from "../../../src/transformation/utils/errors";
41
import * as util from "../../util";
52

63
test("forof lua iterator", () => {
@@ -16,12 +13,7 @@ test("forof lua iterator", () => {
1613
for (let e of luaIter()) { result += e; }
1714
return result;
1815
`;
19-
const compilerOptions = {
20-
luaLibImport: tstl.LuaLibImportKind.Require,
21-
luaTarget: tstl.LuaTarget.Lua53,
22-
target: ts.ScriptTarget.ES2015,
23-
};
24-
const result = util.transpileAndExecute(code, compilerOptions);
16+
const result = util.transpileAndExecute(code);
2517
expect(result).toBe("abc");
2618
});
2719

@@ -38,12 +30,7 @@ test("forof array lua iterator", () => {
3830
for (let e of luaIter()) { result += e; }
3931
return result;
4032
`;
41-
const compilerOptions = {
42-
luaLibImport: tstl.LuaLibImportKind.Require,
43-
luaTarget: tstl.LuaTarget.Lua53,
44-
target: ts.ScriptTarget.ES2015,
45-
};
46-
const result = util.transpileAndExecute(code, compilerOptions);
33+
const result = util.transpileAndExecute(code);
4734
expect(result).toBe("abc");
4835
});
4936

@@ -61,12 +48,7 @@ test("forof lua iterator with existing variable", () => {
6148
for (e of luaIter()) { result += e; }
6249
return result;
6350
`;
64-
const compilerOptions = {
65-
luaLibImport: tstl.LuaLibImportKind.Require,
66-
luaTarget: tstl.LuaTarget.Lua53,
67-
target: ts.ScriptTarget.ES2015,
68-
};
69-
const result = util.transpileAndExecute(code, compilerOptions);
51+
const result = util.transpileAndExecute(code);
7052
expect(result).toBe("abc");
7153
});
7254

@@ -83,12 +65,7 @@ test("forof lua iterator destructuring", () => {
8365
for (let [a, b] of luaIter()) { result += a + b; }
8466
return result;
8567
`;
86-
const compilerOptions = {
87-
luaLibImport: tstl.LuaLibImportKind.Require,
88-
luaTarget: tstl.LuaTarget.Lua53,
89-
target: ts.ScriptTarget.ES2015,
90-
};
91-
const result = util.transpileAndExecute(code, compilerOptions);
68+
const result = util.transpileAndExecute(code);
9269
expect(result).toBe("0a1b2c");
9370
});
9471

@@ -107,12 +84,7 @@ test("forof lua iterator destructuring with existing variables", () => {
10784
for ([a, b] of luaIter()) { result += a + b; }
10885
return result;
10986
`;
110-
const compilerOptions = {
111-
luaLibImport: tstl.LuaLibImportKind.Require,
112-
luaTarget: tstl.LuaTarget.Lua53,
113-
target: ts.ScriptTarget.ES2015,
114-
};
115-
const result = util.transpileAndExecute(code, compilerOptions);
87+
const result = util.transpileAndExecute(code);
11688
expect(result).toBe("0a1b2c");
11789
});
11890

@@ -134,12 +106,7 @@ test("forof lua iterator tuple-return", () => {
134106
for (let [a, b] of luaIter()) { result += a + b; }
135107
return result;
136108
`;
137-
const compilerOptions = {
138-
luaLibImport: tstl.LuaLibImportKind.Require,
139-
luaTarget: tstl.LuaTarget.Lua53,
140-
target: ts.ScriptTarget.ES2015,
141-
};
142-
const result = util.transpileAndExecute(code, compilerOptions);
109+
const result = util.transpileAndExecute(code);
143110
expect(result).toBe("0a1b2c");
144111
});
145112

@@ -163,37 +130,24 @@ test("forof lua iterator tuple-return with existing variables", () => {
163130
for ([a, b] of luaIter()) { result += a + b; }
164131
return result;
165132
`;
166-
const compilerOptions = {
167-
luaLibImport: tstl.LuaLibImportKind.Require,
168-
luaTarget: tstl.LuaTarget.Lua53,
169-
target: ts.ScriptTarget.ES2015,
170-
};
171-
const result = util.transpileAndExecute(code, compilerOptions);
133+
const result = util.transpileAndExecute(code);
172134
expect(result).toBe("0a1b2c");
173135
});
174136

175137
test("forof lua iterator tuple-return single variable", () => {
176-
const code = `
138+
util.testModule`
177139
/**
178140
* @luaIterator
179141
* @tupleReturn
180142
*/
181143
interface Iter extends Iterable<[string, string]> {}
182144
declare function luaIter(): Iter;
183145
for (let x of luaIter()) {}
184-
`;
185-
const compilerOptions = {
186-
luaLibImport: tstl.LuaLibImportKind.Require,
187-
luaTarget: tstl.LuaTarget.Lua53,
188-
target: ts.ScriptTarget.ES2015,
189-
};
190-
expect(() => util.transpileString(code, compilerOptions)).toThrowExactError(
191-
UnsupportedNonDestructuringLuaIterator(util.nodeStub)
192-
);
146+
`.expectDiagnosticsToMatchSnapshot();
193147
});
194148

195149
test("forof lua iterator tuple-return single existing variable", () => {
196-
const code = `
150+
util.testModule`
197151
/**
198152
* @luaIterator
199153
* @tupleReturn
@@ -202,15 +156,7 @@ test("forof lua iterator tuple-return single existing variable", () => {
202156
declare function luaIter(): Iter;
203157
let x: [string, string];
204158
for (x of luaIter()) {}
205-
`;
206-
const compilerOptions = {
207-
luaLibImport: tstl.LuaLibImportKind.Require,
208-
luaTarget: tstl.LuaTarget.Lua53,
209-
target: ts.ScriptTarget.ES2015,
210-
};
211-
expect(() => util.transpileString(code, compilerOptions)).toThrowExactError(
212-
UnsupportedNonDestructuringLuaIterator(util.nodeStub)
213-
);
159+
`.expectDiagnosticsToMatchSnapshot();
214160
});
215161

216162
test("forof forwarded lua iterator", () => {
@@ -231,12 +177,7 @@ test("forof forwarded lua iterator", () => {
231177
for (let a of forward()) { result += a; }
232178
return result;
233179
`;
234-
const compilerOptions = {
235-
luaLibImport: tstl.LuaLibImportKind.Require,
236-
luaTarget: tstl.LuaTarget.Lua53,
237-
target: ts.ScriptTarget.ES2015,
238-
};
239-
const result = util.transpileAndExecute(code, compilerOptions);
180+
const result = util.transpileAndExecute(code);
240181
expect(result).toBe("abc");
241182
});
242183

@@ -262,11 +203,6 @@ test("forof forwarded lua iterator with tupleReturn", () => {
262203
for (let [a, b] of forward()) { result += a + b; }
263204
return result;
264205
`;
265-
const compilerOptions = {
266-
luaLibImport: tstl.LuaLibImportKind.Require,
267-
luaTarget: tstl.LuaTarget.Lua53,
268-
target: ts.ScriptTarget.ES2015,
269-
};
270-
const result = util.transpileAndExecute(code, compilerOptions);
206+
const result = util.transpileAndExecute(code);
271207
expect(result).toBe("0a1b2c");
272208
});

0 commit comments

Comments
 (0)