Skip to content

Commit 25891d5

Browse files
tomblindPerryvw
authored andcommitted
fix for parenthesis stripping (#620)
* fix for parenthesis-stripping - logic moved to printer - decision to strip is now explicit based on inner expression type to avoid surprises fixes #619 * not operator precedence test
1 parent 6db5684 commit 25891d5

File tree

4 files changed

+83
-8
lines changed

4 files changed

+83
-8
lines changed

src/LuaPrinter.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,8 +610,25 @@ export class LuaPrinter {
610610
return this.createSourceNode(expression, chunks);
611611
}
612612

613+
private canStripParenthesis(expression: tstl.Expression): boolean {
614+
return (
615+
tstl.isParenthesizedExpression(expression) ||
616+
tstl.isTableIndexExpression(expression) ||
617+
tstl.isCallExpression(expression) ||
618+
tstl.isMethodCallExpression(expression) ||
619+
tstl.isIdentifier(expression) ||
620+
tstl.isNilLiteral(expression) ||
621+
tstl.isNumericLiteral(expression) ||
622+
tstl.isBooleanLiteral(expression)
623+
);
624+
}
625+
613626
public printParenthesizedExpression(expression: tstl.ParenthesizedExpression): SourceNode {
614-
return this.createSourceNode(expression, ["(", this.printExpression(expression.innerExpression), ")"]);
627+
const innerExpression = this.printExpression(expression.innerExpression);
628+
if (this.canStripParenthesis(expression.innerExpression)) {
629+
return this.createSourceNode(expression, innerExpression);
630+
}
631+
return this.createSourceNode(expression, ["(", innerExpression, ")"]);
615632
}
616633

617634
public printCallExpression(expression: tstl.CallExpression): SourceNode {

src/LuaTransformer.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3432,11 +3432,6 @@ export class LuaTransformer {
34323432
}
34333433

34343434
public transformParenthesizedExpression(expression: ts.ParenthesizedExpression): ExpressionVisitResult {
3435-
if (ts.isAssertionExpression(expression.expression)) {
3436-
// Strip parenthesis from casts
3437-
return this.transformExpression(expression.expression);
3438-
}
3439-
34403435
return tstl.createParenthesizedExpression(this.transformExpression(expression.expression), expression);
34413436
}
34423437

test/translation/__snapshots__/transformation.spec.ts.snap

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ exports[`Transformation (tryCatch) 1`] = `
580580
local ____TS_try, er = pcall(function()
581581
local a = 42
582582
end)
583-
if not (____TS_try) then
583+
if not ____TS_try then
584584
local b = \\"fail\\"
585585
end
586586
end"
@@ -591,7 +591,7 @@ exports[`Transformation (tryCatchFinally) 1`] = `
591591
local ____TS_try, er = pcall(function()
592592
local a = 42
593593
end)
594-
if not (____TS_try) then
594+
if not ____TS_try then
595595
local b = \\"fail\\"
596596
end
597597
do

test/unit/expressions.spec.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,3 +527,66 @@ test.each([
527527
`;
528528
expect(util.transpileAndExecute(code)).toBe(1);
529529
});
530+
531+
test("binary expression with 'as' type assertion wrapped in parenthesis", () => {
532+
expect(util.transpileAndExecute("return 2 * (3 - 2 as number);")).toBe(2);
533+
});
534+
535+
test.each([
536+
"(x as any).foo;",
537+
"(y.x as any).foo;",
538+
"(y['x'] as any).foo;",
539+
"(z() as any).foo;",
540+
"(y.z() as any).foo;",
541+
"(<any>x).foo;",
542+
"(<any>y.x).foo;",
543+
"(<any>y['x']).foo;",
544+
"(<any>z()).foo;",
545+
"(<any>y.z()).foo;",
546+
"(x as unknown as any).foo;",
547+
"(<unknown>x as any).foo;",
548+
"((x as unknown) as any).foo;",
549+
"((<unknown>x) as any).foo;",
550+
])("'as' type assertion should strip parenthesis (%p)", expression => {
551+
const code = `
552+
declare let x: unknown;
553+
declare let y: { x: unknown; z(this: void): unknown; };
554+
declare function z(this: void): unknown;
555+
${expression}`;
556+
557+
const lua = util.transpileString(code, undefined, false);
558+
expect(lua).not.toMatch(/\(.+\)/);
559+
});
560+
561+
test.each([
562+
"(x + 1 as any).foo;",
563+
"(!x as any).foo;",
564+
"(x ** 2 as any).foo;",
565+
"(x < 2 as any).foo;",
566+
"(x in y as any).foo;",
567+
"(<any>x + 1).foo;",
568+
"(<any>!x).foo;",
569+
"(x + 1 as unknown as any).foo;",
570+
"((x + 1 as unknown) as any).foo;",
571+
"(!x as unknown as any).foo;",
572+
"((!x as unknown) as any).foo;",
573+
"(<unknown>!x as any).foo;",
574+
"((<unknown>!x) as any).foo;",
575+
])("'as' type assertion should not strip parenthesis (%p)", expression => {
576+
const code = `
577+
declare let x: number;
578+
declare let y: {};
579+
${expression}`;
580+
581+
const lua = util.transpileString(code, undefined, false);
582+
expect(lua).toMatch(/\(.+\)/);
583+
});
584+
585+
test("not operator precedence (%p)", () => {
586+
const code = `
587+
const a = true;
588+
const b = false;
589+
return !a && b;`;
590+
591+
expect(util.transpileAndExecute(code)).toBe(false);
592+
});

0 commit comments

Comments
 (0)