Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
680585e
Add diagnostic reporting and convert forbidden for...in array error
ark120202 Dec 9, 2019
6ecdec3
Include generated code in diagnostic snapshots
ark120202 Dec 9, 2019
a3a9732
Remove some errors that already have TypeScript diagnostics
ark120202 Dec 9, 2019
2763d0d
Replace unactionable errors with assertions
ark120202 Dec 9, 2019
4997d61
Replace empty json file diagnostic with runtime error
ark120202 Dec 9, 2019
f4673a8
Refactor `@forRange` annotation tests
ark120202 Dec 9, 2019
95471f3
Move invalid `@forRange` call error to diagnostics
ark120202 Dec 9, 2019
05f9f50
Make annotation errors diagnostics
ark120202 Dec 11, 2019
a08e239
Make unsupported `luaIterator` usage error a diagnostic
ark120202 Dec 11, 2019
0306f28
Make function assignment errors diagnostics
ark120202 Dec 11, 2019
8d9a91b
Replace UnsupportedKind errors with diagnostics or better types
ark120202 Dec 11, 2019
8aff305
Make `UnsupportedForTarget` error a diagnostic
ark120202 Dec 11, 2019
2b9c2a6
Make `UnsupportedProperty` error a diagnostic
ark120202 Dec 11, 2019
85df23f
Make loop errors diagnostics
ark120202 Dec 11, 2019
65562cb
Simplift `isValidLuaIdentifier` usage
ark120202 Dec 12, 2019
1cfec8e
Make `InvalidAmbientIdentifierName` error a diagnostic
ark120202 Dec 12, 2019
afb40a5
Remove `UndefinedScope`
ark120202 Dec 12, 2019
2b8303e
Make class transform safer, fixes #771
ark120202 Dec 12, 2019
dc45631
Make all other errors diagnostics
ark120202 Dec 12, 2019
11c3ee4
Make `bundle` tests check diagnostic snapshots
ark120202 Dec 12, 2019
d70666f
Remove remaining TranspileError handling code
ark120202 Dec 12, 2019
f0f5773
Merge remote-tracking branch 'upstream/master' into diagnostics
ark120202 Dec 12, 2019
9c3a235
Fix formatting
ark120202 Dec 12, 2019
42f2dca
Update `loops.spec.ts.snap`
ark120202 Dec 12, 2019
ef22253
Temporary loosen `for...of` initializer variable declaration check
ark120202 Dec 12, 2019
3434bce
Refactor SimpleOperator handling
ark120202 Dec 15, 2019
03e9aa3
Fix typo
ark120202 Dec 15, 2019
9e80934
Merge remote-tracking branch 'upstream/master' into diagnostics
ark120202 Jan 1, 2020
f2feeb4
Remove unused missing import
ark120202 Jan 1, 2020
54aa5c2
Remove `test.only` from `conditionals.spec.ts`
ark120202 Jan 1, 2020
9312a0c
Use inline snapshots for simple tests
ark120202 Jan 6, 2020
e6883bf
Revert "Use inline snapshots for simple tests"
ark120202 Jan 6, 2020
0969123
Merge remote-tracking branch 'upstream/master' into diagnostics
ark120202 Jan 6, 2020
ac40ce2
Merge remote-tracking branch 'upstream/master' into diagnostics
ark120202 Jan 21, 2020
be9bb93
Merge remote-tracking branch 'upstream/master' into diagnostics
ark120202 Feb 29, 2020
e23b1d6
Factorize transpilation diagnostics
ark120202 Mar 9, 2020
e533ed7
Improve some diagnostic code spans
ark120202 Mar 9, 2020
0473f45
Allow to specify expected diagnostic codes in matchers
ark120202 Mar 9, 2020
5ef5ff2
Add expected diagnostics to snapshot assertions
ark120202 Mar 11, 2020
e11d018
Add expected diagnostics to function assignability cast tests
ark120202 Mar 11, 2020
51574a8
Add expected diagnostics to bundle tests
ark120202 Mar 11, 2020
9eaa4ad
Add changelog entry
ark120202 Mar 11, 2020
75de99d
Merge branch 'master' into diagnostics
ark120202 Mar 12, 2020
9434b22
Refactor for initializer variable transformation
ark120202 Mar 13, 2020
22cc853
Merge remote-tracking branch 'upstream/master' into diagnostics
ark120202 Mar 13, 2020
7021186
Update tests
ark120202 Mar 13, 2020
3040f6e
Refactor diagnostc factories
ark120202 Mar 14, 2020
c19626a
Fix `luaTable` interface declaration tests
ark120202 Mar 14, 2020
58cb0a0
Make condition more readable
ark120202 Mar 14, 2020
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
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,35 @@

