Skip to content

Commit 42740d6

Browse files
author
Andy
authored
Improve performance of suggestionDiagnostics for convertToAsyncFunction (#28089)
1 parent fe2a33f commit 42740d6

File tree

2 files changed

+32
-42
lines changed

2 files changed

+32
-42
lines changed

src/services/codefixes/convertToAsyncFunction.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ namespace ts.codefix {
5858
const allVarNames: SymbolAndIdentifier[] = [];
5959
const isInJavascript = isInJSFile(functionToConvert);
6060
const setOfExpressionsToReturn = getAllPromiseExpressionsToReturn(functionToConvert, checker);
61-
const functionToConvertRenamed: FunctionLikeDeclaration = renameCollidingVarNames(functionToConvert, checker, synthNamesMap, context, setOfExpressionsToReturn, originalTypeMap, allVarNames);
61+
const functionToConvertRenamed = renameCollidingVarNames(functionToConvert, checker, synthNamesMap, context, setOfExpressionsToReturn, originalTypeMap, allVarNames);
6262
const constIdentifiers = getConstIdentifiers(synthNamesMap);
63-
const returnStatements = getReturnStatementsWithPromiseHandlers(functionToConvertRenamed);
63+
const returnStatements = functionToConvertRenamed.body && isBlock(functionToConvertRenamed.body) ? getReturnStatementsWithPromiseHandlers(functionToConvertRenamed.body) : emptyArray;
6464
const transformer: Transformer = { checker, synthNamesMap, allVarNames, setOfExpressionsToReturn, constIdentifiers, originalTypeMap, isInJSFile: isInJavascript };
6565

6666
if (!returnStatements.length) {
@@ -87,6 +87,14 @@ namespace ts.codefix {
8787
}
8888
}
8989

90+
function getReturnStatementsWithPromiseHandlers(body: Block): ReadonlyArray<ReturnStatement> {
91+
const res: ReturnStatement[] = [];
92+
forEachReturnStatement(body, ret => {
93+
if (isReturnStatementWithFixablePromiseHandler(ret)) res.push(ret);
94+
});
95+
return res;
96+
}
97+
9098
// Returns the identifiers that are never reassigned in the refactor
9199
function getConstIdentifiers(synthNamesMap: ReadonlyMap<SynthIdentifier>): Identifier[] {
92100
const constIdentifiers: Identifier[] = [];
@@ -442,7 +450,7 @@ namespace ts.codefix {
442450
seenReturnStatement = true;
443451
}
444452

445-
if (getReturnStatementsWithPromiseHandlers(statement).length) {
453+
if (isReturnStatementWithFixablePromiseHandler(statement)) {
446454
refactoredStmts = refactoredStmts.concat(getInnerTransformationBody(transformer, [statement], prevArgName));
447455
}
448456
else {
@@ -458,7 +466,7 @@ namespace ts.codefix {
458466
seenReturnStatement);
459467
}
460468
else {
461-
const innerRetStmts = getReturnStatementsWithPromiseHandlers(createReturn(funcBody));
469+
const innerRetStmts = isFixablePromiseHandler(funcBody) ? [createReturn(funcBody)] : emptyArray;
462470
const innerCbBody = getInnerTransformationBody(transformer, innerRetStmts, prevArgName);
463471

464472
if (innerCbBody.length > 0) {

src/services/suggestionDiagnostics.ts

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -113,59 +113,41 @@ namespace ts {
113113
}
114114
}
115115

116-
function addConvertToAsyncFunctionDiagnostics(node: FunctionLikeDeclaration, checker: TypeChecker, diags: DiagnosticWithLocation[]): void {
117-
118-
if (isAsyncFunction(node) || !node.body) {
119-
return;
116+
function addConvertToAsyncFunctionDiagnostics(node: FunctionLikeDeclaration, checker: TypeChecker, diags: Push<DiagnosticWithLocation>): void {
117+
if (!isAsyncFunction(node) &&
118+
node.body &&
119+
isBlock(node.body) &&
120+
hasReturnStatementWithPromiseHandler(node.body) &&
121+
returnsPromise(node, checker)) {
122+
diags.push(createDiagnosticForNode(
123+
!node.name && isVariableDeclaration(node.parent) && isIdentifier(node.parent.name) ? node.parent.name : node,
124+
Diagnostics.This_may_be_converted_to_an_async_function));
120125
}
126+
}
121127

128+
function returnsPromise(node: FunctionLikeDeclaration, checker: TypeChecker): boolean {
122129
const functionType = checker.getTypeAtLocation(node);
123-
124130
const callSignatures = checker.getSignaturesOfType(functionType, SignatureKind.Call);
125131
const returnType = callSignatures.length ? checker.getReturnTypeOfSignature(callSignatures[0]) : undefined;
126-
127-
if (!returnType || !checker.getPromisedTypeOfPromise(returnType)) {
128-
return;
129-
}
130-
131-
// collect all the return statements
132-
// check that a property access expression exists in there and that it is a handler
133-
const returnStatements = getReturnStatementsWithPromiseHandlers(node);
134-
if (returnStatements.length > 0) {
135-
diags.push(createDiagnosticForNode(!node.name && isVariableDeclaration(node.parent) && isIdentifier(node.parent.name) ? node.parent.name : node, Diagnostics.This_may_be_converted_to_an_async_function));
136-
}
132+
return !!returnType && !!checker.getPromisedTypeOfPromise(returnType);
137133
}
138134

139135
function getErrorNodeFromCommonJsIndicator(commonJsModuleIndicator: Node): Node {
140136
return isBinaryExpression(commonJsModuleIndicator) ? commonJsModuleIndicator.left : commonJsModuleIndicator;
141137
}
142138

143-
/** @internal */
144-
export function getReturnStatementsWithPromiseHandlers(node: Node): ReturnStatement[] {
145-
const returnStatements: ReturnStatement[] = [];
146-
if (isFunctionLike(node)) {
147-
forEachChild(node, visit);
148-
}
149-
else {
150-
visit(node);
151-
}
152-
153-
function visit(child: Node) {
154-
if (isFunctionLike(child)) {
155-
return;
156-
}
157-
158-
if (isReturnStatement(child) && child.expression && isFixablePromiseHandler(child.expression)) {
159-
returnStatements.push(child);
160-
}
139+
function hasReturnStatementWithPromiseHandler(body: Block): boolean {
140+
return !!forEachReturnStatement(body, isReturnStatementWithFixablePromiseHandler);
141+
}
161142

162-
forEachChild(child, visit);
163-
}
164-
return returnStatements;
143+
/* @internal */
144+
export function isReturnStatementWithFixablePromiseHandler(node: Node): node is ReturnStatement {
145+
return isReturnStatement(node) && !!node.expression && isFixablePromiseHandler(node.expression);
165146
}
166147

167148
// Should be kept up to date with transformExpression in convertToAsyncFunction.ts
168-
function isFixablePromiseHandler(node: Node): boolean {
149+
/* @internal */
150+
export function isFixablePromiseHandler(node: Node): boolean {
169151
// ensure outermost call exists and is a promise handler
170152
if (!isPromiseHandler(node) || !node.arguments.every(isFixablePromiseArgument)) {
171153
return false;

0 commit comments

Comments
 (0)