diff --git a/package-lock.json b/package-lock.json index 4f4838c85..b2fdb24d8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "typescript-to-lua", - "version": "1.31.1", + "version": "1.31.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "typescript-to-lua", - "version": "1.31.1", + "version": "1.31.2", "license": "MIT", "dependencies": { "@typescript-to-lua/language-extensions": "1.19.0", diff --git a/package.json b/package.json index c1102a52c..3a1e7efe8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typescript-to-lua", - "version": "1.31.1", + "version": "1.31.2", "description": "A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua!", "repository": "https://github.com/TypeScriptToLua/TypeScriptToLua", "homepage": "https://typescripttolua.github.io/", diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 1f9fb521b..d37561910 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -159,7 +159,7 @@ export function transformContextualCallExpression( ); return lua.createCallExpression(expression, transformedArguments, node); } - } else if (ts.isIdentifier(left)) { + } else if (ts.isIdentifier(left) || ts.isCallExpression(left)) { const callContext = context.isStrict ? ts.factory.createNull() : ts.factory.createIdentifier("_G"); let expression: lua.Expression; [expression, transformedArguments] = transformCallWithArguments( diff --git a/src/transformation/visitors/loops/for.ts b/src/transformation/visitors/loops/for.ts index 3a82b5a24..0c272e225 100644 --- a/src/transformation/visitors/loops/for.ts +++ b/src/transformation/visitors/loops/for.ts @@ -4,10 +4,13 @@ import { FunctionVisitor } from "../../context"; import { transformInPrecedingStatementScope } from "../../utils/preceding-statements"; import { checkVariableDeclarationList, transformVariableDeclaration } from "../variable-declaration"; import { invertCondition, transformLoopBody } from "./utils"; +import { ScopeType } from "../../utils/scope"; export const transformForStatement: FunctionVisitor = (statement, context) => { const result: lua.Statement[] = []; + context.pushScope(ScopeType.Loop); + if (statement.initializer) { if (ts.isVariableDeclarationList(statement.initializer)) { checkVariableDeclarationList(context, statement.initializer); @@ -61,5 +64,7 @@ export const transformForStatement: FunctionVisitor = (statemen // while (condition) do ... end result.push(lua.createWhileStatement(lua.createBlock(body), condition, statement)); + context.popScope(); + return lua.createDoStatement(result, statement); }; diff --git a/src/transformation/visitors/loops/utils.ts b/src/transformation/visitors/loops/utils.ts index 01466b450..0997c6583 100644 --- a/src/transformation/visitors/loops/utils.ts +++ b/src/transformation/visitors/loops/utils.ts @@ -31,13 +31,25 @@ export function transformLoopBody( const identifier = lua.createIdentifier(`__continue${scopeId}`); const literalTrue = lua.createBooleanLiteral(true); + // If there is a break in the body statements, do not include any code afterwards + const transformedBodyStatements = []; + let bodyBroken = false; + for (const statement of body) { + transformedBodyStatements.push(statement); + if (lua.isBreakStatement(statement)) { + bodyBroken = true; + break; + } + } + if (!bodyBroken) { + // Tell loop to continue if not broken + transformedBodyStatements.push(lua.createAssignmentStatement(identifier, literalTrue)); + } + return [ lua.createDoStatement([ lua.createVariableDeclarationStatement(identifier), - lua.createRepeatStatement( - lua.createBlock([...body, lua.createAssignmentStatement(identifier, literalTrue)]), - literalTrue - ), + lua.createRepeatStatement(lua.createBlock(transformedBodyStatements), literalTrue), lua.createIfStatement( lua.createUnaryExpression(identifier, lua.SyntaxKind.NotOperator), lua.createBlock([lua.createBreakStatement()]) diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index 903cf6a1e..1591da181 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -540,10 +540,10 @@ for (const testCase of [ "for (const a of []) { continue; }", ]) { const expectContinueVariable: util.TapCallback = builder => - expect(builder.getMainLuaCodeChunk()).toMatch("local __continue2"); + expect(builder.getMainLuaCodeChunk()).toMatch(/local __continue\d+/); const expectContinueGotoLabel: util.TapCallback = builder => - expect(builder.getMainLuaCodeChunk()).toMatch("::__continue2::"); + expect(builder.getMainLuaCodeChunk()).toMatch(/::__continue\d+::/); const expectContinueStatement: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toMatch("continue;"); @@ -560,6 +560,20 @@ for (const testCase of [ }); } +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1638 +test.each([tstl.LuaTarget.Universal, tstl.LuaTarget.Lua50, tstl.LuaTarget.Lua51])( + "no unreachable code when using continue for target %s (#1638)", + target => { + util.testFunction` + let i = 0; + while(++i < 10) continue; + return i; + ` + .setOptions({ luaTarget: target }) + .expectToMatchJsResult(); + } +); + test("do...while", () => { util.testFunction` let result = 0; @@ -624,3 +638,12 @@ test("for...in with pre-defined variable keeps last value", () => { // Need custom matcher because order is not guaranteed in neither JS nor Lua expect([keyX, keyFoo]).toContain(result); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1631 +test("loop variables should not be global (#1631)", () => { + const code = util.testModule` + for (let val = 0; val < 2; ++val) {} + `.getMainLuaCodeChunk(); + + expect(code).toContain("local val"); +}); diff --git a/test/unit/templateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts index d5dc519a5..318804ee9 100644 --- a/test/unit/templateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -83,3 +83,26 @@ test.each(["string", "'string literal type'", "string & unknown"])( .expectToMatchJsResult(); } ); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1637 +test("tagged template literal returned from function call (#1637)", () => { + util.testModule` + function templateFactory() { + return (template: TemplateStringsArray) => { + return "bar"; + } + } + export let result = templateFactory()\`foo\`; + `.expectToEqual({ result: "bar" }); +}); + +test("tagged template literal returned from function call, explicit no context (#1637)", () => { + util.testModule` + function templateFactory() { + return function(this: void, template: TemplateStringsArray) { + return "bar"; + } + } + export let result = templateFactory()\`foo\`; + `.expectToEqual({ result: "bar" }); +});