This change simplifies our codebase and opens a path to object accessors implementation

- Errors reported during transpilation now are created as TypeScript diagnostics, instead of being thrown as JavaScript errors. This makes TypeScriptToLua always try to generate valid code (even in presence of errors) and allows multiple errors to be reported in a single file:

<!-- prettier-ignore -->
```ts
for (var x in []) {}
```

```shell
# Before

$ tstl file.ts
file.ts:1:1 - error TSTL: Iterating over arrays with 'for ... in' is not allowed.

$ cat file.lua
error("Iterating over arrays with 'for ... in' is not allowed.")
```

```shell
# Now

$ tstl file.ts
file.ts:1:1 - error TSTL: Iterating over arrays with 'for ... in' is not allowed.
file.ts:1:6 - error TSTL: `var` declarations are not supported. Use `let` or `const` instead.

$ cat file.lua
for x in pairs({}) do
end
```

## 0.31.0

- **Breaking:** The old annotation syntax (`/* !varArg */`) **no longer works**, the only currently supported syntax is:
Expand Down
30 changes: 3 additions & 27 deletions src/CompilerOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import * as ts from "typescript";
import * as diagnosticFactories from "./transpilation/diagnostics";

type KnownKeys<T> = { [K in keyof T]: string extends K ? never : number extends K ? never : K } extends {
[_ in keyof T]: infer U;
Expand Down Expand Up @@ -47,37 +48,12 @@ export function validateOptions(options: CompilerOptions): ts.Diagnostic[] {
const diagnostics: ts.Diagnostic[] = [];

if (options.luaBundle && !options.luaBundleEntry) {
diagnostics.push(configErrorDiagnostic(`'luaBundleEntry' is required when 'luaBundle' is enabled.`));
diagnostics.push(diagnosticFactories.luaBundleEntryIsRequired());
}

if (options.luaBundle && options.luaLibImport === LuaLibImportKind.Inline) {
diagnostics.push(
configWarningDiagnostic(
`Using 'luaBundle' with 'luaLibImport: "inline"' might generate duplicate code. ` +
`It is recommended to use 'luaLibImport: "require"'`
)
);
diagnostics.push(diagnosticFactories.usingLuaBundleWithInlineMightGenerateDuplicateCode());
}

return diagnostics;
}

const configErrorDiagnostic = (message: string): ts.Diagnostic => ({
file: undefined,
start: undefined,
length: undefined,
category: ts.DiagnosticCategory.Error,
code: 0,
source: "typescript-to-lua",
messageText: message,
});

const configWarningDiagnostic = (message: string): ts.Diagnostic => ({
file: undefined,
start: undefined,
length: undefined,
category: ts.DiagnosticCategory.Warning,
code: 0,
source: "typescript-to-lua",
messageText: message,
});
14 changes: 3 additions & 11 deletions src/LuaPrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as ts from "typescript";
import { CompilerOptions, LuaLibImportKind } from "./CompilerOptions";
import * as lua from "./LuaAST";
import { loadLuaLibFeatures, LuaLibFeature } from "./LuaLib";
import { isValidLuaIdentifier, luaKeywords } from "./transformation/utils/safe-names";
import { isValidLuaIdentifier } from "./transformation/utils/safe-names";
import { EmitHost } from "./transpilation";
import { trimExtension } from "./utils";

Expand Down Expand Up @@ -660,11 +660,7 @@ export class LuaPrinter {
const value = this.printExpression(expression.value);

if (expression.key) {
if (
lua.isStringLiteral(expression.key) &&
isValidLuaIdentifier(expression.key.value) &&
!luaKeywords.has(expression.key.value)
) {
if (lua.isStringLiteral(expression.key) && isValidLuaIdentifier(expression.key.value)) {
chunks.push(expression.key.value, " = ", value);
} else {
chunks.push("[", this.printExpression(expression.key), "] = ", value);
Expand Down Expand Up @@ -761,11 +757,7 @@ export class LuaPrinter {
const chunks: SourceChunk[] = [];

chunks.push(this.printExpressionInParenthesesIfNeeded(expression.table));
if (
lua.isStringLiteral(expression.index) &&
isValidLuaIdentifier(expression.index.value) &&
!luaKeywords.has(expression.index.value)
) {
if (lua.isStringLiteral(expression.index) && isValidLuaIdentifier(expression.index.value)) {
chunks.push(".", this.createSourceNode(expression.index, expression.index.value));
} else {
chunks.push("[", this.printExpression(expression.index), "]");
Expand Down
22 changes: 5 additions & 17 deletions src/cli/diagnostics.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import * as ts from "typescript";
import { createSerialDiagnosticFactory, createDiagnosticFactoryWithCode } from "../utils";

export const tstlOptionsAreMovingToTheTstlObject = (tstl: Record<string, any>): ts.Diagnostic => ({
file: undefined,
start: undefined,
length: undefined,
export const tstlOptionsAreMovingToTheTstlObject = createSerialDiagnosticFactory((tstl: Record<string, any>) => ({
category: ts.DiagnosticCategory.Warning,
code: 0,
source: "typescript-to-lua",
messageText:
'TSTL options are moving to the "tstl" object. Adjust your tsconfig to look like\n' +
`"tstl": ${JSON.stringify(tstl, undefined, 4)}`,
});
}));

export const watchErrorSummary = (errorCount: number): ts.Diagnostic => ({
file: undefined,
Expand All @@ -24,16 +20,8 @@ export const watchErrorSummary = (errorCount: number): ts.Diagnostic => ({
: `Found ${errorCount} errors. Watching for file changes.`,
});

const createCommandLineError = <Args extends any[]>(code: number, getMessage: (...args: Args) => string) => (
...args: Args
): ts.Diagnostic => ({
file: undefined,
start: undefined,
length: undefined,
category: ts.DiagnosticCategory.Error,
code,
messageText: getMessage(...args),
});
const createCommandLineError = <TArgs extends any[]>(code: number, getMessage: (...args: TArgs) => string) =>
createDiagnosticFactoryWithCode(code, (...args: TArgs) => ({ messageText: getMessage(...args) }));

export const unknownCompilerOption = createCommandLineError(
5023,
Expand Down
11 changes: 4 additions & 7 deletions src/cli/report.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import * as ts from "typescript";

export const prepareDiagnosticForFormatting = (diagnostic: ts.Diagnostic) =>
diagnostic.source === "typescript-to-lua" ? { ...diagnostic, code: "TL" as any } : diagnostic;

export function createDiagnosticReporter(pretty: boolean, system = ts.sys): ts.DiagnosticReporter {
const reporter = ts.createDiagnosticReporter(system, pretty);
return diagnostic => {
if (diagnostic.source === "typescript-to-lua") {
diagnostic = { ...diagnostic, code: ("TL" + diagnostic.code) as any };
}

reporter(diagnostic);
};
return diagnostic => reporter(prepareDiagnosticForFormatting(diagnostic));
}
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ export * from "./LuaAST";
export { LuaLibFeature } from "./LuaLib";
export * from "./LuaPrinter";
export * from "./transformation/context";
export { TranspileError } from "./transformation/utils/errors";
export * from "./transpilation";
6 changes: 3 additions & 3 deletions src/transformation/builtins/array.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import * as ts from "typescript";
import * as lua from "../../LuaAST";
import { TransformationContext } from "../context";
import { UnsupportedProperty } from "../utils/errors";
import { unsupportedProperty } from "../utils/diagnostics";
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
import { PropertyCallExpression, transformArguments } from "../visitors/call";

export function transformArrayPrototypeCall(
context: TransformationContext,
node: PropertyCallExpression
): lua.CallExpression {
): lua.CallExpression | undefined {
const expression = node.expression;
const signature = context.checker.getResolvedSignature(node);
const params = transformArguments(context, node.arguments, signature);
Expand Down Expand Up @@ -79,7 +79,7 @@ export function transformArrayPrototypeCall(
case "flatMap":
return transformLuaLibFunction(context, LuaLibFeature.ArrayFlatMap, node, caller, ...params);
default:
throw UnsupportedProperty("array", expressionName, node);
context.diagnostics.push(unsupportedProperty(expression.name, "array", expressionName));
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/transformation/builtins/console.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import * as ts from "typescript";
import * as lua from "../../LuaAST";
import { TransformationContext } from "../context";
import { UnsupportedProperty } from "../utils/errors";
import { unsupportedProperty } from "../utils/diagnostics";
import { PropertyCallExpression, transformArguments } from "../visitors/call";

const isStringFormatTemplate = (node: ts.Expression) => ts.isStringLiteral(node) && node.text.includes("%");

export function transformConsoleCall(
context: TransformationContext,
expression: PropertyCallExpression
): lua.Expression {
): lua.Expression | undefined {
const method = expression.expression;
const methodName = method.name.text;
const signature = context.checker.getResolvedSignature(expression);
Expand Down Expand Up @@ -61,6 +61,6 @@ export function transformConsoleCall(
);
return lua.createCallExpression(lua.createIdentifier("print"), [debugTracebackCall]);
default:
throw UnsupportedProperty("console", methodName, expression);
context.diagnostics.push(unsupportedProperty(method.name, "console", methodName));
}
}
8 changes: 4 additions & 4 deletions src/transformation/builtins/function.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import * as lua from "../../LuaAST";
import { TransformationContext } from "../context";
import { UnsupportedProperty, UnsupportedSelfFunctionConversion } from "../utils/errors";
import { unsupportedProperty, unsupportedSelfFunctionConversion } from "../utils/diagnostics";
import { ContextType, getFunctionContextType } from "../utils/function-context";
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
import { PropertyCallExpression, transformArguments } from "../visitors/call";

export function transformFunctionPrototypeCall(
context: TransformationContext,
node: PropertyCallExpression
): lua.CallExpression {
): lua.CallExpression | undefined {
const expression = node.expression;
const callerType = context.checker.getTypeAtLocation(expression.expression);
if (getFunctionContextType(context, callerType) === ContextType.Void) {
throw UnsupportedSelfFunctionConversion(node);
context.diagnostics.push(unsupportedSelfFunctionConversion(node));
}

const signature = context.checker.getResolvedSignature(node);
Expand All @@ -27,6 +27,6 @@ export function transformFunctionPrototypeCall(
case "call":
return transformLuaLibFunction(context, LuaLibFeature.FunctionCall, node, caller, ...params);
default:
throw UnsupportedProperty("function", expressionName, node);
context.diagnostics.push(unsupportedProperty(expression.name, "function", expressionName));
}
}
26 changes: 12 additions & 14 deletions src/transformation/builtins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,24 +28,22 @@ export function transformBuiltinPropertyAccessExpression(
context: TransformationContext,
node: ts.PropertyAccessExpression
): lua.Expression | undefined {
const type = context.checker.getTypeAtLocation(node.expression);
if (isStringType(context, type)) {
const ownerType = context.checker.getTypeAtLocation(node.expression);

if (isStringType(context, ownerType)) {
return transformStringProperty(context, node);
} else if (isArrayType(context, type)) {
const arrayPropertyAccess = transformArrayProperty(context, node);
if (arrayPropertyAccess) {
return arrayPropertyAccess;
}
}

if (ts.isIdentifier(node.expression)) {
const ownerType = context.checker.getTypeAtLocation(node.expression);
if (isArrayType(context, ownerType)) {
return transformArrayProperty(context, node);
}

if (isStandardLibraryType(context, ownerType, "Math")) {
return transformMathProperty(node);
} else if (isStandardLibraryType(context, ownerType, "Symbol")) {
// Pull in Symbol lib
importLuaLibFeature(context, LuaLibFeature.Symbol);
if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, ownerType, undefined)) {
switch (node.expression.text) {
case "Math":
return transformMathProperty(context, node);
case "Symbol":
importLuaLibFeature(context, LuaLibFeature.Symbol);
}
}
}
Expand Down
16 changes: 11 additions & 5 deletions src/transformation/builtins/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import * as ts from "typescript";
import * as lua from "../../LuaAST";
import { LuaTarget } from "../../CompilerOptions";
import { TransformationContext } from "../context";
import { UnsupportedProperty } from "../utils/errors";
import { unsupportedProperty } from "../utils/diagnostics";
import { PropertyCallExpression, transformArguments } from "../visitors/call";

export function transformMathProperty(node: ts.PropertyAccessExpression): lua.Expression {
export function transformMathProperty(
context: TransformationContext,
node: ts.PropertyAccessExpression
): lua.Expression | undefined {
const name = node.name.text;
switch (name) {
case "PI":
Expand All @@ -23,11 +26,14 @@ export function transformMathProperty(node: ts.PropertyAccessExpression): lua.Ex
return lua.createNumericLiteral(Math[name], node);

default:
throw UnsupportedProperty("Math", name, node);
context.diagnostics.push(unsupportedProperty(node.name, "Math", name));
}
}

export function transformMathCall(context: TransformationContext, node: PropertyCallExpression): lua.Expression {
export function transformMathCall(
context: TransformationContext,
node: PropertyCallExpression
): lua.Expression | undefined {
const expression = node.expression;
const signature = context.checker.getResolvedSignature(node);
const params = transformArguments(context, node.arguments, signature);
Expand Down Expand Up @@ -93,6 +99,6 @@ export function transformMathCall(context: TransformationContext, node: Property
}

default:
throw UnsupportedProperty("Math", expressionName, expression);
context.diagnostics.push(unsupportedProperty(expression.name, "Math", expressionName));
}
}
10 changes: 5 additions & 5 deletions src/transformation/builtins/number.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as lua from "../../LuaAST";
import { TransformationContext } from "../context";
import { UnsupportedProperty } from "../utils/errors";
import { unsupportedProperty } from "../utils/diagnostics";
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
import { PropertyCallExpression, transformArguments } from "../visitors/call";

export function transformNumberPrototypeCall(
context: TransformationContext,
node: PropertyCallExpression
): lua.Expression {
): lua.Expression | undefined {
const expression = node.expression;
const signature = context.checker.getResolvedSignature(node);
const params = transformArguments(context, node.arguments, signature);
Expand All @@ -20,14 +20,14 @@ export function transformNumberPrototypeCall(
? lua.createCallExpression(lua.createIdentifier("tostring"), [caller], node)
: transformLuaLibFunction(context, LuaLibFeature.NumberToString, node, caller, ...params);
default:
throw UnsupportedProperty("number", expressionName, node);
context.diagnostics.push(unsupportedProperty(expression.name, "number", expressionName));
}
}

export function transformNumberConstructorCall(
context: TransformationContext,
expression: PropertyCallExpression
): lua.CallExpression {
): lua.CallExpression | undefined {
const method = expression.expression;
const parameters = transformArguments(context, expression.arguments);
const methodName = method.name.text;
Expand All @@ -37,6 +37,6 @@ export function transformNumberConstructorCall(
case "isFinite":
return transformLuaLibFunction(context, LuaLibFeature.NumberIsFinite, expression, ...parameters);
default:
throw UnsupportedProperty("Number", methodName, expression);
context.diagnostics.push(unsupportedProperty(method.name, "Number", methodName));
}
}
Loading