Skip to content

Commit

Permalink
Refactoring promises returning functions to use async and await (#26373)
Browse files Browse the repository at this point in the history
  • Loading branch information
elizabethdinella authored Aug 10, 2018
1 parent 08f5edb commit 421730a
Show file tree
Hide file tree
Showing 82 changed files with 3,589 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ namespace ts {
return node ? getTypeFromTypeNode(node) : errorType;
},
getParameterType: getTypeAtPosition,
getPromisedTypeOfPromise,
getReturnTypeOfSignature,
getNullableType,
getNonNullableType,
Expand Down
16 changes: 14 additions & 2 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4183,7 +4183,10 @@
"category": "Suggestion",
"code": 80005
},

"This may be converted to an async function.": {
"category": "Suggestion",
"code": 80006
},
"Add missing 'super()' call": {
"category": "Message",
"code": 90001
Expand Down Expand Up @@ -4556,12 +4559,21 @@
"category": "Message",
"code": 95062
},

"Add missing enum member '{0}'": {
"category": "Message",
"code": 95063
},
"Add all missing imports": {
"category": "Message",
"code": 95064
},
"Convert to async function":{
"category": "Message",
"code": 95065
},
"Convert all to async functions": {
"category": "Message",
"code": 95066
}
}
}
2 changes: 2 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2911,6 +2911,8 @@ namespace ts {
getBaseTypes(type: InterfaceType): BaseType[];
getBaseTypeOfLiteralType(type: Type): Type;
getWidenedType(type: Type): Type;
/* @internal */
getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type | undefined;
getReturnTypeOfSignature(signature: Signature): Type;
/**
* Gets the type of a parameter at a given position in a signature.
Expand Down
538 changes: 538 additions & 0 deletions src/services/codefixes/convertToAsyncFunction.ts

Large diffs are not rendered by default.

61 changes: 61 additions & 0 deletions src/services/suggestionDiagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace ts {
export function computeSuggestionDiagnostics(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): DiagnosticWithLocation[] {
program.getSemanticDiagnostics(sourceFile, cancellationToken);
const diags: DiagnosticWithLocation[] = [];
const checker = program.getDiagnosticsProducingTypeChecker();

if (sourceFile.commonJsModuleIndicator &&
(programContainsEs6Modules(program) || compilerOptionsIndicateEs6Modules(program.getCompilerOptions())) &&
Expand Down Expand Up @@ -68,6 +69,9 @@ namespace ts {
}
}

if (isFunctionLikeDeclaration(node)) {
addConvertToAsyncFunctionDiagnostics(node, checker, diags);
}
node.forEachChild(check);
}
}
Expand Down Expand Up @@ -109,7 +113,64 @@ namespace ts {
}
}

function addConvertToAsyncFunctionDiagnostics(node: FunctionLikeDeclaration, checker: TypeChecker, diags: DiagnosticWithLocation[]): void {

const functionType = node.type ? checker.getTypeFromTypeNode(node.type) : undefined;
if (isAsyncFunction(node) || !node.body || !functionType) {
return;
}

const callSignatures = checker.getSignaturesOfType(functionType, SignatureKind.Call);
const returnType = callSignatures.length ? checker.getReturnTypeOfSignature(callSignatures[0]) : undefined;

if (!returnType || !checker.getPromisedTypeOfPromise(returnType)) {
return;
}

// collect all the return statements
// check that a property access expression exists in there and that it is a handler
const returnStatements = getReturnStatementsWithPromiseHandlers(node);
if (returnStatements.length > 0) {
diags.push(createDiagnosticForNode(isVariableDeclaration(node.parent) ? node.parent.name : node, Diagnostics.This_may_be_converted_to_an_async_function));
}
}

function getErrorNodeFromCommonJsIndicator(commonJsModuleIndicator: Node): Node {
return isBinaryExpression(commonJsModuleIndicator) ? commonJsModuleIndicator.left : commonJsModuleIndicator;
}

/** @internal */
export function getReturnStatementsWithPromiseHandlers(node: Node): Node[] {
const returnStatements: Node[] = [];
if (isFunctionLike(node)) {
forEachChild(node, visit);
}
else {
visit(node);
}

function visit(child: Node) {
if (isFunctionLike(child)) {
return;
}

if (isReturnStatement(child)) {
forEachChild(child, addHandlers);
}

function addHandlers(returnChild: Node) {
if (isPromiseHandler(returnChild)) {
returnStatements.push(child as ReturnStatement);
}
}

forEachChild(child, visit);
}
return returnStatements;
}

function isPromiseHandler(node: Node): boolean {
return (isCallExpression(node) && isPropertyAccessExpression(node.expression) &&
(node.expression.name.text === "then" || node.expression.name.text === "catch"));
}
}
1 change: 1 addition & 0 deletions src/services/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"codefixes/addMissingInvocationForDecorator.ts",
"codefixes/annotateWithTypeFromJSDoc.ts",
"codefixes/convertFunctionToEs6Class.ts",
"codefixes/convertToAsyncFunction.ts",
"codefixes/convertToEs6Module.ts",
"codefixes/correctQualifiedNameToIndexedAccessType.ts",
"codefixes/fixClassIncorrectlyImplementsInterface.ts",
Expand Down
38 changes: 34 additions & 4 deletions src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ namespace ts {

export function isJumpStatementTarget(node: Node): node is Identifier & { parent: BreakOrContinueStatement } {
return node.kind === SyntaxKind.Identifier && isBreakOrContinueStatement(node.parent) && node.parent.label === node;
}
}

