X Tutup
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e0ca0a4
passing nil instead of _G as context for global functions when in ES …
tomblind Oct 25, 2018
dcb71a2
fixed logic for determining strict mode
tomblind Oct 25, 2018
a24d775
replaced hack-around when passing nil as a function context with a nu…
tomblind Oct 25, 2018
f9a7eb3
testing viability of wrapping context/no-context calls on assignment
tomblind Oct 26, 2018
90b7eb9
working on more function assignment situations
tomblind Oct 26, 2018
10be963
fixed getting constructor signature and refactored things a bit
tomblind Oct 29, 2018
fa5dd9b
checking resolved signature when comparing function types passed as a…
tomblind Oct 29, 2018
a24caa2
working on assignment checks for methods vs functions
tomblind Nov 19, 2018
7fb2cad
handling context in calls and decls
tomblind Nov 19, 2018
8c75ff0
refactoring and handling tuple destructuring
tomblind Nov 19, 2018
fec41f0
generalized tuple assignment checking
tomblind Nov 19, 2018
422e676
overloads with function and method signatures default to functions now
tomblind Nov 21, 2018
7731342
preventing non-methods from being passed to bind/call/apply
tomblind Nov 21, 2018
eca6cc2
removed uneccessary helpers
tomblind Nov 21, 2018
8314a1e
using proper exceptions for function conversion errors
tomblind Nov 21, 2018
abde08f
removed context arg from custom constructors and added check for assi…
tomblind Nov 22, 2018
07887a5
updated tests
tomblind Nov 22, 2018
4ac27f5
Merge remote-tracking branch 'upstream/new-functions' into new-functions
tomblind Nov 22, 2018
5b10da7
Merge branch 'new-functions' into new-functions-assign-experiment
tomblind Nov 22, 2018
7170080
removing leftover NoContext decorators
tomblind Nov 23, 2018
f90e2ec
Merge remote-tracking branch 'upstream/new-functions' into new-functions
tomblind Nov 24, 2018
3ac149e
recursing into interfaces during assignment validation
tomblind Nov 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,19 @@ export class TSTLErrors {
public static UnsupportedObjectLiteralElement = (elementKind: ts.SyntaxKind, node: ts.Node) =>
new TranspileError(`Unsupported object literal element: ${elementKind}.`, node)

public static UnsupportedFunctionConversion = (node: ts.Node) =>
new TranspileError(`Unsupported conversion from method to function.`, node)
public static UnsupportedFunctionConversion = (node: ts.Node, name?: string) => {
if (name) {
return new TranspileError(`Unsupported conversion from method to function "${name}".`, node);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this error should be something like 'Can not implicitly convert method to function, to explicitly convert, wrap function call in a lambda function.'

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I figured this could be improved upon a lot once we've nailed things down.

} else {
return new TranspileError(`Unsupported conversion from method to function.`, node);
}
}

public static UnsupportedMethodConversion = (node: ts.Node) =>
new TranspileError(`Unsupported conversion from function to method.`, node)
public static UnsupportedMethodConversion = (node: ts.Node, name?: string) => {
if (name) {
return new TranspileError(`Unsupported conversion from function to method "${name}".`, node);
} else {
return new TranspileError(`Unsupported conversion from function to method.`, node);
}
}
}
64 changes: 50 additions & 14 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ export abstract class LuaTranspiler {

public luaLibFeatureSet: Set<LuaLibFeature>;

private readonly typeValidationCache: Map<ts.Type, Set<ts.Type>> = new Map<ts.Type, Set<ts.Type>>();

constructor(checker: ts.TypeChecker, options: CompilerOptions, sourceFile: ts.SourceFile) {
this.indent = "";
this.checker = checker;
Expand Down Expand Up @@ -948,6 +950,7 @@ export abstract class LuaTranspiler {
this.validateAssignment(node.right, rightType, leftType);

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

public validateAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type): void {
public validateAssignment(node: ts.Node, fromType: ts.Type, toType: ts.Type, toName?: string): void {
if (toType === fromType) {
return;
}

if ((toType.flags & ts.TypeFlags.Any) !== 0) {
// Assigning to un-typed variable
return;
} else if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) {
}

// Use cache to avoid repeating check for same types (protects against infinite loop in recursive types)
let fromTypeCache = this.typeValidationCache.get(fromType);
if (fromTypeCache) {
if (fromTypeCache.has(toType)) {
return;
}
} else {
fromTypeCache = new Set();
this.typeValidationCache.set(fromType, fromTypeCache);
}
fromTypeCache.add(toType);

// Check function assignments
const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker);
const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker);
if (fromHasContext !== toHasContext) {
if (fromHasContext) {
throw TSTLErrors.UnsupportedFunctionConversion(node, toName);
} else {
throw TSTLErrors.UnsupportedMethodConversion(node, toName);
}
}

if ((fromType as ts.TypeReference).typeArguments && (toType as ts.TypeReference).typeArguments) {
// Recurse into tuples/arrays
(toType as ts.TypeReference).typeArguments.forEach((t, i) => {
this.validateAssignment(node, (fromType as ts.TypeReference).typeArguments[i], t);
this.validateAssignment(node, (fromType as ts.TypeReference).typeArguments[i], t, toName);
});
} else {
// Check function assignments
const fromHasContext = tsHelper.isFunctionWithContext(fromType, this.checker);
const toHasContext = tsHelper.isFunctionWithContext(toType, this.checker);
if (fromHasContext !== toHasContext) {
if (fromHasContext) {
throw TSTLErrors.UnsupportedFunctionConversion(node);
} else {
throw TSTLErrors.UnsupportedMethodConversion(node);
}

if ((toType.flags & ts.TypeFlags.Object) !== 0
&& ((toType as ts.ObjectType).objectFlags & ts.ObjectFlags.ClassOrInterface) !== 0
&& toType.symbol && toType.symbol.members && fromType.symbol && fromType.symbol.members) {
// Recurse into interfaces
toType.symbol.members.forEach(
(toMember, memberName) => {
const fromMember = fromType.symbol.members.get(memberName);
const toMemberType = this.checker.getTypeOfSymbolAtLocation(toMember, node);
const fromMemberType = this.checker.getTypeOfSymbolAtLocation(fromMember, node);
this.validateAssignment(node, fromMemberType, toMemberType,
toName ? `${toName}.${memberName}` : memberName.toString());
}
}
);
}
}

Expand All @@ -1446,7 +1482,7 @@ export abstract class LuaTranspiler {
const param = params[i];
const paramType = this.checker.getTypeAtLocation(param);
const sigType = this.checker.getTypeAtLocation(sig.parameters[i].valueDeclaration);
this.validateAssignment(param, paramType, sigType);
this.validateAssignment(param, paramType, sigType, sig.parameters[i].name);
parameters.push(this.transpileExpression(param));
}
} else {
Expand Down
X Tutup