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
25 changes: 14 additions & 11 deletions src/transformation/utils/lua-ast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ import * as lua from "../../LuaAST";
import { assert, castArray } from "../../utils";
import { TransformationContext } from "../context";
import { createExportedIdentifier, getIdentifierExportScope } from "./export";
import { peekScope, ScopeType } from "./scope";
import { isFunctionType } from "./typescript";
import { peekScope, ScopeType, Scope } from "./scope";
import { transformLuaLibFunction } from "./lualib";
import { LuaLibFeature } from "../../LuaLib";

Expand Down Expand Up @@ -129,6 +128,17 @@ export function createHoistableVariableDeclarationStatement(
return declaration;
}

function hasMultipleReferences(scope: Scope, identifiers: lua.Identifier | lua.Identifier[]) {
const scopeSymbols = scope.referencedSymbols;
if (!scopeSymbols) {
return false;
}

const referenceLists = castArray(identifiers).map(i => i.symbolId && scopeSymbols.get(i.symbolId));

return referenceLists.some(symbolRefs => symbolRefs && symbolRefs.length > 1);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe

Suggested change
return referenceLists.some(symbolRefs => symbolRefs && symbolRefs.length > 1);
return referenceLists.some(symbolRefs => symbolRefs?.length > 1);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change won't work as symbolRefs?.length resolves to number | undefined which can't be compared to a number like that. I could change it to something like (symbolRefs?.length || 0) > 1, but I feel like it looses clarity that way.

}

export function createLocalOrExportedOrGlobalDeclaration(
context: TransformationContext,
lhs: lua.Identifier | lua.Identifier[],
Expand Down Expand Up @@ -163,15 +173,8 @@ export function createLocalOrExportedOrGlobalDeclaration(
const isTopLevelVariable = scope.type === ScopeType.File;

if (context.isModule || !isTopLevelVariable) {
const isPossibleWrappedFunction =
!isFunctionDeclaration &&
tsOriginal &&
ts.isVariableDeclaration(tsOriginal) &&
tsOriginal.initializer &&
isFunctionType(context, context.checker.getTypeAtLocation(tsOriginal.initializer));

if (isPossibleWrappedFunction || scope.type === ScopeType.Switch) {
// Split declaration and assignment for wrapped function types to allow recursion
if (scope.type === ScopeType.Switch || (!isFunctionDeclaration && hasMultipleReferences(scope, lhs))) {
// Split declaration and assignment of identifiers that reference themselves in their declaration
declaration = lua.createVariableDeclarationStatement(lhs, undefined, tsOriginal);
assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal);
} else {
Expand Down
36 changes: 36 additions & 0 deletions test/unit/assignments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,39 @@ test.each([
return { r, o, a };
`.expectToMatchJsResult();
});

test("local variable declaration referencing self indirectly", () => {
util.testFunction`
let cb: () => void;

function foo(newCb: () => void) {
cb = newCb;
return "foo";
}

let bar = foo(() => {
bar = "bar";
});

cb();
return bar;
`.expectToMatchJsResult();
});

test("local multiple variable declaration referencing self indirectly", () => {
util.testFunction`
let cb: () => void;

function foo(newCb: () => void) {
cb = newCb;
return ["a", "foo", "c"];
}

let [a, bar, c] = foo(() => {
bar = "bar";
});

cb();
return bar;
`.expectToMatchJsResult();
});