X Tutup
Skip to content

Commit 441a2e8

Browse files
tomblindPerryvw
authored andcommitted
New functions - interface recursion (#287)
* recursing into interfaces during assignment validation
1 parent 515e5de commit 441a2e8

File tree

2 files changed

+64
-18
lines changed

2 files changed

+64
-18
lines changed

src/Errors.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,19 @@ export class TSTLErrors {
6969
public static UnsupportedObjectLiteralElement = (elementKind: ts.SyntaxKind, node: ts.Node) =>
7070
new TranspileError(`Unsupported object literal element: ${elementKind}.`, node)
7171

72-
public static UnsupportedFunctionConversion = (node: ts.Node) =>
73-
new TranspileError(`Unsupported conversion from method to function.`, node)
72+
public static UnsupportedFunctionConversion = (node: ts.Node, name?: string) => {
73+
if (name) {
74+
return new TranspileError(`Unsupported conversion from method to function "${name}".`, node);
75+
} else {
76+
return new TranspileError(`Unsupported conversion from method to function.`, node);
77+
}
78+
}
7479

75-
public static UnsupportedMethodConversion = (node: ts.Node) =>
76-
new TranspileError(`Unsupported conversion from function to method.`, node)
80+
public static UnsupportedMethodConversion = (node: ts.Node, name?: string) => {
81+
if (name) {
82+
return new TranspileError(`Unsupported conversion from function to method "${name}".`, node);
83+
} else {
84+
return new TranspileError(`Unsupported conversion from function to method.`, node);
85+
}
86+
}
7787
}

src/Transpiler.ts

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ export abstract class LuaTranspiler {
8282

8383
public luaLibFeatureSet: Set<LuaLibFeature>;
8484

85+
private readonly typeValidationCache: Map<ts.Type, Set<ts.Type>> = new Map<ts.Type, Set<ts.Type>>();
86+
8587
constructor(checker: ts.TypeChecker, options: CompilerOptions, sourceFile: ts.SourceFile) {
8688
this.indent = "";
8789
this.checker = checker;
@@ -948,6 +950,7 @@ export abstract class LuaTranspiler {
948950
this.validateAssignment(node.right, rightType, leftType);
949951

950952
if (ts.isArrayLiteralExpression(node.left)) {
953+
// Destructuring assignment
951954
const vars = node.left.elements.map(e => this.transpileExpression(e)).join(",");
952955
const vals = tsHelper.isTupleReturnCall(node.right, this.checker)
953956
? rhs : this.transpileDestructingAssignmentValue(node.right);
@@ -1409,26 +1412,59 @@ export abstract class LuaTranspiler {
14091412
}
14101413
}
14111414

1412-
public validateAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type): void {
1415+
public validateAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type, toName?: string): void {
1416+
if (toType === fromType) {
1417+
return;
1418+
}
1419+
14131420
if ((toType.flags & ts.TypeFlags.Any) !== 0) {
14141421
// Assigning to un-typed variable
14151422
return;
1416-
} else if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) {
1423+
}
1424+
1425+
// Use cache to avoid repeating check for same types (protects against infinite loop in recursive types)
1426+
let fromTypeCache = this.typeValidationCache.get(fromType);
1427+
if (fromTypeCache) {
1428+
if (fromTypeCache.has(toType)) {
1429+
return;
1430+
}
1431+
} else {
1432+
fromTypeCache = new Set();
1433+
this.typeValidationCache.set(fromType, fromTypeCache);
1434+
}
1435+
fromTypeCache.add(toType);
1436+
1437+
// Check function assignments
1438+
const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker);
1439+
const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker);
1440+
if (fromHasContext !== toHasContext) {
1441+
if (fromHasContext) {
1442+
throw TSTLErrors.UnsupportedFunctionConversion(node, toName);
1443+
} else {
1444+
throw TSTLErrors.UnsupportedMethodConversion(node, toName);
1445+
}
1446+
}
1447+
1448+
if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) {
14171449
// Recurse into tuples/arrays
14181450
(toType as ts.TypeReference).typeArguments.forEach((t, i) => {
1419-
this.validateAssignment(node, (fromType as ts.TypeReference).typeArguments[i], t);
1451+
this.validateAssignment(node, (fromType as ts.TypeReference).typeArguments[i], t, toName);
14201452
});
1421-
} else {
1422-
// Check function assignments
1423-
const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker);
1424-
const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker);
1425-
if (fromHasContext !== toHasContext) {
1426-
if (fromHasContext) {
1427-
throw TSTLErrors.UnsupportedFunctionConversion(node);
1428-
} else {
1429-
throw TSTLErrors.UnsupportedMethodConversion(node);
1453+
}
1454+
1455+
if ((toType.flags & ts.TypeFlags.Object) !== 0
1456+
&& ((toType as ts.ObjectType).objectFlags & ts.ObjectFlags.ClassOrInterface) !== 0
1457+
&& toType.symbol && toType.symbol.members && fromType.symbol && fromType.symbol.members) {
1458+
// Recurse into interfaces
1459+
toType.symbol.members.forEach(
1460+
(toMember, memberName) => {
1461+
const fromMember = fromType.symbol.members.get(memberName);
1462+
const toMemberType = this.checker.getTypeOfSymbolAtLocation(toMember, node);
1463+
const fromMemberType = this.checker.getTypeOfSymbolAtLocation(fromMember, node);
1464+
this.validateAssignment(node, fromMemberType, toMemberType,
1465+
toName ? `${toName}.${memberName}` : memberName.toString());
14301466
}
1431-
}
1467+
);
14321468
}
14331469
}
14341470

@@ -1446,7 +1482,7 @@ export abstract class LuaTranspiler {
14461482
const param = params[i];
14471483
const paramType = this.checker.getTypeAtLocation(param);
14481484
const sigType = this.checker.getTypeAtLocation(sig.parameters[i].valueDeclaration);
1449-
this.validateAssignment(param, paramType, sigType);
1485+
this.validateAssignment(param, paramType, sigType, sig.parameters[i].name);
14501486
parameters.push(this.transpileExpression(param));
14511487
}
14521488
} else {

0 commit comments

Comments
 (0)
X Tutup