• 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        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