Skip to content

Commit 2bb18e1

Browse files
authored
Fix/dead code after break (#1694)
* strip dead code after break in printer * change tests from translation to no execution error * remove unused expectAllVersions * improve break dead code tests: target affected versions, check output values
1 parent c3dc829 commit 2bb18e1

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

src/LuaPrinter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ export class LuaPrinter {
328328

329329
statementNodes.push(node);
330330

331-
if (lua.isReturnStatement(statement)) break;
331+
if (lua.isReturnStatement(statement) || lua.isBreakStatement(statement)) break;
332332
}
333333

334334
return statementNodes.length > 0 ? [...intersperse<SourceChunk>(statementNodes, "\n"), "\n"] : [];
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import * as tstl from "../../../src";
2+
import * as util from "../../util";
3+
4+
// In Lua 5.0, 5.1, and LuaJIT, break must be the last statement in a block.
5+
// Any code after break is a syntax error (e.g. `while true do break; local b = 8 end`
6+
// fails with "'end' expected near 'local'"). Lua 5.2+ relaxed this restriction.
7+
// TSTL should strip dead code after break on all targets to avoid these errors.
8+
9+
function expectNoDeadCode(builder: util.TestBuilder) {
10+
const lua = builder.getMainLuaCodeChunk();
11+
expect(lua).not.toContain("local b = 8");
12+
}
13+
14+
const affectedVersions: Record<tstl.LuaTarget, ((builder: util.TestBuilder) => void) | boolean> = {
15+
[tstl.LuaTarget.Universal]: false,
16+
[tstl.LuaTarget.Lua50]: builder => builder.tap(expectNoDeadCode).expectToMatchJsResult(),
17+
[tstl.LuaTarget.Lua51]: builder => builder.tap(expectNoDeadCode).expectToMatchJsResult(),
18+
[tstl.LuaTarget.Lua52]: false,
19+
[tstl.LuaTarget.Lua53]: false,
20+
[tstl.LuaTarget.Lua54]: false,
21+
[tstl.LuaTarget.Lua55]: false,
22+
[tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectNoDeadCode),
23+
[tstl.LuaTarget.Luau]: false,
24+
};
25+
26+
util.testEachVersion(
27+
"for dead code after break",
28+
() => util.testFunction`
29+
let result = 0;
30+
for (let i = 0; i < 10; i++) { result = i; break; const b = 8; }
31+
return result;
32+
`,
33+
affectedVersions
34+
);
35+
36+
util.testEachVersion(
37+
"for..in dead code after break",
38+
() => util.testFunction`
39+
let result = "";
40+
for (let a in {"a": 5, "b": 8}) { result = a; break; const b = 8; }
41+
return result;
42+
`,
43+
affectedVersions
44+
);
45+
46+
util.testEachVersion(
47+
"for..of dead code after break",
48+
() => util.testFunction`
49+
let result = 0;
50+
for (let a of [1,2,4]) { result = a; break; const b = 8; }
51+
return result;
52+
`,
53+
affectedVersions
54+
);
55+
56+
util.testEachVersion(
57+
"while dead code after break",
58+
() => util.testFunction`
59+
let result = "done";
60+
while (true) { break; const b = 8; }
61+
return result;
62+
`,
63+
affectedVersions
64+
);
65+
66+
util.testEachVersion(
67+
"switch dead code after break",
68+
() => util.testFunction`
69+
let result = "none";
70+
switch ("abc" as string) {
71+
case "def":
72+
result = "def";
73+
break;
74+
let abc = 4;
75+
case "abc":
76+
result = "abc";
77+
break;
78+
let def = 6;
79+
}
80+
return result;
81+
`,
82+
affectedVersions
83+
);
84+
85+
util.testEachVersion(
86+
"do-while dead code after break",
87+
() => util.testFunction`
88+
let result = "done";
89+
do { break; const b = 8; } while (true);
90+
return result;
91+
`,
92+
affectedVersions
93+
);

0 commit comments

Comments
 (0)