From 46b9c9c82cf6fabe4b3344a9e419c75ee18afd3c Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 11 Feb 2021 07:36:42 -0700 Subject: [PATCH 01/10] added LuaIterable and LuaMultiIterable language extensions --- language-extensions/index.d.ts | 18 ++ src/lualib/Iterator.ts | 4 +- src/lualib/declarations/global.d.ts | 8 +- src/transformation/utils/diagnostics.ts | 8 + .../utils/language-extensions.ts | 4 + src/transformation/visitors/access.ts | 4 + src/transformation/visitors/call.ts | 3 + .../visitors/language-extensions/iterable.ts | 90 +++++++ src/transformation/visitors/loops/for-of.ts | 10 + .../unit/language-extensions/iterable.spec.ts | 231 ++++++++++++++++++ 10 files changed, 371 insertions(+), 9 deletions(-) create mode 100644 src/transformation/visitors/language-extensions/iterable.ts create mode 100644 test/unit/language-extensions/iterable.spec.ts diff --git a/language-extensions/index.d.ts b/language-extensions/index.d.ts index 033012960..cd37fe2ba 100644 --- a/language-extensions/index.d.ts +++ b/language-extensions/index.d.ts @@ -25,6 +25,24 @@ declare type LuaMultiReturn = T & { readonly __luaMultiReturnBr */ declare function $range(start: number, limit: number, step?: number): Iterable; +/** + * Represents a Lua-style iteratable which iterates single values in a `for...in` loop (ex. `for x in iter() do`). + * This type can only be used in a `for...of` loop or a return statement. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * + * @param T The type of value returned each iteration. + */ +declare type LuaIterable = Iterable & { readonly __luaIterableBrand: unique symbol }; + +/** + * Represents a Lua-style iteratable which iterates multiple values in a `for...in` loop (ex. `for x, y in iter() do`). + * This type can only be used in a `for...of` loop or a return statement. In a `for...of` loop it must be destructured. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * + * @param T A tuple type indicating the types of values returned each iteration. + */ +declare type LuaMultiIterable = Iterable & { readonly __luaMultiIterableBrand: unique symbol }; + /** * Calls to functions with this type are translated to `left + right`. * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions diff --git a/src/lualib/Iterator.ts b/src/lualib/Iterator.ts index 809ca69ee..5865eebac 100644 --- a/src/lualib/Iterator.ts +++ b/src/lualib/Iterator.ts @@ -27,7 +27,7 @@ function __TS__IteratorStringStep(this: string, index: number): [number, string] function __TS__Iterator( this: void, iterable: string | GeneratorIterator | Iterable | readonly T[] -): [(...args: any[]) => [any, any] | [], ...any[]] { +): [(...args: any[]) => [any, any] | [], ...any[]] | LuaMultiIterable<[number, T]> { if (typeof iterable === "string") { return [__TS__IteratorStringStep, iterable, 0]; } else if ("____coroutine" in iterable) { @@ -36,6 +36,6 @@ function __TS__Iterator( const iterator = iterable[Symbol.iterator](); return [__TS__IteratorIteratorStep, iterator]; } else { - return ipairs(iterable as readonly T[]) as any; + return ipairs(iterable as readonly T[]); } } diff --git a/src/lualib/declarations/global.d.ts b/src/lualib/declarations/global.d.ts index 450fba8dc..02ef7c839 100644 --- a/src/lualib/declarations/global.d.ts +++ b/src/lualib/declarations/global.d.ts @@ -25,10 +25,4 @@ declare function unpack(list: T[], i?: number, j?: number): T[]; declare function select(index: number, ...args: T[]): T; declare function select(index: "#", ...args: T[]): number; -/** - * @luaIterator - * @tupleReturn - */ -type LuaTupleIterator = Iterable & { " LuaTupleIterator": never }; - -declare function ipairs(t: Record): LuaTupleIterator<[number, T]>; +declare function ipairs(t: Record): LuaMultiIterable<[number, T]>; diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index 76e8b4632..c17474906 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -118,6 +118,14 @@ export const luaIteratorForbiddenUsage = createErrorDiagnosticFactory( "the '@tupleReturn' annotation." ); +export const invalidIterableUse = createErrorDiagnosticFactory( + "LuaIterable and LuaMultiIterable types can only be used in for...of loops or return statements." +); + +export const invalidMultiIterableWithoutDestructuring = createErrorDiagnosticFactory( + "LuaMultiIterable return value type must be destructured." +); + export const unsupportedAccessorInObjectLiteral = createErrorDiagnosticFactory( "Accessors in object literal are not supported." ); diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index 391993f8a..51e7c467c 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -5,6 +5,8 @@ export enum ExtensionKind { MultiFunction = "MultiFunction", MultiType = "MultiType", RangeFunction = "RangeFunction", + IterableType = "IterableType", + MultiIterableType = "MultiIterableType", AdditionOperatorType = "AdditionOperatorType", AdditionOperatorMethodType = "AdditionOperatorMethodType", SubtractionOperatorType = "SubtractionOperatorType", @@ -50,6 +52,8 @@ const functionNameToExtensionKind: { [name: string]: ExtensionKind } = { const typeNameToExtensionKind: { [name: string]: ExtensionKind } = { LuaMultiReturn: ExtensionKind.MultiType, + LuaIterable: ExtensionKind.IterableType, + LuaMultiIterable: ExtensionKind.MultiIterableType, LuaAddition: ExtensionKind.AdditionOperatorType, LuaAdditionMethod: ExtensionKind.AdditionOperatorMethodType, LuaSubtraction: ExtensionKind.SubtractionOperatorType, diff --git a/src/transformation/visitors/access.ts b/src/transformation/visitors/access.ts index d33c5c5c6..3dc017ac0 100644 --- a/src/transformation/visitors/access.ts +++ b/src/transformation/visitors/access.ts @@ -8,6 +8,7 @@ import { addToNumericExpression } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; import { isArrayType, isNumberType, isStringType } from "../utils/typescript"; import { tryGetConstEnumValue } from "./enum"; +import { validateIterableTypeUse } from "./language-extensions/iterable"; import { returnsMultiType } from "./language-extensions/multi"; import { transformLuaTablePropertyAccessExpression, validateLuaTableElementAccessExpression } from "./lua-table"; @@ -28,6 +29,7 @@ export function transformElementAccessArgument( export const transformElementAccessExpression: FunctionVisitor = (node, context) => { validateLuaTableElementAccessExpression(context, node); + validateIterableTypeUse(context, node); const constEnumValue = tryGetConstEnumValue(context, node); if (constEnumValue) { @@ -63,6 +65,8 @@ export const transformPropertyAccessExpression: FunctionVisitor { + validateIterableTypeUse(context, expression); + if (ts.isOptionalChain(expression)) { context.diagnostics.push(optionalChainingNotSupported(expression)); } diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index f29cbd4e9..8b1828b68 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -13,6 +13,7 @@ import { transformElementAccessArgument } from "./access"; import { transformLuaTableCallExpression } from "./lua-table"; import { shouldMultiReturnCallBeWrapped, returnsMultiType } from "./language-extensions/multi"; import { isOperatorMapping, transformOperatorMappingExpression } from "./language-extensions/operators"; +import { validateIterableTypeUse } from "./language-extensions/iterable"; export type PropertyCallExpression = ts.CallExpression & { expression: ts.PropertyAccessExpression }; @@ -214,6 +215,8 @@ export const transformCallExpression: FunctionVisitor = (node return transformOperatorMappingExpression(context, node); } + validateIterableTypeUse(context, node); + if (ts.isPropertyAccessExpression(node.expression)) { const result = transformPropertyCall(context, node as PropertyCallExpression); return wrapResult ? wrapInTable(result) : result; diff --git a/src/transformation/visitors/language-extensions/iterable.ts b/src/transformation/visitors/language-extensions/iterable.ts new file mode 100644 index 000000000..9d1b5768a --- /dev/null +++ b/src/transformation/visitors/language-extensions/iterable.ts @@ -0,0 +1,90 @@ +import * as ts from "typescript"; +import * as lua from "../../../LuaAST"; +import * as extensions from "../../utils/language-extensions"; +import { TransformationContext } from "../../context"; +import { getVariableDeclarationBinding, transformForInitializer } from "../loops/utils"; +import { transformArrayBindingElement } from "../variable-declaration"; +import { invalidIterableUse, invalidMultiIterableWithoutDestructuring } from "../../utils/diagnostics"; +import { cast } from "../../../utils"; + +const isIterableTypeDeclaration = (declaration: ts.Declaration): boolean => + extensions.getExtensionKind(declaration) === extensions.ExtensionKind.IterableType; + +const isMultiIterableTypeDeclaration = (declaration: ts.Declaration): boolean => + extensions.getExtensionKind(declaration) === extensions.ExtensionKind.MultiIterableType; + +export function isIterableExpression(context: TransformationContext, expression: ts.Expression): boolean { + const type = context.checker.getTypeAtLocation(expression); + return type.aliasSymbol?.declarations?.some(isIterableTypeDeclaration) ?? false; +} + +export function isMultiIterableExpression(context: TransformationContext, expression: ts.Expression): boolean { + const type = context.checker.getTypeAtLocation(expression); + return type.aliasSymbol?.declarations?.some(isMultiIterableTypeDeclaration) ?? false; +} + +export function transformForOfIterableStatement( + context: TransformationContext, + statement: ts.ForOfStatement, + block: lua.Block +): lua.Statement { + const luaIterator = context.transformExpression(statement.expression); + const identifier = transformForInitializer(context, statement.initializer, block); + return lua.createForInStatement(block, [identifier], [luaIterator], statement); +} + +export function transformForOfMultiIterableStatement( + context: TransformationContext, + statement: ts.ForOfStatement, + block: lua.Block +): lua.Statement { + const luaIterator = context.transformExpression(statement.expression); + let identifiers: lua.Identifier[] = []; + + if (ts.isVariableDeclarationList(statement.initializer)) { + // Variables declared in for loop + // for ${initializer} in ${iterable} do + const binding = getVariableDeclarationBinding(context, statement.initializer); + if (ts.isArrayBindingPattern(binding)) { + identifiers = binding.elements.map(e => transformArrayBindingElement(context, e)); + } else { + context.diagnostics.push(invalidMultiIterableWithoutDestructuring(binding)); + } + } else if (ts.isArrayLiteralExpression(statement.initializer)) { + // Variables NOT declared in for loop - catch iterator values in temps and assign + // for ____value0 in ${iterable} do + // ${initializer} = ____value0 + identifiers = statement.initializer.elements.map((_, i) => lua.createIdentifier(`____value${i}`)); + if (identifiers.length > 0) { + block.statements.unshift( + lua.createAssignmentStatement( + statement.initializer.elements.map(e => + cast(context.transformExpression(e), lua.isAssignmentLeftHandSideExpression) + ), + identifiers + ) + ); + } + } else { + context.diagnostics.push(invalidMultiIterableWithoutDestructuring(statement.initializer)); + } + + if (identifiers.length === 0) { + identifiers.push(lua.createAnonymousIdentifier()); + } + + return lua.createForInStatement(block, identifiers, [luaIterator], statement); +} + +export function validateIterableTypeUse(context: TransformationContext, node: ts.Expression) { + if (!isIterableExpression(context, node) && !isMultiIterableExpression(context, node)) { + return; + } + if (ts.isForOfStatement(node.parent)) { + return; + } + if (ts.isReturnStatement(node.parent) || ts.isArrowFunction(node.parent)) { + return; + } + context.diagnostics.push(invalidIterableUse(node)); +} diff --git a/src/transformation/visitors/loops/for-of.ts b/src/transformation/visitors/loops/for-of.ts index 047ed3535..1f19faedc 100644 --- a/src/transformation/visitors/loops/for-of.ts +++ b/src/transformation/visitors/loops/for-of.ts @@ -8,6 +8,12 @@ import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; import { isArrayType, isNumberType } from "../../utils/typescript"; import { transformArguments } from "../call"; import { transformIdentifier } from "../identifier"; +import { + isIterableExpression, + isMultiIterableExpression, + transformForOfIterableStatement, + transformForOfMultiIterableStatement, +} from "../language-extensions/iterable"; import { isRangeFunction, transformRangeStatement } from "../language-extensions/range"; import { transformArrayBindingElement } from "../variable-declaration"; import { getVariableDeclarationBinding, transformForInitializer, transformLoopBody } from "./utils"; @@ -156,6 +162,10 @@ export const transformForOfStatement: FunctionVisitor = (node return transformRangeStatement(context, node, body); } else if (ts.isCallExpression(node.expression) && isForRangeType(context, node.expression.expression)) { return transformForRangeStatement(context, node, body); + } else if (isIterableExpression(context, node.expression)) { + return transformForOfIterableStatement(context, node, body); + } else if (isMultiIterableExpression(context, node.expression)) { + return transformForOfMultiIterableStatement(context, node, body); } else if (isLuaIteratorType(context, node.expression)) { return transformForOfLuaIteratorStatement(context, node, body); } else if (isArrayType(context, context.checker.getTypeAtLocation(node.expression))) { diff --git a/test/unit/language-extensions/iterable.spec.ts b/test/unit/language-extensions/iterable.spec.ts new file mode 100644 index 000000000..0a983b3e8 --- /dev/null +++ b/test/unit/language-extensions/iterable.spec.ts @@ -0,0 +1,231 @@ +import * as path from "path"; +import * as util from "../../util"; +import * as tstl from "../../../src"; +import { + invalidIterableUse, + invalidMultiIterableWithoutDestructuring, +} from "../../../src/transformation/utils/diagnostics"; + +const iterableProjectOptions: tstl.CompilerOptions = { + types: [path.resolve(__dirname, "../../../language-extensions")], +}; + +const testIterable = ` +function testIterable(): LuaIterable { + const strs = ["a", "b", "c"]; + let i = 0; + function iterator() { + return strs[i++]; + } + return iterator as any; +} +`; + +const testArrayIterable = ` +function testArrayIterable(): LuaIterable { + const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + let i = 0; + function iterator() { + return strs[i++]; + } + return iterator as any; +} +`; + +const testMultiIterable = ` +function testMultiIterable(): LuaMultiIterable<[string, string]> { + const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + let i = 0; + function iterator() { + const j = i++; + if (strs[j]) { + return $multi(...strs[j]); + } + } + return iterator as any; +} +`; + +const testIterableProperty = ` +class IterablePropertyTest { + public get testIterable(): LuaIterable { + const strs = ["a", "b", "c"]; + let i = 0; + function iterator() { + return strs[i++]; + } + return iterator as any; + } +} +const tester = new IterablePropertyTest(); +`; + +test.each(["const s", "let s"])("LuaIterable basic use", initializer => { + util.testFunction` + ${testIterable} + const results: string[] = []; + for (${initializer} of testIterable()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a", "b", "c"]); +}); + +test("LuaIterable with external control variable", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + let s: string; + for (s of testIterable()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a", "b", "c"]); +}); + +test.each(["const [x, y]", "let [x, y]"])("LuaIterable array destructuring", initializer => { + util.testFunction` + ${testArrayIterable} + const results: string[] = []; + for (${initializer} of testArrayIterable()) { + results.push(x); + results.push(y); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); +}); + +test("LuaIterable array destructuring with external control variable", () => { + util.testFunction` + ${testArrayIterable} + const results: string[] = []; + let x: string, y: string; + for ([x, y] of testArrayIterable()) { + results.push(x); + results.push(y); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); +}); + +test.each(["const [x, y]", "let [x, y]"])("LuaMultiIterable basic use", initializer => { + util.testFunction` + ${testMultiIterable} + const results: string[] = []; + for (${initializer} of testMultiIterable()) { + results.push(x); + results.push(y); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); +}); + +test("LuaMultiIterable with external control variables", () => { + util.testFunction` + ${testMultiIterable} + const results: string[] = []; + let x: string, y: string; + for ([x, y] of testMultiIterable()) { + results.push(x); + results.push(y); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); +}); + +test.each([".testIterable", '["testIterable"]'])("LuaIterable property", access => { + util.testFunction` + ${testIterableProperty} + const results: string[] = []; + for (const s of tester${access}) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a", "b", "c"]); +}); + +function makeForwardTests(call: string, code: string) { + return [`${code} function forward() { return ${call}; }`, `${code} const forward = () => ${call};`]; +} + +test.each( + [ + ["testIterable()", testIterable], + ["tester.testIterable", testIterableProperty], + ].map(([call, code]) => makeForwardTests(call, code)) +)("LuaIterable return forward", forwardFunction => { + util.testFunction` + ${forwardFunction} + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a", "b", "c"]); +}); + +test.each(makeForwardTests("testMultiIterable()", testMultiIterable))( + "LuaMultiIterable return forward", + forwardFunction => { + util.testFunction` + ${forwardFunction} + const results: string[] = []; + for (const [x, y] of forward()) { + results.push(x); + results.push(y); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); + } +); + +test.each( + [ + ["testIterable()", testIterable], + ["testMultiIterable()", testMultiIterable], + ["tester.testIterable", testIterableProperty], + ].flatMap( + ([call, code]): Array<[string, string]> => [ + [`for (const s in ${call}) {}`, code], + [`const i = ${call};`, code], + [`function foo(i: any) {} foo(${call});`, code], + ] + ) +)("invalid use of LuaIterable (%p)", (statement, code) => { + util.testFunction` + ${code} + ${statement} + ` + .setOptions(iterableProjectOptions) + .expectToHaveDiagnostics([invalidIterableUse.code]); +}); + +test.each(["for (const s of testMultiIterable()) {}", "let s; for (s of testMultiIterable()) {}"])( + "invalid LuaMultiIterable without destructuring (%p)", + statement => { + util.testFunction` + ${testMultiIterable} + ${statement} + ` + .setOptions(iterableProjectOptions) + .expectToHaveDiagnostics([invalidMultiIterableWithoutDestructuring.code]); + } +); From 7fdc39a437927cd293548187b902c36da75666cd Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 11 Feb 2021 18:49:56 -0700 Subject: [PATCH 02/10] indentation fix --- .../unit/language-extensions/iterable.spec.ts | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/test/unit/language-extensions/iterable.spec.ts b/test/unit/language-extensions/iterable.spec.ts index 0a983b3e8..bbf22c2b1 100644 --- a/test/unit/language-extensions/iterable.spec.ts +++ b/test/unit/language-extensions/iterable.spec.ts @@ -12,50 +12,50 @@ const iterableProjectOptions: tstl.CompilerOptions = { const testIterable = ` function testIterable(): LuaIterable { - const strs = ["a", "b", "c"]; - let i = 0; - function iterator() { - return strs[i++]; - } - return iterator as any; + const strs = ["a", "b", "c"]; + let i = 0; + function iterator() { + return strs[i++]; + } + return iterator as any; } `; const testArrayIterable = ` function testArrayIterable(): LuaIterable { - const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; - let i = 0; - function iterator() { - return strs[i++]; - } - return iterator as any; + const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + let i = 0; + function iterator() { + return strs[i++]; + } + return iterator as any; } `; const testMultiIterable = ` function testMultiIterable(): LuaMultiIterable<[string, string]> { - const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; - let i = 0; - function iterator() { - const j = i++; - if (strs[j]) { - return $multi(...strs[j]); - } - } - return iterator as any; + const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + let i = 0; + function iterator() { + const j = i++; + if (strs[j]) { + return $multi(...strs[j]); + } + } + return iterator as any; } `; const testIterableProperty = ` class IterablePropertyTest { - public get testIterable(): LuaIterable { - const strs = ["a", "b", "c"]; - let i = 0; - function iterator() { - return strs[i++]; - } - return iterator as any; - } + public get testIterable(): LuaIterable { + const strs = ["a", "b", "c"]; + let i = 0; + function iterator() { + return strs[i++]; + } + return iterator as any; + } } const tester = new IterablePropertyTest(); `; From 33a5af7fc0028ff3295e141d4d83cf44acc32dbf Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 13 Feb 2021 07:02:15 -0700 Subject: [PATCH 03/10] remove LuaMultiIterable in favor of LuaIterable --- language-extensions/index.d.ts | 11 +----- src/lualib/Iterator.ts | 2 +- src/lualib/declarations/global.d.ts | 2 +- src/transformation/utils/diagnostics.ts | 4 +- .../utils/language-extensions.ts | 2 - .../visitors/language-extensions/iterable.ts | 38 +++++++++---------- src/transformation/visitors/loops/for-of.ts | 9 +---- .../unit/language-extensions/iterable.spec.ts | 10 ++--- 8 files changed, 29 insertions(+), 49 deletions(-) diff --git a/language-extensions/index.d.ts b/language-extensions/index.d.ts index cd37fe2ba..da02bb153 100644 --- a/language-extensions/index.d.ts +++ b/language-extensions/index.d.ts @@ -30,19 +30,10 @@ declare function $range(start: number, limit: number, step?: number): Iterable = Iterable & { readonly __luaIterableBrand: unique symbol }; -/** - * Represents a Lua-style iteratable which iterates multiple values in a `for...in` loop (ex. `for x, y in iter() do`). - * This type can only be used in a `for...of` loop or a return statement. In a `for...of` loop it must be destructured. - * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions - * - * @param T A tuple type indicating the types of values returned each iteration. - */ -declare type LuaMultiIterable = Iterable & { readonly __luaMultiIterableBrand: unique symbol }; - /** * Calls to functions with this type are translated to `left + right`. * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions diff --git a/src/lualib/Iterator.ts b/src/lualib/Iterator.ts index 5865eebac..d6112cd88 100644 --- a/src/lualib/Iterator.ts +++ b/src/lualib/Iterator.ts @@ -27,7 +27,7 @@ function __TS__IteratorStringStep(this: string, index: number): [number, string] function __TS__Iterator( this: void, iterable: string | GeneratorIterator | Iterable | readonly T[] -): [(...args: any[]) => [any, any] | [], ...any[]] | LuaMultiIterable<[number, T]> { +): [(...args: any[]) => [any, any] | [], ...any[]] | LuaIterable> { if (typeof iterable === "string") { return [__TS__IteratorStringStep, iterable, 0]; } else if ("____coroutine" in iterable) { diff --git a/src/lualib/declarations/global.d.ts b/src/lualib/declarations/global.d.ts index 02ef7c839..ee133ca08 100644 --- a/src/lualib/declarations/global.d.ts +++ b/src/lualib/declarations/global.d.ts @@ -25,4 +25,4 @@ declare function unpack(list: T[], i?: number, j?: number): T[]; declare function select(index: number, ...args: T[]): T; declare function select(index: "#", ...args: T[]): number; -declare function ipairs(t: Record): LuaMultiIterable<[number, T]>; +declare function ipairs(t: Record): LuaIterable>; diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index c17474906..861fd08b2 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -119,11 +119,11 @@ export const luaIteratorForbiddenUsage = createErrorDiagnosticFactory( ); export const invalidIterableUse = createErrorDiagnosticFactory( - "LuaIterable and LuaMultiIterable types can only be used in for...of loops or return statements." + "LuaIterable type can only be used in for...of loops or return statements." ); export const invalidMultiIterableWithoutDestructuring = createErrorDiagnosticFactory( - "LuaMultiIterable return value type must be destructured." + "LuaIterable with a LuaMultiReturn return value type must be destructured." ); export const unsupportedAccessorInObjectLiteral = createErrorDiagnosticFactory( diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index 51e7c467c..4c46d92d1 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -6,7 +6,6 @@ export enum ExtensionKind { MultiType = "MultiType", RangeFunction = "RangeFunction", IterableType = "IterableType", - MultiIterableType = "MultiIterableType", AdditionOperatorType = "AdditionOperatorType", AdditionOperatorMethodType = "AdditionOperatorMethodType", SubtractionOperatorType = "SubtractionOperatorType", @@ -53,7 +52,6 @@ const functionNameToExtensionKind: { [name: string]: ExtensionKind } = { const typeNameToExtensionKind: { [name: string]: ExtensionKind } = { LuaMultiReturn: ExtensionKind.MultiType, LuaIterable: ExtensionKind.IterableType, - LuaMultiIterable: ExtensionKind.MultiIterableType, LuaAddition: ExtensionKind.AdditionOperatorType, LuaAdditionMethod: ExtensionKind.AdditionOperatorMethodType, LuaSubtraction: ExtensionKind.SubtractionOperatorType, diff --git a/src/transformation/visitors/language-extensions/iterable.ts b/src/transformation/visitors/language-extensions/iterable.ts index 9d1b5768a..da85bcff2 100644 --- a/src/transformation/visitors/language-extensions/iterable.ts +++ b/src/transformation/visitors/language-extensions/iterable.ts @@ -6,34 +6,17 @@ import { getVariableDeclarationBinding, transformForInitializer } from "../loops import { transformArrayBindingElement } from "../variable-declaration"; import { invalidIterableUse, invalidMultiIterableWithoutDestructuring } from "../../utils/diagnostics"; import { cast } from "../../../utils"; +import { isMultiReturnType } from "./multi"; const isIterableTypeDeclaration = (declaration: ts.Declaration): boolean => extensions.getExtensionKind(declaration) === extensions.ExtensionKind.IterableType; -const isMultiIterableTypeDeclaration = (declaration: ts.Declaration): boolean => - extensions.getExtensionKind(declaration) === extensions.ExtensionKind.MultiIterableType; - export function isIterableExpression(context: TransformationContext, expression: ts.Expression): boolean { const type = context.checker.getTypeAtLocation(expression); return type.aliasSymbol?.declarations?.some(isIterableTypeDeclaration) ?? false; } -export function isMultiIterableExpression(context: TransformationContext, expression: ts.Expression): boolean { - const type = context.checker.getTypeAtLocation(expression); - return type.aliasSymbol?.declarations?.some(isMultiIterableTypeDeclaration) ?? false; -} - -export function transformForOfIterableStatement( - context: TransformationContext, - statement: ts.ForOfStatement, - block: lua.Block -): lua.Statement { - const luaIterator = context.transformExpression(statement.expression); - const identifier = transformForInitializer(context, statement.initializer, block); - return lua.createForInStatement(block, [identifier], [luaIterator], statement); -} - -export function transformForOfMultiIterableStatement( +function transformForOfMultiIterableStatement( context: TransformationContext, statement: ts.ForOfStatement, block: lua.Block @@ -76,8 +59,23 @@ export function transformForOfMultiIterableStatement( return lua.createForInStatement(block, identifiers, [luaIterator], statement); } +export function transformForOfIterableStatement( + context: TransformationContext, + statement: ts.ForOfStatement, + block: lua.Block +): lua.Statement { + const type = context.checker.getTypeAtLocation(statement.expression); + if (type.aliasTypeArguments?.length === 1 && isMultiReturnType(type.aliasTypeArguments[0])) { + return transformForOfMultiIterableStatement(context, statement, block); + } + + const luaIterator = context.transformExpression(statement.expression); + const identifier = transformForInitializer(context, statement.initializer, block); + return lua.createForInStatement(block, [identifier], [luaIterator], statement); +} + export function validateIterableTypeUse(context: TransformationContext, node: ts.Expression) { - if (!isIterableExpression(context, node) && !isMultiIterableExpression(context, node)) { + if (!isIterableExpression(context, node)) { return; } if (ts.isForOfStatement(node.parent)) { diff --git a/src/transformation/visitors/loops/for-of.ts b/src/transformation/visitors/loops/for-of.ts index 1f19faedc..e8241f40f 100644 --- a/src/transformation/visitors/loops/for-of.ts +++ b/src/transformation/visitors/loops/for-of.ts @@ -8,12 +8,7 @@ import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; import { isArrayType, isNumberType } from "../../utils/typescript"; import { transformArguments } from "../call"; import { transformIdentifier } from "../identifier"; -import { - isIterableExpression, - isMultiIterableExpression, - transformForOfIterableStatement, - transformForOfMultiIterableStatement, -} from "../language-extensions/iterable"; +import { isIterableExpression, transformForOfIterableStatement } from "../language-extensions/iterable"; import { isRangeFunction, transformRangeStatement } from "../language-extensions/range"; import { transformArrayBindingElement } from "../variable-declaration"; import { getVariableDeclarationBinding, transformForInitializer, transformLoopBody } from "./utils"; @@ -164,8 +159,6 @@ export const transformForOfStatement: FunctionVisitor = (node return transformForRangeStatement(context, node, body); } else if (isIterableExpression(context, node.expression)) { return transformForOfIterableStatement(context, node, body); - } else if (isMultiIterableExpression(context, node.expression)) { - return transformForOfMultiIterableStatement(context, node, body); } else if (isLuaIteratorType(context, node.expression)) { return transformForOfLuaIteratorStatement(context, node, body); } else if (isArrayType(context, context.checker.getTypeAtLocation(node.expression))) { diff --git a/test/unit/language-extensions/iterable.spec.ts b/test/unit/language-extensions/iterable.spec.ts index bbf22c2b1..3b55c7e55 100644 --- a/test/unit/language-extensions/iterable.spec.ts +++ b/test/unit/language-extensions/iterable.spec.ts @@ -33,7 +33,7 @@ function testArrayIterable(): LuaIterable { `; const testMultiIterable = ` -function testMultiIterable(): LuaMultiIterable<[string, string]> { +function testMultiIterable(): LuaIterable> { const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; let i = 0; function iterator() { @@ -116,7 +116,7 @@ test("LuaIterable array destructuring with external control variable", () => { .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); }); -test.each(["const [x, y]", "let [x, y]"])("LuaMultiIterable basic use", initializer => { +test.each(["const [x, y]", "let [x, y]"])("LuaIterable basic use", initializer => { util.testFunction` ${testMultiIterable} const results: string[] = []; @@ -130,7 +130,7 @@ test.each(["const [x, y]", "let [x, y]"])("LuaMultiIterable basic use", initiali .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); }); -test("LuaMultiIterable with external control variables", () => { +test("LuaIterable with external control variables", () => { util.testFunction` ${testMultiIterable} const results: string[] = []; @@ -181,7 +181,7 @@ test.each( }); test.each(makeForwardTests("testMultiIterable()", testMultiIterable))( - "LuaMultiIterable return forward", + "LuaIterable return forward", forwardFunction => { util.testFunction` ${forwardFunction} @@ -219,7 +219,7 @@ test.each( }); test.each(["for (const s of testMultiIterable()) {}", "let s; for (s of testMultiIterable()) {}"])( - "invalid LuaMultiIterable without destructuring (%p)", + "invalid LuaIterable without destructuring (%p)", statement => { util.testFunction` ${testMultiIterable} From f81e97b04c395b0674d0f8fb4f504c58b3681e58 Mon Sep 17 00:00:00 2001 From: Tom Date: Sat, 13 Feb 2021 07:08:45 -0700 Subject: [PATCH 04/10] switched from expectToHaveDiagnostics to expectDiagnosticsToMatchSnapshot --- .../__snapshots__/iterable.spec.ts.snap | 319 ++++++++++++++++++ .../unit/language-extensions/iterable.spec.ts | 4 +- 2 files changed, 321 insertions(+), 2 deletions(-) create mode 100644 test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap diff --git a/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap b/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap new file mode 100644 index 000000000..78c10be1b --- /dev/null +++ b/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap @@ -0,0 +1,319 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`invalid LuaIterable without destructuring ("for (const s of testMultiIterable()) {}"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function testMultiIterable(self) + local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local i = 0 + local function iterator(self) + local j = (function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + if strs[j + 1] then + return table.unpack(strs[j + 1]) + end + end + return iterator + end + for ____ in testMultiIterable(nil) do + end +end +return ____exports" +`; + +exports[`invalid LuaIterable without destructuring ("for (const s of testMultiIterable()) {}"): diagnostics 1`] = `"main.ts(15,20): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; + +exports[`invalid LuaIterable without destructuring ("let s; for (s of testMultiIterable()) {}"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function testMultiIterable(self) + local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local i = 0 + local function iterator(self) + local j = (function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + if strs[j + 1] then + return table.unpack(strs[j + 1]) + end + end + return iterator + end + local s + for ____ in testMultiIterable(nil) do + end +end +return ____exports" +`; + +exports[`invalid LuaIterable without destructuring ("let s; for (s of testMultiIterable()) {}"): diagnostics 1`] = `"main.ts(15,21): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; + +exports[`invalid use of LuaIterable ("const i = testIterable();"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function testIterable(self) + local strs = {\\"a\\", \\"b\\", \\"c\\"} + local i = 0 + local function iterator(self) + return strs[(function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + 1] + end + return iterator + end + local i = testIterable(nil) +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("const i = testIterable();"): diagnostics 1`] = `"main.ts(12,19): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; + +exports[`invalid use of LuaIterable ("const i = testMultiIterable();"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function testMultiIterable(self) + local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local i = 0 + local function iterator(self) + local j = (function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + if strs[j + 1] then + return table.unpack(strs[j + 1]) + end + end + return iterator + end + local i = testMultiIterable(nil) +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("const i = testMultiIterable();"): diagnostics 1`] = `"main.ts(15,19): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; + +exports[`invalid use of LuaIterable ("const i = tester.testIterable;"): code 1`] = ` +"require(\\"lualib_bundle\\"); +local ____exports = {} +function ____exports.__main(self) + local IterablePropertyTest = __TS__Class() + IterablePropertyTest.name = \\"IterablePropertyTest\\" + function IterablePropertyTest.prototype.____constructor(self) + end + __TS__SetDescriptor( + IterablePropertyTest.prototype, + \\"testIterable\\", + { + get = function(self) + local strs = {\\"a\\", \\"b\\", \\"c\\"} + local i = 0 + local function iterator(self) + return strs[(function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + 1] + end + return iterator + end + }, + true + ) + local tester = __TS__New(IterablePropertyTest) + local i = tester.testIterable +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("const i = tester.testIterable;"): diagnostics 1`] = `"main.ts(15,19): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; + +exports[`invalid use of LuaIterable ("for (const s in testIterable()) {}"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function testIterable(self) + local strs = {\\"a\\", \\"b\\", \\"c\\"} + local i = 0 + local function iterator(self) + return strs[(function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + 1] + end + return iterator + end + for s in pairs( + testIterable(nil) + ) do + end +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("for (const s in testIterable()) {}"): diagnostics 1`] = `"main.ts(12,25): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; + +exports[`invalid use of LuaIterable ("for (const s in testMultiIterable()) {}"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function testMultiIterable(self) + local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local i = 0 + local function iterator(self) + local j = (function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + if strs[j + 1] then + return table.unpack(strs[j + 1]) + end + end + return iterator + end + for s in pairs( + testMultiIterable(nil) + ) do + end +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("for (const s in testMultiIterable()) {}"): diagnostics 1`] = `"main.ts(15,25): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; + +exports[`invalid use of LuaIterable ("for (const s in tester.testIterable) {}"): code 1`] = ` +"require(\\"lualib_bundle\\"); +local ____exports = {} +function ____exports.__main(self) + local IterablePropertyTest = __TS__Class() + IterablePropertyTest.name = \\"IterablePropertyTest\\" + function IterablePropertyTest.prototype.____constructor(self) + end + __TS__SetDescriptor( + IterablePropertyTest.prototype, + \\"testIterable\\", + { + get = function(self) + local strs = {\\"a\\", \\"b\\", \\"c\\"} + local i = 0 + local function iterator(self) + return strs[(function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + 1] + end + return iterator + end + }, + true + ) + local tester = __TS__New(IterablePropertyTest) + for s in pairs(tester.testIterable) do + end +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("for (const s in tester.testIterable) {}"): diagnostics 1`] = `"main.ts(15,25): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; + +exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(testIterable());"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function testIterable(self) + local strs = {\\"a\\", \\"b\\", \\"c\\"} + local i = 0 + local function iterator(self) + return strs[(function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + 1] + end + return iterator + end + local function foo(self, i) + end + foo( + nil, + testIterable(nil) + ) +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(testIterable());"): diagnostics 1`] = `"main.ts(12,37): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; + +exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(testMultiIterable());"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function testMultiIterable(self) + local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local i = 0 + local function iterator(self) + local j = (function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + if strs[j + 1] then + return table.unpack(strs[j + 1]) + end + end + return iterator + end + local function foo(self, i) + end + foo( + nil, + testMultiIterable(nil) + ) +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(testMultiIterable());"): diagnostics 1`] = `"main.ts(15,37): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; + +exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(tester.testIterable);"): code 1`] = ` +"require(\\"lualib_bundle\\"); +local ____exports = {} +function ____exports.__main(self) + local IterablePropertyTest = __TS__Class() + IterablePropertyTest.name = \\"IterablePropertyTest\\" + function IterablePropertyTest.prototype.____constructor(self) + end + __TS__SetDescriptor( + IterablePropertyTest.prototype, + \\"testIterable\\", + { + get = function(self) + local strs = {\\"a\\", \\"b\\", \\"c\\"} + local i = 0 + local function iterator(self) + return strs[(function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + 1] + end + return iterator + end + }, + true + ) + local tester = __TS__New(IterablePropertyTest) + local function foo(self, i) + end + foo(nil, tester.testIterable) +end +return ____exports" +`; + +exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(tester.testIterable);"): diagnostics 1`] = `"main.ts(15,37): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; diff --git a/test/unit/language-extensions/iterable.spec.ts b/test/unit/language-extensions/iterable.spec.ts index 3b55c7e55..07e0adb5f 100644 --- a/test/unit/language-extensions/iterable.spec.ts +++ b/test/unit/language-extensions/iterable.spec.ts @@ -215,7 +215,7 @@ test.each( ${statement} ` .setOptions(iterableProjectOptions) - .expectToHaveDiagnostics([invalidIterableUse.code]); + .expectDiagnosticsToMatchSnapshot([invalidIterableUse.code]); }); test.each(["for (const s of testMultiIterable()) {}", "let s; for (s of testMultiIterable()) {}"])( @@ -226,6 +226,6 @@ test.each(["for (const s of testMultiIterable()) {}", "let s; for (s of testMult ${statement} ` .setOptions(iterableProjectOptions) - .expectToHaveDiagnostics([invalidMultiIterableWithoutDestructuring.code]); + .expectDiagnosticsToMatchSnapshot([invalidMultiIterableWithoutDestructuring.code]); } ); From 1d0f94461a62c5b1279e52bba0b87f8082a13ba7 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Feb 2021 08:09:03 -0700 Subject: [PATCH 05/10] updates based on feedback and discussions - LuaIterable type reworked for better lua compatibility - language extensions now checked by brand instead of type alias name - fixed LuaMultiReturn indirect forward, which also affected LuaIterable - reorganized tests and added some for manual iterable usage --- language-extensions/index.d.ts | 32 +- src/lualib/declarations/global.d.ts | 2 +- src/transformation/utils/diagnostics.ts | 4 - .../utils/language-extensions.ts | 123 ++-- src/transformation/visitors/access.ts | 4 - src/transformation/visitors/call.ts | 3 - .../visitors/language-extensions/iterable.ts | 30 +- .../visitors/language-extensions/multi.ts | 26 +- .../visitors/language-extensions/operators.ts | 21 +- .../visitors/language-extensions/range.ts | 8 +- src/transformation/visitors/return.ts | 7 +- .../__snapshots__/iterable.spec.ts.snap | 321 +---------- .../unit/language-extensions/iterable.spec.ts | 523 +++++++++++------- test/unit/language-extensions/multi.spec.ts | 22 + 14 files changed, 487 insertions(+), 639 deletions(-) diff --git a/language-extensions/index.d.ts b/language-extensions/index.d.ts index da02bb153..6208b776e 100644 --- a/language-extensions/index.d.ts +++ b/language-extensions/index.d.ts @@ -5,7 +5,9 @@ * @param T A tuple type with each element type representing a return value's type. * @param values Return values. */ -declare function $multi(...values: T): LuaMultiReturn; +declare const $multi: ((...values: T) => LuaMultiReturn) & { + readonly __luaMultiFunctionBrand: unique symbol; +}; /** * Represents multiple return values as a tuple. @@ -23,16 +25,36 @@ declare type LuaMultiReturn = T & { readonly __luaMultiReturnBr * @param limit The last number in the sequence to iterate over. * @param step The amount to increment each iteration. */ -declare function $range(start: number, limit: number, step?: number): Iterable; +declare const $range: ((start: number, limit: number, step?: number) => Iterable) & { + readonly __luaRangeFunctionBrand: unique symbol; +}; + +/** + * Represents a Lua-style iterator function which is returned from a LuaIterable. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * + * @param state The state object returned from the LuaIterable. + * @param lastValue The last value returned from this function. If iterating LuaMultiReturn values, this is the first value of the tuple. + */ +declare type LuaIterator = TState extends undefined + ? (this: void) => TValue + : ( + this: void, + state: TState, + lastValue: TValue extends LuaMultiReturn ? TTuple[0] : TValue + ) => TValue; /** * Represents a Lua-style iteratable which iterates single values in a `for...in` loop (ex. `for x in iter() do`). - * This type can only be used in a `for...of` loop or a return statement. * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions * - * @param T The type of value returned each iteration. If this is LuaMultiReturn, multiple values will be returned each iteration. + * @param TValue The type of value returned each iteration. If this is LuaMultiReturn, multiple values will be returned each iteration. + * @param TState The type of the state value passed back to the iterator function each iteration. */ -declare type LuaIterable = Iterable & { readonly __luaIterableBrand: unique symbol }; +declare type LuaIterable = Iterable & + LuaMultiReturn< + [LuaIterator, TState, TValue extends LuaMultiReturn ? TTuple[0] : TValue] + > & { readonly __luaIterableBrand: unique symbol }; /** * Calls to functions with this type are translated to `left + right`. diff --git a/src/lualib/declarations/global.d.ts b/src/lualib/declarations/global.d.ts index ee133ca08..117438817 100644 --- a/src/lualib/declarations/global.d.ts +++ b/src/lualib/declarations/global.d.ts @@ -25,4 +25,4 @@ declare function unpack(list: T[], i?: number, j?: number): T[]; declare function select(index: number, ...args: T[]): T; declare function select(index: "#", ...args: T[]): number; -declare function ipairs(t: Record): LuaIterable>; +declare function ipairs(t: Record): LuaIterable, Record>; diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index 861fd08b2..e8873746a 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -118,10 +118,6 @@ export const luaIteratorForbiddenUsage = createErrorDiagnosticFactory( "the '@tupleReturn' annotation." ); -export const invalidIterableUse = createErrorDiagnosticFactory( - "LuaIterable type can only be used in for...of loops or return statements." -); - export const invalidMultiIterableWithoutDestructuring = createErrorDiagnosticFactory( "LuaIterable with a LuaMultiReturn return value type must be destructured." ); diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index 4c46d92d1..efcd7d810 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -1,5 +1,5 @@ import * as ts from "typescript"; -import * as path from "path"; +import { TransformationContext } from "../context"; export enum ExtensionKind { MultiFunction = "MultiFunction", @@ -44,75 +44,66 @@ export enum ExtensionKind { LengthOperatorMethodType = "LengthOperatorMethodType", } -const functionNameToExtensionKind: { [name: string]: ExtensionKind } = { - $multi: ExtensionKind.MultiFunction, - $range: ExtensionKind.RangeFunction, +const extensionKindToFunctionName: { [T in ExtensionKind]?: string } = { + [ExtensionKind.MultiFunction]: "$multi", + [ExtensionKind.RangeFunction]: "$range", }; -const typeNameToExtensionKind: { [name: string]: ExtensionKind } = { - LuaMultiReturn: ExtensionKind.MultiType, - LuaIterable: ExtensionKind.IterableType, - LuaAddition: ExtensionKind.AdditionOperatorType, - LuaAdditionMethod: ExtensionKind.AdditionOperatorMethodType, - LuaSubtraction: ExtensionKind.SubtractionOperatorType, - LuaSubtractionMethod: ExtensionKind.SubtractionOperatorMethodType, - LuaMultiplication: ExtensionKind.MultiplicationOperatorType, - LuaMultiplicationMethod: ExtensionKind.MultiplicationOperatorMethodType, - LuaDivision: ExtensionKind.DivisionOperatorType, - LuaDivisionMethod: ExtensionKind.DivisionOperatorMethodType, - LuaModulo: ExtensionKind.ModuloOperatorType, - LuaModuloMethod: ExtensionKind.ModuloOperatorMethodType, - LuaPower: ExtensionKind.PowerOperatorType, - LuaPowerMethod: ExtensionKind.PowerOperatorMethodType, - LuaFloorDivision: ExtensionKind.FloorDivisionOperatorType, - LuaFloorDivisionMethod: ExtensionKind.FloorDivisionOperatorMethodType, - LuaBitwiseAnd: ExtensionKind.BitwiseAndOperatorType, - LuaBitwiseAndMethod: ExtensionKind.BitwiseAndOperatorMethodType, - LuaBitwiseOr: ExtensionKind.BitwiseOrOperatorType, - LuaBitwiseOrMethod: ExtensionKind.BitwiseOrOperatorMethodType, - LuaBitwiseExclusiveOr: ExtensionKind.BitwiseExclusiveOrOperatorType, - LuaBitwiseExclusiveOrMethod: ExtensionKind.BitwiseExclusiveOrOperatorMethodType, - LuaBitwiseLeftShift: ExtensionKind.BitwiseLeftShiftOperatorType, - LuaBitwiseLeftShiftMethod: ExtensionKind.BitwiseLeftShiftOperatorMethodType, - LuaBitwiseRightShift: ExtensionKind.BitwiseRightShiftOperatorType, - LuaBitwiseRightShiftMethod: ExtensionKind.BitwiseRightShiftOperatorMethodType, - LuaConcat: ExtensionKind.ConcatOperatorType, - LuaConcatMethod: ExtensionKind.ConcatOperatorMethodType, - LuaLessThan: ExtensionKind.LessThanOperatorType, - LuaLessThanMethod: ExtensionKind.LessThanOperatorMethodType, - LuaGreaterThan: ExtensionKind.GreaterThanOperatorType, - LuaGreaterThanMethod: ExtensionKind.GreaterThanOperatorMethodType, - LuaNegation: ExtensionKind.NegationOperatorType, - LuaNegationMethod: ExtensionKind.NegationOperatorMethodType, - LuaBitwiseNot: ExtensionKind.BitwiseNotOperatorType, - LuaBitwiseNotMethod: ExtensionKind.BitwiseNotOperatorMethodType, - LuaLength: ExtensionKind.LengthOperatorType, - LuaLengthMethod: ExtensionKind.LengthOperatorMethodType, +const extensionKindToTypeBrand: { [T in ExtensionKind]: string } = { + [ExtensionKind.MultiFunction]: "__luaMultiFunctionBrand", + [ExtensionKind.MultiType]: "__luaMultiReturnBrand", + [ExtensionKind.RangeFunction]: "__luaRangeFunctionBrand", + [ExtensionKind.IterableType]: "__luaIterableBrand", + [ExtensionKind.AdditionOperatorType]: "__luaAdditionBrand", + [ExtensionKind.AdditionOperatorMethodType]: "__luaAdditionMethodBrand", + [ExtensionKind.SubtractionOperatorType]: "__luaSubtractionBrand", + [ExtensionKind.SubtractionOperatorMethodType]: "__luaSubtractionMethodBrand", + [ExtensionKind.MultiplicationOperatorType]: "__luaMultiplicationBrand", + [ExtensionKind.MultiplicationOperatorMethodType]: "__luaMultiplicationMethodBrand", + [ExtensionKind.DivisionOperatorType]: "__luaDivisionBrand", + [ExtensionKind.DivisionOperatorMethodType]: "__luaDivisionMethodBrand", + [ExtensionKind.ModuloOperatorType]: "__luaModuloBrand", + [ExtensionKind.ModuloOperatorMethodType]: "__luaModuloMethodBrand", + [ExtensionKind.PowerOperatorType]: "__luaPowerBrand", + [ExtensionKind.PowerOperatorMethodType]: "__luaPowerMethodBrand", + [ExtensionKind.FloorDivisionOperatorType]: "__luaFloorDivisionBrand", + [ExtensionKind.FloorDivisionOperatorMethodType]: "__luaFloorDivisionMethodBrand", + [ExtensionKind.BitwiseAndOperatorType]: "__luaBitwiseAndBrand", + [ExtensionKind.BitwiseAndOperatorMethodType]: "__luaBitwiseAndMethodBrand", + [ExtensionKind.BitwiseOrOperatorType]: "__luaBitwiseOrBrand", + [ExtensionKind.BitwiseOrOperatorMethodType]: "__luaBitwiseOrMethodBrand", + [ExtensionKind.BitwiseExclusiveOrOperatorType]: "__luaBitwiseExclusiveOrBrand", + [ExtensionKind.BitwiseExclusiveOrOperatorMethodType]: "__luaBitwiseExclusiveOrMethodBrand", + [ExtensionKind.BitwiseLeftShiftOperatorType]: "__luaBitwiseLeftShiftBrand", + [ExtensionKind.BitwiseLeftShiftOperatorMethodType]: "__luaBitwiseLeftShiftMethodBrand", + [ExtensionKind.BitwiseRightShiftOperatorType]: "__luaBitwiseRightShiftBrand", + [ExtensionKind.BitwiseRightShiftOperatorMethodType]: "__luaBitwiseRightShiftMethodBrand", + [ExtensionKind.ConcatOperatorType]: "__luaConcatBrand", + [ExtensionKind.ConcatOperatorMethodType]: "__luaConcatMethodBrand", + [ExtensionKind.LessThanOperatorType]: "__luaLessThanBrand", + [ExtensionKind.LessThanOperatorMethodType]: "__luaLessThanMethodBrand", + [ExtensionKind.GreaterThanOperatorType]: "__luaGreaterThanBrand", + [ExtensionKind.GreaterThanOperatorMethodType]: "__luaGreaterThanMethodBrand", + [ExtensionKind.NegationOperatorType]: "__luaNegationBrand", + [ExtensionKind.NegationOperatorMethodType]: "__luaNegationMethodBrand", + [ExtensionKind.BitwiseNotOperatorType]: "__luaBitwiseNotBrand", + [ExtensionKind.BitwiseNotOperatorMethodType]: "__luaBitwiseNotMethodBrand", + [ExtensionKind.LengthOperatorType]: "__luaLengthBrand", + [ExtensionKind.LengthOperatorMethodType]: "__luaLengthMethodBrand", }; -function isSourceFileFromLanguageExtensions(sourceFile: ts.SourceFile): boolean { - const extensionDirectory = path.resolve(__dirname, "../../../language-extensions"); - const sourceFileDirectory = path.dirname(path.normalize(sourceFile.fileName)); - return extensionDirectory === sourceFileDirectory; +export function isExtensionType(type: ts.Type, extensionKind: ExtensionKind): boolean { + const typeBrand = extensionKindToTypeBrand[extensionKind]; + return typeBrand !== undefined && type.getProperty(typeBrand) !== undefined; } -export function getExtensionKind(declaration: ts.Declaration): ExtensionKind | undefined { - const sourceFile = declaration.getSourceFile(); - if (isSourceFileFromLanguageExtensions(sourceFile)) { - if (ts.isFunctionDeclaration(declaration) && declaration.name?.text) { - const extensionKind = functionNameToExtensionKind[declaration.name.text]; - if (extensionKind) { - return extensionKind; - } - } - - if (ts.isTypeAliasDeclaration(declaration)) { - const extensionKind = typeNameToExtensionKind[declaration.name.text]; - if (extensionKind) { - return extensionKind; - } - } - - throw new Error("Unknown extension kind"); - } +export function isExtensionFunction( + context: TransformationContext, + symbol: ts.Symbol, + extensionKind: ExtensionKind +): boolean { + return ( + symbol.getName() === extensionKindToFunctionName[extensionKind] && + symbol.declarations.some(d => isExtensionType(context.checker.getTypeAtLocation(d), extensionKind)) + ); } diff --git a/src/transformation/visitors/access.ts b/src/transformation/visitors/access.ts index 3dc017ac0..d33c5c5c6 100644 --- a/src/transformation/visitors/access.ts +++ b/src/transformation/visitors/access.ts @@ -8,7 +8,6 @@ import { addToNumericExpression } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; import { isArrayType, isNumberType, isStringType } from "../utils/typescript"; import { tryGetConstEnumValue } from "./enum"; -import { validateIterableTypeUse } from "./language-extensions/iterable"; import { returnsMultiType } from "./language-extensions/multi"; import { transformLuaTablePropertyAccessExpression, validateLuaTableElementAccessExpression } from "./lua-table"; @@ -29,7 +28,6 @@ export function transformElementAccessArgument( export const transformElementAccessExpression: FunctionVisitor = (node, context) => { validateLuaTableElementAccessExpression(context, node); - validateIterableTypeUse(context, node); const constEnumValue = tryGetConstEnumValue(context, node); if (constEnumValue) { @@ -65,8 +63,6 @@ export const transformPropertyAccessExpression: FunctionVisitor { - validateIterableTypeUse(context, expression); - if (ts.isOptionalChain(expression)) { context.diagnostics.push(optionalChainingNotSupported(expression)); } diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 8b1828b68..f29cbd4e9 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -13,7 +13,6 @@ import { transformElementAccessArgument } from "./access"; import { transformLuaTableCallExpression } from "./lua-table"; import { shouldMultiReturnCallBeWrapped, returnsMultiType } from "./language-extensions/multi"; import { isOperatorMapping, transformOperatorMappingExpression } from "./language-extensions/operators"; -import { validateIterableTypeUse } from "./language-extensions/iterable"; export type PropertyCallExpression = ts.CallExpression & { expression: ts.PropertyAccessExpression }; @@ -215,8 +214,6 @@ export const transformCallExpression: FunctionVisitor = (node return transformOperatorMappingExpression(context, node); } - validateIterableTypeUse(context, node); - if (ts.isPropertyAccessExpression(node.expression)) { const result = transformPropertyCall(context, node as PropertyCallExpression); return wrapResult ? wrapInTable(result) : result; diff --git a/src/transformation/visitors/language-extensions/iterable.ts b/src/transformation/visitors/language-extensions/iterable.ts index da85bcff2..769e15093 100644 --- a/src/transformation/visitors/language-extensions/iterable.ts +++ b/src/transformation/visitors/language-extensions/iterable.ts @@ -4,16 +4,23 @@ import * as extensions from "../../utils/language-extensions"; import { TransformationContext } from "../../context"; import { getVariableDeclarationBinding, transformForInitializer } from "../loops/utils"; import { transformArrayBindingElement } from "../variable-declaration"; -import { invalidIterableUse, invalidMultiIterableWithoutDestructuring } from "../../utils/diagnostics"; +import { invalidMultiIterableWithoutDestructuring } from "../../utils/diagnostics"; import { cast } from "../../../utils"; import { isMultiReturnType } from "./multi"; -const isIterableTypeDeclaration = (declaration: ts.Declaration): boolean => - extensions.getExtensionKind(declaration) === extensions.ExtensionKind.IterableType; +export function isIterableType(type: ts.Type): boolean { + return extensions.isExtensionType(type, extensions.ExtensionKind.IterableType); +} + +export function returnsIterableType(context: TransformationContext, node: ts.CallExpression): boolean { + const signature = context.checker.getResolvedSignature(node); + const type = signature?.getReturnType(); + return type ? isIterableType(type) : false; +} export function isIterableExpression(context: TransformationContext, expression: ts.Expression): boolean { const type = context.checker.getTypeAtLocation(expression); - return type.aliasSymbol?.declarations?.some(isIterableTypeDeclaration) ?? false; + return isIterableType(type); } function transformForOfMultiIterableStatement( @@ -65,7 +72,7 @@ export function transformForOfIterableStatement( block: lua.Block ): lua.Statement { const type = context.checker.getTypeAtLocation(statement.expression); - if (type.aliasTypeArguments?.length === 1 && isMultiReturnType(type.aliasTypeArguments[0])) { + if (type.aliasTypeArguments?.length === 2 && isMultiReturnType(type.aliasTypeArguments[0])) { return transformForOfMultiIterableStatement(context, statement, block); } @@ -73,16 +80,3 @@ export function transformForOfIterableStatement( const identifier = transformForInitializer(context, statement.initializer, block); return lua.createForInStatement(block, [identifier], [luaIterator], statement); } - -export function validateIterableTypeUse(context: TransformationContext, node: ts.Expression) { - if (!isIterableExpression(context, node)) { - return; - } - if (ts.isForOfStatement(node.parent)) { - return; - } - if (ts.isReturnStatement(node.parent) || ts.isArrowFunction(node.parent)) { - return; - } - context.diagnostics.push(invalidIterableUse(node)); -} diff --git a/src/transformation/visitors/language-extensions/multi.ts b/src/transformation/visitors/language-extensions/multi.ts index 8ddb54c83..b292d9aa1 100644 --- a/src/transformation/visitors/language-extensions/multi.ts +++ b/src/transformation/visitors/language-extensions/multi.ts @@ -1,22 +1,16 @@ import * as ts from "typescript"; import * as extensions from "../../utils/language-extensions"; import { TransformationContext } from "../../context"; -import { invalidMultiFunctionUse } from "../../utils/diagnostics"; import { findFirstNodeAbove } from "../../utils/typescript"; - -const isMultiFunctionDeclaration = (declaration: ts.Declaration): boolean => - extensions.getExtensionKind(declaration) === extensions.ExtensionKind.MultiFunction; - -const isMultiTypeDeclaration = (declaration: ts.Declaration): boolean => - extensions.getExtensionKind(declaration) === extensions.ExtensionKind.MultiType; +import { isIterableExpression } from "./iterable"; +import { invalidMultiFunctionUse } from "../../utils/diagnostics"; export function isMultiReturnType(type: ts.Type): boolean { - return type.aliasSymbol?.declarations?.some(isMultiTypeDeclaration) ?? false; + return extensions.isExtensionType(type, extensions.ExtensionKind.MultiType); } export function isMultiFunctionCall(context: TransformationContext, expression: ts.CallExpression): boolean { - const type = context.checker.getTypeAtLocation(expression.expression); - return type.symbol?.declarations?.some(isMultiFunctionDeclaration) ?? false; + return isMultiFunctionNode(context, expression.expression); } export function returnsMultiType(context: TransformationContext, node: ts.CallExpression): boolean { @@ -30,8 +24,8 @@ export function isMultiReturnCall(context: TransformationContext, expression: ts } export function isMultiFunctionNode(context: TransformationContext, node: ts.Node): boolean { - const type = context.checker.getTypeAtLocation(node); - return type.symbol?.declarations?.some(isMultiFunctionDeclaration) ?? false; + const symbol = context.checker.getSymbolAtLocation(node); + return symbol ? extensions.isExtensionFunction(context, symbol, extensions.ExtensionKind.MultiFunction) : false; } export function isInMultiReturnFunction(context: TransformationContext, node: ts.Node) { @@ -86,6 +80,11 @@ export function shouldMultiReturnCallBeWrapped(context: TransformationContext, n return false; } + // LuaIterable in for...of + if (ts.isForOfStatement(node.parent) && isIterableExpression(context, node)) { + return false; + } + return true; } @@ -99,8 +98,7 @@ export function findMultiAssignmentViolations( if (!ts.isShorthandPropertyAssignment(element)) continue; const valueSymbol = context.checker.getShorthandAssignmentValueSymbol(element); if (valueSymbol) { - const declaration = valueSymbol.valueDeclaration; - if (declaration && isMultiFunctionDeclaration(declaration)) { + if (extensions.isExtensionFunction(context, valueSymbol, extensions.ExtensionKind.MultiFunction)) { context.diagnostics.push(invalidMultiFunctionUse(element)); result.push(element); } diff --git a/src/transformation/visitors/language-extensions/operators.ts b/src/transformation/visitors/language-extensions/operators.ts index b217a26e8..dddecf338 100644 --- a/src/transformation/visitors/language-extensions/operators.ts +++ b/src/transformation/visitors/language-extensions/operators.ts @@ -49,10 +49,7 @@ const unaryOperatorMappings = new Map([ - ...binaryOperatorMappings.keys(), - ...unaryOperatorMappings.keys(), -]); +const operatorMapExtensions = [...binaryOperatorMappings.keys(), ...unaryOperatorMappings.keys()]; const bitwiseOperatorMapExtensions = new Set([ extensions.ExtensionKind.BitwiseAndOperatorType, @@ -84,25 +81,15 @@ function getOperatorMapExtensionKindForCall(context: TransformationContext, node if (!typeDeclaration) { return; } - const mapping = extensions.getExtensionKind(typeDeclaration); - if (mapping !== undefined && operatorMapExtensions.has(mapping)) { - return mapping; - } -} - -function isOperatorMapDeclaration(declaration: ts.Declaration) { - const typeDeclaration = getTypeDeclaration(declaration); - if (typeDeclaration) { - const extensionKind = extensions.getExtensionKind(typeDeclaration); - return extensionKind !== undefined ? operatorMapExtensions.has(extensionKind) : false; - } + const type = context.checker.getTypeFromTypeNode(typeDeclaration.type); + return operatorMapExtensions.find(extensionKind => extensions.isExtensionType(type, extensionKind)); } function isOperatorMapType(context: TransformationContext, type: ts.Type): boolean { if (type.isUnionOrIntersection()) { return type.types.some(t => isOperatorMapType(context, t)); } else { - return type.symbol?.declarations?.some(isOperatorMapDeclaration); + return operatorMapExtensions.some(extensionKind => extensions.isExtensionType(type, extensionKind)); } } diff --git a/src/transformation/visitors/language-extensions/range.ts b/src/transformation/visitors/language-extensions/range.ts index d68729c3c..00a3c249e 100644 --- a/src/transformation/visitors/language-extensions/range.ts +++ b/src/transformation/visitors/language-extensions/range.ts @@ -8,17 +8,13 @@ import { transformArguments } from "../call"; import { assert } from "../../../utils"; import { invalidRangeControlVariable } from "../../utils/diagnostics"; -const isRangeFunctionDeclaration = (declaration: ts.Declaration): boolean => - extensions.getExtensionKind(declaration) === extensions.ExtensionKind.RangeFunction; - export function isRangeFunction(context: TransformationContext, expression: ts.CallExpression): boolean { - const type = context.checker.getTypeAtLocation(expression.expression); - return type.symbol?.declarations?.some(isRangeFunctionDeclaration) ?? false; + return isRangeFunctionNode(context, expression.expression); } export function isRangeFunctionNode(context: TransformationContext, node: ts.Node): boolean { const symbol = context.checker.getSymbolAtLocation(node); - return symbol?.declarations?.some(isRangeFunctionDeclaration) ?? false; + return symbol ? extensions.isExtensionFunction(context, symbol, extensions.ExtensionKind.RangeFunction) : false; } function getControlVariable(context: TransformationContext, statement: ts.ForOfStatement) { diff --git a/src/transformation/visitors/return.ts b/src/transformation/visitors/return.ts index 7e7a7ddf8..f309874f7 100644 --- a/src/transformation/visitors/return.ts +++ b/src/transformation/visitors/return.ts @@ -12,6 +12,7 @@ import { shouldMultiReturnCallBeWrapped, isMultiFunctionCall, isMultiReturnType, + isInMultiReturnFunction, } from "./language-extensions/multi"; import { invalidMultiFunctionReturnType } from "../utils/diagnostics"; @@ -20,6 +21,8 @@ function transformExpressionsInReturn( node: ts.Expression, insideTryCatch: boolean ): lua.Expression[] { + const expressionType = context.checker.getTypeAtLocation(node); + if (ts.isCallExpression(node)) { // $multi(...) if (isMultiFunctionCall(context, node)) { @@ -40,6 +43,9 @@ function transformExpressionsInReturn( if (insideTryCatch && returnsMultiType(context, node) && !shouldMultiReturnCallBeWrapped(context, node)) { return [wrapInTable(context.transformExpression(node))]; } + } else if (isInMultiReturnFunction(context, node) && isMultiReturnType(expressionType)) { + // Unpack objects typed as LuaMultiReturn + return [createUnpackCall(context, context.transformExpression(node), node)]; } if (!isInTupleReturnFunction(context, node)) { @@ -47,7 +53,6 @@ function transformExpressionsInReturn( } let results: lua.Expression[]; - const expressionType = context.checker.getTypeAtLocation(node); // Parent function is a TupleReturn function if (ts.isArrayLiteralExpression(node)) { diff --git a/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap b/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap index 78c10be1b..6feb3bfd7 100644 --- a/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap @@ -1,319 +1,52 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`invalid LuaIterable without destructuring ("for (const s of testMultiIterable()) {}"): code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function testMultiIterable(self) - local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} - local i = 0 - local function iterator(self) - local j = (function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() - if strs[j + 1] then - return table.unpack(strs[j + 1]) - end - end - return iterator - end - for ____ in testMultiIterable(nil) do - end -end -return ____exports" -`; - -exports[`invalid LuaIterable without destructuring ("for (const s of testMultiIterable()) {}"): diagnostics 1`] = `"main.ts(15,20): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; - -exports[`invalid LuaIterable without destructuring ("let s; for (s of testMultiIterable()) {}"): code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function testMultiIterable(self) - local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} - local i = 0 - local function iterator(self) - local j = (function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() - if strs[j + 1] then - return table.unpack(strs[j + 1]) - end - end - return iterator - end - local s - for ____ in testMultiIterable(nil) do - end -end -return ____exports" -`; - -exports[`invalid LuaIterable without destructuring ("let s; for (s of testMultiIterable()) {}"): diagnostics 1`] = `"main.ts(15,21): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; - -exports[`invalid use of LuaIterable ("const i = testIterable();"): code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function testIterable(self) - local strs = {\\"a\\", \\"b\\", \\"c\\"} - local i = 0 - local function iterator(self) - return strs[(function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() + 1] - end - return iterator - end - local i = testIterable(nil) -end -return ____exports" -`; - -exports[`invalid use of LuaIterable ("const i = testIterable();"): diagnostics 1`] = `"main.ts(12,19): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; - -exports[`invalid use of LuaIterable ("const i = testMultiIterable();"): code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function testMultiIterable(self) - local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} - local i = 0 - local function iterator(self) - local j = (function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() - if strs[j + 1] then - return table.unpack(strs[j + 1]) - end - end - return iterator - end - local i = testMultiIterable(nil) -end -return ____exports" -`; - -exports[`invalid use of LuaIterable ("const i = testMultiIterable();"): diagnostics 1`] = `"main.ts(15,19): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; - -exports[`invalid use of LuaIterable ("const i = tester.testIterable;"): code 1`] = ` +exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("for (const s of testIterable()) {}"): code 1`] = ` "require(\\"lualib_bundle\\"); local ____exports = {} function ____exports.__main(self) - local IterablePropertyTest = __TS__Class() - IterablePropertyTest.name = \\"IterablePropertyTest\\" - function IterablePropertyTest.prototype.____constructor(self) - end - __TS__SetDescriptor( - IterablePropertyTest.prototype, - \\"testIterable\\", - { - get = function(self) - local strs = {\\"a\\", \\"b\\", \\"c\\"} - local i = 0 - local function iterator(self) - return strs[(function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() + 1] - end - return iterator - end - }, - true - ) - local tester = __TS__New(IterablePropertyTest) - local i = tester.testIterable -end -return ____exports" -`; - -exports[`invalid use of LuaIterable ("const i = tester.testIterable;"): diagnostics 1`] = `"main.ts(15,19): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; - -exports[`invalid use of LuaIterable ("for (const s in testIterable()) {}"): code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function testIterable(self) - local strs = {\\"a\\", \\"b\\", \\"c\\"} - local i = 0 - local function iterator(self) - return strs[(function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() + 1] + local function testIterator(strsArray, lastStr) + local str = strsArray[(__TS__ArrayFindIndex( + strsArray, + function(____, strs) return strs[1] == lastStr end + ) + 1) + 1] + if str then + return table.unpack(str) end - return iterator end - for s in pairs( - testIterable(nil) - ) do + local strsArray = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local function testIterable() + return testIterator, strsArray, \\"\\" end -end -return ____exports" -`; - -exports[`invalid use of LuaIterable ("for (const s in testIterable()) {}"): diagnostics 1`] = `"main.ts(12,25): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; - -exports[`invalid use of LuaIterable ("for (const s in testMultiIterable()) {}"): code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function testMultiIterable(self) - local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} - local i = 0 - local function iterator(self) - local j = (function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() - if strs[j + 1] then - return table.unpack(strs[j + 1]) - end - end - return iterator - end - for s in pairs( - testMultiIterable(nil) - ) do + for ____ in testIterable(nil) do end end return ____exports" `; -exports[`invalid use of LuaIterable ("for (const s in testMultiIterable()) {}"): diagnostics 1`] = `"main.ts(15,25): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; +exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("for (const s of testIterable()) {}"): diagnostics 1`] = `"main.ts(13,24): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; -exports[`invalid use of LuaIterable ("for (const s in tester.testIterable) {}"): code 1`] = ` +exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("let s; for (s of testIterable()) {}"): code 1`] = ` "require(\\"lualib_bundle\\"); local ____exports = {} function ____exports.__main(self) - local IterablePropertyTest = __TS__Class() - IterablePropertyTest.name = \\"IterablePropertyTest\\" - function IterablePropertyTest.prototype.____constructor(self) - end - __TS__SetDescriptor( - IterablePropertyTest.prototype, - \\"testIterable\\", - { - get = function(self) - local strs = {\\"a\\", \\"b\\", \\"c\\"} - local i = 0 - local function iterator(self) - return strs[(function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() + 1] - end - return iterator - end - }, - true - ) - local tester = __TS__New(IterablePropertyTest) - for s in pairs(tester.testIterable) do - end -end -return ____exports" -`; - -exports[`invalid use of LuaIterable ("for (const s in tester.testIterable) {}"): diagnostics 1`] = `"main.ts(15,25): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; - -exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(testIterable());"): code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function testIterable(self) - local strs = {\\"a\\", \\"b\\", \\"c\\"} - local i = 0 - local function iterator(self) - return strs[(function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() + 1] + local function testIterator(strsArray, lastStr) + local str = strsArray[(__TS__ArrayFindIndex( + strsArray, + function(____, strs) return strs[1] == lastStr end + ) + 1) + 1] + if str then + return table.unpack(str) end - return iterator end - local function foo(self, i) + local strsArray = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local function testIterable() + return testIterator, strsArray, \\"\\" end - foo( - nil, - testIterable(nil) - ) -end -return ____exports" -`; - -exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(testIterable());"): diagnostics 1`] = `"main.ts(12,37): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; - -exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(testMultiIterable());"): code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function testMultiIterable(self) - local strs = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} - local i = 0 - local function iterator(self) - local j = (function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() - if strs[j + 1] then - return table.unpack(strs[j + 1]) - end - end - return iterator - end - local function foo(self, i) - end - foo( - nil, - testMultiIterable(nil) - ) -end -return ____exports" -`; - -exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(testMultiIterable());"): diagnostics 1`] = `"main.ts(15,37): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; - -exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(tester.testIterable);"): code 1`] = ` -"require(\\"lualib_bundle\\"); -local ____exports = {} -function ____exports.__main(self) - local IterablePropertyTest = __TS__Class() - IterablePropertyTest.name = \\"IterablePropertyTest\\" - function IterablePropertyTest.prototype.____constructor(self) - end - __TS__SetDescriptor( - IterablePropertyTest.prototype, - \\"testIterable\\", - { - get = function(self) - local strs = {\\"a\\", \\"b\\", \\"c\\"} - local i = 0 - local function iterator(self) - return strs[(function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() + 1] - end - return iterator - end - }, - true - ) - local tester = __TS__New(IterablePropertyTest) - local function foo(self, i) + local s + for ____ in testIterable(nil) do end - foo(nil, tester.testIterable) end return ____exports" `; -exports[`invalid use of LuaIterable ("function foo(i: any) {} foo(tester.testIterable);"): diagnostics 1`] = `"main.ts(15,37): error TSTL: LuaIterable type can only be used in for...of loops or return statements."`; +exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("let s; for (s of testIterable()) {}"): diagnostics 1`] = `"main.ts(13,25): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; diff --git a/test/unit/language-extensions/iterable.spec.ts b/test/unit/language-extensions/iterable.spec.ts index 07e0adb5f..3c419ddd6 100644 --- a/test/unit/language-extensions/iterable.spec.ts +++ b/test/unit/language-extensions/iterable.spec.ts @@ -1,231 +1,342 @@ import * as path from "path"; import * as util from "../../util"; import * as tstl from "../../../src"; -import { - invalidIterableUse, - invalidMultiIterableWithoutDestructuring, -} from "../../../src/transformation/utils/diagnostics"; +import { invalidMultiIterableWithoutDestructuring } from "../../../src/transformation/utils/diagnostics"; const iterableProjectOptions: tstl.CompilerOptions = { types: [path.resolve(__dirname, "../../../language-extensions")], }; -const testIterable = ` -function testIterable(): LuaIterable { - const strs = ["a", "b", "c"]; - let i = 0; - function iterator() { - return strs[i++]; - } - return iterator as any; -} -`; - -const testArrayIterable = ` -function testArrayIterable(): LuaIterable { - const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; - let i = 0; - function iterator() { - return strs[i++]; - } - return iterator as any; -} -`; - -const testMultiIterable = ` -function testMultiIterable(): LuaIterable> { - const strs = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; - let i = 0; - function iterator() { - const j = i++; - if (strs[j]) { - return $multi(...strs[j]); - } - } - return iterator as any; -} -`; - -const testIterableProperty = ` -class IterablePropertyTest { - public get testIterable(): LuaIterable { - const strs = ["a", "b", "c"]; - let i = 0; - function iterator() { - return strs[i++]; - } - return iterator as any; +describe("basic LuaIterable", () => { + const testIterable = ` + function testIterator(this: void, strs: string[], lastStr: string) { + return strs[strs.indexOf(lastStr) + 1]; } -} -const tester = new IterablePropertyTest(); -`; - -test.each(["const s", "let s"])("LuaIterable basic use", initializer => { - util.testFunction` - ${testIterable} - const results: string[] = []; - for (${initializer} of testIterable()) { - results.push(s); - } - return results; - ` - .setOptions(iterableProjectOptions) - .expectToEqual(["a", "b", "c"]); -}); -test("LuaIterable with external control variable", () => { - util.testFunction` - ${testIterable} - const results: string[] = []; - let s: string; - for (s of testIterable()) { - results.push(s); - } - return results; - ` - .setOptions(iterableProjectOptions) - .expectToEqual(["a", "b", "c"]); -}); + const strs = ["a", "b", "c"]; + const testIterable = (() => $multi(testIterator, strs, "")) as (() => LuaIterable); + `; + const testResults = ["a", "b", "c"]; -test.each(["const [x, y]", "let [x, y]"])("LuaIterable array destructuring", initializer => { - util.testFunction` - ${testArrayIterable} - const results: string[] = []; - for (${initializer} of testArrayIterable()) { - results.push(x); - results.push(y); - } - return results; - ` - .setOptions(iterableProjectOptions) - .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); -}); + test("const control variable", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + for (const s of testIterable()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); -test("LuaIterable array destructuring with external control variable", () => { - util.testFunction` - ${testArrayIterable} - const results: string[] = []; - let x: string, y: string; - for ([x, y] of testArrayIterable()) { - results.push(x); - results.push(y); - } - return results; - ` - .setOptions(iterableProjectOptions) - .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); -}); + test("let control variable", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + for (let s of testIterable()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); -test.each(["const [x, y]", "let [x, y]"])("LuaIterable basic use", initializer => { - util.testFunction` - ${testMultiIterable} - const results: string[] = []; - for (${initializer} of testMultiIterable()) { - results.push(x); - results.push(y); - } - return results; - ` - .setOptions(iterableProjectOptions) - .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); -}); + test("external control variable", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + let s: string; + for (s of testIterable()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); -test("LuaIterable with external control variables", () => { - util.testFunction` - ${testMultiIterable} - const results: string[] = []; - let x: string, y: string; - for ([x, y] of testMultiIterable()) { - results.push(x); - results.push(y); - } - return results; - ` - .setOptions(iterableProjectOptions) - .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); + test("function forward", () => { + util.testFunction` + ${testIterable} + function forward() { return testIterable(); } + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("function indirect forward", () => { + util.testFunction` + ${testIterable} + function forward() { const iter = testIterable(); return iter; } + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("arrow function forward", () => { + util.testFunction` + ${testIterable} + const forward = () => testIterable(); + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("manual use", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + let [iter, state, val] = testIterable(); + while (true) { + val = iter(state, val); + if (!val) { + break; + } + results.push(val); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); }); -test.each([".testIterable", '["testIterable"]'])("LuaIterable property", access => { - util.testFunction` - ${testIterableProperty} - const results: string[] = []; - for (const s of tester${access}) { - results.push(s); - } - return results; - ` - .setOptions(iterableProjectOptions) - .expectToEqual(["a", "b", "c"]); +describe("LuaIterable with array value type", () => { + const testIterable = ` + function testIterator(this: void, strsArray: Array, lastStrs: string[]) { + return strsArray[strsArray.indexOf(lastStrs) + 1]; + } + + const strsArray = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + const testIterable = (() => $multi(testIterator, strsArray, [""])) as (() => LuaIterable>); + `; + const testResults = [ + ["a1", "a2"], + ["b1", "b2"], + ["c1", "c2"], + ]; + + test("basic destructuring", () => { + util.testFunction` + ${testIterable} + const results: Array = []; + for (const [x, y] of testIterable()) { + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("destructure with external control variable", () => { + util.testFunction` + ${testIterable} + const results: Array = []; + let x: string, y: string; + for ([x, y] of testIterable()) { + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("destructure with function forward", () => { + util.testFunction` + ${testIterable} + function forward() { return testIterable(); } + const results: Array = []; + for (const [x, y] of forward()) { + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("destructure with function indirect forward", () => { + util.testFunction` + ${testIterable} + function forward() { const iter = testIterable(); return iter; } + const results: Array = []; + for (const [x, y] of forward()) { + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("destructure arrow function forward", () => { + util.testFunction` + ${testIterable} + const forward = () => testIterable(); + const results: Array = []; + for (const [x, y] of forward()) { + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("manual use", () => { + util.testFunction` + ${testIterable} + const results: Array = []; + let [iter, state, val] = testIterable(); + while (true) { + val = iter(state, val); + if (!val) { + break; + } + results.push(val); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); }); -function makeForwardTests(call: string, code: string) { - return [`${code} function forward() { return ${call}; }`, `${code} const forward = () => ${call};`]; -} - -test.each( - [ - ["testIterable()", testIterable], - ["tester.testIterable", testIterableProperty], - ].map(([call, code]) => makeForwardTests(call, code)) -)("LuaIterable return forward", forwardFunction => { - util.testFunction` - ${forwardFunction} - const results: string[] = []; - for (const s of forward()) { - results.push(s); +describe("LuaIterable with LuaMultiReturn value type", () => { + const testIterable = ` + function testIterator(this: void, strsArray: Array, lastStr: string) { + const str = strsArray[strsArray.findIndex(strs => strs[0] === lastStr) + 1]; + if (str) { + return $multi(...str); } - return results; - ` - .setOptions(iterableProjectOptions) - .expectToEqual(["a", "b", "c"]); -}); + } + + const strsArray = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + const testIterable = (() => $multi(testIterator, strsArray, "")) as (() => LuaIterable, Array>); + `; + const testResults = [ + ["a1", "a2"], + ["b1", "b2"], + ["c1", "c2"], + ]; -test.each(makeForwardTests("testMultiIterable()", testMultiIterable))( - "LuaIterable return forward", - forwardFunction => { + test("basic destructuring", () => { util.testFunction` - ${forwardFunction} - const results: string[] = []; - for (const [x, y] of forward()) { - results.push(x); - results.push(y); - } - return results; - ` + ${testIterable} + const results: Array = []; + for (const [x, y] of testIterable()) { + results.push([x, y]); + } + return results; + ` .setOptions(iterableProjectOptions) - .expectToEqual(["a1", "a2", "b1", "b2", "c1", "c2"]); - } -); - -test.each( - [ - ["testIterable()", testIterable], - ["testMultiIterable()", testMultiIterable], - ["tester.testIterable", testIterableProperty], - ].flatMap( - ([call, code]): Array<[string, string]> => [ - [`for (const s in ${call}) {}`, code], - [`const i = ${call};`, code], - [`function foo(i: any) {} foo(${call});`, code], - ] - ) -)("invalid use of LuaIterable (%p)", (statement, code) => { - util.testFunction` - ${code} - ${statement} - ` - .setOptions(iterableProjectOptions) - .expectDiagnosticsToMatchSnapshot([invalidIterableUse.code]); -}); + .expectToEqual(testResults); + }); -test.each(["for (const s of testMultiIterable()) {}", "let s; for (s of testMultiIterable()) {}"])( - "invalid LuaIterable without destructuring (%p)", - statement => { + test("destructure with external control variable", () => { util.testFunction` - ${testMultiIterable} - ${statement} - ` + ${testIterable} + const results: Array = []; + let x: string, y: string; + for ([x, y] of testIterable()) { + results.push([x, y]); + } + return results; + ` .setOptions(iterableProjectOptions) - .expectDiagnosticsToMatchSnapshot([invalidMultiIterableWithoutDestructuring.code]); - } -); + .expectToEqual(testResults); + }); + + test("destructure with function forward", () => { + util.testFunction` + ${testIterable} + function forward() { return testIterable(); } + const results: Array = []; + for (const [x, y] of forward()) { + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("destructure with function indirect forward", () => { + util.testFunction` + ${testIterable} + function forward() { const iter = testIterable(); return iter; } + const results: Array = []; + for (const [x, y] of forward()) { + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("destructure arrow function forward", () => { + util.testFunction` + ${testIterable} + const forward = () => testIterable(); + const results: Array = []; + for (const [x, y] of forward()) { + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("destructure manual use", () => { + util.testFunction` + ${testIterable} + const results: Array = []; + let [iter, state, x] = testIterable(); + let y: string; + while (true) { + [x, y] = iter(state, x); + if (!x) { + break; + } + results.push([x, y]); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test.each(["for (const s of testIterable()) {}", "let s; for (s of testIterable()) {}"])( + "invalid LuaIterable without destructuring (%p)", + statement => { + util.testFunction` + ${testIterable} + ${statement} + ` + .setOptions(iterableProjectOptions) + .expectDiagnosticsToMatchSnapshot([invalidMultiIterableWithoutDestructuring.code]); + } + ); +}); diff --git a/test/unit/language-extensions/multi.spec.ts b/test/unit/language-extensions/multi.spec.ts index ff1fd403a..6295810f8 100644 --- a/test/unit/language-extensions/multi.spec.ts +++ b/test/unit/language-extensions/multi.spec.ts @@ -130,6 +130,28 @@ test("allow $multi call in ArrowFunction body", () => { .expectToEqual(1); }); +test("forward $multi call", () => { + util.testFunction` + function foo() { return $multi(1); } + function call() { return foo(); } + const [result] = call(); + return result; + ` + .setOptions(multiProjectOptions) + .expectToEqual(1); +}); + +test("forward $multi call indirect", () => { + util.testFunction` + function foo() { return $multi(1); } + function call() { const m = foo(); return m; } + const [result] = call(); + return result; + ` + .setOptions(multiProjectOptions) + .expectToEqual(1); +}); + test("forward $multi call in ArrowFunction body", () => { util.testFunction` const foo = () => $multi(1); From 44645cda9b5106e26e45d8141272f75abb2b6ef5 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 15 Feb 2021 09:18:25 -0700 Subject: [PATCH 06/10] fixed issue with no-state vs state iterables, and updated tests to check both (also added test for property based iterables) --- language-extensions/index.d.ts | 24 +- .../__snapshots__/iterable.spec.ts.snap | 60 ++-- .../unit/language-extensions/iterable.spec.ts | 256 ++++++++++++++++-- 3 files changed, 278 insertions(+), 62 deletions(-) diff --git a/language-extensions/index.d.ts b/language-extensions/index.d.ts index 6208b776e..e4290ce8e 100644 --- a/language-extensions/index.d.ts +++ b/language-extensions/index.d.ts @@ -30,7 +30,9 @@ declare const $range: ((start: number, limit: number, step?: number) => Iterable }; /** - * Represents a Lua-style iterator function which is returned from a LuaIterable. + * Represents a Lua-style iterator which is returned from a LuaIterable. + * For simple iterators (with no state), this is just a function. + * For complex iterators that use a state, this is a LuaMultiReturn tuple containing a function, the state, and the initial value to pass to the function. * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions * * @param state The state object returned from the LuaIterable. @@ -38,11 +40,17 @@ declare const $range: ((start: number, limit: number, step?: number) => Iterable */ declare type LuaIterator = TState extends undefined ? (this: void) => TValue - : ( - this: void, - state: TState, - lastValue: TValue extends LuaMultiReturn ? TTuple[0] : TValue - ) => TValue; + : LuaMultiReturn< + [ + ( + this: void, + state: TState, + lastValue: TValue extends LuaMultiReturn ? TTuple[0] : TValue + ) => TValue, + TState, + TValue extends LuaMultiReturn ? TTuple[0] : TValue + ] + >; /** * Represents a Lua-style iteratable which iterates single values in a `for...in` loop (ex. `for x in iter() do`). @@ -52,9 +60,7 @@ declare type LuaIterator = TState extends undefined * @param TState The type of the state value passed back to the iterator function each iteration. */ declare type LuaIterable = Iterable & - LuaMultiReturn< - [LuaIterator, TState, TValue extends LuaMultiReturn ? TTuple[0] : TValue] - > & { readonly __luaIterableBrand: unique symbol }; + LuaIterator & { readonly __luaIterableBrand: unique symbol }; /** * Calls to functions with this type are translated to `left + right`. diff --git a/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap b/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap index 6feb3bfd7..1f76110a4 100644 --- a/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap @@ -1,52 +1,52 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("for (const s of testIterable()) {}"): code 1`] = ` -"require(\\"lualib_bundle\\"); -local ____exports = {} +"local ____exports = {} function ____exports.__main(self) - local function testIterator(strsArray, lastStr) - local str = strsArray[(__TS__ArrayFindIndex( - strsArray, - function(____, strs) return strs[1] == lastStr end - ) + 1) + 1] - if str then - return table.unpack(str) - end - end - local strsArray = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} local function testIterable() - return testIterator, strsArray, \\"\\" + local strsArray = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local i = 0 + return function() + local strs = strsArray[(function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + 1] + if strs then + return table.unpack(strs) + end + end end - for ____ in testIterable(nil) do + for ____ in testIterable() do end end return ____exports" `; -exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("for (const s of testIterable()) {}"): diagnostics 1`] = `"main.ts(13,24): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; +exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("for (const s of testIterable()) {}"): diagnostics 1`] = `"main.ts(14,24): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("let s; for (s of testIterable()) {}"): code 1`] = ` -"require(\\"lualib_bundle\\"); -local ____exports = {} +"local ____exports = {} function ____exports.__main(self) - local function testIterator(strsArray, lastStr) - local str = strsArray[(__TS__ArrayFindIndex( - strsArray, - function(____, strs) return strs[1] == lastStr end - ) + 1) + 1] - if str then - return table.unpack(str) - end - end - local strsArray = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} local function testIterable() - return testIterator, strsArray, \\"\\" + local strsArray = {{\\"a1\\", \\"a2\\"}, {\\"b1\\", \\"b2\\"}, {\\"c1\\", \\"c2\\"}} + local i = 0 + return function() + local strs = strsArray[(function() + local ____tmp = i + i = ____tmp + 1 + return ____tmp + end)() + 1] + if strs then + return table.unpack(strs) + end + end end local s - for ____ in testIterable(nil) do + for ____ in testIterable() do end end return ____exports" `; -exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("let s; for (s of testIterable()) {}"): diagnostics 1`] = `"main.ts(13,25): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; +exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable without destructuring ("let s; for (s of testIterable()) {}"): diagnostics 1`] = `"main.ts(14,25): error TSTL: LuaIterable with a LuaMultiReturn return value type must be destructured."`; diff --git a/test/unit/language-extensions/iterable.spec.ts b/test/unit/language-extensions/iterable.spec.ts index 3c419ddd6..b6f7ab141 100644 --- a/test/unit/language-extensions/iterable.spec.ts +++ b/test/unit/language-extensions/iterable.spec.ts @@ -7,14 +7,123 @@ const iterableProjectOptions: tstl.CompilerOptions = { types: [path.resolve(__dirname, "../../../language-extensions")], }; -describe("basic LuaIterable", () => { +describe("simple LuaIterable", () => { const testIterable = ` - function testIterator(this: void, strs: string[], lastStr: string) { - return strs[strs.indexOf(lastStr) + 1]; + function testIterable(this: void): LuaIterable { + const strs = ["a", "b", "c"]; + let i = 0; + return (() => strs[i++]) as any; } + `; + const testResults = ["a", "b", "c"]; + + test("const control variable", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + for (const s of testIterable()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("let control variable", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + for (let s of testIterable()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("external control variable", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + let s: string; + for (s of testIterable()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("function forward", () => { + util.testFunction` + ${testIterable} + function forward() { return testIterable(); } + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("function indirect forward", () => { + util.testFunction` + ${testIterable} + function forward() { const iter = testIterable(); return iter; } + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("arrow function forward", () => { + util.testFunction` + ${testIterable} + const forward = () => testIterable(); + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("manual use", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + const iter = testIterable(); + while (true) { + const val = iter(); + if (!val) { + break; + } + results.push(val); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); +}); - const strs = ["a", "b", "c"]; - const testIterable = (() => $multi(testIterator, strs, "")) as (() => LuaIterable); +describe("LuaIterable using state", () => { + const testIterable = ` + function iterator(this: void, strs: string[], lastStr: string) { + return strs[strs.indexOf(lastStr) + 1]; + } + const testIterable = (() => $multi(iterator, ["a", "b", "c"], "")) as (() => LuaIterable); `; const testResults = ["a", "b", "c"]; @@ -121,12 +230,11 @@ describe("basic LuaIterable", () => { describe("LuaIterable with array value type", () => { const testIterable = ` - function testIterator(this: void, strsArray: Array, lastStrs: string[]) { - return strsArray[strsArray.indexOf(lastStrs) + 1]; + function testIterable(this: void): LuaIterable { + const strsArray = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + let i = 0; + return (() => strsArray[i++]) as any; } - - const strsArray = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; - const testIterable = (() => $multi(testIterator, strsArray, [""])) as (() => LuaIterable>); `; const testResults = [ ["a1", "a2"], @@ -207,9 +315,9 @@ describe("LuaIterable with array value type", () => { util.testFunction` ${testIterable} const results: Array = []; - let [iter, state, val] = testIterable(); + const iter = testIterable(); while (true) { - val = iter(state, val); + const val = iter(); if (!val) { break; } @@ -224,15 +332,16 @@ describe("LuaIterable with array value type", () => { describe("LuaIterable with LuaMultiReturn value type", () => { const testIterable = ` - function testIterator(this: void, strsArray: Array, lastStr: string) { - const str = strsArray[strsArray.findIndex(strs => strs[0] === lastStr) + 1]; - if (str) { - return $multi(...str); - } + function testIterable(this: void): LuaIterable> { + const strsArray = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + let i = 0; + return (() => { + const strs = strsArray[i++]; + if (strs) { + return $multi(...strs); + } + }) as any; } - - const strsArray = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; - const testIterable = (() => $multi(testIterator, strsArray, "")) as (() => LuaIterable, Array>); `; const testResults = [ ["a1", "a2"], @@ -313,10 +422,9 @@ describe("LuaIterable with LuaMultiReturn value type", () => { util.testFunction` ${testIterable} const results: Array = []; - let [iter, state, x] = testIterable(); - let y: string; + const iter = testIterable(); while (true) { - [x, y] = iter(state, x); + const [x, y] = iter(); if (!x) { break; } @@ -340,3 +448,105 @@ describe("LuaIterable with LuaMultiReturn value type", () => { } ); }); + +describe("LuaIterable property", () => { + const testIterable = ` + class IterableTester { + public strs = ["a", "b", "c"]; + + public get values(): LuaIterable { + let i = 0; + return (() => this.strs[i++]) as any; + } + } + const tester = new IterableTester(); + `; + const testResults = ["a", "b", "c"]; + + test("basic usage", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + for (const s of tester.values) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("external control variable", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + let s: string; + for (s of tester.values) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("function forward", () => { + util.testFunction` + ${testIterable} + function forward() { return tester.values; } + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("function indirect forward", () => { + util.testFunction` + ${testIterable} + function forward() { const iter = tester.values; return iter; } + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("arrow function forward", () => { + util.testFunction` + ${testIterable} + const forward = () => tester.values; + const results: string[] = []; + for (const s of forward()) { + results.push(s); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); + + test("manual use", () => { + util.testFunction` + ${testIterable} + const results: string[] = []; + const iter = tester.values; + while (true) { + const val = iter(); + if (!val) { + break; + } + results.push(val); + } + return results; + ` + .setOptions(iterableProjectOptions) + .expectToEqual(testResults); + }); +}); From 12ec43577618bb54393373cfbb5cf26858a7351d Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 18 Feb 2021 18:37:28 -0700 Subject: [PATCH 07/10] updated extension kind checking to reduce complexity --- language-extensions/index.d.ts | 273 +++++++++++------- .../utils/language-extensions.ts | 100 ++++--- .../visitors/language-extensions/iterable.ts | 2 +- .../visitors/language-extensions/multi.ts | 10 +- .../visitors/language-extensions/operators.ts | 20 +- .../visitors/language-extensions/range.ts | 3 +- 6 files changed, 228 insertions(+), 180 deletions(-) diff --git a/language-extensions/index.d.ts b/language-extensions/index.d.ts index e4290ce8e..b51eaee8e 100644 --- a/language-extensions/index.d.ts +++ b/language-extensions/index.d.ts @@ -1,3 +1,11 @@ +/** + * Indicates a type is a language extension provided by TypescriptToLua. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + */ +interface LuaExtension { + readonly __luaExtensionBrand: unique symbol; +} + /** * Returns multiple values from a function, by wrapping them in a LuaMultiReturn tuple. * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions @@ -5,9 +13,10 @@ * @param T A tuple type with each element type representing a return value's type. * @param values Return values. */ -declare const $multi: ((...values: T) => LuaMultiReturn) & { - readonly __luaMultiFunctionBrand: unique symbol; -}; +declare const $multi: ((...values: T) => LuaMultiReturn) & + LuaExtension & { + readonly __luaMultiFunctionBrand: unique symbol; + }; /** * Represents multiple return values as a tuple. @@ -15,7 +24,7 @@ declare const $multi: ((...values: T) => LuaMultiReturn) & { * * @param T A tuple type with each element type representing a return value's type. */ -declare type LuaMultiReturn = T & { readonly __luaMultiReturnBrand: unique symbol }; +declare type LuaMultiReturn = T & LuaExtension & { readonly __luaMultiReturnBrand: unique symbol }; /** * Creates a Lua-style numeric for loop (for i=start,limit,step) when used in for...of. Not valid in any other context. @@ -25,9 +34,10 @@ declare type LuaMultiReturn = T & { readonly __luaMultiReturnBr * @param limit The last number in the sequence to iterate over. * @param step The amount to increment each iteration. */ -declare const $range: ((start: number, limit: number, step?: number) => Iterable) & { - readonly __luaRangeFunctionBrand: unique symbol; -}; +declare const $range: ((start: number, limit: number, step?: number) => Iterable) & + LuaExtension & { + readonly __luaRangeFunctionBrand: unique symbol; + }; /** * Represents a Lua-style iterator which is returned from a LuaIterable. @@ -60,7 +70,8 @@ declare type LuaIterator = TState extends undefined * @param TState The type of the state value passed back to the iterator function each iteration. */ declare type LuaIterable = Iterable & - LuaIterator & { readonly __luaIterableBrand: unique symbol }; + LuaIterator & + LuaExtension & { readonly __luaIterableBrand: unique symbol }; /** * Calls to functions with this type are translated to `left + right`. @@ -70,9 +81,10 @@ declare type LuaIterable = Iterable & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaAddition = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaAdditionBrand: unique symbol; -}; +declare type LuaAddition = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaAdditionBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left + right`, where `left` is the object with the method. @@ -81,9 +93,10 @@ declare type LuaAddition = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaAdditionMethod = ((right: TRight) => TReturn) & { - readonly __luaAdditionMethodBrand: unique symbol; -}; +declare type LuaAdditionMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaAdditionMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left - right`. @@ -93,9 +106,10 @@ declare type LuaAdditionMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaSubtraction = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaSubtractionBrand: unique symbol; -}; +declare type LuaSubtraction = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaSubtractionBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left - right`, where `left` is the object with the method. @@ -104,9 +118,10 @@ declare type LuaSubtraction = ((left: TLeft, right: TRig * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaSubtractionMethod = ((right: TRight) => TReturn) & { - readonly __luaSubtractionMethodBrand: unique symbol; -}; +declare type LuaSubtractionMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaSubtractionMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left * right`. @@ -116,9 +131,10 @@ declare type LuaSubtractionMethod = ((right: TRight) => TReturn * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaMultiplication = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaMultiplicationBrand: unique symbol; -}; +declare type LuaMultiplication = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaMultiplicationBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left * right`, where `left` is the object with the method. @@ -127,9 +143,10 @@ declare type LuaMultiplication = ((left: TLeft, right: T * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaMultiplicationMethod = ((right: TRight) => TReturn) & { - readonly __luaMultiplicationMethodBrand: unique symbol; -}; +declare type LuaMultiplicationMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaMultiplicationMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left / right`. @@ -139,9 +156,10 @@ declare type LuaMultiplicationMethod = ((right: TRight) => TRet * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaDivision = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaDivisionBrand: unique symbol; -}; +declare type LuaDivision = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaDivisionBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left / right`, where `left` is the object with the method. @@ -150,9 +168,10 @@ declare type LuaDivision = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaDivisionMethod = ((right: TRight) => TReturn) & { - readonly __luaDivisionMethodBrand: unique symbol; -}; +declare type LuaDivisionMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaDivisionMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left % right`. @@ -162,9 +181,10 @@ declare type LuaDivisionMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaModulo = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaModuloBrand: unique symbol; -}; +declare type LuaModulo = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaModuloBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left % right`, where `left` is the object with the method. @@ -173,9 +193,10 @@ declare type LuaModulo = ((left: TLeft, right: TRight) = * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaModuloMethod = ((right: TRight) => TReturn) & { - readonly __luaModuloMethodBrand: unique symbol; -}; +declare type LuaModuloMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaModuloMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left ^ right`. @@ -185,9 +206,10 @@ declare type LuaModuloMethod = ((right: TRight) => TReturn) & { * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaPower = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaPowerBrand: unique symbol; -}; +declare type LuaPower = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaPowerBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left ^ right`, where `left` is the object with the method. @@ -196,9 +218,10 @@ declare type LuaPower = ((left: TLeft, right: TRight) => * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaPowerMethod = ((right: TRight) => TReturn) & { - readonly __luaPowerMethodBrand: unique symbol; -}; +declare type LuaPowerMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaPowerMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left // right`. @@ -208,9 +231,10 @@ declare type LuaPowerMethod = ((right: TRight) => TReturn) & { * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaFloorDivision = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaFloorDivisionBrand: unique symbol; -}; +declare type LuaFloorDivision = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaFloorDivisionBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left // right`, where `left` is the object with the method. @@ -219,9 +243,10 @@ declare type LuaFloorDivision = ((left: TLeft, right: TR * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaFloorDivisionMethod = ((right: TRight) => TReturn) & { - readonly __luaFloorDivisionMethodBrand: unique symbol; -}; +declare type LuaFloorDivisionMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaFloorDivisionMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left & right`. @@ -231,9 +256,10 @@ declare type LuaFloorDivisionMethod = ((right: TRight) => TRetu * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseAnd = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseAndBrand: unique symbol; -}; +declare type LuaBitwiseAnd = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseAndBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left & right`, where `left` is the object with the method. @@ -242,9 +268,10 @@ declare type LuaBitwiseAnd = ((left: TLeft, right: TRigh * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseAndMethodBrand: unique symbol; -}; +declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseAndMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left | right`. @@ -254,9 +281,10 @@ declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseOr = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseOrBrand: unique symbol; -}; +declare type LuaBitwiseOr = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseOrBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left | right`, where `left` is the object with the method. @@ -265,9 +293,10 @@ declare type LuaBitwiseOr = ((left: TLeft, right: TRight * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseOrMethodBrand: unique symbol; -}; +declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseOrMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left ~ right`. @@ -277,9 +306,10 @@ declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseExclusiveOr = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseExclusiveOrBrand: unique symbol; -}; +declare type LuaBitwiseExclusiveOr = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseExclusiveOrBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left ~ right`, where `left` is the object with the method. @@ -288,9 +318,10 @@ declare type LuaBitwiseExclusiveOr = ((left: TLeft, righ * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseExclusiveOrMethodBrand: unique symbol; -}; +declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseExclusiveOrMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left << right`. @@ -300,9 +331,10 @@ declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseLeftShift = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseLeftShiftBrand: unique symbol; -}; +declare type LuaBitwiseLeftShift = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseLeftShiftBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left << right`, where `left` is the object with the method. @@ -311,9 +343,10 @@ declare type LuaBitwiseLeftShift = ((left: TLeft, right: * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseLeftShiftMethodBrand: unique symbol; -}; +declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseLeftShiftMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left >> right`. @@ -323,9 +356,10 @@ declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TR * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseRightShift = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseRightShiftBrand: unique symbol; -}; +declare type LuaBitwiseRightShift = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseRightShiftBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left >> right`, where `left` is the object with the method. @@ -334,9 +368,10 @@ declare type LuaBitwiseRightShift = ((left: TLeft, right * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseRightShiftMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseRightShiftMethodBrand: unique symbol; -}; +declare type LuaBitwiseRightShiftMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaBitwiseRightShiftMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left .. right`. @@ -346,9 +381,10 @@ declare type LuaBitwiseRightShiftMethod = ((right: TRight) => T * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaConcat = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaConcatBrand: unique symbol; -}; +declare type LuaConcat = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaConcatBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left .. right`, where `left` is the object with the method. @@ -357,9 +393,10 @@ declare type LuaConcat = ((left: TLeft, right: TRight) = * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaConcatMethod = ((right: TRight) => TReturn) & { - readonly __luaConcatMethodBrand: unique symbol; -}; +declare type LuaConcatMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaConcatMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left < right`. @@ -369,9 +406,10 @@ declare type LuaConcatMethod = ((right: TRight) => TReturn) & { * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLessThan = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaLessThanBrand: unique symbol; -}; +declare type LuaLessThan = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaLessThanBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left < right`, where `left` is the object with the method. @@ -380,9 +418,10 @@ declare type LuaLessThan = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLessThanMethod = ((right: TRight) => TReturn) & { - readonly __luaLessThanMethodBrand: unique symbol; -}; +declare type LuaLessThanMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaLessThanMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `left > right`. @@ -392,9 +431,10 @@ declare type LuaLessThanMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaGreaterThan = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaGreaterThanBrand: unique symbol; -}; +declare type LuaGreaterThan = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension & { + readonly __luaGreaterThanBrand: unique symbol; + }; /** * Calls to methods with this type are translated to `left > right`, where `left` is the object with the method. @@ -403,9 +443,10 @@ declare type LuaGreaterThan = ((left: TLeft, right: TRig * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaGreaterThanMethod = ((right: TRight) => TReturn) & { - readonly __luaGreaterThanMethodBrand: unique symbol; -}; +declare type LuaGreaterThanMethod = ((right: TRight) => TReturn) & + LuaExtension & { + readonly __luaGreaterThanMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `-operand`. @@ -414,9 +455,10 @@ declare type LuaGreaterThanMethod = ((right: TRight) => TReturn * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaNegation = ((operand: TOperand) => TReturn) & { - readonly __luaNegationBrand: unique symbol; -}; +declare type LuaNegation = ((operand: TOperand) => TReturn) & + LuaExtension & { + readonly __luaNegationBrand: unique symbol; + }; /** * Calls to method with this type are translated to `-operand`, where `operand` is the object with the method. @@ -424,7 +466,10 @@ declare type LuaNegation = ((operand: TOperand) => TReturn) & * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaNegationMethod = (() => TReturn) & { readonly __luaNegationMethodBrand: unique symbol }; +declare type LuaNegationMethod = (() => TReturn) & + LuaExtension & { + readonly __luaNegationMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `~operand`. @@ -433,9 +478,10 @@ declare type LuaNegationMethod = (() => TReturn) & { readonly __luaNega * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) & { - readonly __luaBitwiseNotBrand: unique symbol; -}; +declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) & + LuaExtension & { + readonly __luaBitwiseNotBrand: unique symbol; + }; /** * Calls to method with this type are translated to `~operand`, where `operand` is the object with the method. @@ -443,7 +489,10 @@ declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseNotMethod = (() => TReturn) & { readonly __luaBitwiseNotMethodBrand: unique symbol }; +declare type LuaBitwiseNotMethod = (() => TReturn) & + LuaExtension & { + readonly __luaBitwiseNotMethodBrand: unique symbol; + }; /** * Calls to functions with this type are translated to `#operand`. @@ -452,9 +501,10 @@ declare type LuaBitwiseNotMethod = (() => TReturn) & { readonly __luaBi * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLength = ((operand: TOperand) => TReturn) & { - readonly __luaLengthBrand: unique symbol; -}; +declare type LuaLength = ((operand: TOperand) => TReturn) & + LuaExtension & { + readonly __luaLengthBrand: unique symbol; + }; /** * Calls to method with this type are translated to `#operand`, where `operand` is the object with the method. @@ -462,4 +512,7 @@ declare type LuaLength = ((operand: TOperand) => TReturn) & { * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLengthMethod = (() => TReturn) & { readonly __luaLengthMethodBrand: unique symbol }; +declare type LuaLengthMethod = (() => TReturn) & + LuaExtension & { + readonly __luaLengthMethodBrand: unique symbol; + }; diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index efcd7d810..aedf6df9e 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -49,61 +49,69 @@ const extensionKindToFunctionName: { [T in ExtensionKind]?: string } = { [ExtensionKind.RangeFunction]: "$range", }; -const extensionKindToTypeBrand: { [T in ExtensionKind]: string } = { - [ExtensionKind.MultiFunction]: "__luaMultiFunctionBrand", - [ExtensionKind.MultiType]: "__luaMultiReturnBrand", - [ExtensionKind.RangeFunction]: "__luaRangeFunctionBrand", - [ExtensionKind.IterableType]: "__luaIterableBrand", - [ExtensionKind.AdditionOperatorType]: "__luaAdditionBrand", - [ExtensionKind.AdditionOperatorMethodType]: "__luaAdditionMethodBrand", - [ExtensionKind.SubtractionOperatorType]: "__luaSubtractionBrand", - [ExtensionKind.SubtractionOperatorMethodType]: "__luaSubtractionMethodBrand", - [ExtensionKind.MultiplicationOperatorType]: "__luaMultiplicationBrand", - [ExtensionKind.MultiplicationOperatorMethodType]: "__luaMultiplicationMethodBrand", - [ExtensionKind.DivisionOperatorType]: "__luaDivisionBrand", - [ExtensionKind.DivisionOperatorMethodType]: "__luaDivisionMethodBrand", - [ExtensionKind.ModuloOperatorType]: "__luaModuloBrand", - [ExtensionKind.ModuloOperatorMethodType]: "__luaModuloMethodBrand", - [ExtensionKind.PowerOperatorType]: "__luaPowerBrand", - [ExtensionKind.PowerOperatorMethodType]: "__luaPowerMethodBrand", - [ExtensionKind.FloorDivisionOperatorType]: "__luaFloorDivisionBrand", - [ExtensionKind.FloorDivisionOperatorMethodType]: "__luaFloorDivisionMethodBrand", - [ExtensionKind.BitwiseAndOperatorType]: "__luaBitwiseAndBrand", - [ExtensionKind.BitwiseAndOperatorMethodType]: "__luaBitwiseAndMethodBrand", - [ExtensionKind.BitwiseOrOperatorType]: "__luaBitwiseOrBrand", - [ExtensionKind.BitwiseOrOperatorMethodType]: "__luaBitwiseOrMethodBrand", - [ExtensionKind.BitwiseExclusiveOrOperatorType]: "__luaBitwiseExclusiveOrBrand", - [ExtensionKind.BitwiseExclusiveOrOperatorMethodType]: "__luaBitwiseExclusiveOrMethodBrand", - [ExtensionKind.BitwiseLeftShiftOperatorType]: "__luaBitwiseLeftShiftBrand", - [ExtensionKind.BitwiseLeftShiftOperatorMethodType]: "__luaBitwiseLeftShiftMethodBrand", - [ExtensionKind.BitwiseRightShiftOperatorType]: "__luaBitwiseRightShiftBrand", - [ExtensionKind.BitwiseRightShiftOperatorMethodType]: "__luaBitwiseRightShiftMethodBrand", - [ExtensionKind.ConcatOperatorType]: "__luaConcatBrand", - [ExtensionKind.ConcatOperatorMethodType]: "__luaConcatMethodBrand", - [ExtensionKind.LessThanOperatorType]: "__luaLessThanBrand", - [ExtensionKind.LessThanOperatorMethodType]: "__luaLessThanMethodBrand", - [ExtensionKind.GreaterThanOperatorType]: "__luaGreaterThanBrand", - [ExtensionKind.GreaterThanOperatorMethodType]: "__luaGreaterThanMethodBrand", - [ExtensionKind.NegationOperatorType]: "__luaNegationBrand", - [ExtensionKind.NegationOperatorMethodType]: "__luaNegationMethodBrand", - [ExtensionKind.BitwiseNotOperatorType]: "__luaBitwiseNotBrand", - [ExtensionKind.BitwiseNotOperatorMethodType]: "__luaBitwiseNotMethodBrand", - [ExtensionKind.LengthOperatorType]: "__luaLengthBrand", - [ExtensionKind.LengthOperatorMethodType]: "__luaLengthMethodBrand", +const typeBrandToExtensionKind: { [brand: string]: ExtensionKind } = { + __luaMultiFunctionBrand: ExtensionKind.MultiFunction, + __luaMultiReturnBrand: ExtensionKind.MultiType, + __luaRangeFunctionBrand: ExtensionKind.RangeFunction, + __luaIterableBrand: ExtensionKind.IterableType, + __luaAdditionBrand: ExtensionKind.AdditionOperatorType, + __luaAdditionMethodBrand: ExtensionKind.AdditionOperatorMethodType, + __luaSubtractionBrand: ExtensionKind.SubtractionOperatorType, + __luaSubtractionMethodBrand: ExtensionKind.SubtractionOperatorMethodType, + __luaMultiplicationBrand: ExtensionKind.MultiplicationOperatorType, + __luaMultiplicationMethodBrand: ExtensionKind.MultiplicationOperatorMethodType, + __luaDivisionBrand: ExtensionKind.DivisionOperatorType, + __luaDivisionMethodBrand: ExtensionKind.DivisionOperatorMethodType, + __luaModuloBrand: ExtensionKind.ModuloOperatorType, + __luaModuloMethodBrand: ExtensionKind.ModuloOperatorMethodType, + __luaPowerBrand: ExtensionKind.PowerOperatorType, + __luaPowerMethodBrand: ExtensionKind.PowerOperatorMethodType, + __luaFloorDivisionBrand: ExtensionKind.FloorDivisionOperatorType, + __luaFloorDivisionMethodBrand: ExtensionKind.FloorDivisionOperatorMethodType, + __luaBitwiseAndBrand: ExtensionKind.BitwiseAndOperatorType, + __luaBitwiseAndMethodBrand: ExtensionKind.BitwiseAndOperatorMethodType, + __luaBitwiseOrBrand: ExtensionKind.BitwiseOrOperatorType, + __luaBitwiseOrMethodBrand: ExtensionKind.BitwiseOrOperatorMethodType, + __luaBitwiseExclusiveOrBrand: ExtensionKind.BitwiseExclusiveOrOperatorType, + __luaBitwiseExclusiveOrMethodBrand: ExtensionKind.BitwiseExclusiveOrOperatorMethodType, + __luaBitwiseLeftShiftBrand: ExtensionKind.BitwiseLeftShiftOperatorType, + __luaBitwiseLeftShiftMethodBrand: ExtensionKind.BitwiseLeftShiftOperatorMethodType, + __luaBitwiseRightShiftBrand: ExtensionKind.BitwiseRightShiftOperatorType, + __luaBitwiseRightShiftMethodBrand: ExtensionKind.BitwiseRightShiftOperatorMethodType, + __luaConcatBrand: ExtensionKind.ConcatOperatorType, + __luaConcatMethodBrand: ExtensionKind.ConcatOperatorMethodType, + __luaLessThanBrand: ExtensionKind.LessThanOperatorType, + __luaLessThanMethodBrand: ExtensionKind.LessThanOperatorMethodType, + __luaGreaterThanBrand: ExtensionKind.GreaterThanOperatorType, + __luaGreaterThanMethodBrand: ExtensionKind.GreaterThanOperatorMethodType, + __luaNegationBrand: ExtensionKind.NegationOperatorType, + __luaNegationMethodBrand: ExtensionKind.NegationOperatorMethodType, + __luaBitwiseNotBrand: ExtensionKind.BitwiseNotOperatorType, + __luaBitwiseNotMethodBrand: ExtensionKind.BitwiseNotOperatorMethodType, + __luaLengthBrand: ExtensionKind.LengthOperatorType, + __luaLengthMethodBrand: ExtensionKind.LengthOperatorMethodType, }; -export function isExtensionType(type: ts.Type, extensionKind: ExtensionKind): boolean { - const typeBrand = extensionKindToTypeBrand[extensionKind]; - return typeBrand !== undefined && type.getProperty(typeBrand) !== undefined; +export function getExtensionKinds(type: ts.Type): ExtensionKind[] { + if (type.getProperty("__luaExtensionBrand")) { + return type + .getProperties() + .map(property => typeBrandToExtensionKind[property.name]) + .filter(kind => kind !== undefined); + } else { + return []; + } } export function isExtensionFunction( context: TransformationContext, - symbol: ts.Symbol, + node: ts.Node, extensionKind: ExtensionKind ): boolean { + const symbol = context.checker.getSymbolAtLocation(node); return ( + symbol !== undefined && symbol.getName() === extensionKindToFunctionName[extensionKind] && - symbol.declarations.some(d => isExtensionType(context.checker.getTypeAtLocation(d), extensionKind)) + getExtensionKinds(context.checker.getTypeAtLocation(node)).includes(extensionKind) ); } diff --git a/src/transformation/visitors/language-extensions/iterable.ts b/src/transformation/visitors/language-extensions/iterable.ts index 769e15093..6a4194d2f 100644 --- a/src/transformation/visitors/language-extensions/iterable.ts +++ b/src/transformation/visitors/language-extensions/iterable.ts @@ -9,7 +9,7 @@ import { cast } from "../../../utils"; import { isMultiReturnType } from "./multi"; export function isIterableType(type: ts.Type): boolean { - return extensions.isExtensionType(type, extensions.ExtensionKind.IterableType); + return extensions.getExtensionKinds(type).includes(extensions.ExtensionKind.IterableType); } export function returnsIterableType(context: TransformationContext, node: ts.CallExpression): boolean { diff --git a/src/transformation/visitors/language-extensions/multi.ts b/src/transformation/visitors/language-extensions/multi.ts index b292d9aa1..bfd720edd 100644 --- a/src/transformation/visitors/language-extensions/multi.ts +++ b/src/transformation/visitors/language-extensions/multi.ts @@ -6,7 +6,7 @@ import { isIterableExpression } from "./iterable"; import { invalidMultiFunctionUse } from "../../utils/diagnostics"; export function isMultiReturnType(type: ts.Type): boolean { - return extensions.isExtensionType(type, extensions.ExtensionKind.MultiType); + return extensions.getExtensionKinds(type).includes(extensions.ExtensionKind.MultiType); } export function isMultiFunctionCall(context: TransformationContext, expression: ts.CallExpression): boolean { @@ -24,8 +24,7 @@ export function isMultiReturnCall(context: TransformationContext, expression: ts } export function isMultiFunctionNode(context: TransformationContext, node: ts.Node): boolean { - const symbol = context.checker.getSymbolAtLocation(node); - return symbol ? extensions.isExtensionFunction(context, symbol, extensions.ExtensionKind.MultiFunction) : false; + return extensions.isExtensionFunction(context, node, extensions.ExtensionKind.MultiFunction); } export function isInMultiReturnFunction(context: TransformationContext, node: ts.Node) { @@ -97,8 +96,9 @@ export function findMultiAssignmentViolations( for (const element of node.properties) { if (!ts.isShorthandPropertyAssignment(element)) continue; const valueSymbol = context.checker.getShorthandAssignmentValueSymbol(element); - if (valueSymbol) { - if (extensions.isExtensionFunction(context, valueSymbol, extensions.ExtensionKind.MultiFunction)) { + if (valueSymbol?.valueDeclaration) { + const type = context.checker.getTypeAtLocation(valueSymbol.valueDeclaration); + if (extensions.getExtensionKinds(type).includes(extensions.ExtensionKind.MultiFunction)) { context.diagnostics.push(invalidMultiFunctionUse(element)); result.push(element); } diff --git a/src/transformation/visitors/language-extensions/operators.ts b/src/transformation/visitors/language-extensions/operators.ts index dddecf338..2060e24fa 100644 --- a/src/transformation/visitors/language-extensions/operators.ts +++ b/src/transformation/visitors/language-extensions/operators.ts @@ -49,7 +49,7 @@ const unaryOperatorMappings = new Map([ extensions.ExtensionKind.BitwiseAndOperatorType, @@ -82,27 +82,15 @@ function getOperatorMapExtensionKindForCall(context: TransformationContext, node return; } const type = context.checker.getTypeFromTypeNode(typeDeclaration.type); - return operatorMapExtensions.find(extensionKind => extensions.isExtensionType(type, extensionKind)); -} - -function isOperatorMapType(context: TransformationContext, type: ts.Type): boolean { - if (type.isUnionOrIntersection()) { - return type.types.some(t => isOperatorMapType(context, t)); - } else { - return operatorMapExtensions.some(extensionKind => extensions.isExtensionType(type, extensionKind)); - } -} - -function isOperatorMapIdentifier(context: TransformationContext, node: ts.Identifier) { - const type = context.checker.getTypeAtLocation(node); - return isOperatorMapType(context, type); + return extensions.getExtensionKinds(type).find(extensionKind => operatorMapExtensions.has(extensionKind)); } export function isOperatorMapping(context: TransformationContext, node: ts.CallExpression | ts.Identifier) { if (ts.isCallExpression(node)) { return getOperatorMapExtensionKindForCall(context, node) !== undefined; } else { - return isOperatorMapIdentifier(context, node); + const type = context.checker.getTypeAtLocation(node); + return extensions.getExtensionKinds(type).some(extensionKind => operatorMapExtensions.has(extensionKind)); } } diff --git a/src/transformation/visitors/language-extensions/range.ts b/src/transformation/visitors/language-extensions/range.ts index 00a3c249e..9430ec1a0 100644 --- a/src/transformation/visitors/language-extensions/range.ts +++ b/src/transformation/visitors/language-extensions/range.ts @@ -13,8 +13,7 @@ export function isRangeFunction(context: TransformationContext, expression: ts.C } export function isRangeFunctionNode(context: TransformationContext, node: ts.Node): boolean { - const symbol = context.checker.getSymbolAtLocation(node); - return symbol ? extensions.isExtensionFunction(context, symbol, extensions.ExtensionKind.RangeFunction) : false; + return extensions.isExtensionFunction(context, node, extensions.ExtensionKind.RangeFunction); } function getControlVariable(context: TransformationContext, statement: ts.ForOfStatement) { From 74d797b40436a72069a432ec17d18e1e7b4ec6fe Mon Sep 17 00:00:00 2001 From: Tom Date: Thu, 18 Feb 2021 18:46:44 -0700 Subject: [PATCH 08/10] updated new multi tests to actually use multiple values --- test/unit/language-extensions/multi.spec.ts | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/test/unit/language-extensions/multi.spec.ts b/test/unit/language-extensions/multi.spec.ts index 6295810f8..0b99cf4dc 100644 --- a/test/unit/language-extensions/multi.spec.ts +++ b/test/unit/language-extensions/multi.spec.ts @@ -132,35 +132,35 @@ test("allow $multi call in ArrowFunction body", () => { test("forward $multi call", () => { util.testFunction` - function foo() { return $multi(1); } + function foo() { return $multi(1, 2); } function call() { return foo(); } - const [result] = call(); - return result; + const [resultA, resultB] = call(); + return [resultA, resultB]; ` .setOptions(multiProjectOptions) - .expectToEqual(1); + .expectToEqual([1, 2]); }); test("forward $multi call indirect", () => { util.testFunction` - function foo() { return $multi(1); } + function foo() { return $multi(1, 2); } function call() { const m = foo(); return m; } - const [result] = call(); - return result; + const [resultA, resultB] = call(); + return [resultA, resultB]; ` .setOptions(multiProjectOptions) - .expectToEqual(1); + .expectToEqual([1, 2]); }); test("forward $multi call in ArrowFunction body", () => { util.testFunction` - const foo = () => $multi(1); + const foo = () => $multi(1, 2); const call = () => foo(); - const [result] = call(); - return result; + const [resultA, resultB] = call(); + return [resultA, resultB]; ` .setOptions(multiProjectOptions) - .expectToEqual(1); + .expectToEqual([1, 2]); }); test.each(["0", "i"])("allow LuaMultiReturn numeric access (%s)", expression => { From f44c6c71766762a36fd5d5779cfe0eb8e501a8eb Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 19 Feb 2021 17:48:17 -0700 Subject: [PATCH 09/10] Revert "updated extension kind checking to reduce complexity" This reverts commit 12ec43577618bb54393373cfbb5cf26858a7351d. --- language-extensions/index.d.ts | 273 +++++++----------- .../utils/language-extensions.ts | 100 +++---- .../visitors/language-extensions/iterable.ts | 2 +- .../visitors/language-extensions/multi.ts | 10 +- .../visitors/language-extensions/operators.ts | 20 +- .../visitors/language-extensions/range.ts | 3 +- 6 files changed, 180 insertions(+), 228 deletions(-) diff --git a/language-extensions/index.d.ts b/language-extensions/index.d.ts index b51eaee8e..e4290ce8e 100644 --- a/language-extensions/index.d.ts +++ b/language-extensions/index.d.ts @@ -1,11 +1,3 @@ -/** - * Indicates a type is a language extension provided by TypescriptToLua. - * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions - */ -interface LuaExtension { - readonly __luaExtensionBrand: unique symbol; -} - /** * Returns multiple values from a function, by wrapping them in a LuaMultiReturn tuple. * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions @@ -13,10 +5,9 @@ interface LuaExtension { * @param T A tuple type with each element type representing a return value's type. * @param values Return values. */ -declare const $multi: ((...values: T) => LuaMultiReturn) & - LuaExtension & { - readonly __luaMultiFunctionBrand: unique symbol; - }; +declare const $multi: ((...values: T) => LuaMultiReturn) & { + readonly __luaMultiFunctionBrand: unique symbol; +}; /** * Represents multiple return values as a tuple. @@ -24,7 +15,7 @@ declare const $multi: ((...values: T) => LuaMultiReturn) & * * @param T A tuple type with each element type representing a return value's type. */ -declare type LuaMultiReturn = T & LuaExtension & { readonly __luaMultiReturnBrand: unique symbol }; +declare type LuaMultiReturn = T & { readonly __luaMultiReturnBrand: unique symbol }; /** * Creates a Lua-style numeric for loop (for i=start,limit,step) when used in for...of. Not valid in any other context. @@ -34,10 +25,9 @@ declare type LuaMultiReturn = T & LuaExtension & { readonly __l * @param limit The last number in the sequence to iterate over. * @param step The amount to increment each iteration. */ -declare const $range: ((start: number, limit: number, step?: number) => Iterable) & - LuaExtension & { - readonly __luaRangeFunctionBrand: unique symbol; - }; +declare const $range: ((start: number, limit: number, step?: number) => Iterable) & { + readonly __luaRangeFunctionBrand: unique symbol; +}; /** * Represents a Lua-style iterator which is returned from a LuaIterable. @@ -70,8 +60,7 @@ declare type LuaIterator = TState extends undefined * @param TState The type of the state value passed back to the iterator function each iteration. */ declare type LuaIterable = Iterable & - LuaIterator & - LuaExtension & { readonly __luaIterableBrand: unique symbol }; + LuaIterator & { readonly __luaIterableBrand: unique symbol }; /** * Calls to functions with this type are translated to `left + right`. @@ -81,10 +70,9 @@ declare type LuaIterable = Iterable & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaAddition = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaAdditionBrand: unique symbol; - }; +declare type LuaAddition = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaAdditionBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left + right`, where `left` is the object with the method. @@ -93,10 +81,9 @@ declare type LuaAddition = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaAdditionMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaAdditionMethodBrand: unique symbol; - }; +declare type LuaAdditionMethod = ((right: TRight) => TReturn) & { + readonly __luaAdditionMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left - right`. @@ -106,10 +93,9 @@ declare type LuaAdditionMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaSubtraction = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaSubtractionBrand: unique symbol; - }; +declare type LuaSubtraction = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaSubtractionBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left - right`, where `left` is the object with the method. @@ -118,10 +104,9 @@ declare type LuaSubtraction = ((left: TLeft, right: TRig * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaSubtractionMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaSubtractionMethodBrand: unique symbol; - }; +declare type LuaSubtractionMethod = ((right: TRight) => TReturn) & { + readonly __luaSubtractionMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left * right`. @@ -131,10 +116,9 @@ declare type LuaSubtractionMethod = ((right: TRight) => TReturn * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaMultiplication = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaMultiplicationBrand: unique symbol; - }; +declare type LuaMultiplication = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaMultiplicationBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left * right`, where `left` is the object with the method. @@ -143,10 +127,9 @@ declare type LuaMultiplication = ((left: TLeft, right: T * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaMultiplicationMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaMultiplicationMethodBrand: unique symbol; - }; +declare type LuaMultiplicationMethod = ((right: TRight) => TReturn) & { + readonly __luaMultiplicationMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left / right`. @@ -156,10 +139,9 @@ declare type LuaMultiplicationMethod = ((right: TRight) => TRet * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaDivision = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaDivisionBrand: unique symbol; - }; +declare type LuaDivision = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaDivisionBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left / right`, where `left` is the object with the method. @@ -168,10 +150,9 @@ declare type LuaDivision = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaDivisionMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaDivisionMethodBrand: unique symbol; - }; +declare type LuaDivisionMethod = ((right: TRight) => TReturn) & { + readonly __luaDivisionMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left % right`. @@ -181,10 +162,9 @@ declare type LuaDivisionMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaModulo = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaModuloBrand: unique symbol; - }; +declare type LuaModulo = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaModuloBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left % right`, where `left` is the object with the method. @@ -193,10 +173,9 @@ declare type LuaModulo = ((left: TLeft, right: TRight) = * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaModuloMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaModuloMethodBrand: unique symbol; - }; +declare type LuaModuloMethod = ((right: TRight) => TReturn) & { + readonly __luaModuloMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left ^ right`. @@ -206,10 +185,9 @@ declare type LuaModuloMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaPower = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaPowerBrand: unique symbol; - }; +declare type LuaPower = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaPowerBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left ^ right`, where `left` is the object with the method. @@ -218,10 +196,9 @@ declare type LuaPower = ((left: TLeft, right: TRight) => * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaPowerMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaPowerMethodBrand: unique symbol; - }; +declare type LuaPowerMethod = ((right: TRight) => TReturn) & { + readonly __luaPowerMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left // right`. @@ -231,10 +208,9 @@ declare type LuaPowerMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaFloorDivision = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaFloorDivisionBrand: unique symbol; - }; +declare type LuaFloorDivision = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaFloorDivisionBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left // right`, where `left` is the object with the method. @@ -243,10 +219,9 @@ declare type LuaFloorDivision = ((left: TLeft, right: TR * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaFloorDivisionMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaFloorDivisionMethodBrand: unique symbol; - }; +declare type LuaFloorDivisionMethod = ((right: TRight) => TReturn) & { + readonly __luaFloorDivisionMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left & right`. @@ -256,10 +231,9 @@ declare type LuaFloorDivisionMethod = ((right: TRight) => TRetu * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseAnd = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseAndBrand: unique symbol; - }; +declare type LuaBitwiseAnd = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaBitwiseAndBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left & right`, where `left` is the object with the method. @@ -268,10 +242,9 @@ declare type LuaBitwiseAnd = ((left: TLeft, right: TRigh * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseAndMethodBrand: unique symbol; - }; +declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) & { + readonly __luaBitwiseAndMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left | right`. @@ -281,10 +254,9 @@ declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseOr = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseOrBrand: unique symbol; - }; +declare type LuaBitwiseOr = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaBitwiseOrBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left | right`, where `left` is the object with the method. @@ -293,10 +265,9 @@ declare type LuaBitwiseOr = ((left: TLeft, right: TRight * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseOrMethodBrand: unique symbol; - }; +declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) & { + readonly __luaBitwiseOrMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left ~ right`. @@ -306,10 +277,9 @@ declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseExclusiveOr = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseExclusiveOrBrand: unique symbol; - }; +declare type LuaBitwiseExclusiveOr = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaBitwiseExclusiveOrBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left ~ right`, where `left` is the object with the method. @@ -318,10 +288,9 @@ declare type LuaBitwiseExclusiveOr = ((left: TLeft, righ * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseExclusiveOrMethodBrand: unique symbol; - }; +declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => TReturn) & { + readonly __luaBitwiseExclusiveOrMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left << right`. @@ -331,10 +300,9 @@ declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseLeftShift = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseLeftShiftBrand: unique symbol; - }; +declare type LuaBitwiseLeftShift = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaBitwiseLeftShiftBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left << right`, where `left` is the object with the method. @@ -343,10 +311,9 @@ declare type LuaBitwiseLeftShift = ((left: TLeft, right: * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseLeftShiftMethodBrand: unique symbol; - }; +declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TReturn) & { + readonly __luaBitwiseLeftShiftMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left >> right`. @@ -356,10 +323,9 @@ declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TR * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseRightShift = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseRightShiftBrand: unique symbol; - }; +declare type LuaBitwiseRightShift = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaBitwiseRightShiftBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left >> right`, where `left` is the object with the method. @@ -368,10 +334,9 @@ declare type LuaBitwiseRightShift = ((left: TLeft, right * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseRightShiftMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaBitwiseRightShiftMethodBrand: unique symbol; - }; +declare type LuaBitwiseRightShiftMethod = ((right: TRight) => TReturn) & { + readonly __luaBitwiseRightShiftMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left .. right`. @@ -381,10 +346,9 @@ declare type LuaBitwiseRightShiftMethod = ((right: TRight) => T * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaConcat = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaConcatBrand: unique symbol; - }; +declare type LuaConcat = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaConcatBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left .. right`, where `left` is the object with the method. @@ -393,10 +357,9 @@ declare type LuaConcat = ((left: TLeft, right: TRight) = * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaConcatMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaConcatMethodBrand: unique symbol; - }; +declare type LuaConcatMethod = ((right: TRight) => TReturn) & { + readonly __luaConcatMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left < right`. @@ -406,10 +369,9 @@ declare type LuaConcatMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLessThan = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaLessThanBrand: unique symbol; - }; +declare type LuaLessThan = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaLessThanBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left < right`, where `left` is the object with the method. @@ -418,10 +380,9 @@ declare type LuaLessThan = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLessThanMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaLessThanMethodBrand: unique symbol; - }; +declare type LuaLessThanMethod = ((right: TRight) => TReturn) & { + readonly __luaLessThanMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `left > right`. @@ -431,10 +392,9 @@ declare type LuaLessThanMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaGreaterThan = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension & { - readonly __luaGreaterThanBrand: unique symbol; - }; +declare type LuaGreaterThan = ((left: TLeft, right: TRight) => TReturn) & { + readonly __luaGreaterThanBrand: unique symbol; +}; /** * Calls to methods with this type are translated to `left > right`, where `left` is the object with the method. @@ -443,10 +403,9 @@ declare type LuaGreaterThan = ((left: TLeft, right: TRig * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaGreaterThanMethod = ((right: TRight) => TReturn) & - LuaExtension & { - readonly __luaGreaterThanMethodBrand: unique symbol; - }; +declare type LuaGreaterThanMethod = ((right: TRight) => TReturn) & { + readonly __luaGreaterThanMethodBrand: unique symbol; +}; /** * Calls to functions with this type are translated to `-operand`. @@ -455,10 +414,9 @@ declare type LuaGreaterThanMethod = ((right: TRight) => TReturn * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaNegation = ((operand: TOperand) => TReturn) & - LuaExtension & { - readonly __luaNegationBrand: unique symbol; - }; +declare type LuaNegation = ((operand: TOperand) => TReturn) & { + readonly __luaNegationBrand: unique symbol; +}; /** * Calls to method with this type are translated to `-operand`, where `operand` is the object with the method. @@ -466,10 +424,7 @@ declare type LuaNegation = ((operand: TOperand) => TReturn) & * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaNegationMethod = (() => TReturn) & - LuaExtension & { - readonly __luaNegationMethodBrand: unique symbol; - }; +declare type LuaNegationMethod = (() => TReturn) & { readonly __luaNegationMethodBrand: unique symbol }; /** * Calls to functions with this type are translated to `~operand`. @@ -478,10 +433,9 @@ declare type LuaNegationMethod = (() => TReturn) & * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) & - LuaExtension & { - readonly __luaBitwiseNotBrand: unique symbol; - }; +declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) & { + readonly __luaBitwiseNotBrand: unique symbol; +}; /** * Calls to method with this type are translated to `~operand`, where `operand` is the object with the method. @@ -489,10 +443,7 @@ declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseNotMethod = (() => TReturn) & - LuaExtension & { - readonly __luaBitwiseNotMethodBrand: unique symbol; - }; +declare type LuaBitwiseNotMethod = (() => TReturn) & { readonly __luaBitwiseNotMethodBrand: unique symbol }; /** * Calls to functions with this type are translated to `#operand`. @@ -501,10 +452,9 @@ declare type LuaBitwiseNotMethod = (() => TReturn) & * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLength = ((operand: TOperand) => TReturn) & - LuaExtension & { - readonly __luaLengthBrand: unique symbol; - }; +declare type LuaLength = ((operand: TOperand) => TReturn) & { + readonly __luaLengthBrand: unique symbol; +}; /** * Calls to method with this type are translated to `#operand`, where `operand` is the object with the method. @@ -512,7 +462,4 @@ declare type LuaLength = ((operand: TOperand) => TReturn) & * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLengthMethod = (() => TReturn) & - LuaExtension & { - readonly __luaLengthMethodBrand: unique symbol; - }; +declare type LuaLengthMethod = (() => TReturn) & { readonly __luaLengthMethodBrand: unique symbol }; diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index aedf6df9e..efcd7d810 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -49,69 +49,61 @@ const extensionKindToFunctionName: { [T in ExtensionKind]?: string } = { [ExtensionKind.RangeFunction]: "$range", }; -const typeBrandToExtensionKind: { [brand: string]: ExtensionKind } = { - __luaMultiFunctionBrand: ExtensionKind.MultiFunction, - __luaMultiReturnBrand: ExtensionKind.MultiType, - __luaRangeFunctionBrand: ExtensionKind.RangeFunction, - __luaIterableBrand: ExtensionKind.IterableType, - __luaAdditionBrand: ExtensionKind.AdditionOperatorType, - __luaAdditionMethodBrand: ExtensionKind.AdditionOperatorMethodType, - __luaSubtractionBrand: ExtensionKind.SubtractionOperatorType, - __luaSubtractionMethodBrand: ExtensionKind.SubtractionOperatorMethodType, - __luaMultiplicationBrand: ExtensionKind.MultiplicationOperatorType, - __luaMultiplicationMethodBrand: ExtensionKind.MultiplicationOperatorMethodType, - __luaDivisionBrand: ExtensionKind.DivisionOperatorType, - __luaDivisionMethodBrand: ExtensionKind.DivisionOperatorMethodType, - __luaModuloBrand: ExtensionKind.ModuloOperatorType, - __luaModuloMethodBrand: ExtensionKind.ModuloOperatorMethodType, - __luaPowerBrand: ExtensionKind.PowerOperatorType, - __luaPowerMethodBrand: ExtensionKind.PowerOperatorMethodType, - __luaFloorDivisionBrand: ExtensionKind.FloorDivisionOperatorType, - __luaFloorDivisionMethodBrand: ExtensionKind.FloorDivisionOperatorMethodType, - __luaBitwiseAndBrand: ExtensionKind.BitwiseAndOperatorType, - __luaBitwiseAndMethodBrand: ExtensionKind.BitwiseAndOperatorMethodType, - __luaBitwiseOrBrand: ExtensionKind.BitwiseOrOperatorType, - __luaBitwiseOrMethodBrand: ExtensionKind.BitwiseOrOperatorMethodType, - __luaBitwiseExclusiveOrBrand: ExtensionKind.BitwiseExclusiveOrOperatorType, - __luaBitwiseExclusiveOrMethodBrand: ExtensionKind.BitwiseExclusiveOrOperatorMethodType, - __luaBitwiseLeftShiftBrand: ExtensionKind.BitwiseLeftShiftOperatorType, - __luaBitwiseLeftShiftMethodBrand: ExtensionKind.BitwiseLeftShiftOperatorMethodType, - __luaBitwiseRightShiftBrand: ExtensionKind.BitwiseRightShiftOperatorType, - __luaBitwiseRightShiftMethodBrand: ExtensionKind.BitwiseRightShiftOperatorMethodType, - __luaConcatBrand: ExtensionKind.ConcatOperatorType, - __luaConcatMethodBrand: ExtensionKind.ConcatOperatorMethodType, - __luaLessThanBrand: ExtensionKind.LessThanOperatorType, - __luaLessThanMethodBrand: ExtensionKind.LessThanOperatorMethodType, - __luaGreaterThanBrand: ExtensionKind.GreaterThanOperatorType, - __luaGreaterThanMethodBrand: ExtensionKind.GreaterThanOperatorMethodType, - __luaNegationBrand: ExtensionKind.NegationOperatorType, - __luaNegationMethodBrand: ExtensionKind.NegationOperatorMethodType, - __luaBitwiseNotBrand: ExtensionKind.BitwiseNotOperatorType, - __luaBitwiseNotMethodBrand: ExtensionKind.BitwiseNotOperatorMethodType, - __luaLengthBrand: ExtensionKind.LengthOperatorType, - __luaLengthMethodBrand: ExtensionKind.LengthOperatorMethodType, +const extensionKindToTypeBrand: { [T in ExtensionKind]: string } = { + [ExtensionKind.MultiFunction]: "__luaMultiFunctionBrand", + [ExtensionKind.MultiType]: "__luaMultiReturnBrand", + [ExtensionKind.RangeFunction]: "__luaRangeFunctionBrand", + [ExtensionKind.IterableType]: "__luaIterableBrand", + [ExtensionKind.AdditionOperatorType]: "__luaAdditionBrand", + [ExtensionKind.AdditionOperatorMethodType]: "__luaAdditionMethodBrand", + [ExtensionKind.SubtractionOperatorType]: "__luaSubtractionBrand", + [ExtensionKind.SubtractionOperatorMethodType]: "__luaSubtractionMethodBrand", + [ExtensionKind.MultiplicationOperatorType]: "__luaMultiplicationBrand", + [ExtensionKind.MultiplicationOperatorMethodType]: "__luaMultiplicationMethodBrand", + [ExtensionKind.DivisionOperatorType]: "__luaDivisionBrand", + [ExtensionKind.DivisionOperatorMethodType]: "__luaDivisionMethodBrand", + [ExtensionKind.ModuloOperatorType]: "__luaModuloBrand", + [ExtensionKind.ModuloOperatorMethodType]: "__luaModuloMethodBrand", + [ExtensionKind.PowerOperatorType]: "__luaPowerBrand", + [ExtensionKind.PowerOperatorMethodType]: "__luaPowerMethodBrand", + [ExtensionKind.FloorDivisionOperatorType]: "__luaFloorDivisionBrand", + [ExtensionKind.FloorDivisionOperatorMethodType]: "__luaFloorDivisionMethodBrand", + [ExtensionKind.BitwiseAndOperatorType]: "__luaBitwiseAndBrand", + [ExtensionKind.BitwiseAndOperatorMethodType]: "__luaBitwiseAndMethodBrand", + [ExtensionKind.BitwiseOrOperatorType]: "__luaBitwiseOrBrand", + [ExtensionKind.BitwiseOrOperatorMethodType]: "__luaBitwiseOrMethodBrand", + [ExtensionKind.BitwiseExclusiveOrOperatorType]: "__luaBitwiseExclusiveOrBrand", + [ExtensionKind.BitwiseExclusiveOrOperatorMethodType]: "__luaBitwiseExclusiveOrMethodBrand", + [ExtensionKind.BitwiseLeftShiftOperatorType]: "__luaBitwiseLeftShiftBrand", + [ExtensionKind.BitwiseLeftShiftOperatorMethodType]: "__luaBitwiseLeftShiftMethodBrand", + [ExtensionKind.BitwiseRightShiftOperatorType]: "__luaBitwiseRightShiftBrand", + [ExtensionKind.BitwiseRightShiftOperatorMethodType]: "__luaBitwiseRightShiftMethodBrand", + [ExtensionKind.ConcatOperatorType]: "__luaConcatBrand", + [ExtensionKind.ConcatOperatorMethodType]: "__luaConcatMethodBrand", + [ExtensionKind.LessThanOperatorType]: "__luaLessThanBrand", + [ExtensionKind.LessThanOperatorMethodType]: "__luaLessThanMethodBrand", + [ExtensionKind.GreaterThanOperatorType]: "__luaGreaterThanBrand", + [ExtensionKind.GreaterThanOperatorMethodType]: "__luaGreaterThanMethodBrand", + [ExtensionKind.NegationOperatorType]: "__luaNegationBrand", + [ExtensionKind.NegationOperatorMethodType]: "__luaNegationMethodBrand", + [ExtensionKind.BitwiseNotOperatorType]: "__luaBitwiseNotBrand", + [ExtensionKind.BitwiseNotOperatorMethodType]: "__luaBitwiseNotMethodBrand", + [ExtensionKind.LengthOperatorType]: "__luaLengthBrand", + [ExtensionKind.LengthOperatorMethodType]: "__luaLengthMethodBrand", }; -export function getExtensionKinds(type: ts.Type): ExtensionKind[] { - if (type.getProperty("__luaExtensionBrand")) { - return type - .getProperties() - .map(property => typeBrandToExtensionKind[property.name]) - .filter(kind => kind !== undefined); - } else { - return []; - } +export function isExtensionType(type: ts.Type, extensionKind: ExtensionKind): boolean { + const typeBrand = extensionKindToTypeBrand[extensionKind]; + return typeBrand !== undefined && type.getProperty(typeBrand) !== undefined; } export function isExtensionFunction( context: TransformationContext, - node: ts.Node, + symbol: ts.Symbol, extensionKind: ExtensionKind ): boolean { - const symbol = context.checker.getSymbolAtLocation(node); return ( - symbol !== undefined && symbol.getName() === extensionKindToFunctionName[extensionKind] && - getExtensionKinds(context.checker.getTypeAtLocation(node)).includes(extensionKind) + symbol.declarations.some(d => isExtensionType(context.checker.getTypeAtLocation(d), extensionKind)) ); } diff --git a/src/transformation/visitors/language-extensions/iterable.ts b/src/transformation/visitors/language-extensions/iterable.ts index 6a4194d2f..769e15093 100644 --- a/src/transformation/visitors/language-extensions/iterable.ts +++ b/src/transformation/visitors/language-extensions/iterable.ts @@ -9,7 +9,7 @@ import { cast } from "../../../utils"; import { isMultiReturnType } from "./multi"; export function isIterableType(type: ts.Type): boolean { - return extensions.getExtensionKinds(type).includes(extensions.ExtensionKind.IterableType); + return extensions.isExtensionType(type, extensions.ExtensionKind.IterableType); } export function returnsIterableType(context: TransformationContext, node: ts.CallExpression): boolean { diff --git a/src/transformation/visitors/language-extensions/multi.ts b/src/transformation/visitors/language-extensions/multi.ts index bfd720edd..b292d9aa1 100644 --- a/src/transformation/visitors/language-extensions/multi.ts +++ b/src/transformation/visitors/language-extensions/multi.ts @@ -6,7 +6,7 @@ import { isIterableExpression } from "./iterable"; import { invalidMultiFunctionUse } from "../../utils/diagnostics"; export function isMultiReturnType(type: ts.Type): boolean { - return extensions.getExtensionKinds(type).includes(extensions.ExtensionKind.MultiType); + return extensions.isExtensionType(type, extensions.ExtensionKind.MultiType); } export function isMultiFunctionCall(context: TransformationContext, expression: ts.CallExpression): boolean { @@ -24,7 +24,8 @@ export function isMultiReturnCall(context: TransformationContext, expression: ts } export function isMultiFunctionNode(context: TransformationContext, node: ts.Node): boolean { - return extensions.isExtensionFunction(context, node, extensions.ExtensionKind.MultiFunction); + const symbol = context.checker.getSymbolAtLocation(node); + return symbol ? extensions.isExtensionFunction(context, symbol, extensions.ExtensionKind.MultiFunction) : false; } export function isInMultiReturnFunction(context: TransformationContext, node: ts.Node) { @@ -96,9 +97,8 @@ export function findMultiAssignmentViolations( for (const element of node.properties) { if (!ts.isShorthandPropertyAssignment(element)) continue; const valueSymbol = context.checker.getShorthandAssignmentValueSymbol(element); - if (valueSymbol?.valueDeclaration) { - const type = context.checker.getTypeAtLocation(valueSymbol.valueDeclaration); - if (extensions.getExtensionKinds(type).includes(extensions.ExtensionKind.MultiFunction)) { + if (valueSymbol) { + if (extensions.isExtensionFunction(context, valueSymbol, extensions.ExtensionKind.MultiFunction)) { context.diagnostics.push(invalidMultiFunctionUse(element)); result.push(element); } diff --git a/src/transformation/visitors/language-extensions/operators.ts b/src/transformation/visitors/language-extensions/operators.ts index 2060e24fa..dddecf338 100644 --- a/src/transformation/visitors/language-extensions/operators.ts +++ b/src/transformation/visitors/language-extensions/operators.ts @@ -49,7 +49,7 @@ const unaryOperatorMappings = new Map([ extensions.ExtensionKind.BitwiseAndOperatorType, @@ -82,15 +82,27 @@ function getOperatorMapExtensionKindForCall(context: TransformationContext, node return; } const type = context.checker.getTypeFromTypeNode(typeDeclaration.type); - return extensions.getExtensionKinds(type).find(extensionKind => operatorMapExtensions.has(extensionKind)); + return operatorMapExtensions.find(extensionKind => extensions.isExtensionType(type, extensionKind)); +} + +function isOperatorMapType(context: TransformationContext, type: ts.Type): boolean { + if (type.isUnionOrIntersection()) { + return type.types.some(t => isOperatorMapType(context, t)); + } else { + return operatorMapExtensions.some(extensionKind => extensions.isExtensionType(type, extensionKind)); + } +} + +function isOperatorMapIdentifier(context: TransformationContext, node: ts.Identifier) { + const type = context.checker.getTypeAtLocation(node); + return isOperatorMapType(context, type); } export function isOperatorMapping(context: TransformationContext, node: ts.CallExpression | ts.Identifier) { if (ts.isCallExpression(node)) { return getOperatorMapExtensionKindForCall(context, node) !== undefined; } else { - const type = context.checker.getTypeAtLocation(node); - return extensions.getExtensionKinds(type).some(extensionKind => operatorMapExtensions.has(extensionKind)); + return isOperatorMapIdentifier(context, node); } } diff --git a/src/transformation/visitors/language-extensions/range.ts b/src/transformation/visitors/language-extensions/range.ts index 9430ec1a0..00a3c249e 100644 --- a/src/transformation/visitors/language-extensions/range.ts +++ b/src/transformation/visitors/language-extensions/range.ts @@ -13,7 +13,8 @@ export function isRangeFunction(context: TransformationContext, expression: ts.C } export function isRangeFunctionNode(context: TransformationContext, node: ts.Node): boolean { - return extensions.isExtensionFunction(context, node, extensions.ExtensionKind.RangeFunction); + const symbol = context.checker.getSymbolAtLocation(node); + return symbol ? extensions.isExtensionFunction(context, symbol, extensions.ExtensionKind.RangeFunction) : false; } function getControlVariable(context: TransformationContext, statement: ts.ForOfStatement) { From a5c54ea6988e669ff1278eee5106e507a3fb5791 Mon Sep 17 00:00:00 2001 From: Tom Date: Fri, 19 Feb 2021 18:19:25 -0700 Subject: [PATCH 10/10] replaced raw brands with LuaExtension type --- language-extensions/index.d.ts | 187 ++++++++++++++------------------- 1 file changed, 77 insertions(+), 110 deletions(-) diff --git a/language-extensions/index.d.ts b/language-extensions/index.d.ts index e4290ce8e..79067b1cd 100644 --- a/language-extensions/index.d.ts +++ b/language-extensions/index.d.ts @@ -1,3 +1,11 @@ +/** + * Indicates a type is a language extension provided by TypescriptToLua. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * + * @param TBrand A string used to uniquely identify the language extension type + */ +declare type LuaExtension = { [T in TBrand]: { readonly __luaExtensionSymbol: unique symbol } }; + /** * Returns multiple values from a function, by wrapping them in a LuaMultiReturn tuple. * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions @@ -5,9 +13,7 @@ * @param T A tuple type with each element type representing a return value's type. * @param values Return values. */ -declare const $multi: ((...values: T) => LuaMultiReturn) & { - readonly __luaMultiFunctionBrand: unique symbol; -}; +declare const $multi: ((...values: T) => LuaMultiReturn) & LuaExtension<"__luaMultiFunctionBrand">; /** * Represents multiple return values as a tuple. @@ -15,7 +21,7 @@ declare const $multi: ((...values: T) => LuaMultiReturn) & { * * @param T A tuple type with each element type representing a return value's type. */ -declare type LuaMultiReturn = T & { readonly __luaMultiReturnBrand: unique symbol }; +declare type LuaMultiReturn = T & LuaExtension<"__luaMultiReturnBrand">; /** * Creates a Lua-style numeric for loop (for i=start,limit,step) when used in for...of. Not valid in any other context. @@ -25,9 +31,8 @@ declare type LuaMultiReturn = T & { readonly __luaMultiReturnBr * @param limit The last number in the sequence to iterate over. * @param step The amount to increment each iteration. */ -declare const $range: ((start: number, limit: number, step?: number) => Iterable) & { - readonly __luaRangeFunctionBrand: unique symbol; -}; +declare const $range: ((start: number, limit: number, step?: number) => Iterable) & + LuaExtension<"__luaRangeFunctionBrand">; /** * Represents a Lua-style iterator which is returned from a LuaIterable. @@ -60,7 +65,8 @@ declare type LuaIterator = TState extends undefined * @param TState The type of the state value passed back to the iterator function each iteration. */ declare type LuaIterable = Iterable & - LuaIterator & { readonly __luaIterableBrand: unique symbol }; + LuaIterator & + LuaExtension<"__luaIterableBrand">; /** * Calls to functions with this type are translated to `left + right`. @@ -70,9 +76,8 @@ declare type LuaIterable = Iterable & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaAddition = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaAdditionBrand: unique symbol; -}; +declare type LuaAddition = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaAdditionBrand">; /** * Calls to methods with this type are translated to `left + right`, where `left` is the object with the method. @@ -81,9 +86,8 @@ declare type LuaAddition = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaAdditionMethod = ((right: TRight) => TReturn) & { - readonly __luaAdditionMethodBrand: unique symbol; -}; +declare type LuaAdditionMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaAdditionMethodBrand">; /** * Calls to functions with this type are translated to `left - right`. @@ -93,9 +97,8 @@ declare type LuaAdditionMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaSubtraction = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaSubtractionBrand: unique symbol; -}; +declare type LuaSubtraction = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaSubtractionBrand">; /** * Calls to methods with this type are translated to `left - right`, where `left` is the object with the method. @@ -104,9 +107,8 @@ declare type LuaSubtraction = ((left: TLeft, right: TRig * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaSubtractionMethod = ((right: TRight) => TReturn) & { - readonly __luaSubtractionMethodBrand: unique symbol; -}; +declare type LuaSubtractionMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaSubtractionMethodBrand">; /** * Calls to functions with this type are translated to `left * right`. @@ -116,9 +118,8 @@ declare type LuaSubtractionMethod = ((right: TRight) => TReturn * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaMultiplication = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaMultiplicationBrand: unique symbol; -}; +declare type LuaMultiplication = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaMultiplicationBrand">; /** * Calls to methods with this type are translated to `left * right`, where `left` is the object with the method. @@ -127,9 +128,8 @@ declare type LuaMultiplication = ((left: TLeft, right: T * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaMultiplicationMethod = ((right: TRight) => TReturn) & { - readonly __luaMultiplicationMethodBrand: unique symbol; -}; +declare type LuaMultiplicationMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaMultiplicationMethodBrand">; /** * Calls to functions with this type are translated to `left / right`. @@ -139,9 +139,8 @@ declare type LuaMultiplicationMethod = ((right: TRight) => TRet * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaDivision = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaDivisionBrand: unique symbol; -}; +declare type LuaDivision = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaDivisionBrand">; /** * Calls to methods with this type are translated to `left / right`, where `left` is the object with the method. @@ -150,9 +149,8 @@ declare type LuaDivision = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaDivisionMethod = ((right: TRight) => TReturn) & { - readonly __luaDivisionMethodBrand: unique symbol; -}; +declare type LuaDivisionMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaDivisionMethodBrand">; /** * Calls to functions with this type are translated to `left % right`. @@ -162,9 +160,8 @@ declare type LuaDivisionMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaModulo = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaModuloBrand: unique symbol; -}; +declare type LuaModulo = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaModuloBrand">; /** * Calls to methods with this type are translated to `left % right`, where `left` is the object with the method. @@ -173,9 +170,7 @@ declare type LuaModulo = ((left: TLeft, right: TRight) = * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaModuloMethod = ((right: TRight) => TReturn) & { - readonly __luaModuloMethodBrand: unique symbol; -}; +declare type LuaModuloMethod = ((right: TRight) => TReturn) & LuaExtension<"__luaModuloMethodBrand">; /** * Calls to functions with this type are translated to `left ^ right`. @@ -185,9 +180,8 @@ declare type LuaModuloMethod = ((right: TRight) => TReturn) & { * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaPower = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaPowerBrand: unique symbol; -}; +declare type LuaPower = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaPowerBrand">; /** * Calls to methods with this type are translated to `left ^ right`, where `left` is the object with the method. @@ -196,9 +190,7 @@ declare type LuaPower = ((left: TLeft, right: TRight) => * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaPowerMethod = ((right: TRight) => TReturn) & { - readonly __luaPowerMethodBrand: unique symbol; -}; +declare type LuaPowerMethod = ((right: TRight) => TReturn) & LuaExtension<"__luaPowerMethodBrand">; /** * Calls to functions with this type are translated to `left // right`. @@ -208,9 +200,8 @@ declare type LuaPowerMethod = ((right: TRight) => TReturn) & { * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaFloorDivision = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaFloorDivisionBrand: unique symbol; -}; +declare type LuaFloorDivision = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaFloorDivisionBrand">; /** * Calls to methods with this type are translated to `left // right`, where `left` is the object with the method. @@ -219,9 +210,8 @@ declare type LuaFloorDivision = ((left: TLeft, right: TR * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaFloorDivisionMethod = ((right: TRight) => TReturn) & { - readonly __luaFloorDivisionMethodBrand: unique symbol; -}; +declare type LuaFloorDivisionMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaFloorDivisionMethodBrand">; /** * Calls to functions with this type are translated to `left & right`. @@ -231,9 +221,8 @@ declare type LuaFloorDivisionMethod = ((right: TRight) => TRetu * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseAnd = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseAndBrand: unique symbol; -}; +declare type LuaBitwiseAnd = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseAndBrand">; /** * Calls to methods with this type are translated to `left & right`, where `left` is the object with the method. @@ -242,9 +231,8 @@ declare type LuaBitwiseAnd = ((left: TLeft, right: TRigh * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseAndMethodBrand: unique symbol; -}; +declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseAndMethodBrand">; /** * Calls to functions with this type are translated to `left | right`. @@ -254,9 +242,8 @@ declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseOr = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseOrBrand: unique symbol; -}; +declare type LuaBitwiseOr = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseOrBrand">; /** * Calls to methods with this type are translated to `left | right`, where `left` is the object with the method. @@ -265,9 +252,8 @@ declare type LuaBitwiseOr = ((left: TLeft, right: TRight * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseOrMethodBrand: unique symbol; -}; +declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseOrMethodBrand">; /** * Calls to functions with this type are translated to `left ~ right`. @@ -277,9 +263,8 @@ declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseExclusiveOr = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseExclusiveOrBrand: unique symbol; -}; +declare type LuaBitwiseExclusiveOr = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseExclusiveOrBrand">; /** * Calls to methods with this type are translated to `left ~ right`, where `left` is the object with the method. @@ -288,9 +273,8 @@ declare type LuaBitwiseExclusiveOr = ((left: TLeft, righ * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseExclusiveOrMethodBrand: unique symbol; -}; +declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseExclusiveOrMethodBrand">; /** * Calls to functions with this type are translated to `left << right`. @@ -300,9 +284,8 @@ declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseLeftShift = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseLeftShiftBrand: unique symbol; -}; +declare type LuaBitwiseLeftShift = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseLeftShiftBrand">; /** * Calls to methods with this type are translated to `left << right`, where `left` is the object with the method. @@ -311,9 +294,8 @@ declare type LuaBitwiseLeftShift = ((left: TLeft, right: * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseLeftShiftMethodBrand: unique symbol; -}; +declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseLeftShiftMethodBrand">; /** * Calls to functions with this type are translated to `left >> right`. @@ -323,9 +305,8 @@ declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TR * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseRightShift = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaBitwiseRightShiftBrand: unique symbol; -}; +declare type LuaBitwiseRightShift = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseRightShiftBrand">; /** * Calls to methods with this type are translated to `left >> right`, where `left` is the object with the method. @@ -334,9 +315,8 @@ declare type LuaBitwiseRightShift = ((left: TLeft, right * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseRightShiftMethod = ((right: TRight) => TReturn) & { - readonly __luaBitwiseRightShiftMethodBrand: unique symbol; -}; +declare type LuaBitwiseRightShiftMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaBitwiseRightShiftMethodBrand">; /** * Calls to functions with this type are translated to `left .. right`. @@ -346,9 +326,8 @@ declare type LuaBitwiseRightShiftMethod = ((right: TRight) => T * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaConcat = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaConcatBrand: unique symbol; -}; +declare type LuaConcat = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaConcatBrand">; /** * Calls to methods with this type are translated to `left .. right`, where `left` is the object with the method. @@ -357,9 +336,7 @@ declare type LuaConcat = ((left: TLeft, right: TRight) = * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaConcatMethod = ((right: TRight) => TReturn) & { - readonly __luaConcatMethodBrand: unique symbol; -}; +declare type LuaConcatMethod = ((right: TRight) => TReturn) & LuaExtension<"__luaConcatMethodBrand">; /** * Calls to functions with this type are translated to `left < right`. @@ -369,9 +346,8 @@ declare type LuaConcatMethod = ((right: TRight) => TReturn) & { * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLessThan = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaLessThanBrand: unique symbol; -}; +declare type LuaLessThan = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaLessThanBrand">; /** * Calls to methods with this type are translated to `left < right`, where `left` is the object with the method. @@ -380,9 +356,8 @@ declare type LuaLessThan = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLessThanMethod = ((right: TRight) => TReturn) & { - readonly __luaLessThanMethodBrand: unique symbol; -}; +declare type LuaLessThanMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaLessThanMethodBrand">; /** * Calls to functions with this type are translated to `left > right`. @@ -392,9 +367,8 @@ declare type LuaLessThanMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaGreaterThan = ((left: TLeft, right: TRight) => TReturn) & { - readonly __luaGreaterThanBrand: unique symbol; -}; +declare type LuaGreaterThan = ((left: TLeft, right: TRight) => TReturn) & + LuaExtension<"__luaGreaterThanBrand">; /** * Calls to methods with this type are translated to `left > right`, where `left` is the object with the method. @@ -403,9 +377,8 @@ declare type LuaGreaterThan = ((left: TLeft, right: TRig * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaGreaterThanMethod = ((right: TRight) => TReturn) & { - readonly __luaGreaterThanMethodBrand: unique symbol; -}; +declare type LuaGreaterThanMethod = ((right: TRight) => TReturn) & + LuaExtension<"__luaGreaterThanMethodBrand">; /** * Calls to functions with this type are translated to `-operand`. @@ -414,9 +387,7 @@ declare type LuaGreaterThanMethod = ((right: TRight) => TReturn * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaNegation = ((operand: TOperand) => TReturn) & { - readonly __luaNegationBrand: unique symbol; -}; +declare type LuaNegation = ((operand: TOperand) => TReturn) & LuaExtension<"__luaNegationBrand">; /** * Calls to method with this type are translated to `-operand`, where `operand` is the object with the method. @@ -424,7 +395,7 @@ declare type LuaNegation = ((operand: TOperand) => TReturn) & * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaNegationMethod = (() => TReturn) & { readonly __luaNegationMethodBrand: unique symbol }; +declare type LuaNegationMethod = (() => TReturn) & LuaExtension<"__luaNegationMethodBrand">; /** * Calls to functions with this type are translated to `~operand`. @@ -433,9 +404,7 @@ declare type LuaNegationMethod = (() => TReturn) & { readonly __luaNega * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) & { - readonly __luaBitwiseNotBrand: unique symbol; -}; +declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) & LuaExtension<"__luaBitwiseNotBrand">; /** * Calls to method with this type are translated to `~operand`, where `operand` is the object with the method. @@ -443,7 +412,7 @@ declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseNotMethod = (() => TReturn) & { readonly __luaBitwiseNotMethodBrand: unique symbol }; +declare type LuaBitwiseNotMethod = (() => TReturn) & LuaExtension<"__luaBitwiseNotMethodBrand">; /** * Calls to functions with this type are translated to `#operand`. @@ -452,9 +421,7 @@ declare type LuaBitwiseNotMethod = (() => TReturn) & { readonly __luaBi * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLength = ((operand: TOperand) => TReturn) & { - readonly __luaLengthBrand: unique symbol; -}; +declare type LuaLength = ((operand: TOperand) => TReturn) & LuaExtension<"__luaLengthBrand">; /** * Calls to method with this type are translated to `#operand`, where `operand` is the object with the method. @@ -462,4 +429,4 @@ declare type LuaLength = ((operand: TOperand) => TReturn) & { * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLengthMethod = (() => TReturn) & { readonly __luaLengthMethodBrand: unique symbol }; +declare type LuaLengthMethod = (() => TReturn) & LuaExtension<"__luaLengthMethodBrand">;