diff --git a/src/LuaAST.ts b/src/LuaAST.ts index 10c7b44b3..2b3401614 100644 --- a/src/LuaAST.ts +++ b/src/LuaAST.ts @@ -130,19 +130,18 @@ export interface TextRange { export interface Node extends TextRange { kind: SyntaxKind; - parent?: Node; } -export function createNode(kind: SyntaxKind, tsOriginal?: ts.Node, parent?: Node): Node { +export function createNode(kind: SyntaxKind, tsOriginal?: ts.Node): Node { if (tsOriginal === undefined) { - return { kind, parent }; + return { kind }; } const sourcePosition = getSourcePosition(tsOriginal); if (sourcePosition) { - return { kind, parent, line: sourcePosition.line, column: sourcePosition.column }; + return { kind, line: sourcePosition.line, column: sourcePosition.column }; } else { - return { kind, parent }; + return { kind }; } } @@ -172,19 +171,6 @@ export function setNodeOriginal(node: T | undefined, tsOriginal: return node; } -export function setParent(node: Node | Node[] | undefined, parent: Node): void { - if (!node) { - return; - } - if (Array.isArray(node)) { - node.forEach(n => { - n.parent = parent; - }); - } else { - node.parent = parent; - } -} - function getSourcePosition(sourceNode: ts.Node): TextRange | undefined { if (sourceNode.getSourceFile() !== undefined && sourceNode.pos >= 0) { const { line, character } = ts.getLineAndCharacterOfPosition( @@ -209,9 +195,8 @@ export function isBlock(node: Node): node is Block { return node.kind === SyntaxKind.Block; } -export function createBlock(statements: Statement[], tsOriginal?: ts.Node, parent?: Node): Block { - const block = createNode(SyntaxKind.Block, tsOriginal, parent) as Block; - setParent(statements, block); +export function createBlock(statements: Statement[], tsOriginal?: ts.Node): Block { + const block = createNode(SyntaxKind.Block, tsOriginal) as Block; block.statements = statements; return block; } @@ -229,9 +214,8 @@ export function isDoStatement(node: Node): node is DoStatement { return node.kind === SyntaxKind.DoStatement; } -export function createDoStatement(statements: Statement[], tsOriginal?: ts.Node, parent?: Node): DoStatement { - const statement = createNode(SyntaxKind.DoStatement, tsOriginal, parent) as DoStatement; - setParent(statements, statement); +export function createDoStatement(statements: Statement[], tsOriginal?: ts.Node): DoStatement { + const statement = createNode(SyntaxKind.DoStatement, tsOriginal) as DoStatement; statement.statements = statements; return statement; } @@ -250,26 +234,17 @@ export function isVariableDeclarationStatement(node: Node): node is VariableDecl export function createVariableDeclarationStatement( left: Identifier | Identifier[], right?: Expression | Expression[], - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): VariableDeclarationStatement { - const statement = createNode( - SyntaxKind.VariableDeclarationStatement, - tsOriginal, - parent - ) as VariableDeclarationStatement; - setParent(left, statement); - if (Array.isArray(left)) { - statement.left = left; - } else { - statement.left = [left]; - } - setParent(right, statement); + const statement = createNode(SyntaxKind.VariableDeclarationStatement, tsOriginal) as VariableDeclarationStatement; + statement.left = Array.isArray(left) ? left : [left]; + if (Array.isArray(right)) { statement.right = right; } else if (right) { statement.right = [right]; } + return statement; } @@ -287,22 +262,17 @@ export function isAssignmentStatement(node: Node): node is AssignmentStatement { export function createAssignmentStatement( left: AssignmentLeftHandSideExpression | AssignmentLeftHandSideExpression[], right?: Expression | Expression[], - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): AssignmentStatement { - const statement = createNode(SyntaxKind.AssignmentStatement, tsOriginal, parent) as AssignmentStatement; - setParent(left, statement); - if (Array.isArray(left)) { - statement.left = left; - } else { - statement.left = [left]; - } - setParent(right, statement); + const statement = createNode(SyntaxKind.AssignmentStatement, tsOriginal) as AssignmentStatement; + statement.left = Array.isArray(left) ? left : [left]; + if (Array.isArray(right)) { statement.right = right; } else { statement.right = right ? [right] : []; } + return statement; } @@ -321,15 +291,11 @@ export function createIfStatement( condition: Expression, ifBlock: Block, elseBlock?: Block | IfStatement, - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): IfStatement { - const statement = createNode(SyntaxKind.IfStatement, tsOriginal, parent) as IfStatement; - setParent(condition, statement); + const statement = createNode(SyntaxKind.IfStatement, tsOriginal) as IfStatement; statement.condition = condition; - setParent(ifBlock, statement); statement.ifBlock = ifBlock; - setParent(elseBlock, statement); statement.elseBlock = elseBlock; return statement; } @@ -356,16 +322,9 @@ export function isWhileStatement(node: Node): node is WhileStatement { return node.kind === SyntaxKind.WhileStatement; } -export function createWhileStatement( - body: Block, - condition: Expression, - tsOriginal?: ts.Node, - parent?: Node -): WhileStatement { - const statement = createNode(SyntaxKind.WhileStatement, tsOriginal, parent) as WhileStatement; - setParent(body, statement); +export function createWhileStatement(body: Block, condition: Expression, tsOriginal?: ts.Node): WhileStatement { + const statement = createNode(SyntaxKind.WhileStatement, tsOriginal) as WhileStatement; statement.body = body; - setParent(condition, statement); statement.condition = condition; return statement; } @@ -379,16 +338,9 @@ export function isRepeatStatement(node: Node): node is RepeatStatement { return node.kind === SyntaxKind.RepeatStatement; } -export function createRepeatStatement( - body: Block, - condition: Expression, - tsOriginal?: ts.Node, - parent?: Node -): RepeatStatement { - const statement = createNode(SyntaxKind.RepeatStatement, tsOriginal, parent) as RepeatStatement; - setParent(body, statement); +export function createRepeatStatement(body: Block, condition: Expression, tsOriginal?: ts.Node): RepeatStatement { + const statement = createNode(SyntaxKind.RepeatStatement, tsOriginal) as RepeatStatement; statement.body = body; - setParent(condition, statement); statement.condition = condition; return statement; } @@ -412,19 +364,13 @@ export function createForStatement( controlVariableInitializer: Expression, limitExpression: Expression, stepExpression?: Expression, - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): ForStatement { - const statement = createNode(SyntaxKind.ForStatement, tsOriginal, parent) as ForStatement; - setParent(body, statement); + const statement = createNode(SyntaxKind.ForStatement, tsOriginal) as ForStatement; statement.body = body; - setParent(controlVariable, statement); statement.controlVariable = controlVariable; - setParent(controlVariableInitializer, statement); statement.controlVariableInitializer = controlVariableInitializer; - setParent(limitExpression, statement); statement.limitExpression = limitExpression; - setParent(stepExpression, statement); statement.stepExpression = stepExpression; return statement; } @@ -443,15 +389,11 @@ export function createForInStatement( body: Block, names: Identifier[], expressions: Expression[], - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): ForInStatement { - const statement = createNode(SyntaxKind.ForInStatement, tsOriginal, parent) as ForInStatement; - setParent(body, statement); + const statement = createNode(SyntaxKind.ForInStatement, tsOriginal) as ForInStatement; statement.body = body; - setParent(names, statement); statement.names = names; - setParent(expressions, statement); statement.expressions = expressions; return statement; } @@ -465,8 +407,8 @@ export function isGotoStatement(node: Node): node is GotoStatement { return node.kind === SyntaxKind.GotoStatement; } -export function createGotoStatement(label: string, tsOriginal?: ts.Node, parent?: Node): GotoStatement { - const statement = createNode(SyntaxKind.GotoStatement, tsOriginal, parent) as GotoStatement; +export function createGotoStatement(label: string, tsOriginal?: ts.Node): GotoStatement { + const statement = createNode(SyntaxKind.GotoStatement, tsOriginal) as GotoStatement; statement.label = label; return statement; } @@ -480,8 +422,8 @@ export function isLabelStatement(node: Node): node is LabelStatement { return node.kind === SyntaxKind.LabelStatement; } -export function createLabelStatement(name: string, tsOriginal?: ts.Node, parent?: Node): LabelStatement { - const statement = createNode(SyntaxKind.LabelStatement, tsOriginal, parent) as LabelStatement; +export function createLabelStatement(name: string, tsOriginal?: ts.Node): LabelStatement { + const statement = createNode(SyntaxKind.LabelStatement, tsOriginal) as LabelStatement; statement.name = name; return statement; } @@ -495,13 +437,8 @@ export function isReturnStatement(node: Node): node is ReturnStatement { return node.kind === SyntaxKind.ReturnStatement; } -export function createReturnStatement( - expressions?: Expression[], - tsOriginal?: ts.Node, - parent?: Node -): ReturnStatement { - const statement = createNode(SyntaxKind.ReturnStatement, tsOriginal, parent) as ReturnStatement; - setParent(expressions, statement); +export function createReturnStatement(expressions?: Expression[], tsOriginal?: ts.Node): ReturnStatement { + const statement = createNode(SyntaxKind.ReturnStatement, tsOriginal) as ReturnStatement; statement.expressions = expressions; return statement; } @@ -514,8 +451,8 @@ export function isBreakStatement(node: Node): node is BreakStatement { return node.kind === SyntaxKind.BreakStatement; } -export function createBreakStatement(tsOriginal?: ts.Node, parent?: Node): BreakStatement { - return createNode(SyntaxKind.BreakStatement, tsOriginal, parent) as BreakStatement; +export function createBreakStatement(tsOriginal?: ts.Node): BreakStatement { + return createNode(SyntaxKind.BreakStatement, tsOriginal) as BreakStatement; } export interface ExpressionStatement extends Statement { @@ -527,13 +464,8 @@ export function isExpressionStatement(node: Node): node is ExpressionStatement { return node.kind === SyntaxKind.ExpressionStatement; } -export function createExpressionStatement( - expressions: Expression, - tsOriginal?: ts.Node, - parent?: Node -): ExpressionStatement { - const statement = createNode(SyntaxKind.ExpressionStatement, tsOriginal, parent) as ExpressionStatement; - setParent(expressions, statement); +export function createExpressionStatement(expressions: Expression, tsOriginal?: ts.Node): ExpressionStatement { + const statement = createNode(SyntaxKind.ExpressionStatement, tsOriginal) as ExpressionStatement; statement.expression = expressions; return statement; } @@ -552,8 +484,8 @@ export function isNilLiteral(node: Node): node is NilLiteral { return node.kind === SyntaxKind.NilKeyword; } -export function createNilLiteral(tsOriginal?: ts.Node, parent?: Node): NilLiteral { - return createNode(SyntaxKind.NilKeyword, tsOriginal, parent) as NilLiteral; +export function createNilLiteral(tsOriginal?: ts.Node): NilLiteral { + return createNode(SyntaxKind.NilKeyword, tsOriginal) as NilLiteral; } export interface BooleanLiteral extends Expression { @@ -564,11 +496,11 @@ export function isBooleanLiteral(node: Node): node is BooleanLiteral { return node.kind === SyntaxKind.TrueKeyword || node.kind === SyntaxKind.FalseKeyword; } -export function createBooleanLiteral(value: boolean, tsOriginal?: ts.Node, parent?: Node): BooleanLiteral { +export function createBooleanLiteral(value: boolean, tsOriginal?: ts.Node): BooleanLiteral { if (value) { - return createNode(SyntaxKind.TrueKeyword, tsOriginal, parent) as BooleanLiteral; + return createNode(SyntaxKind.TrueKeyword, tsOriginal) as BooleanLiteral; } else { - return createNode(SyntaxKind.FalseKeyword, tsOriginal, parent) as BooleanLiteral; + return createNode(SyntaxKind.FalseKeyword, tsOriginal) as BooleanLiteral; } } @@ -581,8 +513,8 @@ export function isDotsLiteral(node: Node): node is DotsLiteral { return node.kind === SyntaxKind.DotsKeyword; } -export function createDotsLiteral(tsOriginal?: ts.Node, parent?: Node): DotsLiteral { - return createNode(SyntaxKind.DotsKeyword, tsOriginal, parent) as DotsLiteral; +export function createDotsLiteral(tsOriginal?: ts.Node): DotsLiteral { + return createNode(SyntaxKind.DotsKeyword, tsOriginal) as DotsLiteral; } // StringLiteral / NumberLiteral @@ -599,8 +531,8 @@ export function isNumericLiteral(node: Node): node is NumericLiteral { return node.kind === SyntaxKind.NumericLiteral; } -export function createNumericLiteral(value: number, tsOriginal?: ts.Node, parent?: Node): NumericLiteral { - const expression = createNode(SyntaxKind.NumericLiteral, tsOriginal, parent) as NumericLiteral; +export function createNumericLiteral(value: number, tsOriginal?: ts.Node): NumericLiteral { + const expression = createNode(SyntaxKind.NumericLiteral, tsOriginal) as NumericLiteral; expression.value = value; return expression; } @@ -614,8 +546,8 @@ export function isStringLiteral(node: Node): node is StringLiteral { return node.kind === SyntaxKind.StringLiteral; } -export function createStringLiteral(value: string, tsOriginal?: ts.Node, parent?: Node): StringLiteral { - const expression = createNode(SyntaxKind.StringLiteral, tsOriginal, parent) as StringLiteral; +export function createStringLiteral(value: string, tsOriginal?: ts.Node): StringLiteral { + const expression = createNode(SyntaxKind.StringLiteral, tsOriginal) as StringLiteral; expression.value = value; return expression; } @@ -645,17 +577,12 @@ export function createFunctionExpression( dots?: DotsLiteral, restParamName?: Identifier, flags = FunctionExpressionFlags.None, - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): FunctionExpression { - const expression = createNode(SyntaxKind.FunctionExpression, tsOriginal, parent) as FunctionExpression; - setParent(body, expression); + const expression = createNode(SyntaxKind.FunctionExpression, tsOriginal) as FunctionExpression; expression.body = body; - setParent(params, expression); expression.params = params; - setParent(dots, expression); expression.dots = dots; - setParent(restParamName, expression); expression.restParamName = restParamName; expression.flags = flags; return expression; @@ -674,13 +601,10 @@ export function isTableFieldExpression(node: Node): node is TableFieldExpression export function createTableFieldExpression( value: Expression, key?: Expression, - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): TableFieldExpression { - const expression = createNode(SyntaxKind.TableFieldExpression, tsOriginal, parent) as TableFieldExpression; - setParent(value, expression); + const expression = createNode(SyntaxKind.TableFieldExpression, tsOriginal) as TableFieldExpression; expression.value = value; - setParent(key, expression); expression.key = key; return expression; } @@ -694,13 +618,8 @@ export function isTableExpression(node: Node): node is TableExpression { return node.kind === SyntaxKind.TableExpression; } -export function createTableExpression( - fields: TableFieldExpression[] = [], - tsOriginal?: ts.Node, - parent?: Node -): TableExpression { - const expression = createNode(SyntaxKind.TableExpression, tsOriginal, parent) as TableExpression; - setParent(fields, expression); +export function createTableExpression(fields: TableFieldExpression[] = [], tsOriginal?: ts.Node): TableExpression { + const expression = createNode(SyntaxKind.TableExpression, tsOriginal) as TableExpression; expression.fields = fields; return expression; } @@ -718,11 +637,9 @@ export function isUnaryExpression(node: Node): node is UnaryExpression { export function createUnaryExpression( operand: Expression, operator: UnaryOperator, - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): UnaryExpression { - const expression = createNode(SyntaxKind.UnaryExpression, tsOriginal, parent) as UnaryExpression; - setParent(operand, expression); + const expression = createNode(SyntaxKind.UnaryExpression, tsOriginal) as UnaryExpression; expression.operand = operand; expression.operator = operator; return expression; @@ -743,13 +660,10 @@ export function createBinaryExpression( left: Expression, right: Expression, operator: BinaryOperator, - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): BinaryExpression { - const expression = createNode(SyntaxKind.BinaryExpression, tsOriginal, parent) as BinaryExpression; - setParent(left, expression); + const expression = createNode(SyntaxKind.BinaryExpression, tsOriginal) as BinaryExpression; expression.left = left; - setParent(right, expression); expression.right = right; expression.operator = operator; return expression; @@ -766,11 +680,9 @@ export function isParenthesizedExpression(node: Node): node is ParenthesizedExpr export function createParenthesizedExpression( innerExpression: Expression, - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): ParenthesizedExpression { - const expression = createNode(SyntaxKind.ParenthesizedExpression, tsOriginal, parent) as ParenthesizedExpression; - setParent(innerExpression, expression); + const expression = createNode(SyntaxKind.ParenthesizedExpression, tsOriginal) as ParenthesizedExpression; expression.innerExpression = innerExpression; return expression; } @@ -788,13 +700,10 @@ export function isCallExpression(node: Node): node is CallExpression { export function createCallExpression( expression: Expression, params?: Expression[], - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): CallExpression { - const callExpression = createNode(SyntaxKind.CallExpression, tsOriginal, parent) as CallExpression; - setParent(expression, callExpression); + const callExpression = createNode(SyntaxKind.CallExpression, tsOriginal) as CallExpression; callExpression.expression = expression; - setParent(params, expression); callExpression.params = params; return callExpression; } @@ -814,15 +723,11 @@ export function createMethodCallExpression( prefixExpression: Expression, name: Identifier, params?: Expression[], - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): MethodCallExpression { - const callExpression = createNode(SyntaxKind.MethodCallExpression, tsOriginal, parent) as MethodCallExpression; - setParent(prefixExpression, callExpression); + const callExpression = createNode(SyntaxKind.MethodCallExpression, tsOriginal) as MethodCallExpression; callExpression.prefixExpression = prefixExpression; - setParent(name, callExpression); callExpression.name = name; - setParent(params, callExpression); callExpression.params = params; return callExpression; } @@ -843,10 +748,9 @@ export function createIdentifier( text: string, tsOriginal?: ts.Node, symbolId?: SymbolId, - originalName?: string, - parent?: Node + originalName?: string ): Identifier { - const expression = createNode(SyntaxKind.Identifier, tsOriginal, parent) as Identifier; + const expression = createNode(SyntaxKind.Identifier, tsOriginal) as Identifier; expression.exportable = true; expression.text = text; expression.symbolId = symbolId; @@ -858,8 +762,8 @@ export function cloneIdentifier(identifier: Identifier, tsOriginal?: ts.Node): I return createIdentifier(identifier.text, tsOriginal, identifier.symbolId, identifier.originalName); } -export function createAnonymousIdentifier(tsOriginal?: ts.Node, parent?: Node): Identifier { - const expression = createNode(SyntaxKind.Identifier, tsOriginal, parent) as Identifier; +export function createAnonymousIdentifier(tsOriginal?: ts.Node): Identifier { + const expression = createNode(SyntaxKind.Identifier, tsOriginal) as Identifier; expression.exportable = false; expression.text = "____"; return expression; @@ -878,13 +782,10 @@ export function isTableIndexExpression(node: Node): node is TableIndexExpression export function createTableIndexExpression( table: Expression, index: Expression, - tsOriginal?: ts.Node, - parent?: Node + tsOriginal?: ts.Node ): TableIndexExpression { - const expression = createNode(SyntaxKind.TableIndexExpression, tsOriginal, parent) as TableIndexExpression; - setParent(table, expression); + const expression = createNode(SyntaxKind.TableIndexExpression, tsOriginal) as TableIndexExpression; expression.table = table; - setParent(index, expression); expression.index = index; return expression; } diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index fda6e89ff..e94269734 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -395,13 +395,10 @@ export class LuaPrinter { return this.createSourceNode(statement, chunks); } - public printIfStatement(statement: lua.IfStatement): SourceNode { + public printIfStatement(statement: lua.IfStatement, isElseIf = false): SourceNode { const chunks: SourceChunk[] = []; - const isElseIf = statement.parent !== undefined && lua.isIfStatement(statement.parent); - const prefix = isElseIf ? "elseif" : "if"; - chunks.push(this.indent(prefix + " "), this.printExpression(statement.condition), " then\n"); this.pushIndent(); @@ -410,7 +407,7 @@ export class LuaPrinter { if (statement.elseBlock) { if (lua.isIfStatement(statement.elseBlock)) { - chunks.push(this.printIfStatement(statement.elseBlock)); + chunks.push(this.printIfStatement(statement.elseBlock, true)); } else { chunks.push(this.indent("else\n")); this.pushIndent(); diff --git a/src/transformation/utils/errors.ts b/src/transformation/utils/errors.ts index bb3f3ce79..09634fcc8 100644 --- a/src/transformation/utils/errors.ts +++ b/src/transformation/utils/errors.ts @@ -148,3 +148,6 @@ export const InvalidAmbientIdentifierName = (node: ts.Identifier) => export const InvalidForRangeCall = (node: ts.Node, message: string) => new TranspileError(`Invalid @forRange call: ${message}`, node); + +export const UnsupportedVarDeclaration = (node: ts.Node) => + new TranspileError("`var` declarations are not supported. Use `let` or `const` instead.", node); diff --git a/src/transformation/utils/lua-ast.ts b/src/transformation/utils/lua-ast.ts index 6043a4c07..15488d982 100644 --- a/src/transformation/utils/lua-ast.ts +++ b/src/transformation/utils/lua-ast.ts @@ -3,10 +3,9 @@ import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { getCurrentNamespace } from "../visitors/namespace"; -import { UndefinedScope } from "./errors"; import { createExportedIdentifier, getIdentifierExportScope } from "./export"; import { findScope, peekScope, ScopeType } from "./scope"; -import { isFirstDeclaration, isFunctionType } from "./typescript"; +import { isFunctionType } from "./typescript"; export type OneToManyVisitorResult = T | T[] | undefined; export function unwrapVisitorResult(result: OneToManyVisitorResult): T[] { @@ -27,22 +26,6 @@ export function createExportsIdentifier(): lua.Identifier { return lua.createIdentifier("____exports"); } -export function replaceStatementInParent(oldNode: lua.Statement, newNode?: lua.Statement): void { - if (!oldNode.parent) { - throw new Error("node has not yet been assigned a parent"); - } - - if (lua.isBlock(oldNode.parent) || lua.isDoStatement(oldNode.parent)) { - if (newNode) { - oldNode.parent.statements.splice(oldNode.parent.statements.indexOf(oldNode), 1, newNode); - } else { - oldNode.parent.statements.splice(oldNode.parent.statements.indexOf(oldNode), 1); - } - } else { - throw new Error("unexpected parent type"); - } -} - export function createExpressionPlusOne(expression: lua.Expression): lua.Expression { if (lua.isNumericLiteral(expression)) { const newNode = lua.cloneNode(expression); @@ -111,10 +94,9 @@ export function createHoistableVariableDeclarationStatement( context: TransformationContext, identifier: lua.Identifier, initializer?: lua.Expression, - tsOriginal?: ts.Node, - parent?: lua.Node + tsOriginal?: ts.Node ): lua.AssignmentStatement | lua.VariableDeclarationStatement { - const declaration = lua.createVariableDeclarationStatement(identifier, initializer, tsOriginal, parent); + const declaration = lua.createVariableDeclarationStatement(identifier, initializer, tsOriginal); if (!context.options.noHoisting && identifier.symbolId) { const scope = peekScope(context); if (!scope.variableDeclarations) { @@ -132,13 +114,13 @@ export function createLocalOrExportedOrGlobalDeclaration( lhs: lua.Identifier | lua.Identifier[], rhs?: lua.Expression | lua.Expression[], tsOriginal?: ts.Node, - parent?: lua.Node, overrideExportScope?: ts.SourceFile | ts.ModuleDeclaration ): lua.Statement[] { let declaration: lua.VariableDeclarationStatement | undefined; let assignment: lua.AssignmentStatement | undefined; - const functionDeclaration = tsOriginal && ts.isFunctionDeclaration(tsOriginal) ? tsOriginal : undefined; + const isVariableDeclaration = tsOriginal !== undefined && ts.isVariableDeclaration(tsOriginal); + const isFunctionDeclaration = tsOriginal !== undefined && ts.isFunctionDeclaration(tsOriginal); const identifiers = Array.isArray(lhs) ? lhs : [lhs]; if (identifiers.length === 0) { @@ -154,48 +136,31 @@ export function createLocalOrExportedOrGlobalDeclaration( assignment = lua.createAssignmentStatement( identifiers.map(identifier => createExportedIdentifier(context, identifier, exportScope)), rhs, - tsOriginal, - parent + tsOriginal ); } } else { const insideFunction = findScope(context, ScopeType.Function) !== undefined; - let isLetOrConst = false; - let isVariableFirstDeclaration = true; // var can have multiple declarations for the same variable :/ - if (tsOriginal && ts.isVariableDeclaration(tsOriginal) && tsOriginal.parent) { - isLetOrConst = (tsOriginal.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !== 0; - isVariableFirstDeclaration = isLetOrConst || isFirstDeclaration(context, tsOriginal); - } - if ( - (context.isModule || getCurrentNamespace(context) || insideFunction || isLetOrConst) && - isVariableFirstDeclaration - ) { + if (context.isModule || getCurrentNamespace(context) || insideFunction || isVariableDeclaration) { // local const isPossibleWrappedFunction = - !functionDeclaration && + !isFunctionDeclaration && tsOriginal && ts.isVariableDeclaration(tsOriginal) && tsOriginal.initializer && isFunctionType(context, context.checker.getTypeAtLocation(tsOriginal.initializer)); if (isPossibleWrappedFunction) { // Split declaration and assignment for wrapped function types to allow recursion - declaration = lua.createVariableDeclarationStatement(lhs, undefined, tsOriginal, parent); - assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal, parent); + declaration = lua.createVariableDeclarationStatement(lhs, undefined, tsOriginal); + assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal); } else { - declaration = lua.createVariableDeclarationStatement(lhs, rhs, tsOriginal, parent); + declaration = lua.createVariableDeclarationStatement(lhs, rhs, tsOriginal); } if (!context.options.noHoisting) { // Remember local variable declarations for hoisting later - const scope = - isLetOrConst || functionDeclaration - ? peekScope(context) - : findScope(context, ScopeType.Function | ScopeType.File); - - if (scope === undefined) { - throw UndefinedScope(); - } + const scope = peekScope(context); if (!scope.variableDeclarations) { scope.variableDeclarations = []; @@ -205,13 +170,13 @@ export function createLocalOrExportedOrGlobalDeclaration( } } else if (rhs) { // global - assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal, parent); + assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal); } else { return []; } } - if (!context.options.noHoisting && functionDeclaration) { + if (!context.options.noHoisting && isFunctionDeclaration) { // Remember function definitions for hoisting later const functionSymbolId = (lhs as lua.Identifier).symbolId; const scope = peekScope(context); diff --git a/src/transformation/utils/scope.ts b/src/transformation/utils/scope.ts index 5077bcc42..55fa88bd7 100644 --- a/src/transformation/utils/scope.ts +++ b/src/transformation/utils/scope.ts @@ -1,9 +1,9 @@ +import * as assert from "assert"; import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { getOrUpdate, isNonNull } from "../../utils"; import { TransformationContext } from "../context"; import { UndefinedFunctionDefinition, UndefinedScope } from "./errors"; -import { replaceStatementInParent } from "./lua-ast"; import { getSymbolInfo } from "./symbols"; import { getFirstDeclarationInFile } from "./typescript"; @@ -168,15 +168,11 @@ function hoistVariableDeclarations( } const index = result.indexOf(declaration); - if (index >= 0) { - if (assignment) { - result.splice(index, 1, assignment); - } else { - result.splice(index, 1); - } + assert(index > -1); + if (assignment) { + result.splice(index, 1, assignment); } else { - // Special case for 'var's declared in child scopes - replaceStatementInParent(declaration, assignment); + result.splice(index, 1); } hoistedLocals.push(...declaration.left); diff --git a/src/transformation/visitors/enum.ts b/src/transformation/visitors/enum.ts index e721464eb..8c3a82213 100644 --- a/src/transformation/visitors/enum.ts +++ b/src/transformation/visitors/enum.ts @@ -75,7 +75,6 @@ export const transformEnumDeclaration: FunctionVisitor = (no : lua.createIdentifier(member.name.getText(), member.name), valueExpression, node, - undefined, exportScope ) ); diff --git a/src/transformation/visitors/loops/do-while.ts b/src/transformation/visitors/loops/do-while.ts index e1a56c496..8e219b6b8 100644 --- a/src/transformation/visitors/loops/do-while.ts +++ b/src/transformation/visitors/loops/do-while.ts @@ -1,7 +1,7 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { FunctionVisitor } from "../../context"; -import { transformLoopBody } from "./body"; +import { transformLoopBody } from "./utils"; export const transformWhileStatement: FunctionVisitor = (statement, context) => { return lua.createWhileStatement( diff --git a/src/transformation/visitors/loops/for-in.ts b/src/transformation/visitors/loops/for-in.ts index b79f18ccf..f4365ac23 100644 --- a/src/transformation/visitors/loops/for-in.ts +++ b/src/transformation/visitors/loops/for-in.ts @@ -4,7 +4,7 @@ import { FunctionVisitor } from "../../context"; import { ForbiddenForIn, UnsupportedForInVariable } from "../../utils/errors"; import { isArrayType } from "../../utils/typescript"; import { transformIdentifier } from "../identifier"; -import { transformLoopBody } from "./body"; +import { getVariableDeclarationBinding, transformLoopBody } from "./utils"; export const transformForInStatement: FunctionVisitor = (statement, context) => { if (isArrayType(context, context.checker.getTypeAtLocation(statement.expression))) { @@ -22,11 +22,13 @@ export const transformForInStatement: FunctionVisitor = (stat // TODO: After the transformation pipeline refactor we should look at refactoring this together with the // for-of initializer transformation. let iterationVariable: lua.Identifier; - if ( - ts.isVariableDeclarationList(statement.initializer) && - ts.isIdentifier(statement.initializer.declarations[0].name) - ) { - iterationVariable = transformIdentifier(context, statement.initializer.declarations[0].name); + if (ts.isVariableDeclarationList(statement.initializer)) { + const binding = getVariableDeclarationBinding(statement.initializer); + if (!ts.isIdentifier(binding)) { + throw UnsupportedForInVariable(statement.initializer); + } + + iterationVariable = transformIdentifier(context, binding); } else if (ts.isIdentifier(statement.initializer)) { // Iteration variable becomes ____key iterationVariable = lua.createIdentifier("____key"); diff --git a/src/transformation/visitors/loops/for-of.ts b/src/transformation/visitors/loops/for-of.ts index 868b2c03d..6689e19e5 100644 --- a/src/transformation/visitors/loops/for-of.ts +++ b/src/transformation/visitors/loops/for-of.ts @@ -15,7 +15,7 @@ import { isArrayType, isNumberType } from "../../utils/typescript"; import { transformArguments } from "../call"; import { transformIdentifier } from "../identifier"; import { transformArrayBindingElement, transformVariableDeclaration } from "../variable-declaration"; -import { transformLoopBody } from "./body"; +import { getVariableDeclarationBinding, transformLoopBody } from "./utils"; function transformForOfInitializer( context: TransformationContext, @@ -23,15 +23,16 @@ function transformForOfInitializer( expression: lua.Expression ): lua.Statement | undefined { if (ts.isVariableDeclarationList(initializer)) { + const binding = getVariableDeclarationBinding(initializer); // Declaration of new variable - if (ts.isArrayBindingPattern(initializer.declarations[0].name)) { - if (initializer.declarations[0].name.elements.length === 0) { + if (ts.isArrayBindingPattern(binding)) { + if (binding.elements.length === 0) { // Ignore empty destructuring assignment return undefined; } expression = createUnpackCall(context, expression, initializer); - } else if (ts.isObjectBindingPattern(initializer.declarations[0].name)) { + } else if (ts.isObjectBindingPattern(binding)) { throw UnsupportedObjectDestructuringInForOf(initializer); } @@ -90,19 +91,19 @@ function transformForRangeStatement( throw InvalidForRangeCall(statement.initializer, "@forRange loop must declare its own control variable."); } - const controlDeclaration = statement.initializer.declarations[0]; - if (!ts.isIdentifier(controlDeclaration.name)) { + const binding = getVariableDeclarationBinding(statement.initializer); + if (!ts.isIdentifier(binding)) { throw InvalidForRangeCall(statement.initializer, "@forRange loop cannot use destructuring."); } - if (!isNumberType(context, context.checker.getTypeAtLocation(controlDeclaration))) { + if (!isNumberType(context, context.checker.getTypeAtLocation(binding))) { throw InvalidForRangeCall( statement.expression, "@forRange function must return Iterable or Array." ); } - const control = transformIdentifier(context, controlDeclaration.name); + const control = transformIdentifier(context, binding); const signature = context.checker.getResolvedSignature(statement.expression); const [start, limit, step] = transformArguments(context, statement.expression.arguments, signature); return lua.createForStatement(block, control, start, limit, step, statement); @@ -121,12 +122,9 @@ function transformForOfLuaIteratorStatement( if (ts.isVariableDeclarationList(statement.initializer)) { // Variables declared in for loop // for ${initializer} in ${iterable} do - const initializerVariable = statement.initializer.declarations[0].name; - if (ts.isArrayBindingPattern(initializerVariable)) { - const identifiers = castEach( - initializerVariable.elements.map(e => transformArrayBindingElement(context, e)), - lua.isIdentifier - ); + const binding = getVariableDeclarationBinding(statement.initializer); + if (ts.isArrayBindingPattern(binding)) { + const identifiers = binding.elements.map(e => transformArrayBindingElement(context, e)); if (identifiers.length === 0) { identifiers.push(lua.createAnonymousIdentifier()); } @@ -163,6 +161,7 @@ function transformForOfLuaIteratorStatement( // LuaIterator (no TupleReturn) if ( ts.isVariableDeclarationList(statement.initializer) && + statement.initializer.declarations.length > 0 && ts.isIdentifier(statement.initializer.declarations[0].name) ) { // Single variable declared in for loop @@ -194,15 +193,15 @@ function transformForOfArrayStatement( let valueVariable: lua.Identifier; if (ts.isVariableDeclarationList(statement.initializer)) { // Declaration of new variable - const variables = statement.initializer.declarations[0].name; - if (ts.isArrayBindingPattern(variables) || ts.isObjectBindingPattern(variables)) { + const binding = getVariableDeclarationBinding(statement.initializer); + if (ts.isArrayBindingPattern(binding) || ts.isObjectBindingPattern(binding)) { valueVariable = lua.createIdentifier("____values"); const initializer = transformForOfInitializer(context, statement.initializer, valueVariable); if (initializer) { block.statements.unshift(initializer); } } else { - valueVariable = transformIdentifier(context, variables); + valueVariable = transformIdentifier(context, binding); } } else { // Assignment to existing variable @@ -228,13 +227,14 @@ function transformForOfIteratorStatement( const iterable = context.transformExpression(statement.expression); if ( ts.isVariableDeclarationList(statement.initializer) && + statement.initializer.declarations.length > 0 && ts.isIdentifier(statement.initializer.declarations[0].name) ) { // Single variable declared in for loop // for ${initializer} in __TS__iterator(${iterator}) do return lua.createForInStatement( block, - [transformIdentifier(context, statement.initializer.declarations[0].name as ts.Identifier)], + [transformIdentifier(context, statement.initializer.declarations[0].name)], [transformLuaLibFunction(context, LuaLibFeature.Iterator, statement.expression, iterable)] ); } else { diff --git a/src/transformation/visitors/loops/for.ts b/src/transformation/visitors/loops/for.ts index 517852fdf..970c2f045 100644 --- a/src/transformation/visitors/loops/for.ts +++ b/src/transformation/visitors/loops/for.ts @@ -1,14 +1,15 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { FunctionVisitor } from "../../context"; -import { transformVariableDeclaration } from "../variable-declaration"; -import { transformLoopBody } from "./body"; +import { checkVariableDeclarationList, transformVariableDeclaration } from "../variable-declaration"; +import { transformLoopBody } from "./utils"; export const transformForStatement: FunctionVisitor = (statement, context) => { const result: lua.Statement[] = []; if (statement.initializer) { if (ts.isVariableDeclarationList(statement.initializer)) { + checkVariableDeclarationList(statement.initializer); // local initializer = value result.push(...statement.initializer.declarations.flatMap(d => transformVariableDeclaration(context, d))); } else { diff --git a/src/transformation/visitors/loops/body.ts b/src/transformation/visitors/loops/utils.ts similarity index 73% rename from src/transformation/visitors/loops/body.ts rename to src/transformation/visitors/loops/utils.ts index e46c4112d..639c0de95 100644 --- a/src/transformation/visitors/loops/body.ts +++ b/src/transformation/visitors/loops/utils.ts @@ -3,6 +3,7 @@ import * as lua from "../../../LuaAST"; import { TransformationContext } from "../../context"; import { performHoisting, popScope, pushScope, ScopeType } from "../../utils/scope"; import { transformBlockOrStatement } from "../block"; +import { checkVariableDeclarationList } from "../variable-declaration"; export function transformLoopBody( context: TransformationContext, @@ -23,3 +24,13 @@ export function transformLoopBody( return baseResult; } + +export function getVariableDeclarationBinding(node: ts.VariableDeclarationList): ts.BindingName { + checkVariableDeclarationList(node); + + if (node.declarations.length === 0) { + return ts.createIdentifier("____"); + } + + return node.declarations[0].name; +} diff --git a/src/transformation/visitors/variable-declaration.ts b/src/transformation/visitors/variable-declaration.ts index 9b932eef3..c02d8ab1d 100644 --- a/src/transformation/visitors/variable-declaration.ts +++ b/src/transformation/visitors/variable-declaration.ts @@ -4,7 +4,7 @@ import { assertNever } from "../../utils"; import { FunctionVisitor, TransformationContext } from "../context"; import { isTupleReturnCall } from "../utils/annotations"; import { validateAssignment } from "../utils/assignment-validation"; -import { UnsupportedKind } from "../utils/errors"; +import { UnsupportedKind, UnsupportedVarDeclaration } from "../utils/errors"; import { addExportToIdentifier } from "../utils/export"; import { createLocalOrExportedOrGlobalDeclaration, createUnpackCall } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; @@ -222,5 +222,13 @@ export function transformVariableDeclaration( } } -export const transformVariableStatement: FunctionVisitor = (node, context) => - node.declarationList.declarations.flatMap(declaration => transformVariableDeclaration(context, declaration)); +export function checkVariableDeclarationList(node: ts.VariableDeclarationList): void { + if ((node.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) === 0) { + throw UnsupportedVarDeclaration(node); + } +} + +export const transformVariableStatement: FunctionVisitor = (node, context) => { + checkVariableDeclarationList(node.declarationList); + return node.declarationList.declarations.flatMap(declaration => transformVariableDeclaration(context, declaration)); +}; diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index 9bb53c78c..c559690d0 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -1,27 +1,43 @@ import * as util from "../util"; -test("Const assignment (%p)", () => { +test("const declaration", () => { const lua = util.transpileString(`const foo = true;`); expect(lua).toBe(`local foo = true`); }); -test("Let assignment (%p)", () => { +test("let declaration", () => { const lua = util.transpileString(`let foo = true;`); expect(lua).toBe(`local foo = true`); }); -test("Var assignment (%p)", () => { - const lua = util.transpileString(`var foo = true;`); - expect(lua).toBe(`foo = true`); +test("var declaration is disallowed", () => { + util.testFunction` + var foo = true; + `.expectToHaveDiagnostics(); }); -test.each(["var myvar;", "let myvar;", "const myvar = null;", "const myvar = undefined;"])( - "Null assignments (%p)", - declaration => { - const result = util.transpileAndExecute(declaration + " return myvar;"); - expect(result).toBe(undefined); - } -); +test("var declaration in for loop is disallowed", () => { + util.testFunction` + for (var foo = 0;;) {} + `.expectToHaveDiagnostics(); +}); + +test("var declaration in for...in loop is disallowed", () => { + util.testFunction` + for (var foo in {}) {} + `.expectToHaveDiagnostics(); +}); + +test("var declaration in for...of loop is disallowed", () => { + util.testFunction` + for (var foo of []) {} + `.expectToHaveDiagnostics(); +}); + +test.each(["let myvar;", "const myvar = null;", "const myvar = undefined;"])("Null assignments (%p)", declaration => { + const result = util.transpileAndExecute(declaration + " return myvar;"); + expect(result).toBe(undefined); +}); test.each(["x = y", "x += y"])("Assignment expressions (%p)", expression => { util.testFunction` diff --git a/test/unit/builtins/map.spec.ts b/test/unit/builtins/map.spec.ts index cc5d1eb9c..f12810b3e 100644 --- a/test/unit/builtins/map.spec.ts +++ b/test/unit/builtins/map.spec.ts @@ -43,7 +43,7 @@ test("map entries", () => { const result = util.transpileAndExecute( `let mymap = new Map([[5, 2],[6, 3],[7, 4]]); let count = 0; - for (var [key, value] of mymap.entries()) { count += key + value; } + for (const [key, value] of mymap.entries()) { count += key + value; } return count;` ); expect(result).toBe(27); @@ -120,7 +120,7 @@ test("map keys", () => { const result = util.transpileAndExecute( `let mymap = new Map([[5, 2],[6, 3],[7, 4]]); let count = 0; - for (var key of mymap.keys()) { count += key; } + for (const key of mymap.keys()) { count += key; } return count;` ); @@ -140,7 +140,7 @@ test("map values", () => { const result = util.transpileAndExecute( `let mymap = new Map([[5, 2],[6, 3],[7, 4]]); let count = 0; - for (var value of mymap.values()) { count += value; } + for (const value of mymap.values()) { count += value; } return count;` ); @@ -160,14 +160,14 @@ describe.each(iterationMethods)("map.%s() preserves insertion order", iterationM test("basic", () => { util.testFunction` const mymap = new Map(); - + mymap.set("x", 1); mymap.set("a", 2); mymap.set(4, 3); mymap.set("b", 6); mymap.set(1, 4); mymap.set("a", 5); - + mymap.delete("b"); return [...mymap.${iterationMethod}()]; @@ -177,11 +177,11 @@ describe.each(iterationMethods)("map.%s() preserves insertion order", iterationM test("after removing last", () => { util.testFunction` const mymap = new Map(); - + mymap.set("x", 1); mymap.set("a", 2); mymap.set(4, 3); - + mymap.delete(4); return [...mymap.${iterationMethod}()]; @@ -191,11 +191,11 @@ describe.each(iterationMethods)("map.%s() preserves insertion order", iterationM test("after removing first", () => { util.testFunction` const mymap = new Map(); - + mymap.set("x", 1); mymap.set("a", 2); mymap.set(4, 3); - + mymap.delete("x"); return [...mymap.${iterationMethod}()]; @@ -205,10 +205,10 @@ describe.each(iterationMethods)("map.%s() preserves insertion order", iterationM test("after removing all", () => { util.testFunction` const mymap = new Map(); - + mymap.set("x", 1); mymap.set("a", 2); - + mymap.delete("a"); mymap.delete("x"); diff --git a/test/unit/builtins/set.spec.ts b/test/unit/builtins/set.spec.ts index c2e2fe433..cbd8cb3f4 100644 --- a/test/unit/builtins/set.spec.ts +++ b/test/unit/builtins/set.spec.ts @@ -49,7 +49,7 @@ test("set entries", () => { util.testFunction` let myset = new Set([5, 6, 7]); let count = 0; - for (var [key, value] of myset.entries()) { count += key + value; } + for (const [key, value] of myset.entries()) { count += key + value; } return count; `.expectToMatchJsResult(); }); @@ -138,14 +138,14 @@ describe.each(iterationMethods)("set.%s() preserves insertion order", iterationM test("basic", () => { util.testFunction` const myset = new Set(); - + myset.add("x"); myset.add("a"); myset.add(4); myset.add("b"); myset.add(1); myset.add("a"); - + myset.delete("b"); return [...myset.${iterationMethod}()]; @@ -155,11 +155,11 @@ describe.each(iterationMethods)("set.%s() preserves insertion order", iterationM test("after removing last", () => { util.testFunction` const myset = new Set(); - + myset.add("x"); myset.add("a"); myset.add(4); - + myset.delete(4); return [...myset.${iterationMethod}()]; @@ -169,11 +169,11 @@ describe.each(iterationMethods)("set.%s() preserves insertion order", iterationM test("after removing first", () => { util.testFunction` const myset = new Set(); - + myset.add("x"); myset.add("a"); myset.add(4); - + myset.delete("x"); return [...myset.${iterationMethod}()]; @@ -183,10 +183,10 @@ describe.each(iterationMethods)("set.%s() preserves insertion order", iterationM test("after removing all", () => { util.testFunction` const myset = new Set(); - + myset.add("x"); myset.add("a"); - + myset.delete("a"); myset.delete("x"); diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index c9553f96e..1a94507a2 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -410,14 +410,17 @@ test("Function local overriding export", () => { }); test("Function using global as this", () => { - const tsHeader = ` - var foo = "foo"; + // Value is provided with top-level return with ts-ignore, because modules are always strict. + // TODO: Provide a different builder kind for such tests? + util.testModule` + (globalThis as any).foo = "foo"; function bar(this: any) { return this.foo; } - `; - util.testExpression`foo`.setTsHeader(tsHeader).expectToMatchJsResult(); + // @ts-ignore + return bar(); + `.expectToEqual("foo"); }); test("Function rest binding pattern", () => { diff --git a/test/unit/hoisting.spec.ts b/test/unit/hoisting.spec.ts index e8ee28947..6f4609f0f 100644 --- a/test/unit/hoisting.spec.ts +++ b/test/unit/hoisting.spec.ts @@ -2,25 +2,6 @@ import * as ts from "typescript"; import { ReferencedBeforeDeclaration } from "../../src/transformation/utils/errors"; import * as util from "../util"; -test("Var Hoisting", () => { - const code = ` - foo = "foo"; - var foo; - return foo; - `; - const result = util.transpileAndExecute(code); - expect(result).toBe("foo"); -}); - -test("Exported Var Hoisting", () => { - const code = ` - foo = "foo"; - export var foo; - `; - const result = util.transpileExecuteAndReturnExport(code, "foo"); - expect(result).toBe("foo"); -}); - test.each(["let", "const"])("Let/Const Hoisting (%p)", varType => { const code = ` let bar: string; @@ -98,37 +79,19 @@ test("Exported Namespace Function Hoisting", () => { }); test.each([ - { varType: "var", expectResult: "foo" }, { varType: "let", expectResult: "bar" }, { varType: "const", expectResult: "bar" }, ])("Hoisting in Non-Function Scope (%p)", ({ varType, expectResult }) => { const code = ` - function foo() { - ${varType} bar = "bar"; - for (let i = 0; i < 1; ++i) { - ${varType} bar = "foo"; + function foo() { + ${varType} bar = "bar"; + for (let i = 0; i < 1; ++i) { + ${varType} bar = "foo"; + } + return bar; } - return bar; - } - return foo(); - `; - const result = util.transpileAndExecute(code); - expect(result).toBe(expectResult); -}); - -test.each([ - { initializer: "", expectResult: "foofoo" }, - { initializer: ' = "bar"', expectResult: "barbar" }, -])("Var hoisting from child scope (%p)", ({ initializer, expectResult }) => { - const code = ` - foo = "foo"; - let result: string; - if (true) { - var foo${initializer}; - result = foo; - } - return foo + result; - `; + return foo(); + `; const result = util.transpileAndExecute(code); expect(result).toBe(expectResult); }); @@ -175,19 +138,20 @@ test("Exported Namespace Hoisting", () => { }); test("Nested Namespace Hoisting", () => { - const code = ` - export namespace Outer { + util.testModule` + const Inner = 0; + namespace Outer { export function bar() { return Inner.foo; } namespace Inner { - export let foo = "foo"; + export const foo = "foo"; } } + export const foo = Outer.bar(); - `; - const result = util.transpileExecuteAndReturnExport(code, "foo"); - expect(result).toBe("foo"); + export { Inner }; + `.expectToMatchJsResult(); }); test("Class Hoisting", () => { diff --git a/test/unit/identifiers.spec.ts b/test/unit/identifiers.spec.ts index 88c5cc23b..5f647a013 100644 --- a/test/unit/identifiers.spec.ts +++ b/test/unit/identifiers.spec.ts @@ -505,17 +505,13 @@ describe("lua keyword as identifier doesn't interfere with lua's value", () => { }); test("variable (_G)", () => { - const tsHeader = ` - var foobar = "foo";`; - const code = ` const _G = "bar"; - function foo(this: any) { - return this.foobar + _G; - } - return foo();`; + (globalThis as any).foo = "foo"; + return (globalThis as any).foo + _G; + `; - expect(util.transpileAndExecute(code, undefined, undefined, tsHeader)).toBe("foobar"); + expect(util.transpileAndExecute(code)).toBe("foobar"); }); test("function parameter", () => {