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
13 changes: 6 additions & 7 deletions src/transformation/builtins/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,17 @@ export function transformBuiltinCallExpression(
node: ts.CallExpression,
isOptionalCall: boolean
): lua.Expression | undefined {
const unsupportedOptionalCall = () => {
context.diagnostics.push(unsupportedBuiltinOptionalCall(node));
return lua.createNilLiteral();
};
const expressionType = context.checker.getTypeAtLocation(node.expression);
if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, expressionType, undefined)) {
// TODO:
checkForLuaLibType(context, expressionType);
const result = transformGlobalCall(context, node);
if (result) {
if (isOptionalCall) return unsupportedOptionalCall();
return result;
}
}
Expand All @@ -75,14 +80,8 @@ export function transformBuiltinCallExpression(
return;
}

assume<PropertyCallExpression>(node);

const isOptionalAccess = expression.questionDotToken;
const unsupportedOptionalCall = () => {
context.diagnostics.push(unsupportedBuiltinOptionalCall(node));
return lua.createNilLiteral();
};

assume<PropertyCallExpression>(node);
// If the function being called is of type owner.func, get the type of owner
const ownerType = context.checker.getTypeAtLocation(expression.expression);

Expand Down
8 changes: 0 additions & 8 deletions src/transformation/utils/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,14 +128,6 @@ export const invalidTableExtensionUse = createErrorDiagnosticFactory(
"This function must be called directly and cannot be referred to."
);

export const invalidTableDeleteExpression = createErrorDiagnosticFactory(
"Table delete extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."
);

export const invalidTableSetExpression = createErrorDiagnosticFactory(
"Table set extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."
);

