1/* @internal */ 2namespace ts.codefix { 3 const fixId = "fixAwaitInSyncFunction"; 4 const errorCodes = [ 5 Diagnostics.await_expressions_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules.code, 6 Diagnostics.for_await_loops_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules.code, 7 Diagnostics.Cannot_find_name_0_Did_you_mean_to_write_this_in_an_async_function.code 8 ]; 9 registerCodeFix({ 10 errorCodes, 11 getCodeActions(context) { 12 const { sourceFile, span } = context; 13 const nodes = getNodes(sourceFile, span.start); 14 if (!nodes) return undefined; 15 const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, nodes)); 16 return [createCodeFixAction(fixId, changes, Diagnostics.Add_async_modifier_to_containing_function, fixId, Diagnostics.Add_all_missing_async_modifiers)]; 17 }, 18 fixIds: [fixId], 19 getAllCodeActions: function getAllCodeActionsToFixAwaitInSyncFunction(context) { 20 const seen = new Map<number, true>(); 21 return codeFixAll(context, errorCodes, (changes, diag) => { 22 const nodes = getNodes(diag.file, diag.start); 23 if (!nodes || !addToSeen(seen, getNodeId(nodes.insertBefore))) return; 24 doChange(changes, context.sourceFile, nodes); 25 }); 26 }, 27 }); 28 29 function getReturnType(expr: FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction) { 30 if (expr.type) { 31 return expr.type; 32 } 33 if (isVariableDeclaration(expr.parent) && 34 expr.parent.type && 35 isFunctionTypeNode(expr.parent.type)) { 36 return expr.parent.type.type; 37 } 38 } 39 40 function getNodes(sourceFile: SourceFile, start: number): { insertBefore: Node, returnType: TypeNode | undefined } | undefined { 41 const token = getTokenAtPosition(sourceFile, start); 42 const containingFunction = getContainingFunction(token); 43 if (!containingFunction) { 44 return; 45 } 46 47 let insertBefore: Node | undefined; 48 switch (containingFunction.kind) { 49 case SyntaxKind.MethodDeclaration: 50 insertBefore = containingFunction.name; 51 break; 52 case SyntaxKind.FunctionDeclaration: 53 case SyntaxKind.FunctionExpression: 54 insertBefore = findChildOfKind(containingFunction, SyntaxKind.FunctionKeyword, sourceFile); 55 break; 56 case SyntaxKind.ArrowFunction: 57 const kind = containingFunction.typeParameters ? SyntaxKind.LessThanToken : SyntaxKind.OpenParenToken; 58 insertBefore = findChildOfKind(containingFunction, kind, sourceFile) || first(containingFunction.parameters); 59 break; 60 default: 61 return; 62 } 63 64 return insertBefore && { 65 insertBefore, 66 returnType: getReturnType(containingFunction) 67 }; 68 } 69 70 function doChange( 71 changes: textChanges.ChangeTracker, 72 sourceFile: SourceFile, 73 { insertBefore, returnType }: { insertBefore: Node, returnType: TypeNode | undefined }): void { 74 75 if (returnType) { 76 const entityName = getEntityNameFromTypeNode(returnType); 77 if (!entityName || entityName.kind !== SyntaxKind.Identifier || entityName.text !== "Promise") { 78 changes.replaceNode(sourceFile, returnType, factory.createTypeReferenceNode("Promise", factory.createNodeArray([returnType]))); 79 } 80 } 81 changes.insertModifierBefore(sourceFile, SyntaxKind.AsyncKeyword, insertBefore); 82 } 83} 84