export function isLabelOfLabeledStatement(node: Node): node is Identifier {
return node.kind === SyntaxKind.Identifier && isLabeledStatement(node.parent) && node.parent.label === node;
Expand Down Expand Up @@ -396,7 +396,7 @@ namespace ts {
export function isThis(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisKeyword:
// case SyntaxKind.ThisType: TODO: GH#9267
// case SyntaxKind.ThisType: TODO: GH#9267
return true;
case SyntaxKind.Identifier:
// 'this' as a parameter
Expand Down Expand Up @@ -1656,8 +1656,34 @@ namespace ts {
return clone;
}

function getSynthesizedDeepCloneWorker<T extends Node>(node: T): T {
const visited = visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
export function getSynthesizedDeepCloneWithRenames<T extends Node | undefined>(node: T, includeTrivia = true, renameMap?: Map<Identifier>, checker?: TypeChecker, callback?: (originalNode: Node, clone: Node) => any): T {

let clone;
if (node && isIdentifier(node!) && renameMap && checker) {
const symbol = checker.getSymbolAtLocation(node!);
const renameInfo = symbol && renameMap.get(String(getSymbolId(symbol)));

if (renameInfo) {
clone = createIdentifier(renameInfo.text);
}
}

if (!clone) {
clone = node && getSynthesizedDeepCloneWorker(node as NonNullable<T>, renameMap, checker, callback);
}

if (clone && !includeTrivia) suppressLeadingAndTrailingTrivia(clone);
if (callback && node) callback(node!, clone);

return clone as T;
}


function getSynthesizedDeepCloneWorker<T extends Node>(node: T, renameMap?: Map<Identifier>, checker?: TypeChecker, callback?: (originalNode: Node, clone: Node) => any): T {
const visited = (renameMap || checker || callback) ?
visitEachChild(node, wrapper, nullTransformationContext) :
visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);

if (visited === node) {
// This only happens for leaf nodes - internal nodes always see their children change.
const clone = getSynthesizedClone(node);
Expand All @@ -1675,6 +1701,10 @@ namespace ts {
// would have made.
visited.parent = undefined!;
return visited;

function wrapper(node: T) {
return getSynthesizedDeepCloneWithRenames(node, /*includeTrivia*/ true, renameMap, checker, callback);
}
}

export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T>, includeTrivia?: boolean): NodeArray<T>;
Expand Down
1 change: 1 addition & 0 deletions src/testRunner/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"unittests/compileOnSave.ts",
"unittests/configurationExtension.ts",
"unittests/convertCompilerOptionsFromJson.ts",
"unittests/convertToAsyncFunction.ts",
"unittests/convertToBase64.ts",
"unittests/convertTypeAcquisitionFromJson.ts",
"unittests/customTransforms.ts",
Expand Down
Loading

0 comments on commit 421730a

Please sign in to comment.