diff --git a/.codecov.yml b/.codecov.yml index 6366b9225..5dc646c46 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,7 +1,11 @@ +comment: off + coverage: status: - patch: false - changes: false - project: false - -comment: false + project: + default: + target: 95 + threshold: null + base: auto + changes: off + patch: off \ No newline at end of file diff --git a/src/Transpiler.ts b/src/Transpiler.ts index f5d2ba25f..6a9cb0f21 100644 --- a/src/Transpiler.ts +++ b/src/Transpiler.ts @@ -224,7 +224,17 @@ export abstract class LuaTranspiler { // Transpile a block public transpileBlock(block: ts.Block): string { this.exportStack.push([]); - let result = block.statements.map(statement => this.transpileNode(statement)).join(""); + + let result = ""; + for (const statement of block.statements) { + result += this.transpileNode(statement); + + // Don't transpile any dead code after a return + if (ts.isReturnStatement(statement)) { + break; + } + } + result += this.makeExports(); return result; @@ -587,13 +597,14 @@ export abstract class LuaTranspiler { this.pushIndent(); this.transpilingSwitch++; - clause.statements.forEach(statement => { - result += this.transpileNode(statement); - }); + result += this.transpileBlock(ts.createBlock(clause.statements)); this.transpilingSwitch--; let i = index + 1; - if (i < clauses.length && !tsHelper.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement)) { + if (i < clauses.length + && !tsHelper.containsStatement(clause.statements, ts.SyntaxKind.BreakStatement) + && !tsHelper.containsStatement(clause.statements, ts.SyntaxKind.ReturnStatement) + ) { let nextClause = clauses[i]; while (i < clauses.length && ts.isCaseClause(nextClause) @@ -605,8 +616,8 @@ export abstract class LuaTranspiler { if (i !== index && nextClause) { if (ts.isCaseClause(nextClause)) { - result += this.indent + - `${jumpTableName}[${this.transpileExpression(nextClause.expression, true)}]()\n`; + const nextValue = this.transpileExpression(nextClause.expression, true); + result += this.indent + `return ${jumpTableName}[${nextValue}]()\n`; } else { result += this.indent + `${jumpTableName}["____default${this.genVarCounter}"]()\n`; } @@ -619,11 +630,13 @@ export abstract class LuaTranspiler { result += this.indent + `end\n`; }); - result += this.indent + - `if ${jumpTableName}[${expression}] then ${jumpTableName}[${expression}]()\n`; - result += this.indent + - `elseif ${jumpTableName}["____default${this.genVarCounter}"] ` + - `then ${jumpTableName}["____default${this.genVarCounter}"]() end\n`; + result += this.indent + `if ${jumpTableName}[${expression}] then\n` + + this.indent + ` local ${jumpTableName}Return = ${jumpTableName}[${expression}]()\n` + + this.indent + ` if ${jumpTableName}Return ~= nil then return ${jumpTableName}Return end\n`; + result += this.indent + `elseif ${jumpTableName}["____default${this.genVarCounter}"] then\n` + + this.indent + ` local ${jumpTableName}Return = ${jumpTableName}["____default${this.genVarCounter}"]()\n` + + this.indent + ` if ${jumpTableName}Return ~= nil then return ${jumpTableName}Return end\n` + + this.indent + `end\n`; result += this.indent + "--------Switch statement end--------\n"; diff --git a/test/unit/conditionals.spec.ts b/test/unit/conditionals.spec.ts index 19d755149..1b33156b3 100644 --- a/test/unit/conditionals.spec.ts +++ b/test/unit/conditionals.spec.ts @@ -6,7 +6,7 @@ export class LuaConditionalsTests { @TestCase(0, 0) @TestCase(1, 1) @Test("if") - public if(inp: number, expected: number) { + public if(inp: number, expected: number): void { // Transpile const lua = util.transpileString( `let input = ${inp} @@ -26,7 +26,7 @@ export class LuaConditionalsTests { @TestCase(0, 0) @TestCase(1, 1) @Test("ifelse") - public ifelse(inp: number, expected: number) { + public ifelse(inp: number, expected: number): void { // Transpile const lua = util.transpileString( `let input = ${inp} @@ -49,7 +49,7 @@ export class LuaConditionalsTests { @TestCase(2, 2) @TestCase(3, 3) @Test("ifelseif") - public ifelseif(inp: number, expected: number) { + public ifelseif(inp: number, expected: number): void { // Transpile const lua = util.transpileString( `let input = ${inp} @@ -75,7 +75,7 @@ export class LuaConditionalsTests { @TestCase(2, 2) @TestCase(3, 3) @Test("ifelseifelse") - public ifelseifelse(inp: number, expected: number) { + public ifelseifelse(inp: number, expected: number): void { // Transpile const lua = util.transpileString( `let input = ${inp} @@ -102,7 +102,7 @@ export class LuaConditionalsTests { @TestCase(2, 2) @TestCase(3, -1) @Test("switch") - public switch(inp: number, expected: number) { + public switch(inp: number, expected: number): void { // Transpile const lua = util.transpileString( `let result = -1; @@ -133,7 +133,7 @@ export class LuaConditionalsTests { @TestCase(2, 2) @TestCase(3, -2) @Test("switchdefault") - public switchdefault(inp: number, expected: number) { + public switchdefault(inp: number, expected: number): void { // Transpile const lua = util.transpileString( `let result = -1; @@ -170,7 +170,7 @@ export class LuaConditionalsTests { @TestCase(5, 15) @TestCase(7, -2) @Test("switchfallthrough") - public switchfallthrough(inp: number, expected: number) { + public switchfallthrough(inp: number, expected: number): void { /// Transpile const lua = util.transpileString( `let result = -1; @@ -213,7 +213,7 @@ export class LuaConditionalsTests { @TestCase(2, 2) @TestCase(3, -2) @Test("nestedSwitch") - public nestedSwitch(inp: number, expected: number) { + public nestedSwitch(inp: number, expected: number): void { // Transpile const lua = util.transpileString( `let result = -1; @@ -251,4 +251,20 @@ export class LuaConditionalsTests { // Assert Expect(result).toBe(expected); } + + @Test("If dead code after return") + public ifDeadCodeAfterReturn(): void { + const result = util.transpileAndExecute( + `if (true) { return 3; const b = 8; }`); + + Expect(result).toBe(3); + } + + @Test("switch dead code after return") + public whileDeadCodeAfterReturn(): void { + const result = util.transpileAndExecute( + `switch ("abc") { case "def": return 4; let abc = 4; case "abc": return 5; let def = 6; }`); + + Expect(result).toBe(5); + } } diff --git a/test/unit/functions.spec.ts b/test/unit/functions.spec.ts index 22d887ce8..2b0ca2a2b 100644 --- a/test/unit/functions.spec.ts +++ b/test/unit/functions.spec.ts @@ -6,7 +6,7 @@ import * as util from "../src/util"; export class FunctionTests { @Test("Arrow Function Expression") - public arrowFunctionExpression() { + public arrowFunctionExpression(): void { // Transpile const lua = util.transpileString(`let add = (a, b) => a+b; return add(1,2);`); @@ -25,7 +25,7 @@ export class FunctionTests { @TestCase("b => a **= b", 100000) @TestCase("b => a %= b", 0) @Test("Arrow function assignment") - public arrowFunctionAssignment(lambda: string, expected: number) { + public arrowFunctionAssignment(lambda: string, expected: number): void { // Transpile const lua = util.transpileString(`let a = 10; let lambda = ${lambda}; lambda(5); return a;`); @@ -41,7 +41,7 @@ export class FunctionTests { @TestCase([5]) @TestCase([1, 2]) @Test("Arrow Default Values") - public arrowFunctionDefaultValues(inp: number[]) { + public arrowFunctionDefaultValues(inp: number[]): void { // Default value is 3 for v1 const v1 = inp.length > 0 ? inp[0] : 3; // Default value is 4 for v2 @@ -61,7 +61,7 @@ export class FunctionTests { } @Test("Function Expression") - public functionExpression() { + public functionExpression(): void { // Transpile const lua = util.transpileString(`let add = function(a, b) {return a+b}; return add(1,2);`); @@ -76,7 +76,7 @@ export class FunctionTests { @TestCase([5], 9) @TestCase([1, 2], 3) @Test("Arrow Default Values") - public functionExpressionDefaultValues(inp: number[]) { + public functionExpressionDefaultValues(inp: number[]): void { // Default value is 3 for v1 const v1 = inp.length > 0 ? inp[0] : 3; // Default value is 4 for v2 @@ -96,7 +96,7 @@ export class FunctionTests { } @Test("Class method call") - public classMethod() { + public classMethod(): void { const returnValue = 4; const source = `class TestClass { public classMethod(): number { return ${returnValue}; } @@ -116,7 +116,7 @@ export class FunctionTests { } @Test("Class dot method call void") - public classDotMethod() { + public classDotMethod(): void { const returnValue = 4; const source = `class TestClass { public dotMethod: () => number = () => ${returnValue}; @@ -136,7 +136,7 @@ export class FunctionTests { } @Test("Class dot method call with parameter") - public classDotMethod2() { + public classDotMethod2(): void { const returnValue = 4; const source = `class TestClass { public dotMethod: (x: number) => number = x => 3 * x; @@ -156,7 +156,7 @@ export class FunctionTests { } @Test("Class static dot method") - public classDotMethodStatic() { + public classDotMethodStatic(): void { const returnValue = 4; const source = `class TestClass { public static dotMethod: () => number = () => ${returnValue}; @@ -175,7 +175,7 @@ export class FunctionTests { } @Test("Class static dot method with parameter") - public classDotMethodStaticWithParameter() { + public classDotMethodStaticWithParameter(): void { const returnValue = 4; const source = `class TestClass { public static dotMethod: (x: number) => number = x => 3 * x; @@ -194,7 +194,7 @@ export class FunctionTests { } @Test("Invalid property access call transpilation") - public invalidPropertyCall() { + public invalidPropertyCall(): void { const transpiler = util.makeTestTranspiler(); const mockObject: any = { @@ -204,4 +204,20 @@ export class FunctionTests { Expect(() => transpiler.transpilePropertyCall(mockObject as ts.CallExpression)) .toThrowError(Error, "Tried to transpile a non-property call as property call."); } + + @Test("Function dead code after return") + public functionDeadCodeAfterReturn(): void { + const result = util.transpileAndExecute( + `function abc() { return 3; const a = 5; } return abc();`); + + Expect(result).toBe(3); + } + + @Test("Method dead code after return") + public methodDeadCodeAfterReturn(): void { + const result = util.transpileAndExecute( + `class def { public static abc() { return 3; const a = 5; } } return def.abc();`); + + Expect(result).toBe(3); + } } diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index b988dcf22..653a470a2 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -8,7 +8,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3], [1, 2, 3, 4]) @Test("while") - public while(inp: number[], expected: number[]) { + public while(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let arrTest = ${JSON.stringify(inp)}; @@ -29,7 +29,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3, 4], [0, 1, 2, 1, 4]) @Test("while with continue") - public whileWithContinue(inp: number[], expected: number[]) { + public whileWithContinue(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let arrTest = ${JSON.stringify(inp)}; @@ -63,7 +63,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3, 4], [0, 1, 2, 1, 4]) @Test("dowhile with continue") - public dowhileWithContinue(inp: number[], expected: number[]) { + public dowhileWithContinue(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let arrTest = ${JSON.stringify(inp)}; @@ -97,7 +97,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3], [1, 2, 3, 4]) @Test("for") - public for(inp: number[], expected: number[]) { + public for(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let arrTest = ${JSON.stringify(inp)}; @@ -116,7 +116,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3, 4], [0, 0, 2, 0, 4]) @Test("for with continue") - public forWithContinue(inp: number[], expected: number[]) { + public forWithContinue(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let arrTest = ${JSON.stringify(inp)}; @@ -145,7 +145,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3], [1, 2, 3, 4]) @Test("forMirror") - public forMirror(inp: number[], expected: number[]) { + public forMirror(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let arrTest = ${JSON.stringify(inp)}; @@ -164,7 +164,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3], [0, 1, 2, 3]) @Test("forBreak") - public forBreak(inp: number[], expected: number[]) { + public forBreak(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let arrTest = ${JSON.stringify(inp)}; @@ -191,7 +191,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3], [0, 2, 2, 4], "let i = arrTest.length - 1; i >= 0; i -= 2") @TestCase([0, 1, 2, 3], [0, 2, 2, 4], "let i = arrTest.length - 1; i > 0; i -= 2") @Test("forheader") - public forheader(inp: number[], expected: number[], header: string) { + public forheader(inp: number[], expected: number[], header: string): void { // Transpile const lua = util.transpileString( `let arrTest = ${JSON.stringify(inp)}; @@ -210,7 +210,7 @@ export class LuaLoopTests { @TestCase({ ["test1"]: 0, ["test2"]: 1, ["test3"]: 2 }, { ["test1"]: 1, ["test2"]: 2, ["test3"]: 3 }) @Test("forin[Object]") - public forinObject(inp: any, expected: any) { + public forinObject(inp: any, expected: any): void { // Transpile const lua = util.transpileString( `let objTest = ${JSON.stringify(inp)}; @@ -229,7 +229,7 @@ export class LuaLoopTests { @TestCase([1, 2, 3]) @Test("forin[Array]") - public forinArray(inp: number[]) { + public forinArray(inp: number[]): void { // Transpile & Assert Expect(() => util.transpileString( @@ -243,7 +243,7 @@ export class LuaLoopTests { @TestCase({a: 0, b: 1, c: 2, d: 3, e: 4}, {a: 0, b: 0, c: 2, d: 0, e: 4}) @Test("forin with continue") - public forinWithContinue(inp: number[], expected: number[]) { + public forinWithContinue(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let obj = ${JSON.stringify(inp)}; @@ -267,7 +267,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2], [1, 2, 3]) @Test("forof") - public forof(inp: any, expected: any) { + public forof(inp: any, expected: any): void { // Transpile const lua = util.transpileString( `let objTest = ${JSON.stringify(inp)}; @@ -287,7 +287,7 @@ export class LuaLoopTests { @TestCase([0, 1, 2, 3, 4], [0, 0, 2, 0, 4]) @Test("forof with continue") - public forofWithContinue(inp: number[], expected: number[]) { + public forofWithContinue(inp: number[], expected: number[]): void { // Transpile const lua = util.transpileString( `let testArr = ${JSON.stringify(inp)}; @@ -322,7 +322,7 @@ export class LuaLoopTests { @TestCase("for (let a in b) {}") @TestCase("for (let a of b) {}") @Test("loop versions") - public whileVersions(loop: string) { + public whileVersions(loop: string): void { // Transpile const lua51 = util.transpileString(loop, { luaTarget: LuaTarget.Lua51 }); const lua52 = util.transpileString(loop, { luaTarget: LuaTarget.Lua52 }); @@ -335,4 +335,36 @@ export class LuaLoopTests { Expect(lua53.indexOf("::__continue0::") !== -1).toBe(true); Expect(luajit.indexOf("::__continue0::") !== -1).toBe(true); } + + @Test("for dead code after return") + public forDeadCodeAfterReturn(): void { + const result = util.transpileAndExecute( + `for (let i = 0; i < 10; i++) { return 3; const b = 8; }`); + + Expect(result).toBe(3); + } + + @Test("for..in dead code after return") + public forInDeadCodeAfterReturn(): void { + const result = util.transpileAndExecute( + `for (let a in {"a": 5, "b": 8}) { return 3; const b = 8; }`); + + Expect(result).toBe(3); + } + + @Test("for..of dead code after return") + public forOfDeadCodeAfterReturn(): void { + const result = util.transpileAndExecute( + `for (let a of [1,2,4]) { return 3; const b = 8; }`); + + Expect(result).toBe(3); + } + + @Test("while dead code after return") + public whileDeadCodeAfterReturn(): void { + const result = util.transpileAndExecute( + `while (true) { return 3; const b = 8; }`); + + Expect(result).toBe(3); + } }