From fe297df9df0d5b5a9c8544670e3781bdfb1a9a46 Mon Sep 17 00:00:00 2001 From: Oleksandr T Date: Mon, 28 Dec 2020 19:34:28 +0200 Subject: [PATCH] fix(42034): allow convert async function with empty catch (#42123) --- .../codefixes/convertToAsyncFunction.ts | 19 +++++++------------ .../services/convertToAsyncFunction.ts | 12 ++++++++++++ .../convertToAsyncFunction_emptyCatch1.js | 13 +++++++++++++ .../convertToAsyncFunction_emptyCatch1.ts | 13 +++++++++++++ .../convertToAsyncFunction_emptyCatch2.js | 14 ++++++++++++++ .../convertToAsyncFunction_emptyCatch2.ts | 14 ++++++++++++++ 6 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch1.js create mode 100644 tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch1.ts create mode 100644 tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch2.js create mode 100644 tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch2.ts diff --git a/src/services/codefixes/convertToAsyncFunction.ts b/src/services/codefixes/convertToAsyncFunction.ts index 4db53ac9af4..fb064d024fc 100644 --- a/src/services/codefixes/convertToAsyncFunction.ts +++ b/src/services/codefixes/convertToAsyncFunction.ts @@ -235,7 +235,6 @@ namespace ts.codefix { return transformThen(node, transformer, prevArgName); } if (isPromiseReturningCallExpression(node, transformer.checker, "catch")) { - if (node.arguments.length === 0) return silentFail(); return transformCatch(node, transformer, prevArgName); } if (isPropertyAccessExpression(node)) { @@ -252,15 +251,13 @@ namespace ts.codefix { } function transformCatch(node: CallExpression, transformer: Transformer, prevArgName?: SynthBindingName): readonly Statement[] { - const func = node.arguments[0]; - const argName = getArgBindingName(func, transformer); + const func = singleOrUndefined(node.arguments); + const argName = func ? getArgBindingName(func, transformer) : undefined; let possibleNameForVarDecl: SynthIdentifier | undefined; - /* - If there is another call in the chain after the .catch() we are transforming, we will need to save the result of both paths (try block and catch block) - To do this, we will need to synthesize a variable that we were not aware of while we were adding identifiers to the synthNamesMap - We will use the prevArgName and then update the synthNamesMap with a new variable name for the next transformation step - */ + // If there is another call in the chain after the .catch() we are transforming, we will need to save the result of both paths (try block and catch block) + // To do this, we will need to synthesize a variable that we were not aware of while we were adding identifiers to the synthNamesMap + // We will use the prevArgName and then update the synthNamesMap with a new variable name for the next transformation step if (prevArgName && !shouldReturn(node, transformer)) { if (isSynthIdentifier(prevArgName)) { possibleNameForVarDecl = prevArgName; @@ -282,14 +279,12 @@ namespace ts.codefix { } const tryBlock = factory.createBlock(transformExpression(node.expression, transformer, possibleNameForVarDecl)); - const transformationBody = getTransformationBody(func, possibleNameForVarDecl, argName, node, transformer); + const transformationBody = func ? getTransformationBody(func, possibleNameForVarDecl, argName, node, transformer) : emptyArray; const catchArg = argName ? isSynthIdentifier(argName) ? argName.identifier.text : argName.bindingPattern : "e"; const catchVariableDeclaration = factory.createVariableDeclaration(catchArg); const catchClause = factory.createCatchClause(catchVariableDeclaration, factory.createBlock(transformationBody)); - /* - In order to avoid an implicit any, we will synthesize a type for the declaration using the unions of the types of both paths (try block and catch block) - */ + // In order to avoid an implicit any, we will synthesize a type for the declaration using the unions of the types of both paths (try block and catch block) let varDeclList: VariableStatement | undefined; let varDeclIdentifier: Identifier | undefined; if (possibleNameForVarDecl && !shouldReturn(node, transformer)) { diff --git a/src/testRunner/unittests/services/convertToAsyncFunction.ts b/src/testRunner/unittests/services/convertToAsyncFunction.ts index e57372b3161..5b09aba9549 100644 --- a/src/testRunner/unittests/services/convertToAsyncFunction.ts +++ b/src/testRunner/unittests/services/convertToAsyncFunction.ts @@ -1577,6 +1577,18 @@ const fn = (): Promise<(message: string) => void> => function [#|f|]() { return fn().then(res => res("test")); } +`); + + _testConvertToAsyncFunction("convertToAsyncFunction_emptyCatch1", ` +function [#|f|]() { + return Promise.resolve().catch(); +} +`); + + _testConvertToAsyncFunction("convertToAsyncFunction_emptyCatch2", ` +function [#|f|]() { + return Promise.resolve(0).then(x => x).catch(); +} `); _testConvertToAsyncFunctionWithModule("convertToAsyncFunction_importedFunction", ` diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch1.js b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch1.js new file mode 100644 index 00000000000..20fa127d990 --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch1.js @@ -0,0 +1,13 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/() { + return Promise.resolve().catch(); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function f() { + try { + return Promise.resolve(); + } catch (e) { } +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch1.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch1.ts new file mode 100644 index 00000000000..20fa127d990 --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch1.ts @@ -0,0 +1,13 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/() { + return Promise.resolve().catch(); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function f() { + try { + return Promise.resolve(); + } catch (e) { } +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch2.js b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch2.js new file mode 100644 index 00000000000..aa7e9f1bb8d --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch2.js @@ -0,0 +1,14 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/() { + return Promise.resolve(0).then(x => x).catch(); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function f() { + try { + const x = await Promise.resolve(0); + return x; + } catch (e) { } +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch2.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch2.ts new file mode 100644 index 00000000000..aa7e9f1bb8d --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_emptyCatch2.ts @@ -0,0 +1,14 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/() { + return Promise.resolve(0).then(x => x).catch(); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function f() { + try { + const x = await Promise.resolve(0); + return x; + } catch (e) { } +}