From 4dbfe08bcad4cfcef19da3317631697c1180e796 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Sun, 15 Jan 2023 10:16:33 -0800 Subject: [PATCH 1/3] Fix crash on invalid call extension operator use --- src/transformation/utils/diagnostics.ts | 8 ++ .../utils/language-extensions.ts | 77 +++++++++++++++ .../visitors/language-extensions/operators.ts | 31 ++---- .../visitors/language-extensions/table.ts | 98 ++++++++----------- .../optionalChaining.spec.ts.snap | 7 +- .../__snapshots__/operators.spec.ts.snap | 12 +++ .../__snapshots__/table.spec.ts.snap | 8 ++ .../language-extensions/operators.spec.ts | 44 ++++++++- test/unit/language-extensions/table.spec.ts | 54 +++++++--- 9 files changed, 242 insertions(+), 97 deletions(-) diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index 94f7781de..fd443e596 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -155,3 +155,11 @@ export const unsupportedOptionalCompileMembersOnly = createErrorDiagnosticFactor export const undefinedInArrayLiteral = createErrorDiagnosticFactory( "Array literals may not contain undefined or null." ); + +export const invalidMethodCallExtensionUse = createErrorDiagnosticFactory( + "This language extension must be called as a method." +); + +export const invalidSpreadInCallExtension = createErrorDiagnosticFactory( + "Spread elements are not supported in call extensions." +); diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index 881da105b..108c6eef1 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -1,5 +1,6 @@ import * as ts from "typescript"; import { TransformationContext } from "../context"; +import { invalidMethodCallExtensionUse, invalidSpreadInCallExtension } from "./diagnostics"; export enum ExtensionKind { MultiFunction = "MultiFunction", @@ -53,6 +54,7 @@ export enum ExtensionKind { TableAddKeyType = "TableAddKey", TableAddKeyMethodType = "TableAddKeyMethod", } + const extensionValues: Set = new Set(Object.values(ExtensionKind)); export function getExtensionKindForType(context: TransformationContext, type: ts.Type): ExtensionKind | undefined { @@ -119,3 +121,78 @@ export function getIterableExtensionKindForNode( const type = context.checker.getTypeAtLocation(node); return getIterableExtensionTypeForType(context, type); } + +export const methodExtensionKinds: ReadonlySet = new Set([ + ExtensionKind.AdditionOperatorMethodType, + ExtensionKind.SubtractionOperatorMethodType, + ExtensionKind.MultiplicationOperatorMethodType, + ExtensionKind.DivisionOperatorMethodType, + ExtensionKind.ModuloOperatorMethodType, + ExtensionKind.PowerOperatorMethodType, + ExtensionKind.FloorDivisionOperatorMethodType, + ExtensionKind.BitwiseAndOperatorMethodType, + ExtensionKind.BitwiseOrOperatorMethodType, + ExtensionKind.BitwiseExclusiveOrOperatorMethodType, + ExtensionKind.BitwiseLeftShiftOperatorMethodType, + ExtensionKind.BitwiseRightShiftOperatorMethodType, + ExtensionKind.ConcatOperatorMethodType, + ExtensionKind.LessThanOperatorMethodType, + ExtensionKind.GreaterThanOperatorMethodType, + ExtensionKind.NegationOperatorMethodType, + ExtensionKind.BitwiseNotOperatorMethodType, + ExtensionKind.LengthOperatorMethodType, + ExtensionKind.TableDeleteMethodType, + ExtensionKind.TableGetMethodType, + ExtensionKind.TableHasMethodType, + ExtensionKind.TableSetMethodType, + ExtensionKind.TableAddKeyMethodType, +]); + +export function getNaryCallExtensionArgs( + context: TransformationContext, + node: ts.CallExpression, + kind: ExtensionKind, + numArgs: number +): readonly ts.Expression[] | undefined { + let expressions: readonly ts.Expression[]; + if (node.arguments.some(ts.isSpreadElement)) { + context.diagnostics.push(invalidSpreadInCallExtension(node)); + return undefined; + } + if (methodExtensionKinds.has(kind)) { + if (!(ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression))) { + context.diagnostics.push(invalidMethodCallExtensionUse(node)); + return undefined; + } + if (node.arguments.length < numArgs - 1) { + // assumed to be TS error + return undefined; + } + expressions = [node.expression.expression, ...node.arguments]; + } else { + if (node.arguments.length < numArgs) { + // assumed to be TS error + return undefined; + } + expressions = node.arguments; + } + return expressions; +} + +export function getUnaryCallExtensionArg( + context: TransformationContext, + node: ts.CallExpression, + kind: ExtensionKind +): ts.Expression | undefined { + return getNaryCallExtensionArgs(context, node, kind, 1)?.[0]; +} + +export function getBinaryCallExtensionArgs( + context: TransformationContext, + node: ts.CallExpression, + kind: ExtensionKind +): readonly [ts.Expression, ts.Expression] | undefined { + const expressions = getNaryCallExtensionArgs(context, node, kind, 2); + if (expressions === undefined) return undefined; + return [expressions[0], expressions[1]]; +} diff --git a/src/transformation/visitors/language-extensions/operators.ts b/src/transformation/visitors/language-extensions/operators.ts index 657c98b92..4cb08abc2 100644 --- a/src/transformation/visitors/language-extensions/operators.ts +++ b/src/transformation/visitors/language-extensions/operators.ts @@ -4,8 +4,9 @@ import { TransformationContext } from "../../context"; import { assert } from "../../../utils"; import { LuaTarget } from "../../../CompilerOptions"; import { unsupportedForTarget } from "../../utils/diagnostics"; -import { ExtensionKind } from "../../utils/language-extensions"; +import { ExtensionKind, getBinaryCallExtensionArgs, getUnaryCallExtensionArg } from "../../utils/language-extensions"; import { LanguageExtensionCallTransformerMap } from "./call-extension"; +import { transformOrderedExpressions } from "../expression-list"; const binaryOperatorMappings = new Map([ [ExtensionKind.AdditionOperatorType, lua.SyntaxKind.AdditionOperator], @@ -81,35 +82,21 @@ for (const kind of unaryOperatorMappings.keys()) { function transformBinaryOperator(context: TransformationContext, node: ts.CallExpression, kind: ExtensionKind) { if (requiresLua53.has(kind)) checkHasLua53(context, node, kind); - let args: readonly ts.Expression[] = node.arguments; - if ( - args.length === 1 && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) - ) { - args = [node.expression.expression, ...args]; - } + const args = getBinaryCallExtensionArgs(context, node, kind); + if (!args) return lua.createNilLiteral(); + + const [left, right] = transformOrderedExpressions(context, args); const luaOperator = binaryOperatorMappings.get(kind); assert(luaOperator); - return lua.createBinaryExpression( - context.transformExpression(args[0]), - context.transformExpression(args[1]), - luaOperator - ); + return lua.createBinaryExpression(left, right, luaOperator); } function transformUnaryOperator(context: TransformationContext, node: ts.CallExpression, kind: ExtensionKind) { if (requiresLua53.has(kind)) checkHasLua53(context, node, kind); - let arg: ts.Expression; - if ( - node.arguments.length === 0 && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) - ) { - arg = node.expression.expression; - } else { - arg = node.arguments[0]; - } + const arg = getUnaryCallExtensionArg(context, node, kind); + if (!arg) return lua.createNilLiteral(); const luaOperator = unaryOperatorMappings.get(kind); assert(luaOperator); diff --git a/src/transformation/visitors/language-extensions/table.ts b/src/transformation/visitors/language-extensions/table.ts index 2d22adf15..d3aaedc5d 100644 --- a/src/transformation/visitors/language-extensions/table.ts +++ b/src/transformation/visitors/language-extensions/table.ts @@ -1,16 +1,22 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { TransformationContext } from "../../context"; -import { ExtensionKind, getExtensionKindForNode } from "../../utils/language-extensions"; -import { transformExpressionList } from "../expression-list"; -import { LanguageExtensionCallTransformer } from "./call-extension"; +import { + ExtensionKind, + getBinaryCallExtensionArgs, + getExtensionKindForNode, + getNaryCallExtensionArgs, +} from "../../utils/language-extensions"; +import { transformOrderedExpressions } from "../expression-list"; +import { LanguageExtensionCallTransformerMap } from "./call-extension"; export function isTableNewCall(context: TransformationContext, node: ts.NewExpression) { return getExtensionKindForNode(context, node.expression) === ExtensionKind.TableNewType; } + export const tableNewExtensions = [ExtensionKind.TableNewType]; -export const tableExtensionTransformers: { [P in ExtensionKind]?: LanguageExtensionCallTransformer } = { +export const tableExtensionTransformers: LanguageExtensionCallTransformerMap = { [ExtensionKind.TableDeleteType]: transformTableDeleteExpression, [ExtensionKind.TableDeleteMethodType]: transformTableDeleteExpression, [ExtensionKind.TableGetType]: transformTableGetExpression, @@ -19,8 +25,8 @@ export const tableExtensionTransformers: { [P in ExtensionKind]?: LanguageExtens [ExtensionKind.TableHasMethodType]: transformTableHasExpression, [ExtensionKind.TableSetType]: transformTableSetExpression, [ExtensionKind.TableSetMethodType]: transformTableSetExpression, - [ExtensionKind.TableAddKeyType]: transformTableAddExpression, - [ExtensionKind.TableAddKeyMethodType]: transformTableAddExpression, + [ExtensionKind.TableAddKeyType]: transformTableAddKeyExpression, + [ExtensionKind.TableAddKeyMethodType]: transformTableAddKeyExpression, }; function transformTableDeleteExpression( @@ -28,48 +34,32 @@ function transformTableDeleteExpression( node: ts.CallExpression, extensionKind: ExtensionKind ): lua.Expression { - const args = node.arguments.slice(); - if ( - extensionKind === ExtensionKind.TableDeleteMethodType && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) - ) { - // In case of method (no table argument), push method owner to front of args list - args.unshift(node.expression.expression); + const args = getBinaryCallExtensionArgs(context, node, extensionKind); + if (!args) { + return lua.createNilLiteral(); } - const [table, accessExpression] = transformExpressionList(context, args); + const [table, key] = transformOrderedExpressions(context, args); // arg0[arg1] = nil context.addPrecedingStatements( - lua.createAssignmentStatement( - lua.createTableIndexExpression(table, accessExpression), - lua.createNilLiteral(), - node - ) + lua.createAssignmentStatement(lua.createTableIndexExpression(table, key), lua.createNilLiteral(), node) ); return lua.createBooleanLiteral(true); } -function transformWithTableArgument(context: TransformationContext, node: ts.CallExpression): lua.Expression[] { - if (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) { - return transformExpressionList(context, [node.expression.expression, ...node.arguments]); - } - // todo: report diagnostic? - return [lua.createNilLiteral(), ...transformExpressionList(context, node.arguments)]; -} - function transformTableGetExpression( context: TransformationContext, node: ts.CallExpression, extensionKind: ExtensionKind ): lua.Expression { - const args = - extensionKind === ExtensionKind.TableGetMethodType - ? transformWithTableArgument(context, node) - : transformExpressionList(context, node.arguments); + const args = getBinaryCallExtensionArgs(context, node, extensionKind); + if (!args) { + return lua.createNilLiteral(); + } - const [table, accessExpression] = args; + const [table, key] = transformOrderedExpressions(context, args); // arg0[arg1] - return lua.createTableIndexExpression(table, accessExpression, node); + return lua.createTableIndexExpression(table, key, node); } function transformTableHasExpression( @@ -77,14 +67,14 @@ function transformTableHasExpression( node: ts.CallExpression, extensionKind: ExtensionKind ): lua.Expression { - const args = - extensionKind === ExtensionKind.TableHasMethodType - ? transformWithTableArgument(context, node) - : transformExpressionList(context, node.arguments); + const args = getBinaryCallExtensionArgs(context, node, extensionKind); + if (!args) { + return lua.createNilLiteral(); + } - const [table, accessExpression] = args; + const [table, key] = transformOrderedExpressions(context, args); // arg0[arg1] - const tableIndexExpression = lua.createTableIndexExpression(table, accessExpression); + const tableIndexExpression = lua.createTableIndexExpression(table, key); // arg0[arg1] ~= nil return lua.createBinaryExpression( @@ -100,37 +90,33 @@ function transformTableSetExpression( node: ts.CallExpression, extensionKind: ExtensionKind ): lua.Expression { - const args = - extensionKind === ExtensionKind.TableSetMethodType - ? transformWithTableArgument(context, node) - : transformExpressionList(context, node.arguments); + const args = getNaryCallExtensionArgs(context, node, extensionKind, 3); + if (!args) { + return lua.createNilLiteral(); + } - const [table, accessExpression, value] = args; + const [table, key, value] = transformOrderedExpressions(context, args); // arg0[arg1] = arg2 context.addPrecedingStatements( - lua.createAssignmentStatement(lua.createTableIndexExpression(table, accessExpression), value, node) + lua.createAssignmentStatement(lua.createTableIndexExpression(table, key), value, node) ); return lua.createNilLiteral(); } -function transformTableAddExpression( +function transformTableAddKeyExpression( context: TransformationContext, node: ts.CallExpression, extensionKind: ExtensionKind ): lua.Expression { - const args = - extensionKind === ExtensionKind.TableAddKeyMethodType - ? transformWithTableArgument(context, node) - : transformExpressionList(context, node.arguments); + const args = getNaryCallExtensionArgs(context, node, extensionKind, 2); + if (!args) { + return lua.createNilLiteral(); + } - const [table, value] = args; + const [table, key] = transformOrderedExpressions(context, args); // arg0[arg1] = true context.addPrecedingStatements( - lua.createAssignmentStatement( - lua.createTableIndexExpression(table, value), - lua.createBooleanLiteral(true), - node - ) + lua.createAssignmentStatement(lua.createTableIndexExpression(table, key), lua.createBooleanLiteral(true), node) ); return lua.createNilLiteral(); } diff --git a/test/unit/__snapshots__/optionalChaining.spec.ts.snap b/test/unit/__snapshots__/optionalChaining.spec.ts.snap index 94655ca01..ed95d93c0 100644 --- a/test/unit/__snapshots__/optionalChaining.spec.ts.snap +++ b/test/unit/__snapshots__/optionalChaining.spec.ts.snap @@ -63,8 +63,11 @@ exports[`Unsupported optional chains Compile members only: diagnostics 1`] = `"m exports[`Unsupported optional chains Language extensions: code 1`] = ` "local ____table_has_result_0 = ({}).has if ____table_has_result_0 ~= nil then - ____table_has_result_0 = nil[3] ~= nil + ____table_has_result_0 = nil end" `; -exports[`Unsupported optional chains Language extensions: diagnostics 1`] = `"main.ts(2,17): error TSTL: Optional calls are not supported for builtin or language extension functions."`; +exports[`Unsupported optional chains Language extensions: diagnostics 1`] = ` +"main.ts(2,17): error TSTL: Optional calls are not supported for builtin or language extension functions. +main.ts(2,17): error TSTL: This language extension must be called as a method." +`; diff --git a/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap b/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap index dda0e61e2..10a8b65ad 100644 --- a/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap @@ -1,5 +1,17 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`does not crash on invalid operator use global function: code 1`] = `""`; + +exports[`does not crash on invalid operator use global function: diagnostics 1`] = `"main.ts(3,9): error TS2554: Expected 2 arguments, but got 1."`; + +exports[`does not crash on invalid operator use method: code 1`] = `"left = {}"`; + +exports[`does not crash on invalid operator use method: diagnostics 1`] = `"main.ts(5,14): error TS2554: Expected 1 arguments, but got 0."`; + +exports[`does not crash on invalid operator use unary operator: code 1`] = `"op(_G)"`; + +exports[`does not crash on invalid operator use unary operator: diagnostics 1`] = `"main.ts(2,27): error TS2304: Cannot find name 'LuaUnaryMinus'."`; + exports[`operator mapping - invalid use (const foo = (op as any)(1, 2);): code 1`] = `"foo = op(_G, 1, 2)"`; exports[`operator mapping - invalid use (const foo = (op as any)(1, 2);): diagnostics 1`] = `"main.ts(3,22): error TSTL: This function must be called directly and cannot be referred to."`; diff --git a/test/unit/language-extensions/__snapshots__/table.spec.ts.snap b/test/unit/language-extensions/__snapshots__/table.spec.ts.snap index 9821d9777..5bf4ef2c8 100644 --- a/test/unit/language-extensions/__snapshots__/table.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/table.spec.ts.snap @@ -133,3 +133,11 @@ __TS__ArrayMap({\\"a\\", \\"b\\", \\"c\\"}, ____table.has)" `; exports[`LuaTableHas extension invalid use method expression ("LuaTable"): diagnostics 1`] = `"main.ts(3,37): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`does not crash on invalid extension use global function: code 1`] = `""`; + +exports[`does not crash on invalid extension use global function: diagnostics 1`] = `"main.ts(3,9): error TS2554: Expected 2 arguments, but got 1."`; + +exports[`does not crash on invalid extension use method: code 1`] = `"left = {}"`; + +exports[`does not crash on invalid extension use method: diagnostics 1`] = `"main.ts(5,14): error TS2554: Expected 2 arguments, but got 0."`; diff --git a/test/unit/language-extensions/operators.spec.ts b/test/unit/language-extensions/operators.spec.ts index 043b1cebb..303a5d0be 100644 --- a/test/unit/language-extensions/operators.spec.ts +++ b/test/unit/language-extensions/operators.spec.ts @@ -2,7 +2,11 @@ import * as path from "path"; import * as util from "../../util"; import * as tstl from "../../../src"; import { LuaTarget } from "../../../src"; -import { unsupportedForTarget, invalidCallExtensionUse } from "../../../src/transformation/utils/diagnostics"; +import { + unsupportedForTarget, + invalidCallExtensionUse, + invalidSpreadInCallExtension, +} from "../../../src/transformation/utils/diagnostics"; const operatorsProjectOptions: tstl.CompilerOptions = { luaTarget: LuaTarget.Lua54, @@ -389,3 +393,41 @@ test.each([ .setOptions(operatorsProjectOptions) .expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]); }); + +describe("does not crash on invalid operator use", () => { + test("global function", () => { + util.testModule` + declare const op: LuaAddition; + op(1) + ` + .setOptions(operatorsProjectOptions) + .expectDiagnosticsToMatchSnapshot(); + }); + test("unary operator", () => { + util.testModule` + declare const op: LuaUnaryMinus; + op() + ` + .setOptions(operatorsProjectOptions) + .expectDiagnosticsToMatchSnapshot(); + }); + test("method", () => { + util.testModule` + const left = {} as { + op: LuaAdditionMethod; + } + left.op() + ` + .setOptions(operatorsProjectOptions) + .expectDiagnosticsToMatchSnapshot(); + }); +}); + +test("does not allow spread", () => { + util.testModule` + declare const op: LuaAddition; + op(...[1, 2] as const); + ` + .setOptions(operatorsProjectOptions) + .expectToHaveDiagnostics([invalidSpreadInCallExtension.code]); +}); diff --git a/test/unit/language-extensions/table.spec.ts b/test/unit/language-extensions/table.spec.ts index ed9e8e37c..18b691ea1 100644 --- a/test/unit/language-extensions/table.spec.ts +++ b/test/unit/language-extensions/table.spec.ts @@ -1,5 +1,5 @@ import * as util from "../../util"; -import { invalidCallExtensionUse } from "../../../src/transformation/utils/diagnostics"; +import {invalidCallExtensionUse} from "../../../src/transformation/utils/diagnostics"; describe("LuaTableGet & LuaTableSet extensions", () => { test("stand-alone function", () => { @@ -92,7 +92,7 @@ describe("LuaTableHas extension", () => { export const hasBaz = tableHas(table, "baz"); ` .withLanguageExtensions() - .expectToEqual({ hasFoo: true, hasBaz: false }); + .expectToEqual({hasFoo: true, hasBaz: false}); }); test("LuaTableHas nested expression", () => { @@ -103,7 +103,7 @@ describe("LuaTableHas extension", () => { export const result = \`table has foo: \${tableHas(table, "foo")}\`; ` .withLanguageExtensions() - .expectToEqual({ result: "table has foo: true" }); + .expectToEqual({result: "table has foo: true"}); }); test("LuaTableHas namespace function", () => { @@ -117,7 +117,7 @@ describe("LuaTableHas extension", () => { export const hasBaz = Table.tableHas(table, "baz"); ` .withLanguageExtensions() - .expectToEqual({ hasFoo: true, hasBaz: false }); + .expectToEqual({hasFoo: true, hasBaz: false}); }); test("LuaTableHasMethod method", () => { @@ -133,7 +133,7 @@ describe("LuaTableHas extension", () => { export const hasBaz = table.has("baz"); ` .withLanguageExtensions() - .expectToEqual({ hasFoo: true, hasBaz: false }); + .expectToEqual({hasFoo: true, hasBaz: false}); }); test("LuaTableHas as statement", () => { @@ -153,7 +153,7 @@ describe("LuaTableHas extension", () => { export const numCalls = count; ` .withLanguageExtensions() - .expectToEqual({ numCalls: 1 }); + .expectToEqual({numCalls: 1}); }); test.each([ @@ -205,7 +205,7 @@ describe("LuaTableDelete extension", () => { tableDelete(table, "foo"); ` .withLanguageExtensions() - .expectToEqual({ table: { baz: "baz" } }); + .expectToEqual({table: {baz: "baz"}}); }); test("LuaTableDelete namespace function", () => { @@ -218,7 +218,7 @@ describe("LuaTableDelete extension", () => { Table.tableDelete(table, "foo"); ` .withLanguageExtensions() - .expectToEqual({ table: { baz: "baz" } }); + .expectToEqual({table: {baz: "baz"}}); }); test("LuaTableHasMethod method", () => { @@ -233,7 +233,7 @@ describe("LuaTableDelete extension", () => { table.delete("foo"); ` .withLanguageExtensions() - .expectToEqual({ table: { bar: 12 } }); + .expectToEqual({table: {bar: 12}}); }); test.each([ @@ -272,7 +272,7 @@ describe("LuaTableAddKey extension", () => { tableAddKey(table, "baz"); ` .withLanguageExtensions() - .expectToEqual({ table: { foo: "bar", baz: true } }); + .expectToEqual({table: {foo: "bar", baz: true}}); }); test("LuaTableAddKey namespace function", () => { @@ -284,7 +284,7 @@ describe("LuaTableAddKey extension", () => { Table.tableAddKey(table, "baz"); ` .withLanguageExtensions() - .expectToEqual({ table: { foo: "bar", baz: true } }); + .expectToEqual({table: {foo: "bar", baz: true}}); }); test("LuaTableAddKey method", () => { @@ -296,7 +296,7 @@ describe("LuaTableAddKey extension", () => { table.addKey("bar"); ` .withLanguageExtensions() - .expectToEqual({ table: { bar: true } }); + .expectToEqual({table: {bar: true}}); }); test.each([ @@ -362,7 +362,7 @@ describe("LuaTable extension interface", () => { keyType => { util.testExpression`new LuaTable<${keyType}, unknown>()` .withLanguageExtensions() - .setOptions({ strict: true }) + .setOptions({strict: true}) .expectToHaveDiagnostics() .expectDiagnosticsToMatchSnapshot(); } @@ -412,7 +412,7 @@ describe("LuaTable extension interface", () => { return tbl; ` .withLanguageExtensions() - .expectToEqual({ baz: 5 }); + .expectToEqual({baz: 5}); }); test("table add", () => { @@ -422,7 +422,7 @@ describe("LuaTable extension interface", () => { return tbl ` .withLanguageExtensions() - .expectToEqual({ foo: true }); + .expectToEqual({foo: true}); }); test.each(['new LuaTable().get("foo");', 'new LuaTable().set("foo", "bar");'])( @@ -445,7 +445,7 @@ describe("LuaTable extension interface", () => { return results; ` .withLanguageExtensions() - .expectToEqual({ foo: 1, bar: 3, baz: 5 }); + .expectToEqual({foo: 1, bar: 3, baz: 5}); }); }); @@ -467,3 +467,25 @@ test.each([ .withLanguageExtensions() .expectToEqual(expected); }); + +describe("does not crash on invalid extension use", () => { + test("global function", () => { + util.testModule` + declare const op: LuaTableGet<{}, string, any> + op({}) + ` + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot() + }) + + test("method", () => { + util.testModule` + const left = {} as { + op: LuaTableGet<{}, string, any> + } + left.op() + ` + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot() + }) +}) From 7530c80190c4c4231e5fef29c4359933dbcbff27 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Sun, 15 Jan 2023 10:30:53 -0800 Subject: [PATCH 2/3] Fix prettier --- test/unit/language-extensions/table.spec.ts | 42 ++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/test/unit/language-extensions/table.spec.ts b/test/unit/language-extensions/table.spec.ts index 18b691ea1..b833de22b 100644 --- a/test/unit/language-extensions/table.spec.ts +++ b/test/unit/language-extensions/table.spec.ts @@ -1,5 +1,5 @@ import * as util from "../../util"; -import {invalidCallExtensionUse} from "../../../src/transformation/utils/diagnostics"; +import { invalidCallExtensionUse } from "../../../src/transformation/utils/diagnostics"; describe("LuaTableGet & LuaTableSet extensions", () => { test("stand-alone function", () => { @@ -92,7 +92,7 @@ describe("LuaTableHas extension", () => { export const hasBaz = tableHas(table, "baz"); ` .withLanguageExtensions() - .expectToEqual({hasFoo: true, hasBaz: false}); + .expectToEqual({ hasFoo: true, hasBaz: false }); }); test("LuaTableHas nested expression", () => { @@ -103,7 +103,7 @@ describe("LuaTableHas extension", () => { export const result = \`table has foo: \${tableHas(table, "foo")}\`; ` .withLanguageExtensions() - .expectToEqual({result: "table has foo: true"}); + .expectToEqual({ result: "table has foo: true" }); }); test("LuaTableHas namespace function", () => { @@ -117,7 +117,7 @@ describe("LuaTableHas extension", () => { export const hasBaz = Table.tableHas(table, "baz"); ` .withLanguageExtensions() - .expectToEqual({hasFoo: true, hasBaz: false}); + .expectToEqual({ hasFoo: true, hasBaz: false }); }); test("LuaTableHasMethod method", () => { @@ -133,7 +133,7 @@ describe("LuaTableHas extension", () => { export const hasBaz = table.has("baz"); ` .withLanguageExtensions() - .expectToEqual({hasFoo: true, hasBaz: false}); + .expectToEqual({ hasFoo: true, hasBaz: false }); }); test("LuaTableHas as statement", () => { @@ -153,7 +153,7 @@ describe("LuaTableHas extension", () => { export const numCalls = count; ` .withLanguageExtensions() - .expectToEqual({numCalls: 1}); + .expectToEqual({ numCalls: 1 }); }); test.each([ @@ -205,7 +205,7 @@ describe("LuaTableDelete extension", () => { tableDelete(table, "foo"); ` .withLanguageExtensions() - .expectToEqual({table: {baz: "baz"}}); + .expectToEqual({ table: { baz: "baz" } }); }); test("LuaTableDelete namespace function", () => { @@ -218,7 +218,7 @@ describe("LuaTableDelete extension", () => { Table.tableDelete(table, "foo"); ` .withLanguageExtensions() - .expectToEqual({table: {baz: "baz"}}); + .expectToEqual({ table: { baz: "baz" } }); }); test("LuaTableHasMethod method", () => { @@ -233,7 +233,7 @@ describe("LuaTableDelete extension", () => { table.delete("foo"); ` .withLanguageExtensions() - .expectToEqual({table: {bar: 12}}); + .expectToEqual({ table: { bar: 12 } }); }); test.each([ @@ -272,7 +272,7 @@ describe("LuaTableAddKey extension", () => { tableAddKey(table, "baz"); ` .withLanguageExtensions() - .expectToEqual({table: {foo: "bar", baz: true}}); + .expectToEqual({ table: { foo: "bar", baz: true } }); }); test("LuaTableAddKey namespace function", () => { @@ -284,7 +284,7 @@ describe("LuaTableAddKey extension", () => { Table.tableAddKey(table, "baz"); ` .withLanguageExtensions() - .expectToEqual({table: {foo: "bar", baz: true}}); + .expectToEqual({ table: { foo: "bar", baz: true } }); }); test("LuaTableAddKey method", () => { @@ -296,7 +296,7 @@ describe("LuaTableAddKey extension", () => { table.addKey("bar"); ` .withLanguageExtensions() - .expectToEqual({table: {bar: true}}); + .expectToEqual({ table: { bar: true } }); }); test.each([ @@ -362,7 +362,7 @@ describe("LuaTable extension interface", () => { keyType => { util.testExpression`new LuaTable<${keyType}, unknown>()` .withLanguageExtensions() - .setOptions({strict: true}) + .setOptions({ strict: true }) .expectToHaveDiagnostics() .expectDiagnosticsToMatchSnapshot(); } @@ -412,7 +412,7 @@ describe("LuaTable extension interface", () => { return tbl; ` .withLanguageExtensions() - .expectToEqual({baz: 5}); + .expectToEqual({ baz: 5 }); }); test("table add", () => { @@ -422,7 +422,7 @@ describe("LuaTable extension interface", () => { return tbl ` .withLanguageExtensions() - .expectToEqual({foo: true}); + .expectToEqual({ foo: true }); }); test.each(['new LuaTable().get("foo");', 'new LuaTable().set("foo", "bar");'])( @@ -445,7 +445,7 @@ describe("LuaTable extension interface", () => { return results; ` .withLanguageExtensions() - .expectToEqual({foo: 1, bar: 3, baz: 5}); + .expectToEqual({ foo: 1, bar: 3, baz: 5 }); }); }); @@ -475,8 +475,8 @@ describe("does not crash on invalid extension use", () => { op({}) ` .withLanguageExtensions() - .expectDiagnosticsToMatchSnapshot() - }) + .expectDiagnosticsToMatchSnapshot(); + }); test("method", () => { util.testModule` @@ -486,6 +486,6 @@ describe("does not crash on invalid extension use", () => { left.op() ` .withLanguageExtensions() - .expectDiagnosticsToMatchSnapshot() - }) -}) + .expectDiagnosticsToMatchSnapshot(); + }); +}); From a149888f5f0ea8a14bb60e84f0633ba2be4d9566 Mon Sep 17 00:00:00 2001 From: GlassBricks <24237065+GlassBricks@users.noreply.github.com> Date: Thu, 19 Jan 2023 19:13:14 -0800 Subject: [PATCH 3/3] Update snapshots --- .../__snapshots__/operators.spec.ts.snap | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap b/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap index 10a8b65ad..a4554a8a1 100644 --- a/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap @@ -2,15 +2,15 @@ exports[`does not crash on invalid operator use global function: code 1`] = `""`; -exports[`does not crash on invalid operator use global function: diagnostics 1`] = `"main.ts(3,9): error TS2554: Expected 2 arguments, but got 1."`; +exports[`does not crash on invalid operator use global function: diagnostics 1`] = `"main.ts(3,13): error TS2554: Expected 2 arguments, but got 1."`; exports[`does not crash on invalid operator use method: code 1`] = `"left = {}"`; -exports[`does not crash on invalid operator use method: diagnostics 1`] = `"main.ts(5,14): error TS2554: Expected 1 arguments, but got 0."`; +exports[`does not crash on invalid operator use method: diagnostics 1`] = `"main.ts(5,18): error TS2554: Expected 1 arguments, but got 0."`; exports[`does not crash on invalid operator use unary operator: code 1`] = `"op(_G)"`; -exports[`does not crash on invalid operator use unary operator: diagnostics 1`] = `"main.ts(2,27): error TS2304: Cannot find name 'LuaUnaryMinus'."`; +exports[`does not crash on invalid operator use unary operator: diagnostics 1`] = `"main.ts(2,31): error TS2304: Cannot find name 'LuaUnaryMinus'."`; exports[`operator mapping - invalid use (const foo = (op as any)(1, 2);): code 1`] = `"foo = op(_G, 1, 2)"`;