From 469795bb6881c925804153b93b56a54ee5c76773 Mon Sep 17 00:00:00 2001 From: Lukas Kurz Date: Tue, 16 Apr 2024 20:44:12 +0200 Subject: [PATCH] Throw `ERRBadAwait` instead of `ERRnoSemic` if applicable (#6955) * Throw `ERRBadAwait` instead of `ERRnoSemic` if applicable --- lib/Parser/Parse.cpp | 10 ++++++++-- lib/Parser/Scan.cpp | 3 ++- lib/Parser/Scan.h | 6 ++++-- test/es7/asyncawait-syntax.js | 27 ++++++++++++++++++--------- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp index d5adfb40cf2..99370b420e0 100644 --- a/lib/Parser/Parse.cpp +++ b/lib/Parser/Parse.cpp @@ -5483,7 +5483,7 @@ ParseNodeFnc * Parser::ParseFncDeclInternal(ushort flags, LPCOLESTR pNameHint, c { // Class member methods have optional separators. We need to check whether we are // getting the IchLim of the correct token. - Assert(this->GetScanner()->m_tkPrevious == tkRCurly && needScanRCurly); + Assert(this->GetScanner()->GetPrevious() == tkRCurly && needScanRCurly); this->m_funcInArray += this->GetScanner()->IchMinTok() - /*tkRCurly*/ 1 - ichMin; } @@ -6529,7 +6529,7 @@ bool Parser::FastScanFormalsAndBody() { int opl; OpCode nop; - tokens tkPrev = this->GetScanner()->m_tkPrevious; + tokens tkPrev = this->GetScanner()->GetPrevious(); if ((this->GetHashTbl()->TokIsBinop(tkPrev, &opl, &nop) && nop != knopNone) || (this->GetHashTbl()->TokIsUnop(tkPrev, &opl, &nop) && nop != knopNone && @@ -11267,6 +11267,12 @@ ParseNodePtr Parser::ParseStatement() default: if (!this->GetScanner()->FHadNewLine()) { + Token previous = this->GetScanner()->GetPreviousToken(); + if (tkID == previous.tk && wellKnownPropertyPids.await == previous.GetIdentifier(this->GetHashTbl())) + { + Error(ERRBadAwait); + } + Error(ERRnoSemic); } else diff --git a/lib/Parser/Scan.cpp b/lib/Parser/Scan.cpp index f1b9efa71c3..d3b2be4eb1b 100644 --- a/lib/Parser/Scan.cpp +++ b/lib/Parser/Scan.cpp @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #include "ParserPch.h" @@ -1592,7 +1593,7 @@ tokens Scanner::ScanCore(bool identifyKwds) bool seenDelimitedCommentEnd = false; // store the last token - m_tkPrevious = m_ptoken->tk; + m_tokenPrevious = *m_ptoken; m_iecpLimTokPrevious = IecpLimTok(); // Introduced for use by lambda parsing to find correct span of expression lambdas m_ichLimTokPrevious = IchLimTok(); size_t savedMultiUnits = this->m_cMultiUnits; diff --git a/lib/Parser/Scan.h b/lib/Parser/Scan.h index b928e5de7f8..199ebfe40b5 100644 --- a/lib/Parser/Scan.h +++ b/lib/Parser/Scan.h @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- #pragma once @@ -705,7 +706,8 @@ class Scanner : public IScanner, public EncodingPolicy } }; - tokens GetPrevious() { return m_tkPrevious; } + tokens GetPrevious() { return m_tokenPrevious.tk; } + Token GetPreviousToken() { return m_tokenPrevious; } void Capture(_Out_ RestorePoint* restorePoint); void SeekTo(const RestorePoint& restorePoint); void SeekToForcingPid(const RestorePoint& restorePoint); @@ -756,7 +758,7 @@ class Scanner : public IScanner, public EncodingPolicy Js::ScriptContext* m_scriptContext; const Js::CharClassifier *charClassifier; - tokens m_tkPrevious; + Token m_tokenPrevious; size_t m_iecpLimTokPrevious; charcount_t m_ichLimTokPrevious; diff --git a/test/es7/asyncawait-syntax.js b/test/es7/asyncawait-syntax.js index e9e6a10ba90..c2547c059d8 100644 --- a/test/es7/asyncawait-syntax.js +++ b/test/es7/asyncawait-syntax.js @@ -1,5 +1,6 @@ //------------------------------------------------------------------------------------------------------- // Copyright (C) Microsoft. All rights reserved. +// Copyright (c) ChakraCore Project Contributors. All rights reserved. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- @@ -58,26 +59,26 @@ var tests = [ { name: "Await in eval global scope", body: function () { - assert.throws(function () { eval("var result = await call();"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'"); - assert.throws(function () { eval("await call();"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'"); + assert.throws(function () { eval("var result = await call();"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`); + assert.throws(function () { eval("await call();"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`); - assert.throws(function () { eval("await a;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'"); - assert.throws(function () { eval("await a[0];"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'"); - assert.throws(function () { eval("await o.p;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'"); + assert.throws(function () { eval("await a;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`); + assert.throws(function () { eval("await a[0];"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`); + assert.throws(function () { eval("await o.p;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`); assert.throws(function () { eval("a[await p];"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ']'"); - assert.throws(function () { eval("a + await p;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'"); - assert.throws(function () { eval("await p + await q;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ';'"); + assert.throws(function () { eval("a + await p;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`); + assert.throws(function () { eval("await p + await q;"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", `'await' expression not allowed in this context`); assert.throws(function () { eval("foo(await p, await q);"); }, SyntaxError, "'await' keyword is not allowed in eval global scope", "Expected ')'"); assert.throws(function () { eval("var lambdaParenNoArg = await () => x < y;"); }, SyntaxError, "'await' keyword is not allowed with a non-async lambda expression", "Syntax error"); - assert.throws(function () { eval("var lambdaArgs = await async (a, b ,c) => a + b + c;"); }, SyntaxError, "There miss parenthises", "Expected ';'"); + assert.throws(function () { eval("var lambdaArgs = await async (a, b ,c) => a + b + c;"); }, SyntaxError, "There miss parenthises", `'await' expression not allowed in this context`); assert.throws(function () { eval("var lambdaArgs = await (async (a, b ,c) => a + b + c);"); }, ReferenceError, "The 'await' function doesn't exists in this scope", "'await' is not defined"); } }, { name: "Await in a non-async function", body: function () { - assert.throws(function () { eval("function method() { var x = await call(); }"); }, SyntaxError, "'await' cannot be used in a non-async function.", "Expected ';'"); + assert.throws(function () { eval("function method() { var x = await call(); }"); }, SyntaxError, "'await' cannot be used in a non-async function.", `'await' expression not allowed in this context`); } }, { @@ -216,6 +217,14 @@ var tests = [ assert.throws(function () { eval("async function af() { (b = (c = await => {}) => {}) => {}; }"); }, SyntaxError, "await cannot appear as the formal name of an unparathensized arrow function in a nested case too", "Unexpected token '=>' after 'await'"); } }, + { + name: "Specific error message when using 'await' as a keyword outside 'async' context", + body: function () { + assert.throws(function () { + eval(`await new Promise(() => {});`); + }, SyntaxError, "await is not a keyword here", `'await' expression not allowed in this context`); + } + } ]; testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });