Skip to content

Commit 6ac05e2

Browse files
committed
Merge branch 'master' into ts-to-lua-ast
# Conflicts: # src/Errors.ts # src/Transpiler.ts
2 parents b757205 + 3c8018c commit 6ac05e2

File tree

4 files changed

+77
-13
lines changed

4 files changed

+77
-13
lines changed

src/LuaTransformer.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,16 +1679,23 @@ export class LuaTransformer {
16791679
}
16801680

16811681
public transformAssignment(lhs: ts.Expression, right: tstl.Expression): tstl.Statement {
1682-
if (ts.isPropertyAccessExpression(lhs) && tsHelper.hasSetAccessor(lhs, this.checker)) {
1683-
return tstl.createExpressionStatement(this.transformSetAccessor(lhs, right), undefined, lhs.parent);
1684-
} else {
1685-
return tstl.createAssignmentStatement(
1686-
this.transformExpression(lhs) as tstl.IdentifierOrTableIndexExpression,
1687-
right,
1688-
undefined,
1689-
lhs.parent
1690-
);
1682+
if (ts.isPropertyAccessExpression(lhs)) {
1683+
const hasSetAccessor = tsHelper.hasSetAccessor(lhs, this.checker);
1684+
if (hasSetAccessor) {
1685+
return tstl.createExpressionStatement(this.transformSetAccessor(lhs, right), undefined, lhs.parent);
1686+
} else if (hasSetAccessor === undefined) {
1687+
// Undefined hasSetAccessor indicates a union with both set accessors and no accessors
1688+
// on the same field.
1689+
throw TSTLErrors.UnsupportedUnionAccessor(lhs);
1690+
}
16911691
}
1692+
1693+
return tstl.createAssignmentStatement(
1694+
this.transformExpression(lhs) as tstl.IdentifierOrTableIndexExpression,
1695+
right,
1696+
undefined,
1697+
lhs.parent
1698+
);
16921699
}
16931700

16941701
public transformAssignmentStatement(expression: ts.BinaryExpression): tstl.Statement {
@@ -2495,8 +2502,12 @@ export class LuaTransformer {
24952502
public transformPropertyAccessExpression(node: ts.PropertyAccessExpression): tstl.Expression {
24962503
const property = node.name.text;
24972504

2498-
if (tsHelper.hasGetAccessor(node, this.checker)) {
2505+
const hasGetAccessor = tsHelper.hasGetAccessor(node, this.checker);
2506+
if (hasGetAccessor) {
24992507
return this.transformGetAccessor(node);
2508+
} else if (hasGetAccessor === undefined) {
2509+
// Undefined hasGetAccessor indicates a union with both get accessors and no accessors on the same field.
2510+
throw TSTLErrors.UnsupportedUnionAccessor(node);
25002511
}
25012512

25022513
// Check for primitive types to override

src/TSHelper.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,11 +268,22 @@ export class TSHelper {
268268
}
269269
}
270270

271-
public static hasGetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean {
271+
public static typeHasGetAccessor(type: ts.Type, name: ts.__String, checker: ts.TypeChecker): boolean | undefined {
272+
if (type.isUnion()) {
273+
if (type.types.some(t => this.typeHasGetAccessor(t, name, checker))) {
274+
// undefined if only a subset of types implements the accessor
275+
return type.types.every(t => this.typeHasGetAccessor(t, name, checker)) ? true : undefined;
276+
}
277+
return false;
278+
}
279+
return this.forTypeOrAnySupertype(type, checker, t => this.hasExplicitGetAccessor(t, name));
280+
}
281+
282+
public static hasGetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean | undefined {
272283
if (ts.isPropertyAccessExpression(node)) {
273284
const name = node.name.escapedText;
274285
const type = checker.getTypeAtLocation(node.expression);
275-
return this.forTypeOrAnySupertype(type, checker, t => this.hasExplicitGetAccessor(t, name));
286+
return this.typeHasGetAccessor(type, name, checker);
276287
}
277288
return false;
278289
}
@@ -284,11 +295,22 @@ export class TSHelper {
284295
}
285296
}
286297

298+
public static typeHasSetAccessor(type: ts.Type, name: ts.__String, checker: ts.TypeChecker): boolean | undefined {
299+
if (type.isUnion()) {
300+
if (type.types.some(t => this.typeHasSetAccessor(t, name, checker))) {
301+
// undefined if only a subset of types implements the accessor
302+
return type.types.every(t => this.typeHasSetAccessor(t, name, checker)) ? true : undefined;
303+
}
304+
return false;
305+
}
306+
return this.forTypeOrAnySupertype(type, checker, t => this.hasExplicitSetAccessor(t, name));
307+
}
308+
287309
public static hasSetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean {
288310
if (ts.isPropertyAccessExpression(node)) {
289311
const name = node.name.escapedText;
290312
const type = checker.getTypeAtLocation(node.expression);
291-
return this.forTypeOrAnySupertype(type, checker, t => this.hasExplicitSetAccessor(t, name));
313+
return this.typeHasSetAccessor(type, name, checker);
292314
}
293315
return false;
294316
}

src/TSTLErrors.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ export class TSTLErrors {
6565
public static UnsupportedObjectLiteralElement = (elementKind: ts.SyntaxKind, node: ts.Node) =>
6666
new TranspileError(`Unsupported object literal element: ${elementKind}.`, node);
6767

68+
public static UnsupportedUnionAccessor = (node: ts.Node) =>
69+
new TranspileError(`Unsupported mixed union of accessor and non-accessor types for the same property.`, node);
70+
6871
public static UnsupportedFunctionConversion = (node: ts.Node, name?: string) => {
6972
if (name) {
7073
return new TranspileError(

test/unit/expressions.spec.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,34 @@ export class ExpressionTests {
310310
Expect(result).toBe(expected);
311311
}
312312

313+
@TestCase("return x.value;", 1)
314+
@TestCase("x.value = 3; return x.value;", 3)
315+
@Test("Union accessors")
316+
public unionAccessors(expression: string, expected: any): void {
317+
const result = util.transpileAndExecute(
318+
`class A{ get value(){ return this.v || 1; } set value(v){ this.v = v; } v: number; }
319+
class B{ get value(){ return this.v || 2; } set value(v){ this.v = v; } v: number; }
320+
let x: A|B = new A();
321+
${expression}`
322+
);
323+
324+
Expect(result).toBe(expected);
325+
}
326+
327+
@TestCase("x.value = 3;")
328+
@TestCase("return x.value;")
329+
@Test("Unsupported Union accessors")
330+
public unsupportedUnionAccessors(expression: string): void {
331+
const source = `class A{ get value(){ return 1; } }
332+
class B{ value:number = 3; }
333+
let x: A|B = new A();
334+
${expression}`;
335+
Expect(() => { util.transpileString(source); }).toThrowError(
336+
TranspileError,
337+
"Unsupported mixed union of accessor and non-accessor types for the same property."
338+
);
339+
}
340+
313341
@TestCase("i++", 10)
314342
@TestCase("i--", 10)
315343
@TestCase("++i", 11)

0 commit comments

Comments
 (0)