Skip to content

fix(42034): Missing codefix with 'This may be converted to an async function' suggestion diagnostic #42123

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 28, 2020
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
19 changes: 7 additions & 12 deletions src/services/codefixes/convertToAsyncFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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;
Expand All @@ -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)) {
Expand Down
12 changes: 12 additions & 0 deletions src/testRunner/unittests/services/convertToAsyncFunction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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", `
Expand Down
Original file line number Diff line number Diff line change
@@ -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) { }
}
Original file line number Diff line number Diff line change
@@ -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) { }
}
Original file line number Diff line number Diff line change
@@ -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) { }
}
Original file line number Diff line number Diff line change
@@ -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) { }
}