Skip to content

Commit c0bdd12

Browse files
srolelmhegazy
authored andcommitted
add fixAwaitInSyncFunction code fix (#21069)
* add fixAwaitInSyncFunction code fix * Just insert the keyword * only one codefix * remove comment * Change explicit return type T to Promise<T> * Review changes * Change codefix message
1 parent 03fd776 commit c0bdd12

21 files changed

+321
-0
lines changed

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3877,6 +3877,10 @@
38773877
"category": "Message",
38783878
"code": 90028
38793879
},
3880+
"Add async modifier to containing function": {
3881+
"category": "Message",
3882+
"code": 90029
3883+
},
38803884
"Convert function to an ES2015 class": {
38813885
"category": "Message",
38823886
"code": 95001
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* @internal */
2+
namespace ts.codefix {
3+
const fixId = "fixAwaitInSyncFunction";
4+
const errorCodes = [
5+
Diagnostics.await_expression_is_only_allowed_within_an_async_function.code,
6+
Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator.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 [{ description: getLocaleSpecificMessage(Diagnostics.Add_async_modifier_to_containing_function), changes, fixId }];
16+
},
17+
fixIds: [fixId],
18+
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
19+
const nodes = getNodes(diag.file, diag.start);
20+
if (!nodes) return;
21+
doChange(changes, context.sourceFile, nodes);
22+
}),
23+
});
24+
25+
function getReturnType(expr: FunctionDeclaration | MethodDeclaration | FunctionExpression | ArrowFunction) {
26+
if (expr.type) {
27+
return expr.type;
28+
}
29+
if (isVariableDeclaration(expr.parent) &&
30+
expr.parent.type &&
31+
isFunctionTypeNode(expr.parent.type)) {
32+
return expr.parent.type.type;
33+
}
34+
}
35+
36+
function getNodes(sourceFile: SourceFile, start: number): { insertBefore: Node, returnType: TypeNode | undefined } | undefined {
37+
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
38+
const containingFunction = getContainingFunction(token);
39+
let insertBefore: Node | undefined;
40+
switch (containingFunction.kind) {
41+
case SyntaxKind.MethodDeclaration:
42+
insertBefore = containingFunction.name;
43+
break;
44+
case SyntaxKind.FunctionDeclaration:
45+
case SyntaxKind.FunctionExpression:
46+
insertBefore = findChildOfKind(containingFunction, SyntaxKind.FunctionKeyword, sourceFile);
47+
break;
48+
case SyntaxKind.ArrowFunction:
49+
insertBefore = findChildOfKind(containingFunction, SyntaxKind.OpenParenToken, sourceFile) || first(containingFunction.parameters);
50+
break;
51+
default:
52+
return;
53+
}
54+
55+
return {
56+
insertBefore,
57+
returnType: getReturnType(containingFunction)
58+
};
59+
}
60+
61+
function doChange(
62+
changes: textChanges.ChangeTracker,
63+
sourceFile: SourceFile,
64+
{ insertBefore, returnType }: { insertBefore: Node | undefined, returnType: TypeNode | undefined }): void {
65+
66+
if (returnType) {
67+
const entityName = getEntityNameFromTypeNode(returnType);
68+
if (!entityName || entityName.kind !== SyntaxKind.Identifier || entityName.text !== "Promise") {
69+
changes.replaceNode(sourceFile, returnType, createTypeReferenceNode("Promise", createNodeArray([returnType])));
70+
}
71+
}
72+
changes.insertModifierBefore(sourceFile, SyntaxKind.AsyncKeyword, insertBefore);
73+
}
74+
}

src/services/codefixes/fixes.ts

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
/// <reference path="fixForgottenThisPropertyAccess.ts" />
1212
/// <reference path='fixUnusedIdentifier.ts' />
1313
/// <reference path='fixJSDocTypes.ts' />
14+
/// <reference path='fixAwaitInSyncFunction.ts' />
1415
/// <reference path='importFixes.ts' />
1516
/// <reference path='disableJsDiagnostics.ts' />
1617
/// <reference path='helpers.ts' />

src/services/textChanges.ts

+5
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,11 @@ namespace ts.textChanges {
345345
return this.replaceWithSingle(sourceFile, startPosition, startPosition, newNode, this.getOptionsForInsertNodeBefore(before, blankLineBetween));
346346
}
347347

