X Tutup
Skip to content

Commit eae12b6

Browse files
authored
Multi-Return update (#978)
* renamed MultiReturn to LuaMultiReturn and added some doc comments * refactored LuaMultiReturn to handle a number of cases it couldn't before * fixed issues found with tests * diagnostic for checking implicit cast from LuaMultiReturn and additional tests * fixed LuaMultiReturn used in assignment expressions * tests for mtulreturn in try/catch * removed unnecessary check in isMultiReturnType * fixed string.find declaration * addressing feedback * snapshot update * renamed multiReturnCallShouldBeWrapped to shouldMultiReturnCallBeWrapped Co-authored-by: Tom <tomblind@users.noreply.github.com>
1 parent 2766de1 commit eae12b6

File tree

15 files changed

+356
-219
lines changed

15 files changed

+356
-219
lines changed

language-extensions/index.d.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1-
declare function $multi<T extends any[]>(...values: T): MultiReturn<T>;
2-
declare type MultiReturn<T extends any[]> = T & { readonly " __multiBrand": unique symbol };
1+
/**
2+
* Returns multiple values from a function, by wrapping them in a LuaMultiReturn tuple.
3+
* For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions
4+
*
5+
* @param T A tuple type with each element type representing a return value's type.
6+
* @param values Return values.
7+
*/
8+
declare function $multi<T extends any[]>(...values: T): LuaMultiReturn<T>;
9+
10+
/**
11+
* Represents multiple return values as a tuple.
12+
* For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions
13+
*
14+
* @param T A tuple type with each element type representing a return value's type.
15+
*/
16+
declare type LuaMultiReturn<T extends any[]> = T & { readonly __luaMultiReturnBrand: unique symbol };
317

418
/**
519
* Creates a Lua-style numeric for loop (for i=start,limit,step) when used in for...of. Not valid in any other context.

src/lualib/StringIncludes.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ function __TS__StringIncludes(this: string, searchString: string, position?: num
55
} else {
66
position += 1;
77
}
8-
return string.find(this, searchString, position, true) !== undefined;
8+
const [index] = string.find(this, searchString, position, true);
9+
return index !== undefined;
910
}

src/lualib/declarations/string.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ declare namespace string {
1919
pattern: string,
2020
start?: number,
2121
plainflag?: boolean
22-
): MultiReturn<[number, number]> | undefined;
22+
): LuaMultiReturn<[number, number] | [undefined]>;
2323
}

src/transformation/utils/diagnostics.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,11 @@ export const unsupportedVarDeclaration = createErrorDiagnosticFactory(
149149
);
150150

151151
export const invalidMultiFunctionUse = createErrorDiagnosticFactory(
152-
"The $multi function must be called in an expression that is returned."
152+
"The $multi function must be called in a return statement."
153153
);
154154

155-
export const invalidMultiTypeToNonArrayBindingPattern = createErrorDiagnosticFactory(
156-
"Expected an array destructuring pattern."
155+
export const invalidMultiFunctionReturnType = createErrorDiagnosticFactory(
156+
"The $multi function cannot be cast to a non-LuaMultiReturn type."
157157
);
158158

159159
export const invalidMultiTypeToNonArrayLiteral = createErrorDiagnosticFactory("Expected an array literal.");
@@ -162,20 +162,8 @@ export const invalidMultiTypeToEmptyPatternOrArrayLiteral = createErrorDiagnosti
162162
"There must be one or more elements specified here."
163163
);
164164

165-
export const invalidMultiTypeArrayBindingPatternElementInitializer = createErrorDiagnosticFactory(
166-
"This array binding pattern cannot have initializers."
167-
);
168-
169-
export const invalidMultiTypeArrayLiteralElementInitializer = createErrorDiagnosticFactory(
170-
"This array literal pattern cannot have initializers."
171-
);
172-
173165
export const invalidMultiReturnAccess = createErrorDiagnosticFactory(
174-
"The MultiReturn type can only be accessed via an element access expression of a numeric type."
175-
);
176-
177-
export const unsupportedMultiFunctionAssignment = createErrorDiagnosticFactory(
178-
"Omitted expressions and BindingElements are expected here."
166+
"The LuaMultiReturn type can only be accessed via an element access expression of a numeric type."
179167
);
180168

181169
export const invalidOperatorMappingUse = createErrorDiagnosticFactory(

src/transformation/utils/language-extensions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ const functionNameToExtensionKind: { [name: string]: ExtensionKind } = {
4949
};
5050

5151
const typeNameToExtensionKind: { [name: string]: ExtensionKind } = {
52-
MultiReturn: ExtensionKind.MultiType,
52+
LuaMultiReturn: ExtensionKind.MultiType,
5353
LuaAddition: ExtensionKind.AdditionOperatorType,
5454
LuaAdditionMethod: ExtensionKind.AdditionOperatorMethodType,
5555
LuaSubtraction: ExtensionKind.SubtractionOperatorType,

src/transformation/visitors/binary-expression/assignments.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { isArrayType, isDestructuringAssignment } from "../../utils/typescript";
1111
import { transformElementAccessArgument } from "../access";
1212
import { transformLuaTablePropertyAccessInAssignment } from "../lua-table";
1313
import { isArrayLength, transformDestructuringAssignment } from "./destructuring-assignments";
14+
import { isMultiReturnCall } from "../language-extensions/multi";
1415

1516
export function transformAssignmentLeftHandSideExpression(
1617
context: TransformationContext,
@@ -91,7 +92,7 @@ export function transformAssignmentExpression(
9192
const rootIdentifier = lua.createAnonymousIdentifier(expression.left);
9293

9394
let right = context.transformExpression(expression.right);
94-
if (isTupleReturnCall(context, expression.right)) {
95+
if (isTupleReturnCall(context, expression.right) || isMultiReturnCall(context, expression.right)) {
9596
right = wrapInTable(right);
9697
}
9798

@@ -184,7 +185,10 @@ export function transformAssignmentStatement(
184185
const rightType = context.checker.getTypeAtLocation(expression.right);
185186
let right = context.transformExpression(expression.right);
186187

187-
if (!isTupleReturnCall(context, expression.right) && isArrayType(context, rightType)) {
188+
if (
189+
!(isTupleReturnCall(context, expression.right) || isMultiReturnCall(context, expression.right)) &&
190+
isArrayType(context, rightType)
191+
) {
188192
right = createUnpackCall(context, right, expression.right);
189193
}
190194

@@ -194,7 +198,7 @@ export function transformAssignmentStatement(
194198
}
195199

196200
let right = context.transformExpression(expression.right);
197-
if (isTupleReturnCall(context, expression.right)) {
201+
if (isTupleReturnCall(context, expression.right) || isMultiReturnCall(context, expression.right)) {
198202
right = wrapInTable(right);
199203
}
200204

src/transformation/visitors/call.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { isValidLuaIdentifier } from "../utils/safe-names";
1111
import { isArrayType, isExpressionWithEvaluationEffect, isInDestructingAssignment } from "../utils/typescript";
1212
import { transformElementAccessArgument } from "./access";
1313
import { transformLuaTableCallExpression } from "./lua-table";
14-
import { returnsMultiType } from "./language-extensions/multi";
14+
import { shouldMultiReturnCallBeWrapped, returnsMultiType } from "./language-extensions/multi";
1515
import { isOperatorMapping, transformOperatorMappingExpression } from "./language-extensions/operators";
1616

1717
export type PropertyCallExpression = ts.CallExpression & { expression: ts.PropertyAccessExpression };
@@ -201,8 +201,9 @@ export const transformCallExpression: FunctionVisitor<ts.CallExpression> = (node
201201
node.parent && ts.isReturnStatement(node.parent) && isInTupleReturnFunction(context, node);
202202
const isInSpread = node.parent && ts.isSpreadElement(node.parent);
203203
const returnValueIsUsed = node.parent && !ts.isExpressionStatement(node.parent);
204-
const wrapResult =
204+
const wrapTupleReturn =
205205
isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment(node) && !isInSpread && returnValueIsUsed;
206+
const wrapResult = wrapTupleReturn || shouldMultiReturnCallBeWrapped(context, node);
206207

207208
const builtinResult = transformBuiltinCallExpression(context, node);
208209
if (builtinResult) {

src/transformation/visitors/errors.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { createUnpackCall } from "../utils/lua-ast";
66
import { findScope, ScopeType } from "../utils/scope";
77
import { transformScopeBlock } from "./block";
88
import { transformIdentifier } from "./identifier";
9+
import { isInMultiReturnFunction } from "./language-extensions/multi";
910

1011
export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statement, context) => {
1112
const [tryBlock, tryScope] = transformScopeBlock(context, statement.tryBlock, ScopeType.Try);
@@ -88,7 +89,7 @@ export const transformTryStatement: FunctionVisitor<ts.TryStatement> = (statemen
8889
returnValues.push(lua.createBooleanLiteral(true));
8990
}
9091

91-
if (isInTupleReturnFunction(context, statement)) {
92+
if (isInTupleReturnFunction(context, statement) || isInMultiReturnFunction(context, statement)) {
9293
returnValues.push(createUnpackCall(context, lua.cloneIdentifier(returnValueIdentifier)));
9394
} else {
9495
returnValues.push(lua.cloneIdentifier(returnValueIdentifier));

src/transformation/visitors/expression-statement.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,8 @@ import { FunctionVisitor } from "../context";
44
import { transformBinaryExpressionStatement } from "./binary-expression";
55
import { transformLuaTableExpressionStatement } from "./lua-table";
66
import { transformUnaryExpressionStatement } from "./unary-expression";
7-
import { returnsMultiType, transformMultiDestructuringAssignmentStatement } from "./language-extensions/multi";
87

98
export const transformExpressionStatement: FunctionVisitor<ts.ExpressionStatement> = (node, context) => {
10-
if (
11-
ts.isBinaryExpression(node.expression) &&
12-
node.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken &&
13-
ts.isCallExpression(node.expression.right) &&
14-
returnsMultiType(context, node.expression.right)
15-
) {
16-
return transformMultiDestructuringAssignmentStatement(context, node);
17-
}
18-
199
const luaTableResult = transformLuaTableExpressionStatement(context, node);
2010
if (luaTableResult) {
2111
return luaTableResult;

src/transformation/visitors/function.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {
1515
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
1616
import { peekScope, performHoisting, popScope, pushScope, Scope, ScopeType } from "../utils/scope";
1717
import { transformIdentifier } from "./identifier";
18-
import { isMultiFunction, transformMultiCallExpressionToReturnStatement } from "./language-extensions/multi";
1918
import { transformExpressionBodyToReturnStatement } from "./return";
2019
import { transformBindingPattern } from "./variable-declaration";
2120

@@ -56,10 +55,6 @@ function isRestParameterReferenced(context: TransformationContext, identifier: l
5655

5756
export function transformFunctionBodyContent(context: TransformationContext, body: ts.ConciseBody): lua.Statement[] {
5857
if (!ts.isBlock(body)) {
59-
if (ts.isCallExpression(body) && isMultiFunction(context, body)) {
60-
return [transformMultiCallExpressionToReturnStatement(context, body)];
61-
}
62-
6358
const returnStatement = transformExpressionBodyToReturnStatement(context, body);
6459
return [returnStatement];
6560
}

0 commit comments

Comments
 (0)
X Tutup