Skip to content

Commit 7522e68

Browse files
committed
Merge branch 'master' into lualib-plugins
2 parents 8035886 + 3bae760 commit 7522e68

File tree

10 files changed

+119
-8
lines changed

10 files changed

+119
-8
lines changed

src/CompilerOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export interface LuaPluginImport {
2323

2424
export type CompilerOptions = OmitIndexSignature<ts.CompilerOptions> & {
2525
buildMode?: BuildMode;
26+
extension?: string;
2627
luaBundle?: string;
2728
luaBundleEntry?: string;
2829
luaTarget?: LuaTarget;

src/cli/parse.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export const optionDeclarations: CommandLineOption[] = [
3030
type: "enum",
3131
choices: Object.values(BuildMode),
3232
},
33+
{
34+
name: "extension",
35+
description: 'File extension for the resulting Lua files. Defaults to ".lua"',
36+
type: "string",
37+
},
3338
{
3439
name: "luaBundle",
3540
description: "The name of the lua file to bundle output lua to. Requires luaBundleEntry.",

src/transformation/builtins/function.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,8 @@ export function transformFunctionPrototypeCall(
2323
const expressionName = expression.name.text;
2424
switch (expressionName) {
2525
case "apply":
26-
return lua.createCallExpression(
27-
caller,
28-
[params[0], createUnpackCall(context, params[1], node.arguments[1])],
29-
node
30-
);
26+
const nonContextArgs = params.length > 1 ? [createUnpackCall(context, params[1], node.arguments[1])] : [];
27+
return lua.createCallExpression(caller, [params[0], ...nonContextArgs], node);
3128
case "bind":
3229
return transformLuaLibFunction(context, LuaLibFeature.FunctionBind, node, caller, ...params);
3330
case "call":

src/transformation/utils/diagnostics.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,3 +155,7 @@ export const unsupportedBuiltinOptionalCall = createErrorDiagnosticFactory(
155155
export const unsupportedOptionalCompileMembersOnly = createErrorDiagnosticFactory(
156156
"Optional calls are not supported on enums marked with @compileMembersOnly."
157157
);
158+
159+
export const undefinedInArrayLiteral = createErrorDiagnosticFactory(
160+
"Array literals may not contain undefined or null."
161+
);

src/transformation/visitors/literal.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import * as ts from "typescript";
22
import * as lua from "../../LuaAST";
33
import { assertNever } from "../../utils";
44
import { FunctionVisitor, TransformationContext, Visitors } from "../context";
5-
import { invalidMultiFunctionUse, unsupportedAccessorInObjectLiteral } from "../utils/diagnostics";
5+
import {
6+
invalidMultiFunctionUse,
7+
undefinedInArrayLiteral,
8+
unsupportedAccessorInObjectLiteral,
9+
} from "../utils/diagnostics";
610
import { createExportedIdentifier, getSymbolExportScope } from "../utils/export";
711
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
812
import { createSafeName, hasUnsafeIdentifierName, hasUnsafeSymbolName } from "../utils/safe-names";
@@ -196,6 +200,9 @@ const transformObjectLiteralExpression: FunctionVisitor<ts.ObjectLiteralExpressi
196200
};
197201

198202
const transformArrayLiteralExpression: FunctionVisitor<ts.ArrayLiteralExpression> = (expression, context) => {
203+
// Disallow using undefined/null in array literals
204+
checkForUndefinedOrNullInArrayLiteral(expression, context);
205+
199206
const filteredElements = expression.elements.map(e =>
200207
ts.isOmittedExpression(e) ? ts.factory.createIdentifier("undefined") : e
201208
);
@@ -204,6 +211,31 @@ const transformArrayLiteralExpression: FunctionVisitor<ts.ArrayLiteralExpression
204211
return lua.createTableExpression(values, expression);
205212
};
206213

214+
function checkForUndefinedOrNullInArrayLiteral(array: ts.ArrayLiteralExpression, context: TransformationContext) {
215+
// Look for last non-nil element in literal
216+
let lastNonUndefinedIndex = array.elements.length - 1;
217+
for (; lastNonUndefinedIndex >= 0; lastNonUndefinedIndex--) {
218+
if (!isUndefinedOrNull(array.elements[lastNonUndefinedIndex])) {
219+
break;
220+
}
221+
}
222+
223+
// Add diagnostics for non-trailing nil elements in array literal
224+
for (let i = 0; i < array.elements.length; i++) {
225+
if (i < lastNonUndefinedIndex && isUndefinedOrNull(array.elements[i])) {
226+
context.diagnostics.push(undefinedInArrayLiteral(array.elements[i]));
227+
}
228+
}
229+
}
230+
231+
function isUndefinedOrNull(node: ts.Node) {
232+
return (
233+
node.kind === ts.SyntaxKind.UndefinedKeyword ||
234+
node.kind === ts.SyntaxKind.NullKeyword ||
235+
(ts.isIdentifier(node) && node.text === "undefined")
236+
);
237+
}
238+
207239
export const literalVisitors: Visitors = {
208240
[ts.SyntaxKind.NullKeyword]: node => lua.createNilLiteral(node),
209241
[ts.SyntaxKind.TrueKeyword]: node => lua.createBooleanLiteral(true, node),

src/transpilation/transpiler.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,11 @@ export function getEmitPathRelativeToOutDir(fileName: string, program: ts.Progra
127127
emitPathSplits[0] = "lua_modules";
128128
}
129129

130-
// Make extension lua
131-
emitPathSplits[emitPathSplits.length - 1] = trimExtension(emitPathSplits[emitPathSplits.length - 1]) + ".lua";
130+
// Set extension
131+
const extension = ((program.getCompilerOptions() as CompilerOptions).extension ?? "lua").trim();
132+
const trimmedExtension = extension.startsWith(".") ? extension.substring(1) : extension;
133+
emitPathSplits[emitPathSplits.length - 1] =
134+
trimExtension(emitPathSplits[emitPathSplits.length - 1]) + "." + trimmedExtension;
132135

133136
return path.join(...emitPathSplits);
134137
}

test/cli/parse.spec.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ describe("command line", () => {
122122

123123
["luaBundle", "foo", { luaBundle: "foo" }],
124124
["luaBundleEntry", "bar", { luaBundleEntry: "bar" }],
125+
126+
["extension", ".lua", { extension: ".lua" }],
127+
["extension", "scar", { extension: "scar" }],
125128
])("--%s %s", (optionName, value, expected) => {
126129
const result = tstl.parseCommandLine([`--${optionName}`, value]);
127130

test/transpile/paths.spec.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,38 @@ describe("getEmitPath", () => {
113113
expect(fileNames).toHaveLength(1);
114114
expect(fileNames).toContain(path.join(cwd, "out1", "out2", "bundle.lua"));
115115
});
116+
117+
test.each([".scar", "scar"])("uses config extension (%p)", extension => {
118+
const { transpiledFiles } = util.testModule``
119+
.setMainFileName("main.ts")
120+
.addExtraFile("dir/extra.ts", "")
121+
.setOptions({ extension })
122+
.expectToHaveNoDiagnostics()
123+
.getLuaResult();
124+
125+
const fileNames = transpiledFiles.map(f => f.outPath);
126+
expect(fileNames).toContain("main.scar");
127+
expect(fileNames).toContain(path.join("dir", "extra.scar"));
128+
});
129+
130+
test("bundle with different extension", () => {
131+
const { transpiledFiles } = util.testModule``
132+
.setMainFileName("src/main.ts")
133+
.addExtraFile("src/extra.ts", "")
134+
.setOptions({
135+
configFilePath,
136+
rootDir: "src",
137+
outDir: "out1",
138+
luaBundle: "out2/bundle.scar",
139+
luaBundleEntry: "src/main.ts",
140+
})
141+
.expectToHaveNoDiagnostics()
142+
.getLuaResult();
143+
144+
const fileNames = transpiledFiles.map(f => f.outPath);
145+
expect(fileNames).toHaveLength(1);
146+
expect(fileNames).toContain(path.join(cwd, "out1", "out2", "bundle.scar"));
147+
});
116148
});
117149

118150
function normalize(path: string) {

test/unit/builtins/array.spec.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { undefinedInArrayLiteral } from "../../../src/transformation/utils/diagnostics";
12
import * as util from "../../util";
23

34
test("omitted expression", () => {
@@ -674,3 +675,28 @@ test("Array.isArray returns true for empty objects", () => {
674675
test("array.prototype.concat issue #738", () => {
675676
util.testExpression`([] as any[]).concat(13, 323, {x: 3}, [2, 3])`.expectToMatchJsResult();
676677
});
678+
679+
test.each(["[1, undefined, 3]", "[1, null, 3]", "[1, undefined, 2, null]"])(
680+
"not allowed to use null or undefined in array literals (%p)",
681+
literal => {
682+
util.testExpression(literal).expectToHaveDiagnostics([undefinedInArrayLiteral.code]);
683+
}
684+
);
685+
686+
test("not allowed to use null or undefined in array literals ([undefined, null, 1])", () => {
687+
util.testExpression`[undefined, null, 1]`.expectToHaveDiagnostics([
688+
undefinedInArrayLiteral.code,
689+
undefinedInArrayLiteral.code,
690+
]);
691+
});
692+
693+
test.each([
694+
"[]",
695+
"[1, 2, undefined]",
696+
"[1, 2, null]",
697+
"[1, undefined, undefined, null]",
698+
"[undefined]",
699+
"[undefined, null]",
700+
])("trailing undefined or null are allowed in array literal (%p)", literal => {
701+
util.testExpression(literal).expectToHaveNoDiagnostics();
702+
});

test/unit/functions/functions.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ test("Function apply", () => {
173173
`.expectToMatchJsResult();
174174
});
175175

176+
// Fix #1226: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1226
177+
test("function apply without arguments should not lead to exception", () => {
178+
util.testFunction`
179+
const f = function (this: number) { return this + 3; }
180+
return f.apply(4);
181+
`.expectToMatchJsResult();
182+
});
183+
176184
test("Function call", () => {
177185
util.testFunction`
178186
const abc = function (this: { a: number }, a: string) { return this.a + a; }

0 commit comments

Comments
 (0)