348+
public insertModifierBefore(sourceFile: SourceFile, modifier: SyntaxKind, before: Node): void {
349+
const pos = before.getStart(sourceFile);
350+
this.replaceWithSingle(sourceFile, pos, pos, createToken(modifier), { suffix: " " });
351+
}
352+
348353
public changeIdentifierToPropertyAccess(sourceFile: SourceFile, prefix: string, node: Identifier): void {
349354
const startPosition = getAdjustedStartPosition(sourceFile, node, {}, Position.Start);
350355
this.replaceWithSingle(sourceFile, startPosition, startPosition, createPropertyAccess(createIdentifier(prefix), ""), {});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f() {
4+
//// await Promise.resolve();
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`async function f() {
11+
await Promise.resolve();
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f: () => number | string = () => {
4+
//// await Promise.resolve('foo');
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`const f: () => Promise<number | string> = async () => {
11+
await Promise.resolve('foo');
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f: string = () => {
4+
//// await Promise.resolve('foo');
5+
////}
6+
7+
// should not change type if it's incorrectly set
8+
verify.codeFix({
9+
description: "Add async modifier to containing function",
10+
newFileContent:
11+
`const f: string = async () => {
12+
await Promise.resolve('foo');
13+
}`,
14+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f: () => Array<number | string> = function() {
4+
//// await Promise.resolve([]);
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`const f: () => Promise<Array<number | string>> = async function() {
11+
await Promise.resolve([]);
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f: () => Promise<number | string> = () => {
4+
//// await Promise.resolve('foo');
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`const f: () => Promise<number | string> = async () => {
11+
await Promise.resolve('foo');
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f = function(): number {
4+
//// await Promise.resolve(1);
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`const f = async function(): Promise<number> {
11+
await Promise.resolve(1);
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f = (): number[] => {
4+
//// await Promise.resolve([1]);
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`const f = async (): Promise<number[]> => {
11+
await Promise.resolve([1]);
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f = function() {
4+
//// await Promise.resolve();
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`const f = async function() {
11+
await Promise.resolve();
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f = {
4+
//// get a() {
5+
//// return await Promise.resolve();
6+
//// },
7+
//// get a() {
8+
//// await Promise.resolve();
9+
//// },
10+
////}
11+
12+
verify.not.codeFixAvailable();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////class Foo {
4+
//// constructor {
5+
//// await Promise.resolve();
6+
//// }
7+
////}
8+
9+
verify.not.codeFixAvailable();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////class Foo {
4+
//// bar() {
5+
//// await Promise.resolve();
6+
//// }
7+
////}
8+
9+
verify.codeFix({
10+
description: "Add async modifier to containing function",
11+
newFileContent:
12+
`class Foo {
13+
async bar() {
14+
await Promise.resolve();
15+
}
16+
}`,
17+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f = promise => {
4+
//// await promise;
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`const f = async promise => {
11+
await promise;
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////const f = (promise) => {
4+
//// await promise;
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`const f = async (promise) => {
11+
await promise;
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f() {
4+
//// for await (const x of g()) {
5+
//// console.log(x);
6+
//// }
7+
////}
8+
9+
verify.codeFix({
10+
description: "Add async modifier to containing function",
11+
newFileContent:
12+
`async function f() {
13+
for await (const x of g()) {
14+
console.log(x);
15+
}
16+
}`,
17+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f(): number | string {
4+
//// await Promise.resolve(8);
5+
////}
6+
7+
verify.codeFix({
8+
description: "Add async modifier to containing function",
9+
newFileContent:
10+
`async function f(): Promise<number | string> {
11+
await Promise.resolve(8);
12+
}`,
13+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////class Foo {
4+
//// bar(): string {
5+
//// await Promise.resolve('baz');
6+
//// }
7+
////}
8+
9+
verify.codeFix({
10+
description: "Add async modifier to containing function",
11+
newFileContent:
12+
`class Foo {
13+
async bar(): Promise<string> {
14+
await Promise.resolve('baz');
15+
}
16+
}`,
17+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////function f() {
4+
//// await Promise.resolve();
5+
////}
6+
////
7+
////const g = () => {
8+
//// await f();
9+
////}
10+
11+
verify.codeFixAll({
12+
fixId: "fixAwaitInSyncFunction",
13+
newFileContent:
14+
`async function f() {
15+
await Promise.resolve();
16+
}
17+
18+
const g = async () => {
19+
await f();
20+
}`,
21+
});

0 commit comments

Comments
 (0)