Skip to content

Commit

Permalink
Improve error message for use of 'await' in non-async context (#31194)
Browse files Browse the repository at this point in the history
Improve error message for use of 'await' in non-async context
  • Loading branch information
DanielRosenwasser committed May 1, 2019
2 parents e8161ef + bafdf4b commit c9eb846
Show file tree
Hide file tree
Showing 9 changed files with 472 additions and 3 deletions.
29 changes: 27 additions & 2 deletions src/compiler/checker.ts
Expand Up @@ -23087,7 +23087,19 @@ namespace ts {
// Grammar checking
if (produceDiagnostics) {
if (!(node.flags & NodeFlags.AwaitContext)) {
grammarErrorOnFirstToken(node, Diagnostics.await_expression_is_only_allowed_within_an_async_function);
// use of 'await' in non-async function
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.await_expression_is_only_allowed_within_an_async_function);
const func = getContainingFunction(node);
if (func && func.kind !== SyntaxKind.Constructor) {
Debug.assert((getFunctionFlags(func) & FunctionFlags.Async) === 0, "Enclosing function should never be an async function.");
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
addRelatedInfo(diagnostic, relatedInfo);
}
diagnostics.add(diagnostic);
}
}

if (isInParameterInitializerBeforeContainingFunction(node)) {
Expand Down Expand Up @@ -31585,7 +31597,20 @@ namespace ts {

if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement && forInOrOfStatement.awaitModifier) {
if ((forInOrOfStatement.flags & NodeFlags.AwaitContext) === NodeFlags.None) {
return grammarErrorOnNode(forInOrOfStatement.awaitModifier, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator);
// use of 'for-await-of' in non-async function
const sourceFile = getSourceFileOfNode(forInOrOfStatement);
if (!hasParseDiagnostics(sourceFile)) {
const diagnostic = createDiagnosticForNode(forInOrOfStatement.awaitModifier, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator);
const func = getContainingFunction(forInOrOfStatement);
if (func && func.kind !== SyntaxKind.Constructor) {
Debug.assert((getFunctionFlags(func) & FunctionFlags.Async) === 0, "Enclosing function should never be an async function.");
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
addRelatedInfo(diagnostic, relatedInfo);
}
diagnostics.add(diagnostic);
return true;
}
return false;
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/compiler/diagnosticMessages.json
Expand Up @@ -1035,6 +1035,10 @@
"category": "Error",
"code": 1355
},
"Did you mean to mark this function as 'async'?": {
"category": "Error",
"code": 1356
},

"Duplicate identifier '{0}'.": {
"category": "Error",
Expand Down Expand Up @@ -2959,7 +2963,7 @@
"category": "Error",
"code": 4104
},

"The current host does not support the '{0}' option.": {
"category": "Error",
"code": 5001
Expand Down
103 changes: 103 additions & 0 deletions tests/baselines/reference/awaitInNonAsyncFunction.errors.txt
@@ -0,0 +1,103 @@
tests/cases/compiler/awaitInNonAsyncFunction.ts(4,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(5,10): error TS1308: 'await' expression is only allowed within an async function.
tests/cases/compiler/awaitInNonAsyncFunction.ts(9,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(10,10): error TS1308: 'await' expression is only allowed within an async function.
tests/cases/compiler/awaitInNonAsyncFunction.ts(14,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(15,3): error TS1308: 'await' expression is only allowed within an async function.
tests/cases/compiler/awaitInNonAsyncFunction.ts(19,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(20,10): error TS1308: 'await' expression is only allowed within an async function.
tests/cases/compiler/awaitInNonAsyncFunction.ts(24,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(25,9): error TS1308: 'await' expression is only allowed within an async function.
tests/cases/compiler/awaitInNonAsyncFunction.ts(30,9): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(31,5): error TS1308: 'await' expression is only allowed within an async function.
tests/cases/compiler/awaitInNonAsyncFunction.ts(34,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(35,5): error TS1308: 'await' expression is only allowed within an async function.
tests/cases/compiler/awaitInNonAsyncFunction.ts(39,5): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1308: 'await' expression is only allowed within an async function.


==== tests/cases/compiler/awaitInNonAsyncFunction.ts (16 errors) ====
// https://github.com/Microsoft/TypeScript/issues/26586

function normalFunc(p: Promise<number>) {
for await (const _ of []);
~~~~~
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:3:10: Did you mean to mark this function as 'async'?
return await p;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:3:10: Did you mean to mark this function as 'async'?
}

export function exportedFunc(p: Promise<number>) {
for await (const _ of []);
~~~~~
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:8:17: Did you mean to mark this function as 'async'?
return await p;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:8:17: Did you mean to mark this function as 'async'?
}

const functionExpression = function(p: Promise<number>) {
for await (const _ of []);
~~~~~
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:13:28: Did you mean to mark this function as 'async'?
await p;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:13:28: Did you mean to mark this function as 'async'?
}

const arrowFunc = (p: Promise<number>) => {
for await (const _ of []);
~~~~~
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:18:19: Did you mean to mark this function as 'async'?
return await p;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:18:19: Did you mean to mark this function as 'async'?
};

function* generatorFunc(p: Promise<number>) {
for await (const _ of []);
~~~~~
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:23:11: Did you mean to mark this function as 'async'?
yield await p;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:23:11: Did you mean to mark this function as 'async'?
}

class clazz {
constructor(p: Promise<number>) {
for await (const _ of []);
~~~~~
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
await p;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
}
method(p: Promise<number>) {
for await (const _ of []);
~~~~~
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:33:3: Did you mean to mark this function as 'async'?
await p;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:33:3: Did you mean to mark this function as 'async'?
}
}

for await (const _ of []);
~~~~~
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
await null;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
84 changes: 84 additions & 0 deletions tests/baselines/reference/awaitInNonAsyncFunction.js
@@ -0,0 +1,84 @@
//// [awaitInNonAsyncFunction.ts]
// https://github.com/Microsoft/TypeScript/issues/26586

function normalFunc(p: Promise<number>) {
for await (const _ of []);
return await p;
}

export function exportedFunc(p: Promise<number>) {
for await (const _ of []);
return await p;
}

const functionExpression = function(p: Promise<number>) {
for await (const _ of []);
await p;
}

const arrowFunc = (p: Promise<number>) => {
for await (const _ of []);
return await p;
};

function* generatorFunc(p: Promise<number>) {
for await (const _ of []);
yield await p;
}

class clazz {
constructor(p: Promise<number>) {
for await (const _ of []);
await p;
}
method(p: Promise<number>) {
for await (const _ of []);
await p;
}
}

for await (const _ of []);
await null;

//// [awaitInNonAsyncFunction.js]
// https://github.com/Microsoft/TypeScript/issues/26586
function normalFunc(p) {
for await (const _ of [])
;
return await p;
}
export function exportedFunc(p) {
for await (const _ of [])
;
return await p;
}
const functionExpression = function (p) {
for await (const _ of [])
;
await p;
};
const arrowFunc = (p) => {
for await (const _ of [])
;
return await p;
};
function* generatorFunc(p) {
for await (const _ of [])
;
yield await p;
}
class clazz {
constructor(p) {
for await (const _ of [])
;
await p;
}
method(p) {
for await (const _ of [])
;
await p;
}
}
for await (const _ of [])
;
await null;
94 changes: 94 additions & 0 deletions tests/baselines/reference/awaitInNonAsyncFunction.symbols
@@ -0,0 +1,94 @@
=== tests/cases/compiler/awaitInNonAsyncFunction.ts ===
// https://github.com/Microsoft/TypeScript/issues/26586

function normalFunc(p: Promise<number>) {
>normalFunc : Symbol(normalFunc, Decl(awaitInNonAsyncFunction.ts, 0, 0))
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 2, 20))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))

for await (const _ of []);
>_ : Symbol(_, Decl(awaitInNonAsyncFunction.ts, 3, 18))

return await p;
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 2, 20))
}

export function exportedFunc(p: Promise<number>) {
>exportedFunc : Symbol(exportedFunc, Decl(awaitInNonAsyncFunction.ts, 5, 1))
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 7, 29))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))

for await (const _ of []);
>_ : Symbol(_, Decl(awaitInNonAsyncFunction.ts, 8, 18))

return await p;
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 7, 29))
}

