Skip to content

Commit 3c9e369

Browse files
authored
Allow numeric access to MultiReturn (TypeScriptToLua#962)
1 parent f90f6af commit 3c9e369

File tree

4 files changed

+75
-1
lines changed

4 files changed

+75
-1
lines changed

src/transformation/utils/diagnostics.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ export const invalidMultiTypeArrayLiteralElementInitializer = createErrorDiagnos
164164
"This array literal pattern cannot have initializers."
165165
);
166166

167+
export const invalidMultiReturnAccess = createErrorDiagnosticFactory(
168+
"The MultiReturn type can only be accessed via an element access expression of a numeric type."
169+
);
170+
167171
export const unsupportedMultiFunctionAssignment = createErrorDiagnosticFactory(
168172
"Omitted expressions and BindingElements are expected here."
169173
);

src/transformation/visitors/access.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import * as lua from "../../LuaAST";
33
import { transformBuiltinPropertyAccessExpression } from "../builtins";
44
import { FunctionVisitor, TransformationContext } from "../context";
55
import { AnnotationKind, getTypeAnnotations } from "../utils/annotations";
6+
import { invalidMultiReturnAccess } from "../utils/diagnostics";
67
import { addToNumericExpression } from "../utils/lua-ast";
78
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
89
import { isArrayType, isNumberType, isStringType } from "../utils/typescript";
910
import { tryGetConstEnumValue } from "./enum";
11+
import { returnsMultiType } from "./language-extensions/multi";
1012
import { transformLuaTablePropertyAccessExpression, validateLuaTableElementAccessExpression } from "./lua-table";
1113

1214
export function transformElementAccessArgument(
@@ -41,7 +43,20 @@ export const transformElementAccessExpression: FunctionVisitor<ts.ElementAccessE
4143
return transformLuaLibFunction(context, LuaLibFeature.StringAccess, node, table, index);
4244
}
4345

44-
return lua.createTableIndexExpression(table, transformElementAccessArgument(context, node), node);
46+
const accessExpression = transformElementAccessArgument(context, node);
47+
48+
if (ts.isCallExpression(node.expression) && returnsMultiType(context, node.expression)) {
49+
const accessType = context.checker.getTypeAtLocation(node.argumentExpression);
50+
if (!isNumberType(context, accessType)) {
51+
context.diagnostics.push(invalidMultiReturnAccess(node));
52+
}
53+
54+
const selectIdentifier = lua.createIdentifier("select");
55+
const selectCall = lua.createCallExpression(selectIdentifier, [accessExpression, table]);
56+
return selectCall;
57+
}
58+
59+
return lua.createTableIndexExpression(table, accessExpression, node);
4560
};
4661

4762
export const transformPropertyAccessExpression: FunctionVisitor<ts.PropertyAccessExpression> = (
@@ -63,6 +78,10 @@ export const transformPropertyAccessExpression: FunctionVisitor<ts.PropertyAcces
6378
return builtinResult;
6479
}
6580

81+
if (ts.isCallExpression(expression.expression) && returnsMultiType(context, expression.expression)) {
82+
context.diagnostics.push(invalidMultiReturnAccess(expression));
83+
}
84+
6685
const property = expression.name.text;
6786
const type = context.checker.getTypeAtLocation(expression.expression);
6887

test/unit/language-extensions/__snapshots__/multi.spec.ts.snap

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`disallow MultiReturn non-numeric access: code 1`] = `
4+
"local ____exports = {}
5+
function ____exports.__main(self)
6+
local function multi(self, ...)
7+
local args = {...}
8+
return table.unpack(args)
9+
end
10+
return select(
11+
\\"forEach\\",
12+
multi(nil)
13+
)
14+
end
15+
return ____exports"
16+
`;
17+
18+
exports[`disallow MultiReturn non-numeric access: code 2`] = `
19+
"local ____exports = {}
20+
function ____exports.__main(self)
21+
local function multi(self, ...)
22+
local args = {...}
23+
return table.unpack(args)
24+
end
25+
return multi(nil).forEach
26+
end
27+
return ____exports"
28+
`;
29+
30+
exports[`disallow MultiReturn non-numeric access: diagnostics 1`] = `"main.ts(7,16): error TSTL: The MultiReturn type can only be accessed via an element access expression of a numeric type."`;
31+
32+
exports[`disallow MultiReturn non-numeric access: diagnostics 2`] = `"main.ts(7,16): error TSTL: The MultiReturn type can only be accessed via an element access expression of a numeric type."`;
33+
334
exports[`invalid $multi call ($multi()): code 1`] = `"____(_G)"`;
435

536
exports[`invalid $multi call ($multi()): diagnostics 1`] = `"main.ts(2,9): error TSTL: The $multi function must be called in an expression that is returned."`;

test/unit/language-extensions/multi.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
invalidMultiFunctionUse,
66
invalidMultiTypeToNonArrayBindingPattern,
77
invalidMultiTypeArrayBindingPatternElementInitializer,
8+
invalidMultiReturnAccess,
89
} from "../../../src/transformation/utils/diagnostics";
910

1011
const multiProjectOptions: tstl.CompilerOptions = {
@@ -126,3 +127,22 @@ test("allow $multi call in ArrowFunction body", () => {
126127
.setOptions(multiProjectOptions)
127128
.expectToEqual(1);
128129
});
130+
131+
test.each(["0", "i"])("allow MultiReturn numeric access", expression => {
132+
util.testFunction`
133+
${multiFunction}
134+
const i = 0;
135+
return multi(1)[${expression}];
136+
`
137+
.setOptions(multiProjectOptions)
138+
.expectToEqual(1);
139+
});
140+
141+
test.each(["multi()['forEach']", "multi().forEach"])("disallow MultiReturn non-numeric access", expression => {
142+
util.testFunction`
143+
${multiFunction}
144+
return ${expression};
145+
`
146+
.setOptions(multiProjectOptions)
147+
.expectDiagnosticsToMatchSnapshot([invalidMultiReturnAccess.code]);
148+
});

0 commit comments

Comments
 (0)