1/* @internal */ 2namespace ts.codefix { 3 type ContextualTrackChangesFunction = (cb: (changeTracker: textChanges.ChangeTracker) => void) => FileTextChanges[]; 4 const fixId = "addMissingAsync"; 5 const errorCodes = [ 6 Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code, 7 Diagnostics.Type_0_is_not_assignable_to_type_1.code, 8 Diagnostics.Type_0_is_not_comparable_to_type_1.code 9 ]; 10 11 registerCodeFix({ 12 fixIds: [fixId], 13 errorCodes, 14 getCodeActions: function getCodeActionsToAddMissingAsync(context) { 15 const { sourceFile, errorCode, cancellationToken, program, span } = context; 16 const diagnostic = find(program.getTypeChecker().getDiagnostics(sourceFile, cancellationToken), getIsMatchingAsyncError(span, errorCode)); 17 const directSpan = diagnostic && diagnostic.relatedInformation && find(diagnostic.relatedInformation, r => r.code === Diagnostics.Did_you_mean_to_mark_this_function_as_async.code) as TextSpan | undefined; 18 19 const decl = getFixableErrorSpanDeclaration(sourceFile, directSpan); 20 if (!decl) { 21 return; 22 } 23 24 const trackChanges: ContextualTrackChangesFunction = cb => textChanges.ChangeTracker.with(context, cb); 25 return [getFix(context, decl, trackChanges)]; 26 }, 27 getAllCodeActions: context => { 28 const { sourceFile } = context; 29 const fixedDeclarations = new Set<number>(); 30 return codeFixAll(context, errorCodes, (t, diagnostic) => { 31 const span = diagnostic.relatedInformation && find(diagnostic.relatedInformation, r => r.code === Diagnostics.Did_you_mean_to_mark_this_function_as_async.code) as TextSpan | undefined; 32 const decl = getFixableErrorSpanDeclaration(sourceFile, span); 33 if (!decl) { 34 return; 35 } 36 const trackChanges: ContextualTrackChangesFunction = cb => (cb(t), []); 37 return getFix(context, decl, trackChanges, fixedDeclarations); 38 }); 39 }, 40 }); 41 42 type FixableDeclaration = ArrowFunction | FunctionDeclaration | FunctionExpression | MethodDeclaration; 43 function getFix(context: CodeFixContext | CodeFixAllContext, decl: FixableDeclaration, trackChanges: ContextualTrackChangesFunction, fixedDeclarations?: Set<number>) { 44 const changes = trackChanges(t => makeChange(t, context.sourceFile, decl, fixedDeclarations)); 45 return createCodeFixAction(fixId, changes, Diagnostics.Add_async_modifier_to_containing_function, fixId, Diagnostics.Add_all_missing_async_modifiers); 46 } 47 48 function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, insertionSite: FixableDeclaration, fixedDeclarations?: Set<number>) { 49 if (fixedDeclarations) { 50 if (fixedDeclarations.has(getNodeId(insertionSite))) { 51 return; 52 } 53 } 54 fixedDeclarations?.add(getNodeId(insertionSite)); 55 const cloneWithModifier = factory.updateModifiers( 56 getSynthesizedDeepClone(insertionSite, /*includeTrivia*/ true), 57 factory.createNodeArray(factory.createModifiersFromModifierFlags(getSyntacticModifierFlags(insertionSite) | ModifierFlags.Async))); 58 changeTracker.replaceNode( 59 sourceFile, 60 insertionSite, 61 cloneWithModifier); 62 } 63 64 function getFixableErrorSpanDeclaration(sourceFile: SourceFile, span: TextSpan | undefined): FixableDeclaration | undefined { 65 if (!span) return undefined; 66 const token = getTokenAtPosition(sourceFile, span.start); 67 // Checker has already done work to determine that async might be possible, and has attached 68 // related info to the node, so start by finding the signature that exactly matches up 69 // with the diagnostic range. 70 const decl = findAncestor(token, node => { 71 if (node.getStart(sourceFile) < span.start || node.getEnd() > textSpanEnd(span)) { 72 return "quit"; 73 } 74 return (isArrowFunction(node) || isMethodDeclaration(node) || isFunctionExpression(node) || isFunctionDeclaration(node)) && textSpansEqual(span, createTextSpanFromNode(node, sourceFile)); 75 }) as FixableDeclaration | undefined; 76 77 return decl; 78 } 79 80 function getIsMatchingAsyncError(span: TextSpan, errorCode: number) { 81 return ({ start, length, relatedInformation, code }: Diagnostic) => 82 isNumber(start) && isNumber(length) && textSpansEqual({ start, length }, span) && 83 code === errorCode && 84 !!relatedInformation && 85 some(relatedInformation, related => related.code === Diagnostics.Did_you_mean_to_mark_this_function_as_async.code); 86 } 87} 88