const functionExpression = function(p: Promise<number>) {
>functionExpression : Symbol(functionExpression, Decl(awaitInNonAsyncFunction.ts, 12, 5))
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 12, 36))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))

for await (const _ of []);
>_ : Symbol(_, Decl(awaitInNonAsyncFunction.ts, 13, 18))

await p;
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 12, 36))
}

const arrowFunc = (p: Promise<number>) => {
>arrowFunc : Symbol(arrowFunc, Decl(awaitInNonAsyncFunction.ts, 17, 5))
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 17, 19))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))

for await (const _ of []);
>_ : Symbol(_, Decl(awaitInNonAsyncFunction.ts, 18, 18))

return await p;
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 17, 19))

};

function* generatorFunc(p: Promise<number>) {
>generatorFunc : Symbol(generatorFunc, Decl(awaitInNonAsyncFunction.ts, 20, 2))
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 22, 24))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))

for await (const _ of []);
>_ : Symbol(_, Decl(awaitInNonAsyncFunction.ts, 23, 18))

yield await p;
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 22, 24))
}

class clazz {
>clazz : Symbol(clazz, Decl(awaitInNonAsyncFunction.ts, 25, 1))

constructor(p: Promise<number>) {
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 28, 14))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))

for await (const _ of []);
>_ : Symbol(_, Decl(awaitInNonAsyncFunction.ts, 29, 20))

await p;
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 28, 14))
}
method(p: Promise<number>) {
>method : Symbol(clazz.method, Decl(awaitInNonAsyncFunction.ts, 31, 3))
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 32, 9))
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))

for await (const _ of []);
>_ : Symbol(_, Decl(awaitInNonAsyncFunction.ts, 33, 18))

await p;
>p : Symbol(p, Decl(awaitInNonAsyncFunction.ts, 32, 9))
}
}

for await (const _ of []);
>_ : Symbol(_, Decl(awaitInNonAsyncFunction.ts, 38, 16))

await null;

0 comments on commit c9eb846

Please sign in to comment.