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