diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index 1e1ba8c72..d5023bf3f 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -21,7 +21,7 @@ export interface LuaPluginImport { [option: string]: any; } -export type CompilerOptions = OmitIndexSignature & { +export interface TypeScriptToLuaOptions { buildMode?: BuildMode; extension?: string; luaBundle?: string; @@ -35,8 +35,13 @@ export type CompilerOptions = OmitIndexSignature & { plugins?: Array; sourceMapTraceback?: boolean; tstlVerbose?: boolean; - [option: string]: any; -}; + lua51AllowTryCatchInAsyncAwait?: boolean; +} + +export type CompilerOptions = OmitIndexSignature & + TypeScriptToLuaOptions & { + [option: string]: any; + }; export enum LuaLibImportKind { None = "none", diff --git a/src/cli/parse.ts b/src/cli/parse.ts index 1a20880c9..4d113610f 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -88,6 +88,11 @@ export const optionDeclarations: CommandLineOption[] = [ description: "An array of paths that tstl should not resolve and keep as-is.", type: "array", }, + { + name: "lua51AllowTryCatchInAsyncAwait", + description: "Always allow try/catch in async/await functions for Lua 5.1.", + type: "boolean", + }, ]; export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): ParsedCommandLine { diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index 25442cdfa..d0ecbd1d1 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -1,5 +1,5 @@ import * as ts from "typescript"; -import { LuaTarget } from "../../CompilerOptions"; +import { LuaTarget, TypeScriptToLuaOptions } from "../../CompilerOptions"; import { createSerialDiagnosticFactory } from "../../utils"; import { AnnotationKind } from "./annotations"; @@ -84,12 +84,23 @@ export const unsupportedRightShiftOperator = createErrorDiagnosticFactory( "Right shift operator is not supported for target Lua 5.3. Use `>>>` instead." ); +type NonUniversalTarget = Exclude; + const getLuaTargetName = (version: LuaTarget) => (version === LuaTarget.LuaJIT ? "LuaJIT" : `Lua ${version}`); export const unsupportedForTarget = createErrorDiagnosticFactory( - (functionality: string, version: Exclude) => + (functionality: string, version: NonUniversalTarget) => `${functionality} is/are not supported for target ${getLuaTargetName(version)}.` ); +export const unsupportedForTargetButOverrideAvailable = createErrorDiagnosticFactory( + (functionality: string, version: NonUniversalTarget, optionName: keyof TypeScriptToLuaOptions) => + `As a precaution, ${functionality} is/are not supported for target ${getLuaTargetName( + version + )} due to language features/limitations. ` + + `However "--${optionName}" can be used to bypass this precaution. ` + + "See https://typescripttolua.github.io/docs/configuration for more information." +); + export const unsupportedProperty = createErrorDiagnosticFactory( (parentName: string, property: string) => `${parentName}.${property} is unsupported.` ); diff --git a/src/transformation/visitors/errors.ts b/src/transformation/visitors/errors.ts index 1dc4995e4..894ba3ad5 100644 --- a/src/transformation/visitors/errors.ts +++ b/src/transformation/visitors/errors.ts @@ -2,7 +2,7 @@ import * as ts from "typescript"; import { LuaTarget } from "../.."; import * as lua from "../../LuaAST"; import { FunctionVisitor } from "../context"; -import { unsupportedForTarget } from "../utils/diagnostics"; +import { unsupportedForTarget, unsupportedForTargetButOverrideAvailable } from "../utils/diagnostics"; import { createUnpackCall } from "../utils/lua-ast"; import { ScopeType } from "../utils/scope"; import { isInAsyncFunction, isInGeneratorFunction } from "../utils/typescript"; @@ -14,8 +14,19 @@ import { createReturnStatement } from "./return"; export const transformTryStatement: FunctionVisitor = (statement, context) => { const [tryBlock, tryScope] = transformScopeBlock(context, statement.tryBlock, ScopeType.Try); - if (context.options.luaTarget === LuaTarget.Lua51 && isInAsyncFunction(statement)) { - context.diagnostics.push(unsupportedForTarget(statement, "try/catch inside async functions", LuaTarget.Lua51)); + if ( + context.options.luaTarget === LuaTarget.Lua51 && + isInAsyncFunction(statement) && + !context.options.lua51AllowTryCatchInAsyncAwait + ) { + context.diagnostics.push( + unsupportedForTargetButOverrideAvailable( + statement, + "try/catch inside async functions", + LuaTarget.Lua51, + "lua51AllowTryCatchInAsyncAwait" + ) + ); return tryBlock.statements; } diff --git a/test/unit/builtins/async-await.spec.ts b/test/unit/builtins/async-await.spec.ts index 21f8cd265..10bf7601f 100644 --- a/test/unit/builtins/async-await.spec.ts +++ b/test/unit/builtins/async-await.spec.ts @@ -1,6 +1,7 @@ import { ModuleKind, ScriptTarget } from "typescript"; import { LuaTarget } from "../../../src"; -import { awaitMustBeInAsyncFunction, unsupportedForTarget } from "../../../src/transformation/utils/diagnostics"; +import { unsupportedForTargetButOverrideAvailable } from "../../../src/transformation/utils/diagnostics"; +import { awaitMustBeInAsyncFunction } from "../../../src/transformation/utils/diagnostics"; import * as util from "../../util"; const promiseTestLib = ` @@ -540,7 +541,8 @@ describe("try/catch in async function", () => { // Cannot execute LuaJIT with test runner { ...util.expectEachVersionExceptJit(builder => builder.expectToEqual({ result: 4 })), - [LuaTarget.Lua51]: builder => builder.expectToHaveDiagnostics([unsupportedForTarget.code]), + [LuaTarget.Lua51]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), } ); @@ -563,7 +565,8 @@ describe("try/catch in async function", () => { ...util.expectEachVersionExceptJit(builder => builder.expectToEqual({ reason: "an error occurred in the async function: test error" }) ), - [LuaTarget.Lua51]: builder => builder.expectToHaveDiagnostics([unsupportedForTarget.code]), + [LuaTarget.Lua51]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), } ); @@ -590,7 +593,8 @@ describe("try/catch in async function", () => { ...util.expectEachVersionExceptJit(builder => builder.expectToEqual({ reason: "an error occurred in the async function: test error" }) ), - [LuaTarget.Lua51]: builder => builder.expectToHaveDiagnostics([unsupportedForTarget.code]), + [LuaTarget.Lua51]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), } ); });