Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 38 additions & 32 deletions src/LuaTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3583,6 +3583,9 @@ export class LuaTransformer {
properties.push(tstl.createTableFieldExpression(expression, name, element));
} else if (ts.isShorthandPropertyAssignment(element)) {
const valueSymbol = this.checker.getShorthandAssignmentValueSymbol(element);
if (valueSymbol) {
this.trackSymbolReference(valueSymbol, element.name);
}
const identifier = this.createShorthandIdentifier(valueSymbol, element.name);
properties.push(tstl.createTableFieldExpression(identifier, name, element));
} else if (ts.isMethodDeclaration(element)) {
Expand Down Expand Up @@ -5483,47 +5486,50 @@ export class LuaTransformer {
return "____" + tsHelper.fixInvalidLuaIdentifier(name);
}

protected getIdentifierSymbolId(identifier: ts.Identifier): tstl.SymbolId | undefined {
const symbol = this.checker.getSymbolAtLocation(identifier);
let symbolId: tstl.SymbolId | undefined;
if (symbol) {
// Track first time symbols are seen
if (!this.symbolIds.has(symbol)) {
symbolId = this.genSymbolIdCounter++;
protected trackSymbolReference(symbol: ts.Symbol, identifier: ts.Identifier): tstl.SymbolId | undefined {
// Track first time symbols are seen
let symbolId = this.symbolIds.get(symbol);
if (!symbolId) {
symbolId = this.genSymbolIdCounter++;

const symbolInfo: SymbolInfo = { symbol, firstSeenAtPos: identifier.pos };
this.symbolIds.set(symbol, symbolId);
this.symbolInfo.set(symbolId, symbolInfo);
} else {
symbolId = this.symbolIds.get(symbol);
}
const symbolInfo: SymbolInfo = { symbol, firstSeenAtPos: identifier.pos };
this.symbolIds.set(symbol, symbolId);
this.symbolInfo.set(symbolId, symbolInfo);
}

if (this.options.noHoisting) {
// Check for reference-before-declaration
const declaration = tsHelper.getFirstDeclaration(symbol, this.currentSourceFile);
if (declaration && identifier.pos < declaration.pos) {
throw TSTLErrors.ReferencedBeforeDeclaration(identifier);
}
if (this.options.noHoisting) {
// Check for reference-before-declaration
const declaration = tsHelper.getFirstDeclaration(symbol, this.currentSourceFile);
if (declaration && identifier.pos < declaration.pos) {
throw TSTLErrors.ReferencedBeforeDeclaration(identifier);
}
}

if (symbolId !== undefined) {
//Mark symbol as seen in all current scopes
for (const scope of this.scopeStack) {
if (!scope.referencedSymbols) {
scope.referencedSymbols = new Map();
}
let references = scope.referencedSymbols.get(symbolId);
if (!references) {
references = [];
scope.referencedSymbols.set(symbolId, references);
}
references.push(identifier);
}
//Mark symbol as seen in all current scopes
for (const scope of this.scopeStack) {
if (!scope.referencedSymbols) {
scope.referencedSymbols = new Map();
}
let references = scope.referencedSymbols.get(symbolId);
if (!references) {
references = [];
scope.referencedSymbols.set(symbolId, references);
}
references.push(identifier);
}

return symbolId;
}

protected getIdentifierSymbolId(identifier: ts.Identifier): tstl.SymbolId | undefined {
const symbol = this.checker.getSymbolAtLocation(identifier);
if (symbol) {
return this.trackSymbolReference(symbol, identifier);
} else {
return undefined;
}
}

protected findScope(scopeTypes: ScopeType): Scope | undefined {
return this.scopeStack
.slice()
Expand Down
12 changes: 12 additions & 0 deletions test/unit/functions.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,18 @@ test.each([{}, { noHoisting: true }])("Function rest parameter (unreferenced)",
expect(util.transpileAndExecute(code, compilerOptions)).toBe("foobar");
});

test.each([{}, { noHoisting: true }])("Function rest parameter (referenced in property shorthand)", compilerOptions => {
const code = `
function foo(a: unknown, ...b: string[]) {
const c = { b };
return c.b.join("");
}
return foo("A", "B", "C", "D");
`;

expect(util.transpileAndExecute(code, compilerOptions)).toBe("BCD");
});

test.each([{}, { noHoisting: true }])("@vararg", compilerOptions => {
const code = `
/** @vararg */ type LuaVarArg<A extends unknown[]> = A & { __luaVarArg?: never };
Expand Down
11 changes: 11 additions & 0 deletions test/unit/hoisting.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ test.each([
},
{ code: `function makeFoo() { return new Foo(); } class Foo {}`, identifier: "Foo" },
{ code: `function bar() { return E.A; } enum E { A = "foo" }`, identifier: "E" },
{ code: `function setBar() { const bar = { foo }; } let foo = "foo";`, identifier: "foo" },
])("No Hoisting (%p)", ({ code, identifier }) => {
expect(() => util.transpileString(code, { noHoisting: true })).toThrowExactError(
TSTLErrors.ReferencedBeforeDeclaration(ts.createIdentifier(identifier))
Expand Down Expand Up @@ -292,3 +293,13 @@ test("Import hoisted before function", () => {
const code = "return bar;";
expect(util.transpileAndExecute(code, undefined, luaHeader, tsHeader)).toBe("foobar");
});

test("Hoisting Shorthand Property", () => {
const code = `
function foo() {
return { bar }.bar;
}
let bar = "foobar";
return foo();`;
expect(util.transpileAndExecute(code)).toBe("foobar");
});