From 4f95b8729ae1a7ae69de61da8822bc05b3512091 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 30 May 2022 21:30:29 +0200 Subject: [PATCH 1/2] Fix methods with non-null operator not being detected as methods --- src/transformation/visitors/call.ts | 40 ++++++++++++++++++--------- test/unit/functions/functions.spec.ts | 8 ++++++ 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 58eb29eab..014e5335c 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -126,7 +126,7 @@ export function transformContextualCallExpression( if (ts.isOptionalChain(node)) { return transformOptionalChain(context, node); } - const left = ts.isCallExpression(node) ? node.expression : node.tag; + const left = ts.isCallExpression(node) ? getCalledExpression(node) : node.tag; let [argPrecedingStatements, transformedArguments] = transformInPrecedingStatementScope(context, () => transformArguments(context, args, signature) @@ -176,10 +176,14 @@ export function transformContextualCallExpression( } } -function transformPropertyCall(context: TransformationContext, node: PropertyCallExpression): lua.Expression { +function transformPropertyCall( + context: TransformationContext, + node: ts.CallExpression, + calledExpression: ts.PropertyAccessExpression +): lua.Expression { const signature = context.checker.getResolvedSignature(node); - if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) { + if (calledExpression.expression.kind === ts.SyntaxKind.SuperKeyword) { // Super calls take the format of super.call(self,...) const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis()); return lua.createCallExpression(context.transformExpression(node.expression), parameters); @@ -211,7 +215,9 @@ function transformElementCall(context: TransformationContext, node: ts.CallExpre } export const transformCallExpression: FunctionVisitor = (node, context) => { - if (node.expression.kind === ts.SyntaxKind.ImportKeyword) { + const calledExpression = getCalledExpression(node); + + if (calledExpression.kind === ts.SyntaxKind.ImportKeyword) { return transformImportExpression(node, context); } @@ -219,8 +225,8 @@ export const transformCallExpression: FunctionVisitor = (node return transformOptionalChain(context, node); } - const optionalContinuation = ts.isIdentifier(node.expression) - ? getOptionalContinuationData(node.expression) + const optionalContinuation = ts.isIdentifier(calledExpression) + ? getOptionalContinuationData(calledExpression) : undefined; const wrapResultInTable = isMultiReturnCall(context, node) && shouldMultiReturnCallBeWrapped(context, node); @@ -236,18 +242,18 @@ export const transformCallExpression: FunctionVisitor = (node return wrapResultInTable ? wrapInTable(builtinOrExtensionResult) : builtinOrExtensionResult; } - if (ts.isPropertyAccessExpression(node.expression)) { - const ownerType = context.checker.getTypeAtLocation(node.expression.expression); + if (ts.isPropertyAccessExpression(calledExpression)) { + const ownerType = context.checker.getTypeAtLocation(calledExpression.expression); const annotations = getTypeAnnotations(ownerType); if (annotations.has(AnnotationKind.LuaTable)) { context.diagnostics.push(annotationRemoved(node, AnnotationKind.LuaTable)); } - const result = transformPropertyCall(context, node as PropertyCallExpression); + const result = transformPropertyCall(context, node, calledExpression); return wrapResultInTable ? wrapInTable(result) : result; } - if (ts.isElementAccessExpression(node.expression)) { + if (ts.isElementAccessExpression(calledExpression)) { const result = transformElementCall(context, node); return wrapResultInTable ? wrapInTable(result) : result; } @@ -255,7 +261,7 @@ export const transformCallExpression: FunctionVisitor = (node const signature = context.checker.getResolvedSignature(node); // Handle super calls properly - if (node.expression.kind === ts.SyntaxKind.SuperKeyword) { + if (calledExpression.kind === ts.SyntaxKind.SuperKeyword) { const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis()); return lua.createCallExpression( @@ -274,14 +280,14 @@ export const transformCallExpression: FunctionVisitor = (node const isContextualCall = !signatureDeclaration || getDeclarationContextType(context, signatureDeclaration) !== ContextType.Void; if (!isContextualCall) { - [callPath, parameters] = transformCallAndArguments(context, node.expression, node.arguments, signature); + [callPath, parameters] = transformCallAndArguments(context, calledExpression, node.arguments, signature); } else { // if is optionalContinuation, context will be handled by transformOptionalChain. const useGlobalContext = !context.isStrict && optionalContinuation === undefined; const callContext = useGlobalContext ? ts.factory.createIdentifier("_G") : ts.factory.createNull(); [callPath, parameters] = transformCallAndArguments( context, - node.expression, + calledExpression, node.arguments, signature, callContext @@ -294,3 +300,11 @@ export const transformCallExpression: FunctionVisitor = (node } return wrapResultInTable ? wrapInTable(callExpression) : callExpression; }; + +function getCalledExpression(node: ts.CallExpression): ts.Expression { + function unwrapExpression(expression: ts.Expression): ts.Expression { + expression = ts.skipOuterExpressions(expression); + return ts.isNonNullExpression(expression) ? unwrapExpression(expression.expression) : expression; + } + return unwrapExpression(node.expression); +} diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index c5a488460..6c8819f70 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -268,6 +268,14 @@ test("Object method declaration", () => { `.expectToMatchJsResult(); }); +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1266 +test("Object method declaration used with non-null operator (#1266)", () => { + util.testFunction` + let o = { v: 4, m(i: number): number { return this.v * i; } }; + return o.m!(3); + `.expectToMatchJsResult(); +}); + test.each([ { args: ["bar"], expected: "foobar" }, { args: ["baz", "bar"], expected: "bazbar" }, From 0168461cd7f5206e1f29eac46d8af187a469bd82 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Mon, 30 May 2022 22:10:26 +0200 Subject: [PATCH 2/2] Fix other occurrences of PropertyAccessCall --- src/transformation/builtins/array.ts | 24 ++++++------- src/transformation/builtins/console.ts | 20 +++++------ src/transformation/builtins/function.ts | 14 ++++---- src/transformation/builtins/index.ts | 46 ++++++++++++------------- src/transformation/builtins/math.ts | 10 +++--- src/transformation/builtins/number.ts | 27 ++++++++------- src/transformation/builtins/object.ts | 36 +++++++++---------- src/transformation/builtins/promise.ts | 14 ++++---- src/transformation/builtins/string.ts | 20 +++++------ src/transformation/builtins/symbol.ts | 17 ++++----- src/transformation/visitors/call.ts | 8 ++--- 11 files changed, 117 insertions(+), 119 deletions(-) diff --git a/src/transformation/builtins/array.ts b/src/transformation/builtins/array.ts index 9567c45ae..2ecf3336a 100644 --- a/src/transformation/builtins/array.ts +++ b/src/transformation/builtins/array.ts @@ -3,20 +3,20 @@ import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments, transformCallAndArguments } from "../visitors/call"; +import { transformArguments, transformCallAndArguments } from "../visitors/call"; import { isStringType, isNumberType, findFirstNonOuterParent } from "../utils/typescript"; import { moveToPrecedingTemp } from "../visitors/expression-list"; import { isUnpackCall, wrapInTable } from "../utils/lua-ast"; export function transformArrayConstructorCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "from": return transformLuaLibFunction(context, LuaLibFeature.ArrayFrom, node, ...params); @@ -25,7 +25,7 @@ export function transformArrayConstructorCall( case "of": return wrapInTable(...params); default: - context.diagnostics.push(unsupportedProperty(expression.name, "Array", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Array", expressionName)); } } @@ -37,7 +37,7 @@ export function transformArrayConstructorCall( */ function transformSingleElementArrayPush( context: TransformationContext, - node: PropertyCallExpression, + node: ts.CallExpression, caller: lua.Expression, param: lua.Expression ): lua.Expression { @@ -68,13 +68,13 @@ function transformSingleElementArrayPush( export function transformArrayPrototypeCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); - const [caller, params] = transformCallAndArguments(context, expression.expression, node.arguments, signature); + const [caller, params] = transformCallAndArguments(context, calledMethod.expression, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "concat": return transformLuaLibFunction(context, LuaLibFeature.ArrayConcat, node, caller, ...params); @@ -143,7 +143,7 @@ export function transformArrayPrototypeCall( case "splice": return transformLuaLibFunction(context, LuaLibFeature.ArraySplice, node, caller, ...params); case "join": - const callerType = context.checker.getTypeAtLocation(expression.expression); + const callerType = context.checker.getTypeAtLocation(calledMethod.expression); const elementType = context.checker.getElementTypeOfArrayType(callerType); if (elementType && (isStringType(context, elementType) || isNumberType(context, elementType))) { const defaultSeparatorLiteral = lua.createStringLiteral(","); @@ -170,7 +170,7 @@ export function transformArrayPrototypeCall( case "flatMap": return transformLuaLibFunction(context, LuaLibFeature.ArrayFlatMap, node, caller, ...params); default: - context.diagnostics.push(unsupportedProperty(expression.name, "array", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "array", expressionName)); } } diff --git a/src/transformation/builtins/console.ts b/src/transformation/builtins/console.ts index af7ceb87e..a32f547da 100644 --- a/src/transformation/builtins/console.ts +++ b/src/transformation/builtins/console.ts @@ -2,25 +2,25 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; const isStringFormatTemplate = (node: ts.Expression) => ts.isStringLiteral(node) && node.text.includes("%"); export function transformConsoleCall( context: TransformationContext, - expression: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const method = expression.expression; - const methodName = method.name.text; - const signature = context.checker.getResolvedSignature(expression); - const parameters = transformArguments(context, expression.arguments, signature); + const methodName = calledMethod.name.text; + const signature = context.checker.getResolvedSignature(node); + const parameters = transformArguments(context, node.arguments, signature); switch (methodName) { case "error": case "info": case "log": case "warn": - if (expression.arguments.length > 0 && isStringFormatTemplate(expression.arguments[0])) { + if (node.arguments.length > 0 && isStringFormatTemplate(node.arguments[0])) { // print(string.format([arguments])) const stringFormatCall = lua.createCallExpression( lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("format")), @@ -31,7 +31,7 @@ export function transformConsoleCall( // print([arguments]) return lua.createCallExpression(lua.createIdentifier("print"), parameters); case "assert": - if (expression.arguments.length > 1 && isStringFormatTemplate(expression.arguments[1])) { + if (node.arguments.length > 1 && isStringFormatTemplate(node.arguments[1])) { // assert([condition], string.format([arguments])) const stringFormatCall = lua.createCallExpression( lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("format")), @@ -42,7 +42,7 @@ export function transformConsoleCall( // assert() return lua.createCallExpression(lua.createIdentifier("assert"), parameters); case "trace": - if (expression.arguments.length > 0 && isStringFormatTemplate(expression.arguments[0])) { + if (node.arguments.length > 0 && isStringFormatTemplate(node.arguments[0])) { // print(debug.traceback(string.format([arguments]))) const stringFormatCall = lua.createCallExpression( lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("format")), @@ -61,6 +61,6 @@ export function transformConsoleCall( ); return lua.createCallExpression(lua.createIdentifier("print"), [debugTracebackCall]); default: - context.diagnostics.push(unsupportedProperty(method.name, "console", methodName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "console", methodName)); } } diff --git a/src/transformation/builtins/function.ts b/src/transformation/builtins/function.ts index 161660461..ebf0a83ca 100644 --- a/src/transformation/builtins/function.ts +++ b/src/transformation/builtins/function.ts @@ -6,21 +6,21 @@ import { unsupportedForTarget, unsupportedProperty, unsupportedSelfFunctionConve import { ContextType, getFunctionContextType } from "../utils/function-context"; import { createUnpackCall } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformCallAndArguments } from "../visitors/call"; +import { transformCallAndArguments } from "../visitors/call"; export function transformFunctionPrototypeCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.CallExpression | undefined { - const expression = node.expression; - const callerType = context.checker.getTypeAtLocation(expression.expression); + const callerType = context.checker.getTypeAtLocation(calledMethod.expression); if (getFunctionContextType(context, callerType) === ContextType.Void) { context.diagnostics.push(unsupportedSelfFunctionConversion(node)); } const signature = context.checker.getResolvedSignature(node); - const [caller, params] = transformCallAndArguments(context, expression.expression, node.arguments, signature); - const expressionName = expression.name.text; + const [caller, params] = transformCallAndArguments(context, calledMethod.expression, node.arguments, signature); + const expressionName = calledMethod.name.text; switch (expressionName) { case "apply": const nonContextArgs = params.length > 1 ? [createUnpackCall(context, params[1], node.arguments[1])] : []; @@ -30,7 +30,7 @@ export function transformFunctionPrototypeCall( case "call": return lua.createCallExpression(caller, params, node); case "toString": - context.diagnostics.push(unsupportedProperty(expression.name, "function", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "function", expressionName)); } } diff --git a/src/transformation/builtins/index.ts b/src/transformation/builtins/index.ts index c462bbe19..bc3be66e6 100644 --- a/src/transformation/builtins/index.ts +++ b/src/transformation/builtins/index.ts @@ -1,6 +1,5 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; -import { assume } from "../../utils"; import { TransformationContext } from "../context"; import { createNaN } from "../utils/lua-ast"; import { importLuaLibFeature, LuaLibFeature } from "../utils/lualib"; @@ -14,7 +13,7 @@ import { isStandardLibraryType, isStringType, } from "../utils/typescript"; -import { PropertyCallExpression } from "../visitors/call"; +import { getCalledExpression } from "../visitors/call"; import { transformArrayConstructorCall, transformArrayProperty, transformArrayPrototypeCall } from "./array"; import { transformConsoleCall } from "./console"; import { transformFunctionPrototypeCall, transformFunctionProperty } from "./function"; @@ -74,79 +73,78 @@ export function transformBuiltinCallExpression( } } - const expression = ts.getOriginalNode(node.expression); - if (!ts.isPropertyAccessExpression(expression)) { + const calledMethod = ts.getOriginalNode(getCalledExpression(node)); + if (!ts.isPropertyAccessExpression(calledMethod)) { return; } - const isOptionalAccess = expression.questionDotToken; - assume(node); + const isOptionalAccess = calledMethod.questionDotToken; // If the function being called is of type owner.func, get the type of owner - const ownerType = context.checker.getTypeAtLocation(expression.expression); + const ownerType = context.checker.getTypeAtLocation(calledMethod.expression); if (isStandardLibraryType(context, ownerType, undefined)) { const symbol = ownerType.getSymbol(); switch (symbol?.name) { case "ArrayConstructor": if (isOptionalCall || isOptionalAccess) return unsupportedOptionalCall(); - return transformArrayConstructorCall(context, node); + return transformArrayConstructorCall(context, node, calledMethod); case "Console": if (isOptionalCall || isOptionalAccess) return unsupportedOptionalCall(); - return transformConsoleCall(context, node); + return transformConsoleCall(context, node, calledMethod); case "Math": if (isOptionalCall || isOptionalAccess) return unsupportedOptionalCall(); - return transformMathCall(context, node); + return transformMathCall(context, node, calledMethod); case "StringConstructor": if (isOptionalCall || isOptionalAccess) return unsupportedOptionalCall(); - return transformStringConstructorCall(context, node); + return transformStringConstructorCall(context, node, calledMethod); case "ObjectConstructor": if (isOptionalCall || isOptionalAccess) return unsupportedOptionalCall(); - return transformObjectConstructorCall(context, node); + return transformObjectConstructorCall(context, node, calledMethod); case "SymbolConstructor": if (isOptionalCall || isOptionalAccess) return unsupportedOptionalCall(); - return transformSymbolConstructorCall(context, node); + return transformSymbolConstructorCall(context, node, calledMethod); case "NumberConstructor": if (isOptionalCall || isOptionalAccess) return unsupportedOptionalCall(); - return transformNumberConstructorCall(context, node); + return transformNumberConstructorCall(context, node, calledMethod); case "PromiseConstructor": if (isOptionalCall || isOptionalAccess) return unsupportedOptionalCall(); - return transformPromiseConstructorCall(context, node); + return transformPromiseConstructorCall(context, node, calledMethod); } } const isStringFunction = isStringType(context, ownerType) || - (expression.questionDotToken && isNullableType(context, ownerType, isStringType)); + (calledMethod.questionDotToken && isNullableType(context, ownerType, isStringType)); if (isStringFunction && hasStandardLibrarySignature(context, node)) { if (isOptionalCall) return unsupportedOptionalCall(); - return transformStringPrototypeCall(context, node); + return transformStringPrototypeCall(context, node, calledMethod); } const isNumberFunction = isNumberType(context, ownerType) || - (expression.questionDotToken && isNullableType(context, ownerType, isNumberType)); + (calledMethod.questionDotToken && isNullableType(context, ownerType, isNumberType)); if (isNumberFunction && hasStandardLibrarySignature(context, node)) { if (isOptionalCall) return unsupportedOptionalCall(); - return transformNumberPrototypeCall(context, node); + return transformNumberPrototypeCall(context, node, calledMethod); } const isArrayFunction = isArrayType(context, ownerType) || - (expression.questionDotToken && isNullableType(context, ownerType, isArrayType)); + (calledMethod.questionDotToken && isNullableType(context, ownerType, isArrayType)); if (isArrayFunction && hasStandardLibrarySignature(context, node)) { if (isOptionalCall) return unsupportedOptionalCall(); - return transformArrayPrototypeCall(context, node); + return transformArrayPrototypeCall(context, node, calledMethod); } const isFunctionFunction = isFunctionType(ownerType) || - (expression.questionDotToken && isNullableType(context, ownerType, (_, t) => isFunctionType(t))); + (calledMethod.questionDotToken && isNullableType(context, ownerType, (_, t) => isFunctionType(t))); if (isFunctionFunction && hasStandardLibrarySignature(context, node)) { if (isOptionalCall) return unsupportedOptionalCall(); - return transformFunctionPrototypeCall(context, node); + return transformFunctionPrototypeCall(context, node, calledMethod); } - const objectResult = transformObjectPrototypeCall(context, node, expression); + const objectResult = transformObjectPrototypeCall(context, node, calledMethod); if (objectResult) { if (isOptionalCall) return unsupportedOptionalCall(); return objectResult; diff --git a/src/transformation/builtins/math.ts b/src/transformation/builtins/math.ts index e215e215d..630f33646 100644 --- a/src/transformation/builtins/math.ts +++ b/src/transformation/builtins/math.ts @@ -4,7 +4,7 @@ import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; export function transformMathProperty( context: TransformationContext, @@ -33,14 +33,14 @@ export function transformMathProperty( export function transformMathCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); const math = lua.createIdentifier("math"); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { // Lua 5.3: math.atan(y, x) // Otherwise: math.atan2(y, x) @@ -107,6 +107,6 @@ export function transformMathCall( } default: - context.diagnostics.push(unsupportedProperty(expression.name, "Math", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Math", expressionName)); } } diff --git a/src/transformation/builtins/number.ts b/src/transformation/builtins/number.ts index 227d82b23..3376c0db3 100644 --- a/src/transformation/builtins/number.ts +++ b/src/transformation/builtins/number.ts @@ -1,42 +1,43 @@ +import ts = require("typescript"); import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; export function transformNumberPrototypeCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); - const caller = context.transformExpression(expression.expression); + const caller = context.transformExpression(calledMethod.expression); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "toString": return params.length === 0 ? lua.createCallExpression(lua.createIdentifier("tostring"), [caller], node) : transformLuaLibFunction(context, LuaLibFeature.NumberToString, node, caller, ...params); default: - context.diagnostics.push(unsupportedProperty(expression.name, "number", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "number", expressionName)); } } export function transformNumberConstructorCall( context: TransformationContext, - expression: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.CallExpression | undefined { - const method = expression.expression; - const parameters = transformArguments(context, expression.arguments); - const methodName = method.name.text; + const parameters = transformArguments(context, node.arguments); + const methodName = calledMethod.name.text; switch (methodName) { case "isNaN": - return transformLuaLibFunction(context, LuaLibFeature.NumberIsNaN, expression, ...parameters); + return transformLuaLibFunction(context, LuaLibFeature.NumberIsNaN, node, ...parameters); case "isFinite": - return transformLuaLibFunction(context, LuaLibFeature.NumberIsFinite, expression, ...parameters); + return transformLuaLibFunction(context, LuaLibFeature.NumberIsFinite, node, ...parameters); default: - context.diagnostics.push(unsupportedProperty(method.name, "Number", methodName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Number", methodName)); } } diff --git a/src/transformation/builtins/object.ts b/src/transformation/builtins/object.ts index 4b806eb32..c23341b97 100644 --- a/src/transformation/builtins/object.ts +++ b/src/transformation/builtins/object.ts @@ -3,56 +3,56 @@ import * as ts from "typescript"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; export function transformObjectConstructorCall( context: TransformationContext, - expression: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const method = expression.expression; - const args = transformArguments(context, expression.arguments); - const methodName = method.name.text; + const args = transformArguments(context, node.arguments); + const methodName = calledMethod.name.text; switch (methodName) { case "assign": - return transformLuaLibFunction(context, LuaLibFeature.ObjectAssign, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectAssign, node, ...args); case "defineProperty": - return transformLuaLibFunction(context, LuaLibFeature.ObjectDefineProperty, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectDefineProperty, node, ...args); case "entries": - return transformLuaLibFunction(context, LuaLibFeature.ObjectEntries, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectEntries, node, ...args); case "fromEntries": - return transformLuaLibFunction(context, LuaLibFeature.ObjectFromEntries, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectFromEntries, node, ...args); case "getOwnPropertyDescriptor": - return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptor, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptor, node, ...args); case "getOwnPropertyDescriptors": - return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptors, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptors, node, ...args); case "keys": - return transformLuaLibFunction(context, LuaLibFeature.ObjectKeys, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectKeys, node, ...args); case "values": - return transformLuaLibFunction(context, LuaLibFeature.ObjectValues, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectValues, node, ...args); default: - context.diagnostics.push(unsupportedProperty(method.name, "Object", methodName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Object", methodName)); } } export function transformObjectPrototypeCall( context: TransformationContext, node: ts.CallExpression, - expression: ts.PropertyAccessExpression + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { const signature = context.checker.getResolvedSignature(node); - const name = expression.name.text; + const name = calledMethod.name.text; switch (name) { case "toString": const toStringIdentifier = lua.createIdentifier("tostring"); return lua.createCallExpression( toStringIdentifier, - [context.transformExpression(expression.expression)], + [context.transformExpression(calledMethod.expression)], node ); case "hasOwnProperty": - const expr = context.transformExpression(expression.expression); + const expr = context.transformExpression(calledMethod.expression); const parameters = transformArguments(context, node.arguments, signature); const rawGetIdentifier = lua.createIdentifier("rawget"); const rawGetCall = lua.createCallExpression(rawGetIdentifier, [expr, ...parameters]); diff --git a/src/transformation/builtins/promise.ts b/src/transformation/builtins/promise.ts index 11d80d45c..07a291d8b 100644 --- a/src/transformation/builtins/promise.ts +++ b/src/transformation/builtins/promise.ts @@ -4,7 +4,7 @@ import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { importLuaLibFeature, LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; import { isStandardLibraryType } from "../utils/typescript"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; export function isPromiseClass(context: TransformationContext, node: ts.Identifier) { const type = context.checker.getTypeAtLocation(node); @@ -17,13 +17,13 @@ export function createPromiseIdentifier(original: ts.Node) { export function transformPromiseConstructorCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "all": return transformLuaLibFunction(context, LuaLibFeature.PromiseAll, node, ...params); @@ -35,12 +35,12 @@ export function transformPromiseConstructorCall( return transformLuaLibFunction(context, LuaLibFeature.PromiseRace, node, ...params); case "resolve": importLuaLibFeature(context, LuaLibFeature.Promise); - return lua.createCallExpression(createStaticPromiseFunctionAccessor("resolve", expression), params, node); + return lua.createCallExpression(createStaticPromiseFunctionAccessor("resolve", calledMethod), params, node); case "reject": importLuaLibFeature(context, LuaLibFeature.Promise); - return lua.createCallExpression(createStaticPromiseFunctionAccessor("reject", expression), params, node); + return lua.createCallExpression(createStaticPromiseFunctionAccessor("reject", calledMethod), params, node); default: - context.diagnostics.push(unsupportedProperty(expression.name, "Promise", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Promise", expressionName)); } } diff --git a/src/transformation/builtins/string.ts b/src/transformation/builtins/string.ts index d151d8d47..95f35133f 100644 --- a/src/transformation/builtins/string.ts +++ b/src/transformation/builtins/string.ts @@ -4,7 +4,7 @@ import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { addToNumericExpression, createNaN, getNumberLiteralValue, wrapInTable } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments, transformCallAndArguments } from "../visitors/call"; +import { transformArguments, transformCallAndArguments } from "../visitors/call"; function createStringCall(methodName: string, tsOriginal: ts.Node, ...params: lua.Expression[]): lua.CallExpression { const stringIdentifier = lua.createIdentifier("string"); @@ -17,13 +17,13 @@ function createStringCall(methodName: string, tsOriginal: ts.Node, ...params: lu export function transformStringPrototypeCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); - const [caller, params] = transformCallAndArguments(context, expression.expression, node.arguments, signature); + const [caller, params] = transformCallAndArguments(context, calledMethod.expression, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "replace": return transformLuaLibFunction(context, LuaLibFeature.StringReplace, node, caller, ...params); @@ -146,19 +146,19 @@ export function transformStringPrototypeCall( case "padEnd": return transformLuaLibFunction(context, LuaLibFeature.StringPadEnd, node, caller, ...params); default: - context.diagnostics.push(unsupportedProperty(expression.name, "string", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "string", expressionName)); } } export function transformStringConstructorCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "fromCharCode": return lua.createCallExpression( @@ -168,7 +168,7 @@ export function transformStringConstructorCall( ); default: - context.diagnostics.push(unsupportedProperty(expression.name, "String", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "String", expressionName)); } } diff --git a/src/transformation/builtins/symbol.ts b/src/transformation/builtins/symbol.ts index 04329e059..4c5b7cbde 100644 --- a/src/transformation/builtins/symbol.ts +++ b/src/transformation/builtins/symbol.ts @@ -1,25 +1,26 @@ +import ts = require("typescript"); import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { importLuaLibFeature, LuaLibFeature } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; export function transformSymbolConstructorCall( context: TransformationContext, - expression: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.CallExpression | undefined { - const method = expression.expression; - const signature = context.checker.getResolvedSignature(expression); - const parameters = transformArguments(context, expression.arguments, signature); - const methodName = method.name.text; + const signature = context.checker.getResolvedSignature(node); + const parameters = transformArguments(context, node.arguments, signature); + const methodName = calledMethod.name.text; switch (methodName) { case "for": case "keyFor": importLuaLibFeature(context, LuaLibFeature.SymbolRegistry); const upperMethodName = methodName[0].toUpperCase() + methodName.slice(1); const functionIdentifier = lua.createIdentifier(`__TS__SymbolRegistry${upperMethodName}`); - return lua.createCallExpression(functionIdentifier, parameters, expression); + return lua.createCallExpression(functionIdentifier, parameters, node); default: - context.diagnostics.push(unsupportedProperty(method.name, "Symbol", methodName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Symbol", methodName)); } } diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index 014e5335c..eb1c1a549 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -17,8 +17,6 @@ import { getOptionalContinuationData, transformOptionalChain } from "./optional- import { transformLanguageExtensionCallExpression } from "./language-extensions"; import { transformImportExpression } from "./modules/import"; -export type PropertyCallExpression = ts.CallExpression & { expression: ts.PropertyAccessExpression }; - export function validateArguments( context: TransformationContext, params: readonly ts.Expression[], @@ -179,11 +177,11 @@ export function transformContextualCallExpression( function transformPropertyCall( context: TransformationContext, node: ts.CallExpression, - calledExpression: ts.PropertyAccessExpression + calledMethod: ts.PropertyAccessExpression ): lua.Expression { const signature = context.checker.getResolvedSignature(node); - if (calledExpression.expression.kind === ts.SyntaxKind.SuperKeyword) { + if (calledMethod.expression.kind === ts.SyntaxKind.SuperKeyword) { // Super calls take the format of super.call(self,...) const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis()); return lua.createCallExpression(context.transformExpression(node.expression), parameters); @@ -301,7 +299,7 @@ export const transformCallExpression: FunctionVisitor = (node return wrapResultInTable ? wrapInTable(callExpression) : callExpression; }; -function getCalledExpression(node: ts.CallExpression): ts.Expression { +export function getCalledExpression(node: ts.CallExpression): ts.Expression { function unwrapExpression(expression: ts.Expression): ts.Expression { expression = ts.skipOuterExpressions(expression); return ts.isNonNullExpression(expression) ? unwrapExpression(expression.expression) : expression;