From 9b1dfc3fec1355080eb426e35e4bb5734b81db79 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 14 Apr 2017 10:24:44 -0700 Subject: [PATCH 1/2] Disallow calls to async function without await --- src/compiler/checker.ts | 13 +++- src/compiler/diagnosticMessages.json | 4 ++ .../reference/missedAwait.errors.txt | 36 ++++++++++ tests/baselines/reference/missedAwait.js | 67 +++++++++++++++++++ tests/cases/compiler/missedAwait.ts | 29 ++++++++ 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/missedAwait.errors.txt create mode 100644 tests/baselines/reference/missedAwait.js create mode 100644 tests/cases/compiler/missedAwait.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 37163e9e8827b..038535d1d93ca 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19388,7 +19388,18 @@ namespace ts { // Grammar checking checkGrammarStatementInAmbientContext(node); - checkExpression(node.expression); + const type = checkExpression(node.expression); + + // Check for missing 'await' keyword in async functions + if (type && + type.symbol === getGlobalPromiseType(false).symbol && + node.expression.kind === ts.SyntaxKind.CallExpression) { + // Calls to async functions without await inside async functions are disallowed + const container = getContainingFunction(node); + if (container && isAsyncFunction(container)) { + error(node.expression, Diagnostics.Return_value_of_async_function_call_was_discarded_Did_you_mean_to_await_its_result); + } + } } function checkIfStatement(node: IfStatement) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 10dc25ef70e61..355defa554c3e 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2107,6 +2107,10 @@ "category": "Error", "code": 2709 }, + "Return value of async function call was discarded. Did you mean to 'await' its result?": { + "category": "Error", + "code": 2710 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/tests/baselines/reference/missedAwait.errors.txt b/tests/baselines/reference/missedAwait.errors.txt new file mode 100644 index 0000000000000..bafd2356dcf78 --- /dev/null +++ b/tests/baselines/reference/missedAwait.errors.txt @@ -0,0 +1,36 @@ +tests/cases/compiler/missedAwait.ts(18,5): error TS2710: Return value of async function call was discarded. Did you mean to 'await' its result? +tests/cases/compiler/missedAwait.ts(19,5): error TS2710: Return value of async function call was discarded. Did you mean to 'await' its result? + + +==== tests/cases/compiler/missedAwait.ts (2 errors) ==== + async function isAsync() { + return 10; + } + + class SomeClass { + async foo() { + return "ok"; + } + } + + var x = new SomeClass(); + function notAsync() { + isAsync(); // OK + x.foo(); // OK + } + + async function alsoAsync() { + isAsync(); // No + ~~~~~~~~~ +!!! error TS2710: Return value of async function call was discarded. Did you mean to 'await' its result? + x.foo(); // No + ~~~~~~~ +!!! error TS2710: Return value of async function call was discarded. Did you mean to 'await' its result? + void isAsync(); // OK + void x.foo(); // OK + + var j = x.foo(); // OK + j = x.foo(); // OK + await isAsync(); // OK + await x.foo(); // OK + } \ No newline at end of file diff --git a/tests/baselines/reference/missedAwait.js b/tests/baselines/reference/missedAwait.js new file mode 100644 index 0000000000000..d1d4dc82fbb60 --- /dev/null +++ b/tests/baselines/reference/missedAwait.js @@ -0,0 +1,67 @@ +//// [missedAwait.ts] +async function isAsync() { + return 10; +} + +class SomeClass { + async foo() { + return "ok"; + } +} + +var x = new SomeClass(); +function notAsync() { + isAsync(); // OK + x.foo(); // OK +} + +async function alsoAsync() { + isAsync(); // No + x.foo(); // No + void isAsync(); // OK + void x.foo(); // OK + + var j = x.foo(); // OK + j = x.foo(); // OK + await isAsync(); // OK + await x.foo(); // OK +} + +//// [missedAwait.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +function isAsync() { + return __awaiter(this, void 0, void 0, function* () { + return 10; + }); +} +class SomeClass { + foo() { + return __awaiter(this, void 0, void 0, function* () { + return "ok"; + }); + } +} +var x = new SomeClass(); +function notAsync() { + isAsync(); // OK + x.foo(); // OK +} +function alsoAsync() { + return __awaiter(this, void 0, void 0, function* () { + isAsync(); // No + x.foo(); // No + void isAsync(); // OK + void x.foo(); // OK + var j = x.foo(); // OK + j = x.foo(); // OK + yield isAsync(); // OK + yield x.foo(); // OK + }); +} diff --git a/tests/cases/compiler/missedAwait.ts b/tests/cases/compiler/missedAwait.ts new file mode 100644 index 0000000000000..1c57ab18a5e11 --- /dev/null +++ b/tests/cases/compiler/missedAwait.ts @@ -0,0 +1,29 @@ +// @target: es6 + +async function isAsync() { + return 10; +} + +class SomeClass { + async foo() { + return "ok"; + } +} + +var x = new SomeClass(); +function notAsync() { + isAsync(); // OK + x.foo(); // OK +} + +async function alsoAsync() { + isAsync(); // No + x.foo(); // No + void isAsync(); // OK + void x.foo(); // OK + + var j = x.foo(); // OK + j = x.foo(); // OK + await isAsync(); // OK + await x.foo(); // OK +} \ No newline at end of file From 11aa43c6d3620c62605df6793c4288d20fa9d9b2 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Fri, 14 Apr 2017 16:26:58 -0700 Subject: [PATCH 2/2] Lint --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 038535d1d93ca..1f7a908e2e5c1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19391,8 +19391,8 @@ namespace ts { const type = checkExpression(node.expression); // Check for missing 'await' keyword in async functions - if (type && - type.symbol === getGlobalPromiseType(false).symbol && + if (type && + type.symbol === getGlobalPromiseType(/*reportErrors*/ false).symbol && node.expression.kind === ts.SyntaxKind.CallExpression) { // Calls to async functions without await inside async functions are disallowed const container = getContainingFunction(node);