Skip to content

Commit e18bf59

Browse files
tomblindPerryvw
authored andcommitted
Allow lua keywords and identifiers with invalid characters (#581)
* allowing lua keywords as identifier names by prepending 4 underscores * big refactor, invalid character support, and more tests * using getIdentifierText and formatting fixes * explicit checks for static "new" method/property
1 parent 63f5568 commit e18bf59

File tree

8 files changed

+857
-121
lines changed

8 files changed

+857
-121
lines changed

src/LuaTransformer.ts

Lines changed: 202 additions & 90 deletions
Large diffs are not rendered by default.

src/TSHelper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,10 @@ export class TSHelper {
650650
return match !== undefined && match !== null && match[0] === str;
651651
}
652652

653+
public static fixInvalidLuaIdentifier(name: string): string {
654+
return name.replace(/[^a-zA-Z0-9_]/g, c => `_${c.charCodeAt(0).toString(16).toUpperCase()}`);
655+
}
656+
653657
// Checks that a name is valid for use in lua function declaration syntax:
654658
// 'foo.bar' => passes ('function foo.bar()' is valid)
655659
// 'getFoo().bar' => fails ('function getFoo().bar()' would be illegal)

src/TSTLErrors.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ export class TSTLErrors {
7878
public static InvalidThrowExpression = (node: ts.Node) =>
7979
new TranspileError(`Invalid throw expression, only strings can be thrown.`, node);
8080

81-
public static KeywordIdentifier = (node: ts.Identifier) =>
82-
new TranspileError(`Cannot use Lua keyword ${node.escapedText} as identifier.`, node);
81+
public static ForbiddenStaticClassPropertyName = (node: ts.Node, name: string) =>
82+
new TranspileError(`Cannot use "${name}" as a static class property or method name.`, node);
8383

8484
public static MissingClassName = (node: ts.Node) =>
8585
new TranspileError(`Class declarations must have a name.`, node);

test/translation/__snapshots__/transformation.spec.ts.snap

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -391,16 +391,16 @@ local TestClass = __TSTL_test.TestClass"
391391
`;
392392

393393
exports[`Transformation (modulesImportNamedSpecialChars) 1`] = `
394-
"local __TSTL_kebab_module = require(\\"kebab-module\\")
395-
local TestClass = __TSTL_kebab_module.TestClass
396-
local __TSTL_dollar_module = require(\\"dollar$module\\")
397-
local TestClass = __TSTL_dollar_module.TestClass
398-
local __TSTL_singlequote_module = require(\\"singlequote'module\\")
399-
local TestClass = __TSTL_singlequote_module.TestClass
400-
local __TSTL_hash_module = require(\\"hash#module\\")
401-
local TestClass = __TSTL_hash_module.TestClass
402-
local __TSTL_space_module = require(\\"space module\\")
403-
local TestClass = __TSTL_space_module.TestClass"
394+
"local __TSTL_kebab_2Dmodule = require(\\"kebab-module\\")
395+
local TestClass = __TSTL_kebab_2Dmodule.TestClass
396+
local __TSTL_dollar_24module = require(\\"dollar$module\\")
397+
local TestClass = __TSTL_dollar_24module.TestClass
398+
local __TSTL_singlequote_27module = require(\\"singlequote'module\\")
399+
local TestClass = __TSTL_singlequote_27module.TestClass
400+
local __TSTL_hash_23module = require(\\"hash#module\\")
401+
local TestClass = __TSTL_hash_23module.TestClass
402+
local __TSTL_space_20module = require(\\"space module\\")
403+
local TestClass = __TSTL_space_20module.TestClass"
404404
`;
405405

406406
exports[`Transformation (modulesImportRenamed) 1`] = `
@@ -409,16 +409,16 @@ local RenamedClass = __TSTL_test.TestClass"
409409
`;
410410

411411
exports[`Transformation (modulesImportRenamedSpecialChars) 1`] = `
412-
"local __TSTL_kebab_module = require(\\"kebab-module\\")
413-
local RenamedClass = __TSTL_kebab_module.TestClass
414-
local __TSTL_dollar_module = require(\\"dollar$module\\")
415-
local RenamedClass = __TSTL_dollar_module.TestClass
416-
local __TSTL_singlequote_module = require(\\"singlequote'module\\")
417-
local RenamedClass = __TSTL_singlequote_module.TestClass
418-
local __TSTL_hash_module = require(\\"hash#module\\")
419-
local RenamedClass = __TSTL_hash_module.TestClass
420-
local __TSTL_space_module = require(\\"space module\\")
421-
local RenamedClass = __TSTL_space_module.TestClass"
412+
"local __TSTL_kebab_2Dmodule = require(\\"kebab-module\\")
413+
local RenamedClass = __TSTL_kebab_2Dmodule.TestClass
414+
local __TSTL_dollar_24module = require(\\"dollar$module\\")
415+
local RenamedClass = __TSTL_dollar_24module.TestClass
416+
local __TSTL_singlequote_27module = require(\\"singlequote'module\\")
417+
local RenamedClass = __TSTL_singlequote_27module.TestClass
418+
local __TSTL_hash_23module = require(\\"hash#module\\")
419+
local RenamedClass = __TSTL_hash_23module.TestClass
420+
local __TSTL_space_20module = require(\\"space module\\")
421+
local RenamedClass = __TSTL_space_20module.TestClass"
422422
`;
423423

424424
exports[`Transformation (modulesImportWithoutFromClause) 1`] = `"require(\\"test\\")"`;

test/unit/assignments/assignments.spec.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,6 @@ test("TupleReturn in expression", () => {
176176
expect(result).toBe("a3");
177177
});
178178

179-
test.each(["and", "local", "nil", "not", "or", "repeat", "then", "until"])(
180-
"Keyword identifier error (%p)",
181-
identifier => {
182-
expect(() => util.transpileString(`const ${identifier} = 3;`)).toThrowExactError(
183-
TSTLErrors.KeywordIdentifier(ts.createIdentifier(identifier)),
184-
);
185-
},
186-
);
187-
188179
test("String table access", () => {
189180
const code = `
190181
const dict : {[key:string]:any} = {};

test/unit/class.spec.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,3 +823,43 @@ test("Class field override in subclass with constructors", () => {
823823
return (new Foo()).field + (new Bar()).field;`;
824824
expect(util.transpileAndExecute(code)).toBe("foobar");
825825
});
826+
827+
test("Class cannot have static new method", () => {
828+
const code = `
829+
class Foo {
830+
static new() {}
831+
}`;
832+
expect(() => util.transpileAndExecute(code)).toThrow(
833+
TSTLErrors.ForbiddenStaticClassPropertyName(ts.createEmptyStatement(), "new").message,
834+
);
835+
});
836+
837+
test("Class cannot have static new property", () => {
838+
const code = `
839+
class Foo {
840+
static new = "foobar";
841+
}`;
842+
expect(() => util.transpileAndExecute(code)).toThrow(
843+
TSTLErrors.ForbiddenStaticClassPropertyName(ts.createEmptyStatement(), "new").message,
844+
);
845+
});
846+
847+
test("Class cannot have static new get accessor", () => {
848+
const code = `
849+
class Foo {
850+
static get new() { return "foobar" }
851+
}`;
852+
expect(() => util.transpileAndExecute(code)).toThrow(
853+
TSTLErrors.ForbiddenStaticClassPropertyName(ts.createEmptyStatement(), "new").message,
854+
);
855+
});
856+
857+
test("Class cannot have static new set accessor", () => {
858+
const code = `
859+
class Foo {
860+
static set new(value: string) {}
861+
}`;
862+
expect(() => util.transpileAndExecute(code)).toThrow(
863+
TSTLErrors.ForbiddenStaticClassPropertyName(ts.createEmptyStatement(), "new").message,
864+
);
865+
});

0 commit comments

Comments
 (0)