From f8f5ccbdc4779dc8f96022b1203cccbecc1fd3fc Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Thu, 27 Feb 2020 16:40:08 +1000 Subject: [PATCH 01/22] Use smarter expression spreading --- src/transformation/visitors/call.ts | 64 +++++++++++++++++++++++++- src/transformation/visitors/literal.ts | 10 ++-- test/unit/spread.spec.ts | 19 ++++++++ 3 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 7656ed01d..1919cc437 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -16,13 +16,75 @@ import { transformLuaTableCallExpression } from "./lua-table"; export type PropertyCallExpression = ts.CallExpression & { expression: ts.PropertyAccessExpression }; +function getExpressionsBeforeAndAfterFirstSpread( + expressions: readonly ts.Expression[] +): [readonly ts.Expression[], readonly ts.Expression[]] { + // [a, b, ...c, d, ...e] --> [a, b] and [...c, d, ...e] + const index = expressions.findIndex(ts.isSpreadElement); + const hasSpreadElement = index !== -1; + const before = hasSpreadElement ? expressions.slice(0, index) : expressions; + const after = hasSpreadElement ? expressions.slice(index, expressions.length) : []; + return [before, after]; +} + +function transformSpreadableExpressionsIntoArrayConcatArguments( + context: TransformationContext, + expressions: readonly ts.Expression[] | ts.NodeArray +): lua.Expression[] { + // [...array, a, b, ...tuple()] --> [ [...array], [a, b], [...tuple()] ] + // chunk non-spread arguments together so they don't concat + const chunks = expressions.reduce((chunks, argument, index, expressions) => { + if (ts.isSpreadElement(argument)) { + chunks.push([argument]); + const next = expressions[index + 1]; + if (next && !ts.isSpreadElement(expressions[index + 1])) { + chunks.push([]); + } + } else { + const lastChunk = chunks[chunks.length - 1]; + lastChunk.push(argument); + } + return chunks; + }, []); + + return chunks.map(chunk => wrapInTable(...chunk.map(expression => context.transformExpression(expression)))); +} + +export function flattenExpressions( + context: TransformationContext, + expressions: readonly ts.Expression[] +): lua.Expression[] { + const [preSpreadExpressions, postSpreadExpressions] = getExpressionsBeforeAndAfterFirstSpread(expressions); + const transformedPreSpreadExpressions = preSpreadExpressions.map(a => context.transformExpression(a)); + + // Nothing special required + if (postSpreadExpressions.length === 0) { + return transformedPreSpreadExpressions; + } + + // Only one spread element at the end? Will work as expected + const [firstExpression] = postSpreadExpressions; + if (postSpreadExpressions.length === 1 && ts.isSpreadElement(firstExpression)) { + return [...transformedPreSpreadExpressions, context.transformExpression(firstExpression)]; + } + + // Use Array.concat and unpack the result of that as the last Expression + const concatArguments = transformSpreadableExpressionsIntoArrayConcatArguments(context, postSpreadExpressions); + const lastExpression = createUnpackCall( + context, + transformLuaLibFunction(context, LuaLibFeature.ArrayConcat, undefined, ...concatArguments) + ); + + return [...transformedPreSpreadExpressions, lastExpression]; +} + export function transformArguments( context: TransformationContext, params: readonly ts.Expression[], signature?: ts.Signature, callContext?: ts.Expression ): lua.Expression[] { - const parameters = params.map(param => context.transformExpression(param)); + const parameters = flattenExpressions(context, params); // Add context as first param if present if (callContext) { diff --git a/src/transformation/visitors/literal.ts b/src/transformation/visitors/literal.ts index 6149e476f..b14b4b4b8 100644 --- a/src/transformation/visitors/literal.ts +++ b/src/transformation/visitors/literal.ts @@ -14,6 +14,7 @@ import { import { getSymbolIdOfSymbol, trackSymbolReference } from "../utils/symbols"; import { isArrayType } from "../utils/typescript"; import { transformFunctionLikeDeclaration } from "./function"; +import { flattenExpressions } from "./call"; // TODO: Move to object-literal.ts? export function transformPropertyName(context: TransformationContext, node: ts.PropertyName): lua.Expression { @@ -137,13 +138,10 @@ const transformObjectLiteralExpression: FunctionVisitor = (expression, context) => { - const values = expression.elements.map(element => - lua.createTableFieldExpression( - ts.isOmittedExpression(element) ? lua.createNilLiteral(element) : context.transformExpression(element), - undefined, - element - ) + const filteredElements = expression.elements.map(e => + ts.isOmittedExpression(e) ? ts.createIdentifier("undefined") : e ); + const values = flattenExpressions(context, filteredElements).map(e => lua.createTableFieldExpression(e)); return lua.createTableExpression(values, expression); }; diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 9f6f19ae4..6fc0c4788 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -87,3 +87,22 @@ describe("in object literal", () => { `.expectToMatchJsResult(); }); }); + +const spreadCases = [ + "1, 2, ...[3, 4, 5]", + "...[1, 2], 3, 4, 5", + "1, 2, ...'spread', 4, 5", + "1, [2], ...[3, 4, 5]", + "...[1, 2, 3], 4, ...[5, 6]", +]; + +test.each(spreadCases)("spread equivalence for array elements (%p)", expression => { + util.testExpression`[${expression}]`.expectToMatchJsResult(); +}); + +test.each(spreadCases)("spread equivalence for call arguments (%p)", expression => { + util.testFunction` + function foo(...args) { return args } + return foo(${expression}); + `.expectToMatchJsResult(); +}); From 18c1e081e4cefef3188c12632e6bc18314ecf0f8 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Thu, 27 Feb 2020 19:25:57 +1000 Subject: [PATCH 02/22] Fix up test to check that concat doesn't flatten args --- test/unit/spread.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 6fc0c4788..d6bf5cbaf 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -92,7 +92,7 @@ const spreadCases = [ "1, 2, ...[3, 4, 5]", "...[1, 2], 3, 4, 5", "1, 2, ...'spread', 4, 5", - "1, [2], ...[3, 4, 5]", + "1, 2, ...[3, 4, 5], [2]", "...[1, 2, 3], 4, ...[5, 6]", ]; From f37503fb98508f96282a4f9b2e2774385c82ae25 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Thu, 27 Feb 2020 19:26:34 +1000 Subject: [PATCH 03/22] flattenExpressions -> flattenSpreadExpressions --- src/transformation/visitors/call.ts | 4 ++-- src/transformation/visitors/literal.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 1919cc437..f9c17c765 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -50,7 +50,7 @@ function transformSpreadableExpressionsIntoArrayConcatArguments( return chunks.map(chunk => wrapInTable(...chunk.map(expression => context.transformExpression(expression)))); } -export function flattenExpressions( +export function flattenSpreadExpressions( context: TransformationContext, expressions: readonly ts.Expression[] ): lua.Expression[] { @@ -84,7 +84,7 @@ export function transformArguments( signature?: ts.Signature, callContext?: ts.Expression ): lua.Expression[] { - const parameters = flattenExpressions(context, params); + const parameters = flattenSpreadExpressions(context, params); // Add context as first param if present if (callContext) { diff --git a/src/transformation/visitors/literal.ts b/src/transformation/visitors/literal.ts index b14b4b4b8..f4384c48f 100644 --- a/src/transformation/visitors/literal.ts +++ b/src/transformation/visitors/literal.ts @@ -14,7 +14,7 @@ import { import { getSymbolIdOfSymbol, trackSymbolReference } from "../utils/symbols"; import { isArrayType } from "../utils/typescript"; import { transformFunctionLikeDeclaration } from "./function"; -import { flattenExpressions } from "./call"; +import { flattenSpreadExpressions } from "./call"; // TODO: Move to object-literal.ts? export function transformPropertyName(context: TransformationContext, node: ts.PropertyName): lua.Expression { @@ -141,7 +141,7 @@ const transformArrayLiteralExpression: FunctionVisitor ts.isOmittedExpression(e) ? ts.createIdentifier("undefined") : e ); - const values = flattenExpressions(context, filteredElements).map(e => lua.createTableFieldExpression(e)); + const values = flattenSpreadExpressions(context, filteredElements).map(e => lua.createTableFieldExpression(e)); return lua.createTableExpression(values, expression); }; From 972dde895fb2dcb36f43d53876adf0586bc9ffab Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Thu, 27 Feb 2020 19:28:37 +1000 Subject: [PATCH 04/22] Use for-of instead of reduce and rename flattenExpressions --- src/transformation/visitors/call.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index f9c17c765..b3121793c 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -33,19 +33,19 @@ function transformSpreadableExpressionsIntoArrayConcatArguments( ): lua.Expression[] { // [...array, a, b, ...tuple()] --> [ [...array], [a, b], [...tuple()] ] // chunk non-spread arguments together so they don't concat - const chunks = expressions.reduce((chunks, argument, index, expressions) => { - if (ts.isSpreadElement(argument)) { - chunks.push([argument]); - const next = expressions[index + 1]; - if (next && !ts.isSpreadElement(expressions[index + 1])) { + const chunks: ts.Expression[][] = []; + for (const expression of expressions) { + if (ts.isSpreadElement(expression)) { + chunks.push([expression]); + const next = expressions[expressions.indexOf(expression) + 1]; + if (next && !ts.isSpreadElement(next)) { chunks.push([]); } } else { const lastChunk = chunks[chunks.length - 1]; - lastChunk.push(argument); + lastChunk.push(expression); } - return chunks; - }, []); + } return chunks.map(chunk => wrapInTable(...chunk.map(expression => context.transformExpression(expression)))); } From 352763f4ca93fc338c0eb7235c1f6361a419af82 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Thu, 27 Feb 2020 19:33:05 +1000 Subject: [PATCH 05/22] Move test cases up to in function call and in array literal --- test/unit/spread.spec.ts | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index d6bf5cbaf..efb932044 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -5,6 +5,14 @@ import * as util from "../util"; const expectUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toMatch(/[^.]unpack\(/); const expectTableUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("table.unpack"); +const spreadCases = [ + "1, 2, ...[3, 4, 5]", + "...[1, 2], 3, 4, 5", + "1, 2, ...'spread', 4, 5", + "1, 2, ...[3, 4, 5], [2]", + "...[1, 2, 3], 4, ...[5, 6]", +]; + describe("in function call", () => { util.testEachVersion( undefined, @@ -23,6 +31,13 @@ describe("in function call", () => { [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), } ); + + test.each(spreadCases)("of arguments (%p)", expression => { + util.testFunction` + function foo(...args) { return args } + return foo(${expression}); + `.expectToMatchJsResult(); + }); }); describe("in array literal", () => { @@ -33,6 +48,10 @@ describe("in array literal", () => { [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), }); + test.each(spreadCases)("of array literal (%p)", expression => { + util.testExpression`[${expression}]`.expectToMatchJsResult(); + }); + test.each(["", "string", "string with spaces", "string 1 2 3"])("of string literal (%p)", str => { util.testExpressionTemplate`[...${str}]`.expectToMatchJsResult(); }); @@ -87,22 +106,3 @@ describe("in object literal", () => { `.expectToMatchJsResult(); }); }); - -const spreadCases = [ - "1, 2, ...[3, 4, 5]", - "...[1, 2], 3, 4, 5", - "1, 2, ...'spread', 4, 5", - "1, 2, ...[3, 4, 5], [2]", - "...[1, 2, 3], 4, ...[5, 6]", -]; - -test.each(spreadCases)("spread equivalence for array elements (%p)", expression => { - util.testExpression`[${expression}]`.expectToMatchJsResult(); -}); - -test.each(spreadCases)("spread equivalence for call arguments (%p)", expression => { - util.testFunction` - function foo(...args) { return args } - return foo(${expression}); - `.expectToMatchJsResult(); -}); From 478d179730e57aa9ed4447d702be249e41360192 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Sat, 29 Feb 2020 22:28:16 +1000 Subject: [PATCH 06/22] Update src/transformation/visitors/call.ts Co-Authored-By: ark120202 --- src/transformation/visitors/call.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index b3121793c..faf78efc0 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -23,7 +23,7 @@ function getExpressionsBeforeAndAfterFirstSpread( const index = expressions.findIndex(ts.isSpreadElement); const hasSpreadElement = index !== -1; const before = hasSpreadElement ? expressions.slice(0, index) : expressions; - const after = hasSpreadElement ? expressions.slice(index, expressions.length) : []; + const after = hasSpreadElement ? expressions.slice(index) : []; return [before, after]; } From debfa31b536610a0cec0687f9c67ac1a1abc7ed6 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Sat, 29 Feb 2020 22:31:01 +1000 Subject: [PATCH 07/22] Update src/transformation/visitors/call.ts Co-Authored-By: ark120202 --- src/transformation/visitors/call.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index faf78efc0..579b7deec 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -34,10 +34,10 @@ function transformSpreadableExpressionsIntoArrayConcatArguments( // [...array, a, b, ...tuple()] --> [ [...array], [a, b], [...tuple()] ] // chunk non-spread arguments together so they don't concat const chunks: ts.Expression[][] = []; - for (const expression of expressions) { + for (const [index, expression] of expressions.entries()) { if (ts.isSpreadElement(expression)) { chunks.push([expression]); - const next = expressions[expressions.indexOf(expression) + 1]; + const next = expressions[index + 1]; if (next && !ts.isSpreadElement(next)) { chunks.push([]); } From 43400fc97c413d72250ae5ee252352fd10011352 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 2 Mar 2020 11:25:20 +1000 Subject: [PATCH 08/22] Add test case to check concat --- test/unit/spread.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index efb932044..377d9ad95 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -9,7 +9,7 @@ const spreadCases = [ "1, 2, ...[3, 4, 5]", "...[1, 2], 3, 4, 5", "1, 2, ...'spread', 4, 5", - "1, 2, ...[3, 4, 5], [2]", + "1, ...[[2]], 3", "...[1, 2, 3], 4, ...[5, 6]", ]; From 0d0af75261faf941896d0bb94f5b3f6b6b8dfa80 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 2 Mar 2020 11:34:54 +1000 Subject: [PATCH 09/22] Add in array literal/of tuple return call test --- test/unit/spread.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 377d9ad95..d4ef5edb2 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -48,6 +48,17 @@ describe("in array literal", () => { [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), }); + test("of tuple return call", () => { + util.testFunction` + /** @tupleReturn */ + function tuple(...args) { + return args; + } + + return [...tuple(1, 2, 3), ...tuple(4, 5, 6)]; + `.expectToMatchJsResult(); + }); + test.each(spreadCases)("of array literal (%p)", expression => { util.testExpression`[${expression}]`.expectToMatchJsResult(); }); From aa2c8d2e231cf1ed1ba6fc55cdb72e4ab50d319e Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 2 Mar 2020 11:37:11 +1000 Subject: [PATCH 10/22] Add test for potential edge case --- test/unit/spread.spec.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index d4ef5edb2..ec0c8380c 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -11,6 +11,7 @@ const spreadCases = [ "1, 2, ...'spread', 4, 5", "1, ...[[2]], 3", "...[1, 2, 3], 4, ...[5, 6]", + "1, 2, ...[3, 4], ...[5, 6]", ]; describe("in function call", () => { From c8ebe4ee9a6f74704287dfa7b4a737f6ce603490 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 2 Mar 2020 11:45:40 +1000 Subject: [PATCH 11/22] Add in array literal/of array literal /w OmittedExpression --- test/unit/spread.spec.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index ec0c8380c..6702376c1 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -60,6 +60,13 @@ describe("in array literal", () => { `.expectToMatchJsResult(); }); + test("of array literal /w OmittedExpression", () => { + util.testFunction` + const array = [1, 2, ...[3], , 5]; + return { a: array[0], b: array[1], c: array[2], d: array[3] }; + `.expectToMatchJsResult(); + }); + test.each(spreadCases)("of array literal (%p)", expression => { util.testExpression`[${expression}]`.expectToMatchJsResult(); }); From a8dee7160151a6c0695c127994061008c8b5ce66 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 2 Mar 2020 12:19:12 +1000 Subject: [PATCH 12/22] Refactor unneeded check in call --- src/transformation/visitors/call.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 579b7deec..144c54b05 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -63,9 +63,8 @@ export function flattenSpreadExpressions( } // Only one spread element at the end? Will work as expected - const [firstExpression] = postSpreadExpressions; - if (postSpreadExpressions.length === 1 && ts.isSpreadElement(firstExpression)) { - return [...transformedPreSpreadExpressions, context.transformExpression(firstExpression)]; + if (postSpreadExpressions.length === 1) { + return [...transformedPreSpreadExpressions, context.transformExpression(postSpreadExpressions[0])]; } // Use Array.concat and unpack the result of that as the last Expression From 185751c1c1e1a50cc2d6f19f182f7bb903b1af98 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 2 Mar 2020 12:33:05 +1000 Subject: [PATCH 13/22] Allow spreadable expressions in any order --- src/transformation/visitors/call.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 144c54b05..549e609f1 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -42,7 +42,11 @@ function transformSpreadableExpressionsIntoArrayConcatArguments( chunks.push([]); } } else { - const lastChunk = chunks[chunks.length - 1]; + let lastChunk = chunks[chunks.length - 1]; + if (!lastChunk) { + lastChunk = []; + chunks.push(lastChunk); + } lastChunk.push(expression); } } From e197a686e8b9c423e7ff0ad9db508e0ec0539932 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Mon, 2 Mar 2020 12:37:32 +1000 Subject: [PATCH 14/22] Add of string literal mixed --- test/unit/spread.spec.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 6702376c1..3a4ac407a 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -8,7 +8,6 @@ const expectTableUnpack: util.TapCallback = builder => expect(builder.getMainLua const spreadCases = [ "1, 2, ...[3, 4, 5]", "...[1, 2], 3, 4, 5", - "1, 2, ...'spread', 4, 5", "1, ...[[2]], 3", "...[1, 2, 3], 4, ...[5, 6]", "1, 2, ...[3, 4], ...[5, 6]", @@ -67,6 +66,10 @@ describe("in array literal", () => { `.expectToMatchJsResult(); }); + test("of string literal mixed", () => { + util.testExpressionTemplate`[..."spread", ..."string"]`.expectToMatchJsResult(); + }); + test.each(spreadCases)("of array literal (%p)", expression => { util.testExpression`[${expression}]`.expectToMatchJsResult(); }); From 4aaa38d1245de011fb3543b2fdd89ec9bc0daf6a Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Wed, 11 Mar 2020 18:37:38 +1000 Subject: [PATCH 15/22] Update test/unit/spread.spec.ts Co-Authored-By: ark120202 --- test/unit/spread.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 3a4ac407a..be7f17b53 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -5,7 +5,7 @@ import * as util from "../util"; const expectUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toMatch(/[^.]unpack\(/); const expectTableUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("table.unpack"); -const spreadCases = [ +const arrayLiteralCases = [ "1, 2, ...[3, 4, 5]", "...[1, 2], 3, 4, 5", "1, ...[[2]], 3", From fd472350fdfc1e46577adf0b4c0120257a75df0f Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Wed, 11 Mar 2020 18:37:48 +1000 Subject: [PATCH 16/22] Update test/unit/spread.spec.ts Co-Authored-By: ark120202 --- test/unit/spread.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index be7f17b53..5594a74ce 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -32,7 +32,7 @@ describe("in function call", () => { } ); - test.each(spreadCases)("of arguments (%p)", expression => { + test.each(arrayLiteralCases)("of array literal (%p)", expression => { util.testFunction` function foo(...args) { return args } return foo(${expression}); From 5ce44ae09060fc3e19a687495047829ed00fe258 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Wed, 11 Mar 2020 18:36:28 +1000 Subject: [PATCH 17/22] of string literal mixed -> of string literal --- test/unit/spread.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 5594a74ce..a1d42eb06 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -66,7 +66,7 @@ describe("in array literal", () => { `.expectToMatchJsResult(); }); - test("of string literal mixed", () => { + test("of string literal", () => { util.testExpressionTemplate`[..."spread", ..."string"]`.expectToMatchJsResult(); }); From 11372c4dbf7cc413a5e9ef653635b9d16db791fa Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Wed, 11 Mar 2020 18:38:24 +1000 Subject: [PATCH 18/22] spreadCases -> arrayLiteralCases --- test/unit/spread.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index a1d42eb06..9a03936bd 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -70,7 +70,7 @@ describe("in array literal", () => { util.testExpressionTemplate`[..."spread", ..."string"]`.expectToMatchJsResult(); }); - test.each(spreadCases)("of array literal (%p)", expression => { + test.each(arrayLiteralCases)("of array literal (%p)", expression => { util.testExpression`[${expression}]`.expectToMatchJsResult(); }); From 03816981dfe571fe78c6e70b7114bd10d7d218b7 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Wed, 11 Mar 2020 18:39:25 +1000 Subject: [PATCH 19/22] Update test/unit/spread.spec.ts Co-Authored-By: ark120202 --- test/unit/spread.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 9a03936bd..4c8fc8965 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -34,7 +34,7 @@ describe("in function call", () => { test.each(arrayLiteralCases)("of array literal (%p)", expression => { util.testFunction` - function foo(...args) { return args } + function foo(...args: any[]) { return args } return foo(${expression}); `.expectToMatchJsResult(); }); From 5c27b9aa233455442526567509aab614c705dc74 Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Wed, 11 Mar 2020 19:36:06 +1000 Subject: [PATCH 20/22] Add tuple return tests to in function call --- test/unit/spread.spec.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 9a03936bd..840ffc922 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -13,6 +13,12 @@ const arrayLiteralCases = [ "1, 2, ...[3, 4], ...[5, 6]", ]; +const tupleReturnDefinition = ` +/** @tupleReturn */ +function tuple(...args: any[]) { + return args; +}`; + describe("in function call", () => { util.testEachVersion( undefined, @@ -33,9 +39,13 @@ describe("in function call", () => { ); test.each(arrayLiteralCases)("of array literal (%p)", expression => { + util.testExpression`((...args: any[]) => args)(${expression})`.expectToMatchJsResult(); + }); + + test.each(arrayLiteralCases)("of tuple return", expression => { util.testFunction` - function foo(...args) { return args } - return foo(${expression}); + ${tupleReturnDefinition} + return [...tuple(${expression})]; `.expectToMatchJsResult(); }); }); @@ -48,14 +58,10 @@ describe("in array literal", () => { [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), }); - test("of tuple return call", () => { + test.each(arrayLiteralCases)("of tuple return call", expression => { util.testFunction` - /** @tupleReturn */ - function tuple(...args) { - return args; - } - - return [...tuple(1, 2, 3), ...tuple(4, 5, 6)]; + ${tupleReturnDefinition} + return [...tuple(${expression})]; `.expectToMatchJsResult(); }); From 53de6be5b77865ccc73c7a07b42068f50c2e50ac Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Wed, 11 Mar 2020 19:45:37 +1000 Subject: [PATCH 21/22] Seperate spread to in function and in array literal --- test/unit/spread.spec.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index a5bd7a314..9b995136f 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -43,9 +43,16 @@ describe("in function call", () => { }); test.each(arrayLiteralCases)("of tuple return", expression => { + util.testFunction` + ${tupleReturnDefinition} + return ((...args: any[]) => args)(...tuple(${expression})); + `.expectToMatchJsResult(); + }); + + test("of string literal", () => { util.testFunction` function foo(...args: any[]) { return args } - return foo(${expression}); + return foo(..."spread", ..."string"); `.expectToMatchJsResult(); }); }); From 59b44f0c17dc834ce8d94261e7536ca34183959d Mon Sep 17 00:00:00 2001 From: hazzard993 Date: Sun, 15 Mar 2020 06:03:01 +1000 Subject: [PATCH 22/22] Join spread function call and array literal tests --- test/unit/spread.spec.ts | 107 ++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 59 deletions(-) diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index 9b995136f..0a54e3a41 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -1,5 +1,6 @@ import * as tstl from "../../src"; import * as util from "../util"; +import { formatCode } from "../util"; // TODO: Make some utils for testing other targets const expectUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toMatch(/[^.]unpack\(/); @@ -19,76 +20,29 @@ function tuple(...args: any[]) { return args; }`; -describe("in function call", () => { - util.testEachVersion( - undefined, - () => util.testFunction` - function foo(a: number, b: number, ...rest: number[]) { - return { a, b, rest } - } - - const array = [0, 1, 2, 3] as const; - return foo(...array); - `, - { - [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), - [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), - [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack), - [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), - } - ); +describe.each(["function call", "array literal"] as const)("in %s", kind => { + // prettier-ignore + const factory = (code: string) => kind === "function call" + ? `((...args: any[]) => args)(${code})` + : `[${code}]`; test.each(arrayLiteralCases)("of array literal (%p)", expression => { - util.testExpression`((...args: any[]) => args)(${expression})`.expectToMatchJsResult(); - }); - - test.each(arrayLiteralCases)("of tuple return", expression => { - util.testFunction` - ${tupleReturnDefinition} - return ((...args: any[]) => args)(...tuple(${expression})); - `.expectToMatchJsResult(); - }); - - test("of string literal", () => { - util.testFunction` - function foo(...args: any[]) { return args } - return foo(..."spread", ..."string"); - `.expectToMatchJsResult(); - }); -}); - -describe("in array literal", () => { - util.testEachVersion("of array literal", () => util.testExpression`[...[0, 1, 2]]`, { - [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), - [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), - [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack), - [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + util.testExpression(factory(expression)).expectToMatchJsResult(); }); - test.each(arrayLiteralCases)("of tuple return call", expression => { + test.each(arrayLiteralCases)("of tuple return call (%p)", expression => { util.testFunction` ${tupleReturnDefinition} - return [...tuple(${expression})]; + return ${factory(`...tuple(${expression})`)}; `.expectToMatchJsResult(); }); - test("of array literal /w OmittedExpression", () => { - util.testFunction` - const array = [1, 2, ...[3], , 5]; - return { a: array[0], b: array[1], c: array[2], d: array[3] }; - `.expectToMatchJsResult(); - }); - - test("of string literal", () => { - util.testExpressionTemplate`[..."spread", ..."string"]`.expectToMatchJsResult(); - }); - - test.each(arrayLiteralCases)("of array literal (%p)", expression => { - util.testExpression`[${expression}]`.expectToMatchJsResult(); + test("of multiple string literals", () => { + util.testExpression(factory('..."spread", ..."string"')).expectToMatchJsResult(); }); test.each(["", "string", "string with spaces", "string 1 2 3"])("of string literal (%p)", str => { - util.testExpressionTemplate`[...${str}]`.expectToMatchJsResult(); + util.testExpression(factory(`...${formatCode(str)}`)).expectToMatchJsResult(); }); test("of iterable", () => { @@ -107,7 +61,42 @@ describe("in array literal", () => { } }; - return [...it] + return ${factory("...it")}; + `.expectToMatchJsResult(); + }); +}); + +describe("in function call", () => { + util.testEachVersion( + undefined, + () => util.testFunction` + function foo(a: number, b: number, ...rest: number[]) { + return { a, b, rest } + } + const array = [0, 1, 2, 3] as const; + return foo(...array); + `, + { + [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack), + [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + } + ); +}); + +describe("in array literal", () => { + util.testEachVersion(undefined, () => util.testExpression`[...[0, 1, 2]]`, { + [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack), + [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + }); + + test("of array literal /w OmittedExpression", () => { + util.testFunction` + const array = [1, 2, ...[3], , 5]; + return { a: array[0], b: array[1], c: array[2], d: array[3] }; `.expectToMatchJsResult(); }); });