export const annotationRemoved = createErrorDiagnosticFactory(
(kind: AnnotationKind) =>
`'@${kind}' has been removed and will no longer have any effect.` +
Expand Down
69 changes: 9 additions & 60 deletions src/transformation/visitors/call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,11 @@ import { isValidLuaIdentifier } from "../utils/safe-names";
import { isExpressionWithEvaluationEffect } from "../utils/typescript";
import { transformElementAccessArgument } from "./access";
import { isMultiReturnCall, shouldMultiReturnCallBeWrapped } from "./language-extensions/multi";
import { isOperatorMapping, transformOperatorMappingExpression } from "./language-extensions/operators";
import {
isTableDeleteCall,
isTableGetCall,
isTableHasCall,
isTableSetCall,
transformTableDeleteExpression,
transformTableGetExpression,
transformTableHasExpression,
transformTableSetExpression,
} from "./language-extensions/table";
import {
annotationRemoved,
invalidTableDeleteExpression,
invalidTableSetExpression,
unsupportedBuiltinOptionalCall,
} from "../utils/diagnostics";
import { annotationRemoved } from "../utils/diagnostics";
import { moveToPrecedingTemp, transformExpressionList } from "./expression-list";
import { transformInPrecedingStatementScope } from "../utils/preceding-statements";
import { transformOptionalChain, getOptionalContinuationData } from "./optional-chaining";
import { getOptionalContinuationData, transformOptionalChain } from "./optional-chaining";
import { transformLanguageExtensionCallExpression } from "./language-extensions";

export type PropertyCallExpression = ts.CallExpression & { expression: ts.PropertyAccessExpression };

Expand Down Expand Up @@ -234,52 +219,16 @@ export const transformCallExpression: FunctionVisitor<ts.CallExpression> = (node
: undefined;
const wrapResultInTable = isMultiReturnCall(context, node) && shouldMultiReturnCallBeWrapped(context, node);

const builtinResult = transformBuiltinCallExpression(context, node, optionalContinuation !== undefined);
if (builtinResult) {
if (optionalContinuation) {
context.diagnostics.push(unsupportedBuiltinOptionalCall(node));
}
return wrapResultInTable ? wrapInTable(builtinResult) : builtinResult;
}

if (isTupleReturnCall(context, node)) {
context.diagnostics.push(annotationRemoved(node, AnnotationKind.TupleReturn));
}

if (isOperatorMapping(context, node)) {
if (optionalContinuation) {
context.diagnostics.push(unsupportedBuiltinOptionalCall(node));
return lua.createNilLiteral();
}
return transformOperatorMappingExpression(context, node);
}

if (isTableDeleteCall(context, node)) {
context.diagnostics.push(invalidTableDeleteExpression(node));
context.addPrecedingStatements(transformTableDeleteExpression(context, node));
return lua.createNilLiteral();
}

if (isTableGetCall(context, node)) {
if (optionalContinuation) {
context.diagnostics.push(unsupportedBuiltinOptionalCall(node));
return lua.createNilLiteral();
}
return transformTableGetExpression(context, node);
}

if (isTableHasCall(context, node)) {
if (optionalContinuation) {
context.diagnostics.push(unsupportedBuiltinOptionalCall(node));
return lua.createNilLiteral();
}
return transformTableHasExpression(context, node);
}

if (isTableSetCall(context, node)) {
context.diagnostics.push(invalidTableSetExpression(node));
context.addPrecedingStatements(transformTableSetExpression(context, node));
return lua.createNilLiteral();
const builtinOrExtensionResult =
transformBuiltinCallExpression(context, node, optionalContinuation !== undefined) ??
transformLanguageExtensionCallExpression(context, node, optionalContinuation !== undefined);
if (builtinOrExtensionResult) {
// unsupportedOptionalCall diagnostic already present
return wrapResultInTable ? wrapInTable(builtinOrExtensionResult) : builtinOrExtensionResult;
}

if (ts.isPropertyAccessExpression(node.expression)) {
Expand Down
51 changes: 24 additions & 27 deletions src/transformation/visitors/expression-statement.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,10 @@
import * as ts from "typescript";
import * as lua from "../../LuaAST";
import { FunctionVisitor } from "../context";
import { FunctionVisitor, tempSymbolId, TransformationContext } from "../context";
import { transformBinaryExpressionStatement } from "./binary-expression";
import {
isTableDeleteCall,
isTableSetCall,
transformTableDeleteExpression,
transformTableSetExpression,
} from "./language-extensions/table";
import { transformUnaryExpressionStatement } from "./unary-expression";
import { transformVoidExpressionStatement } from "./void";

export const transformExpressionStatement: FunctionVisitor<ts.ExpressionStatement> = (node, context) => {
const expression = node.expression;

if (ts.isCallExpression(expression) && isTableDeleteCall(context, expression)) {
return transformTableDeleteExpression(context, expression);
}

if (ts.isCallExpression(expression) && isTableSetCall(context, expression)) {
return transformTableSetExpression(context, expression);
}

if (ts.isVoidExpression(expression)) {
return transformVoidExpressionStatement(expression, context);
}

const unaryExpressionResult = transformUnaryExpressionStatement(context, node);
if (unaryExpressionResult) {
return unaryExpressionResult;
Expand All @@ -36,9 +15,27 @@ export const transformExpressionStatement: FunctionVisitor<ts.ExpressionStatemen
return binaryExpressionResult;
}

const result = context.transformExpression(expression);
return lua.isCallExpression(result) || lua.isMethodCallExpression(result)
? lua.createExpressionStatement(result)
: // Assign expression statements to dummy to make sure they're legal Lua
lua.createVariableDeclarationStatement(lua.createAnonymousIdentifier(), result);
return transformExpressionToStatement(context, node.expression);
};

export function transformExpressionToStatement(
context: TransformationContext,
expression: ts.Expression
): lua.Statement | undefined {
const result = context.transformExpression(expression);

const isTempVariable = lua.isIdentifier(result) && result.symbolId === tempSymbolId;
if (isTempVariable) {
return undefined;
}
// "synthetic": no side effects and no original source
const isSyntheticExpression = (lua.isIdentifier(result) || lua.isLiteral(result)) && result.line === undefined;
if (isSyntheticExpression) {
return undefined;
}
if (lua.isCallExpression(result) || lua.isMethodCallExpression(result)) {
return lua.createExpressionStatement(result);
}
// Assign expression statements to dummy to make sure they're legal Lua
return lua.createVariableDeclarationStatement(lua.createAnonymousIdentifier(), result);
}
4 changes: 2 additions & 2 deletions src/transformation/visitors/identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as ts from "typescript";
import * as lua from "../../LuaAST";
import { transformBuiltinIdentifierExpression } from "../builtins";
import { createPromiseIdentifier, isPromiseClass } from "../builtins/promise";
import { FunctionVisitor, TransformationContext } from "../context";
import { FunctionVisitor, tempSymbolId, TransformationContext } from "../context";
import { AnnotationKind, isForRangeType } from "../utils/annotations";
import {
invalidMultiFunctionUse,
Expand All @@ -25,7 +25,7 @@ import { isOptionalContinuation } from "./optional-chaining";

export function transformIdentifier(context: TransformationContext, identifier: ts.Identifier): lua.Identifier {
if (isOptionalContinuation(identifier)) {
return lua.createIdentifier(identifier.text);
return lua.createIdentifier(identifier.text, undefined, tempSymbolId);
}

if (isMultiFunctionNode(context, identifier)) {
Expand Down
20 changes: 20 additions & 0 deletions src/transformation/visitors/language-extensions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { TransformationContext } from "../../context";
import * as ts from "typescript";
import * as lua from "../../../LuaAST";
import { transformOperatorMappingExpression } from "./operators";
import { transformTableExtensionCall } from "./table";

export function transformLanguageExtensionCallExpression(
context: TransformationContext,
node: ts.CallExpression,
isOptionalCall: boolean
): lua.Expression | undefined {
const operatorMapping = transformOperatorMappingExpression(context, node, isOptionalCall);
if (operatorMapping) {
return operatorMapping;
}
const tableCall = transformTableExtensionCall(context, node, isOptionalCall);
if (tableCall) {
return tableCall;
}
}
13 changes: 9 additions & 4 deletions src/transformation/visitors/language-extensions/operators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import * as extensions from "../../utils/language-extensions";
import { assert } from "../../../utils";
import { getFunctionTypeForCall } from "../../utils/typescript";
import { LuaTarget } from "../../../CompilerOptions";
import { unsupportedForTarget } from "../../utils/diagnostics";
import { unsupportedBuiltinOptionalCall, unsupportedForTarget } from "../../utils/diagnostics";

const binaryOperatorMappings = new Map<extensions.ExtensionKind, lua.BinaryOperator>([
[extensions.ExtensionKind.AdditionOperatorType, lua.SyntaxKind.AdditionOperator],
Expand Down Expand Up @@ -82,10 +82,15 @@ export function isOperatorMapping(context: TransformationContext, node: ts.CallE

export function transformOperatorMappingExpression(
context: TransformationContext,
node: ts.CallExpression
): lua.Expression {
node: ts.CallExpression,
isOptionalCall: boolean
): lua.Expression | undefined {
const extensionKind = getOperatorMapExtensionKindForCall(context, node);
assert(extensionKind);
if (!extensionKind) return undefined;
if (isOptionalCall) {
context.diagnostics.push(unsupportedBuiltinOptionalCall(node));
return lua.createNilLiteral();
}

const isBefore53 =
context.luaTarget === LuaTarget.Lua51 ||
Expand Down
Loading