diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index fd443e596..8fe4a22f5 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -1,4 +1,5 @@ import * as ts from "typescript"; +import * as lua from "../../LuaAST"; import { LuaTarget, TypeScriptToLuaOptions } from "../../CompilerOptions"; import { createSerialDiagnosticFactory } from "../../utils"; import { AnnotationKind } from "./annotations"; @@ -163,3 +164,7 @@ export const invalidMethodCallExtensionUse = createErrorDiagnosticFactory( export const invalidSpreadInCallExtension = createErrorDiagnosticFactory( "Spread elements are not supported in call extensions." ); + +export const cannotAssignToNodeOfKind = createErrorDiagnosticFactory( + (kind: lua.SyntaxKind) => `Cannot create assignment assigning to a node of type ${lua.SyntaxKind[kind]}.` +); diff --git a/src/transformation/visitors/binary-expression/assignments.ts b/src/transformation/visitors/binary-expression/assignments.ts index 2cceff755..a138781dc 100644 --- a/src/transformation/visitors/binary-expression/assignments.ts +++ b/src/transformation/visitors/binary-expression/assignments.ts @@ -1,6 +1,5 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; -import { cast } from "../../../utils"; import { TransformationContext } from "../../context"; import { validateAssignment } from "../../utils/assignment-validation"; import { createExportedIdentifier, getDependenciesOfSymbol, isSymbolExported } from "../../utils/export"; @@ -9,7 +8,7 @@ import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; import { isArrayType, isDestructuringAssignment } from "../../utils/typescript"; import { isArrayLength, transformDestructuringAssignment } from "./destructuring-assignments"; import { isMultiReturnCall } from "../language-extensions/multi"; -import { notAllowedOptionalAssignment } from "../../utils/diagnostics"; +import { cannotAssignToNodeOfKind, notAllowedOptionalAssignment } from "../../utils/diagnostics"; import { transformElementAccessArgument } from "../access"; import { moveToPrecedingTemp, transformExpressionList } from "../expression-list"; import { transformInPrecedingStatementScope } from "../../utils/preceding-statements"; @@ -37,9 +36,16 @@ export function transformAssignmentLeftHandSideExpression( const symbol = context.checker.getSymbolAtLocation(node); const left = context.transformExpression(node); - return lua.isIdentifier(left) && symbol && isSymbolExported(context, symbol) - ? createExportedIdentifier(context, left) - : cast(left, lua.isAssignmentLeftHandSideExpression); + if (lua.isIdentifier(left) && symbol && isSymbolExported(context, symbol)) { + return createExportedIdentifier(context, left); + } + + if (lua.isAssignmentLeftHandSideExpression(left)) { + return left; + } else { + context.diagnostics.push(cannotAssignToNodeOfKind(node, left.kind)); + return lua.createAnonymousIdentifier(); + } } export function transformAssignment( diff --git a/test/unit/builtins/string.spec.ts b/test/unit/builtins/string.spec.ts index d87786231..fecb0e466 100644 --- a/test/unit/builtins/string.spec.ts +++ b/test/unit/builtins/string.spec.ts @@ -401,12 +401,10 @@ test.each(["string | undefined", "string | null", "null | string", "null | undef // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1406 test("string.indexOf without arguments (#1406)", () => { - // Just test we do not throw here - util.testExpression`"".indexOf()`.expectToHaveDiagnostics(); + util.testExpression`"".indexOf()`.expectNoTranspileException(); }); // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1406 test("string.repeat without arguments (#1406)", () => { - // Just test we do not throw here - util.testExpression`"".repeat()`.expectToHaveDiagnostics(); + util.testExpression`"".repeat()`.expectNoTranspileException(); }); diff --git a/test/unit/destructuring.spec.ts b/test/unit/destructuring.spec.ts index e44f8f0c3..80d3d482c 100644 --- a/test/unit/destructuring.spec.ts +++ b/test/unit/destructuring.spec.ts @@ -1,3 +1,4 @@ +import { cannotAssignToNodeOfKind, invalidMultiReturnAccess } from "../../src/transformation/utils/diagnostics"; import * as util from "../util"; const allBindings = "x, y, z, rest"; @@ -225,3 +226,14 @@ describe("array destructuring optimization", () => { .expectToMatchJsResult(); }); }); + +test("no exception from semantically invalid TS", () => { + util.testModule` + declare function testFunc(value: number): LuaMultiReturn<[number, number]>; + let [a, b] = testFunc(5) // Missing ; + [a, b] = testFunc(b) // Interpreted as testFunc(5)[a, b] + ` + .withLanguageExtensions() + .disableSemanticCheck() + .expectToHaveDiagnostics([invalidMultiReturnAccess.code, cannotAssignToNodeOfKind.code]); +}); diff --git a/test/util.ts b/test/util.ts index 1b41bb4a2..a560b94f5 100644 --- a/test/util.ts +++ b/test/util.ts @@ -346,6 +346,11 @@ export abstract class TestBuilder { return this; } + public expectNoTranspileException(): this { + expect(() => this.getLuaResult()).not.toThrow(); + return this; + } + public expectNoExecutionError(): this { const luaResult = this.getLuaExecutionResult(); if (luaResult instanceof ExecutionError) {