From 1f18f53acfb5ea18a1bf1dd6678fd398a6ad4eac Mon Sep 17 00:00:00 2001 From: rhuanjl Date: Sun, 7 Oct 2018 16:00:56 +0100 Subject: [PATCH] Improve parsing error messages - add support for use of %s - replace some frequntely enocuntered generic messages --- lib/Parser/Parse.cpp | 201 +++++++++++++++++++++-- lib/Parser/Parse.h | 3 +- lib/Parser/Scan.h | 1 + lib/Parser/cmperr.cpp | 10 +- lib/Parser/cmperr.h | 7 +- lib/Parser/perrors.h | 9 + lib/Parser/screrror.cpp | 9 +- lib/Parser/screrror.h | 2 +- lib/Runtime/Library/JavascriptError.cpp | 32 +++- test/Basics/IdsWithEscapes.baseline | 14 +- test/Basics/Labels.js | 6 +- test/es5/SemicolonAfterBlockEs5.baseline | 2 +- test/es6/ES6NewTarget.js | 8 +- test/es6/HTMLComments.js | 6 +- test/es6/default.js | 18 +- test/es6/generators-syntax.js | 4 +- test/es6/lambda1.js | 28 ++-- test/es7/asyncawait-syntax.js | 16 +- 18 files changed, 307 insertions(+), 69 deletions(-) diff --git a/lib/Parser/Parse.cpp b/lib/Parser/Parse.cpp index 3aa2af26be3..8631aabf02d 100644 --- a/lib/Parser/Parse.cpp +++ b/lib/Parser/Parse.cpp @@ -159,9 +159,168 @@ void Parser::OutOfMemory() throw ParseExceptionObject(ERRnoMemory); } -void Parser::Error(HRESULT hr) +LPCWSTR Parser::GetTokenString(tokens token) +{ + switch (token) + { + case tkNone : return _u(""); + case tkEOF : return _u("end of script"); + case tkIntCon : return _u("integer literal"); + case tkFltCon : return _u("float literal"); + case tkStrCon : return _u("string literal"); + case tkRegExp : return _u("regular expression literal"); + +// keywords + case tkABSTRACT : return _u("abstract"); + case tkASSERT : return _u("assert"); + case tkAWAIT : return _u("await"); + case tkBOOLEAN : return _u("boolean"); + case tkBREAK : return _u("break"); + case tkBYTE : return _u("byte"); + case tkCASE : return _u("case"); + case tkCATCH : return _u("catch"); + case tkCHAR : return _u("char"); + case tkCONTINUE : return _u("continue"); + case tkDEBUGGER : return _u("debugger"); + case tkDECIMAL : return _u("decimal"); + case tkDEFAULT : return _u("default"); + case tkDELETE : return _u("delete"); + case tkDO : return _u("do"); + case tkDOUBLE : return _u("double"); + case tkELSE : return _u("else"); + case tkENSURE : return _u("ensure"); + case tkEVENT : return _u("event"); + case tkFALSE : return _u("false"); + case tkFINAL : return _u("final"); + case tkFINALLY : return _u("finally"); + case tkFLOAT : return _u("float"); + case tkFOR : return _u("for"); + case tkFUNCTION : return _u("function"); + case tkGET : return _u("get"); + case tkGOTO : return _u("goto"); + case tkIF : return _u("if"); + case tkIN : return _u("in"); + case tkINSTANCEOF : return _u("instanceof"); + case tkINT : return _u("int"); + case tkINTERNAL : return _u("internal"); + case tkINVARIANT : return _u("invariant"); + case tkLONG : return _u("long"); + case tkNAMESPACE : return _u("namespace"); + case tkNATIVE : return _u("native"); + case tkNEW : return _u("new"); + case tkNULL : return _u("null"); + case tkREQUIRE : return _u("require"); + case tkRETURN : return _u("return"); + case tkSBYTE : return _u("sbyte"); + case tkSET : return _u("set"); + case tkSHORT : return _u("short"); + case tkSWITCH : return _u("switch"); + case tkSYNCHRONIZED : return _u("synchronized"); + case tkTHIS : return _u("this"); + case tkTHROW : return _u("throw"); + case tkTHROWS : return _u("throws"); + case tkTRANSIENT : return _u("transient"); + case tkTRUE : return _u("true"); + case tkTRY : return _u("try"); + case tkTYPEOF : return _u("typeof"); + case tkUINT : return _u("uint"); + case tkULONG : return _u("ulong"); + case tkUSE : return _u("use"); + case tkUSHORT : return _u("ushort"); + case tkVAR : return _u("var"); + case tkVOID : return _u("void"); + case tkVOLATILE : return _u("volatile"); + case tkWHILE : return _u("while"); + case tkWITH : return _u("with"); + +// Future reserved words that become keywords in ES6 + case tkCLASS : return _u("class"); + case tkCONST : return _u("const"); + case tkEXPORT : return _u("export"); + case tkEXTENDS : return _u("extends"); + case tkIMPORT : return _u("import"); + case tkLET : return _u("let"); + case tkSUPER : return _u("super"); + case tkYIELD : return _u("yield"); + +// Future reserved words in strict and non-strict modes + case tkENUM : return _u("enum"); + +// Additional future reserved words in strict mode + case tkIMPLEMENTS : return _u("implements"); + case tkINTERFACE : return _u("interface"); + case tkPACKAGE : return _u("package"); + case tkPRIVATE : return _u("private"); + case tkPROTECTED : return _u("protected"); + case tkPUBLIC : return _u("public"); + case tkSTATIC : return _u("static"); + + case tkID: return _u("identifier"); + +// Non-operator non-identifier tokens + case tkSColon: return _u(";"); + case tkRParen: return _u(")"); + case tkRBrack: return _u("]"); + case tkLCurly: return _u("{"); + case tkRCurly: return _u("}"); + +// Operator non-identifier tokens + case tkComma: return _u(","); + case tkDArrow: return _u("=>"); + case tkAsg: return _u("="); + case tkAsgAdd: return _u("+="); + case tkAsgSub: return _u("-="); + case tkAsgMul: return _u("*="); + case tkAsgDiv: return _u("/="); + case tkAsgExpo: return _u("**="); + case tkAsgMod: return _u("%="); + case tkAsgAnd: return _u("&="); + case tkAsgXor: return _u("^="); + case tkAsgOr: return _u("|="); + case tkAsgLsh: return _u("<<="); + case tkAsgRsh: return _u(">>="); + case tkAsgRs2: return _u(">>>="); + case tkQMark: return _u("?"); + case tkColon: return _u(":"); + case tkLogOr: return _u("||"); + case tkLogAnd: return _u("&&"); + case tkOr: return _u("|"); + case tkXor: return _u("^"); + case tkAnd: return _u("&"); + case tkEQ: return _u("=="); + case tkNE: return _u("!="); + case tkEqv: return _u("==="); + case tkNEqv: return _u("!=="); + case tkLT: return _u("<"); + case tkLE: return _u("<="); + case tkGT: return _u(">"); + case tkGE: return _u(">="); + case tkLsh: return _u("<<"); + case tkRsh: return _u(">>"); + case tkRs2: return _u(">>>"); + case tkAdd: return _u("+"); + case tkSub: return _u("-"); + case tkExpo: return _u("**"); + case tkStar: return _u("*"); + case tkDiv: return _u("/"); + case tkPct: return _u("%"); + case tkTilde: return _u("~"); + case tkBang: return _u("!"); + case tkInc: return _u("++"); + case tkDec: return _u("--"); + case tkEllipsis: return _u("..."); + case tkLParen: return _u("("); + case tkLBrack: return _u("["); + case tkDot: return _u("."); + + default: + return _u("unknown token"); + } +} + +void Parser::Error(HRESULT hr, LPCWSTR stringOne, LPCWSTR stringTwo) { - throw ParseExceptionObject(hr); + throw ParseExceptionObject(hr, stringOne, stringTwo); } void Parser::Error(HRESULT hr, ParseNodePtr pnode) @@ -226,6 +385,7 @@ HRESULT Parser::ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isG HRESULT hr; SmartFPUControl smartFpuControl; + bool handled = false; BOOL fDeferSave = m_deferringAST; try @@ -287,9 +447,11 @@ HRESULT Parser::ValidateSyntax(LPCUTF8 pszSrc, size_t encodedCharCount, bool isG { m_deferringAST = fDeferSave; hr = e.GetError(); + hr = pse->ProcessError(this->GetScanner(), hr, /* pnodeBase */ NULL, e.GetStringOne(), e.GetStringTwo()); + handled = true; } - if (nullptr != pse && FAILED(hr)) + if (handled == false && nullptr != pse && FAILED(hr)) { hr = pse->ProcessError(this->GetScanner(), hr, /* pnodeBase */ NULL); } @@ -326,6 +488,7 @@ HRESULT Parser::ParseSourceInternal( ParseNodeProg * pnodeBase = NULL; HRESULT hr; SmartFPUControl smartFpuControl; + bool handled = false; try { @@ -364,13 +527,15 @@ HRESULT Parser::ParseSourceInternal( catch (ParseExceptionObject& e) { hr = e.GetError(); + hr = pse->ProcessError(this->GetScanner(), hr, pnodeBase, e.GetStringOne(), e.GetStringTwo()); + handled = true; } catch (Js::AsmJsParseException&) { hr = JSERR_AsmJsCompileError; } - if (FAILED(hr)) + if (handled == false && FAILED(hr)) { hr = pse->ProcessError(this->GetScanner(), hr, pnodeBase); } @@ -2153,7 +2318,7 @@ IdentPtr Parser::ParseMetaProperty(tokens metaParentKeyword, charcount_t ichMin, } else { - Error(ERRsyntax); + Error(ERRValidIfFollowedBy, _u("'new.'"), _u("'target'")); } } @@ -2442,7 +2607,7 @@ void Parser::ParseImportClause(ModuleImportOrExportEntryList* importEntryList, b // There cannot be a namespace import or named imports list on the left of the comma in a module import clause. if (parsingAfterComma || parsedNamespaceOrNamedImport) { - Error(ERRsyntax); + Error(ERRTokenAfter, _u(","), GetTokenString(this->GetScanner()->GetPrevious())); } this->GetScanner()->Scan(); @@ -2498,7 +2663,7 @@ ParseNodePtr Parser::ParseImport() { if (!m_scriptContext->GetConfig()->IsESDynamicImportEnabled()) { - Error(ERRsyntax); + Error(ERRExperimental); } ParseNodePtr pnode = ParseImportCall(); @@ -3109,7 +3274,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall, // If the token after the right paren is not => or if there was a newline between () and => this is a syntax error if (!IsDoingFastScan() && (m_token.tk != tkDArrow || this->GetScanner()->FHadNewLine())) { - Error(ERRsyntax); + Error(ERRValidIfFollowedBy, _u("Lambda parameter list"), _u("'=>' on the same line")); } if (buildAST) @@ -3423,7 +3588,18 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall, default: LUnknown: - Error(ERRsyntax); + if (m_token.tk == tkNone) + { + Error(ERRInvalidIdentifier, m_token.GetIdentifier(this->GetHashTbl())->Psz(), GetTokenString(GetScanner()->GetPrevious())); + } + else if (m_token.IsKeyword()) + { + Error(ERRKeywordAfter, GetTokenString(m_token.tk), GetTokenString(GetScanner()->GetPrevious())); + } + else + { + Error(ERRTokenAfter, GetTokenString(m_token.tk), GetTokenString(GetScanner()->GetPrevious())); + } break; } @@ -5607,7 +5783,7 @@ void Parser::ParseFncDeclHelper(ParseNodeFnc * pnodeFnc, LPCOLESTR pNameHint, us // this after verifying there was a => token. Otherwise we would throw the wrong error. if (hadNewLine) { - Error(ERRsyntax); + Error(ERRValidIfFollowedBy, _u("Lambda parameter list"), _u("'=>' on the same line")); } } @@ -11893,6 +12069,7 @@ HRESULT Parser::ParseFunctionInBackground(ParseNodeFnc * pnodeFnc, ParseContext ParseNodeBlock * pnodeBlock = StartParseBlock(PnodeBlockType::Function, ScopeType_FunctionBody); pnodeFnc->pnodeScopes = pnodeBlock; m_ppnodeScope = &pnodeBlock->pnodeScopes; + bool handled = false; uint uDeferSave = m_grfscr & (fscrCanDeferFncParse | fscrWillDeferFncParse); @@ -11943,9 +12120,11 @@ HRESULT Parser::ParseFunctionInBackground(ParseNodeFnc * pnodeFnc, ParseContext catch (ParseExceptionObject& e) { hr = e.GetError(); + hr = pse->ProcessError(this->GetScanner(), hr, nullptr, e.GetStringOne(), e.GetStringTwo()); + handled = true; } - if (FAILED(hr)) + if (handled == false && FAILED(hr)) { hr = pse->ProcessError(this->GetScanner(), hr, nullptr); } diff --git a/lib/Parser/Parse.h b/lib/Parser/Parse.h index b29e995ff71..fe6fb48ed5e 100644 --- a/lib/Parser/Parse.h +++ b/lib/Parser/Parse.h @@ -313,7 +313,8 @@ class Parser Js::ScriptContext* m_scriptContext; HashTbl * GetHashTbl() { return this->GetScanner()->GetHashTbl(); } - __declspec(noreturn) void Error(HRESULT hr); + LPCWSTR GetTokenString(tokens token); + __declspec(noreturn) void Error(HRESULT hr, LPCWSTR stringOne = _u(""), LPCWSTR stringTwo = _u("")); private: __declspec(noreturn) void Error(HRESULT hr, ParseNodePtr pnode); __declspec(noreturn) void Error(HRESULT hr, charcount_t ichMin, charcount_t ichLim); diff --git a/lib/Parser/Scan.h b/lib/Parser/Scan.h index ba1843cc622..dc28a81c697 100644 --- a/lib/Parser/Scan.h +++ b/lib/Parser/Scan.h @@ -688,6 +688,7 @@ class Scanner : public IScanner, public EncodingPolicy } }; + tokens GetPrevious() { return m_tkPrevious; } void Capture(_Out_ RestorePoint* restorePoint); void SeekTo(const RestorePoint& restorePoint); void SeekToForcingPid(const RestorePoint& restorePoint); diff --git a/lib/Parser/cmperr.cpp b/lib/Parser/cmperr.cpp index 13684a16a8c..1adee48715e 100644 --- a/lib/Parser/cmperr.cpp +++ b/lib/Parser/cmperr.cpp @@ -4,8 +4,16 @@ //------------------------------------------------------------------------------------------------------- #include "ParserPch.h" -ParseExceptionObject::ParseExceptionObject(HRESULT hr) : m_hr(hr) +ParseExceptionObject::ParseExceptionObject(HRESULT hr, LPCWSTR stringOneIn, LPCWSTR stringTwoIn) { + m_hr = hr; + stringOne = SysAllocString(stringOneIn); + stringTwo = SysAllocString(stringTwoIn); Assert(FAILED(hr)); } +ParseExceptionObject::~ParseExceptionObject() +{ + SysFreeString(stringOne); + SysFreeString(stringTwo); +} diff --git a/lib/Parser/cmperr.h b/lib/Parser/cmperr.h index 91bff7e74db..a385f01d954 100644 --- a/lib/Parser/cmperr.h +++ b/lib/Parser/cmperr.h @@ -17,8 +17,13 @@ enum class ParseExceptionObject { public: - ParseExceptionObject(HRESULT hr); + ParseExceptionObject(HRESULT hr, LPCWSTR stringOneIn = _u(""), LPCWSTR stringTwoIn = _u("")); + ~ParseExceptionObject(); HRESULT GetError() { return m_hr; } + LPCWSTR GetStringOne() { return stringOne; } + LPCWSTR GetStringTwo() { return stringTwo; } private: HRESULT m_hr; + BSTR stringOne; + BSTR stringTwo; }; diff --git a/lib/Parser/perrors.h b/lib/Parser/perrors.h index c47a446acc6..fda87e91e96 100644 --- a/lib/Parser/perrors.h +++ b/lib/Parser/perrors.h @@ -109,3 +109,12 @@ LSC_ERROR_MSG(1093, ERRLabelBeforeClassDeclaration, "Labels not allowed before c LSC_ERROR_MSG(1094, ERRLabelFollowedByEOF, "Unexpected end of script after a label.") LSC_ERROR_MSG(1095, ERRFunctionAfterLabelInStrict, "Function declarations not allowed after a label in strict mode.") LSC_ERROR_MSG(1096, ERRAwaitAsLabelInAsync, "Use of 'await' as label in async function is not allowed.") +LSC_ERROR_MSG(1097, ERRExperimental, "Use of disabled experimental feature") +//1098-1199 available for future use + +// Generic errors intended to be re-usable +LSC_ERROR_MSG(1200, ERRKeywordAfter, "Unexpected keyword '%s' after '%s'") +LSC_ERROR_MSG(1201, ERRTokenAfter, "Unexpected token '%s' after '%s'") +LSC_ERROR_MSG(1202, ERRIdentifierAfter, "Unexpected identifier '%s' after '%s'") +LSC_ERROR_MSG(1203, ERRInvalidIdentifier, "Unexpected invalid identifier '%s' after '%s'") +LSC_ERROR_MSG(1205, ERRValidIfFollowedBy, "%s is only valid if followed by %s") diff --git a/lib/Parser/screrror.cpp b/lib/Parser/screrror.cpp index ecfdf318469..56e6b3826f1 100644 --- a/lib/Parser/screrror.cpp +++ b/lib/Parser/screrror.cpp @@ -251,7 +251,7 @@ void CompileScriptException::CopyInto(CompileScriptException* pse) } } -HRESULT CompileScriptException::ProcessError(IScanner * pScan, HRESULT hr, ParseNode * pnodeBase) +HRESULT CompileScriptException::ProcessError(IScanner * pScan, HRESULT hr, ParseNode * pnodeBase, LPCWSTR stringOne, LPCWSTR stringTwo) { // fill in the ScriptException structure Free(); @@ -267,6 +267,13 @@ HRESULT CompileScriptException::ProcessError(IScanner * pScan, HRESULT hr, Pars if (nullptr == (ei.bstrDescription = SysAllocString(szT))) ei.scode = E_OUTOFMEMORY; } + else if (wcslen(stringOne) > 0) + { + OLECHAR szT[128]; + _snwprintf_s(szT, ARRAYSIZE(szT), ARRAYSIZE(szT)-1, ei.bstrDescription, stringOne, stringTwo); + SysFreeString(ei.bstrDescription); + ei.bstrDescription = SysAllocString(szT); + } ei.bstrSource = BstrGetResourceString(IDS_COMPILATION_ERROR_SOURCE); if (nullptr == pnodeBase && nullptr != pScan) diff --git a/lib/Parser/screrror.h b/lib/Parser/screrror.h index c799e5b7b45..18e29270ed3 100644 --- a/lib/Parser/screrror.h +++ b/lib/Parser/screrror.h @@ -72,7 +72,7 @@ class CompileScriptException : public ScriptException void CopyInto(CompileScriptException* cse); - HRESULT ProcessError(IScanner * pScan, HRESULT hr, ParseNode * pnodeBase); + HRESULT ProcessError(IScanner * pScan, HRESULT hr, ParseNode * pnodeBase, LPCWSTR stringOne = _u(""), LPCWSTR stringTwo = _u("")); friend class ActiveScriptError; }; diff --git a/lib/Runtime/Library/JavascriptError.cpp b/lib/Runtime/Library/JavascriptError.cpp index 7257732cee6..df00cb8407f 100644 --- a/lib/Runtime/Library/JavascriptError.cpp +++ b/lib/Runtime/Library/JavascriptError.cpp @@ -594,10 +594,38 @@ namespace Js hrParser = SCRIPT_E_RECORDED; EXCEPINFO ei; - se->GetError(&hrParser, &ei); + bool shouldFree = false; + + if (se->line > 0) + { + ei = se->ei; + } + else + { + se->GetError(&hrParser, &ei); + shouldFree = true; + } JavascriptError* pError = MapParseError(scriptContext, ei.scode); - JavascriptError::SetMessageAndThrowError(scriptContext, pError, ei.scode, &ei); + + if (ei.bstrDescription != nullptr) + { + uint32 len = SysStringLen(ei.bstrDescription) + 1; + char16 *allocatedString = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, len); + wcscpy_s(allocatedString, len, ei.bstrDescription); + JavascriptError::SetErrorMessageProperties(pError, ei.scode, allocatedString, scriptContext); + } + else + { + JavascriptError::SetErrorMessage(pError, ei.scode, nullptr, scriptContext); + } + + if (shouldFree) + { + FreeExcepInfo(&ei); + } + + JavascriptExceptionOperators::Throw(pError, scriptContext); } ErrorTypeEnum JavascriptError::MapParseError(int32 hCode) diff --git a/test/Basics/IdsWithEscapes.baseline b/test/Basics/IdsWithEscapes.baseline index 3d952445284..1940664c233 100644 --- a/test/Basics/IdsWithEscapes.baseline +++ b/test/Basics/IdsWithEscapes.baseline @@ -3,13 +3,13 @@ 20 20 ReferenceError: 'hello2' is not defined -SyntaxError: Syntax error -SyntaxError: Syntax error -SyntaxError: Syntax error -SyntaxError: Syntax error -SyntaxError: Syntax error -SyntaxError: Syntax error -SyntaxError: Syntax error +SyntaxError: Unexpected invalid identifier 'false' after '(' +SyntaxError: Unexpected invalid identifier 'false' after '=' +SyntaxError: Unexpected invalid identifier 'true' after '=' +SyntaxError: Unexpected keyword 'var' after '=' +SyntaxError: Unexpected invalid identifier 'var' after '=' +SyntaxError: Unexpected keyword 'else' after '=' +SyntaxError: Unexpected invalid identifier 'else' after '=' SyntaxError: The use of a keyword for an identifier is invalid SyntaxError: The use of a keyword for an identifier is invalid SyntaxError: The use of a keyword for an identifier is invalid diff --git a/test/Basics/Labels.js b/test/Basics/Labels.js index 6bba0b7d061..657f944c5a9 100644 --- a/test/Basics/Labels.js +++ b/test/Basics/Labels.js @@ -436,13 +436,13 @@ var tests = [ for(let label in invalidLabels) { - assert.throws(() => eval(label + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier '" + label + "'", invalidLabels[label]); - assert.throws(() => eval(strictMode + label + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier '" + label + "'", invalidLabels[label]); + assert.throws(() => eval(label + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier '" + label + "'"); + assert.throws(() => eval(strictMode + label + testIfLabelIsValid), SyntaxError, "Expected syntax error for using invalid label identifier '" + label + "'"); } for(let invalidLabelInStrict in strictModeOnlyInvalidLabels) { - assert.throws(() => eval(strictMode + invalidLabelInStrict + testIfLabelIsValid), SyntaxError, "Expected syntax error in strict mode for future reserved keyword '" + invalidLabelInStrict + "'", strictModeOnlyInvalidLabels[invalidLabelInStrict]) + assert.throws(() => eval(strictMode + invalidLabelInStrict + testIfLabelIsValid), SyntaxError, "Expected syntax error in strict mode for future reserved keyword '" + invalidLabelInStrict + "'") assert.doesNotThrow(() => eval(invalidLabelInStrict + testIfLabelIsValid), "Expected no syntax error for future reserved keyword '" + invalidLabelInStrict + " in non-strict mode") } } diff --git a/test/es5/SemicolonAfterBlockEs5.baseline b/test/es5/SemicolonAfterBlockEs5.baseline index 02eb770e25a..46848b1fa82 100644 --- a/test/es5/SemicolonAfterBlockEs5.baseline +++ b/test/es5/SemicolonAfterBlockEs5.baseline @@ -1 +1 @@ -'if(true){};else{}' compile failure in ES5SyntaxError: Syntax error +'if(true){};else{}' compile failure in ES5SyntaxError: Unexpected keyword 'else' after ';' diff --git a/test/es6/ES6NewTarget.js b/test/es6/ES6NewTarget.js index 3527b47f686..51a7d8e0d0d 100644 --- a/test/es6/ES6NewTarget.js +++ b/test/es6/ES6NewTarget.js @@ -35,10 +35,10 @@ var tests = [ name: "Test new.target parsing path with badly-formed meta-property references", body: function() { assert.throws(function() { return new['target']; }, TypeError, "Meta-property new.target is not a real property lookup", "Object doesn't support this action"); - assert.throws(function() { return eval('new.'); }, SyntaxError, "Something like 'new.' should fall out of the meta-property parser path", "Syntax error"); - assert.throws(function() { return eval('new.target2'); }, SyntaxError, "No other keywords should produce meta-properties", "Syntax error"); - assert.throws(function() { return eval('new.something'); }, SyntaxError, "No other keywords should produce meta-properties", "Syntax error"); - assert.throws(function() { return eval('new.eval'); }, SyntaxError, "No other keywords should produce meta-properties", "Syntax error"); + assert.throws(function() { return eval('new.'); }, SyntaxError, "Something like 'new.' should fall out of the meta-property parser path", "'new.' is only valid if followed by 'target'"); + assert.throws(function() { return eval('new.target2'); }, SyntaxError, "No other keywords should produce meta-properties", "'new.' is only valid if followed by 'target'"); + assert.throws(function() { return eval('new.something'); }, SyntaxError, "No other keywords should produce meta-properties", "'new.' is only valid if followed by 'target'"); + assert.throws(function() { return eval('new.eval'); }, SyntaxError, "No other keywords should produce meta-properties", "'new.' is only valid if followed by 'target'"); } }, { diff --git a/test/es6/HTMLComments.js b/test/es6/HTMLComments.js index 1ad1eed204a..67564ab9f0c 100644 --- a/test/es6/HTMLComments.js +++ b/test/es6/HTMLComments.js @@ -56,8 +56,8 @@ WScript.Echo("Code before is reachable"); WScript.Echo("Code after */ --> is unreachable"); -assert.throws(function () { eval('/* */ --> WScript.Echo("Code after /* */ --> is parsed");'); }, SyntaxError, "MultiLineComment without a line terminator throws a syntax error", "Syntax error"); -assert.throws(function () { eval('/* */--> WScript.Echo("Code after /* */--> is parsed");'); }, SyntaxError, "MultiLineComment without a line terminator and whitespace sequence throws a syntax error", "Syntax error"); +assert.throws(function () { eval('/* */ --> WScript.Echo("Code after /* */ --> is parsed");'); }, SyntaxError, "MultiLineComment without a line terminator throws a syntax error", "Unexpected token '>' after '--'"); +assert.throws(function () { eval('/* */--> WScript.Echo("Code after /* */--> is parsed");'); }, SyntaxError, "MultiLineComment without a line terminator and whitespace sequence throws a syntax error", "Unexpected token '>' after '--'"); { let x = 0; if (x/* */--> -1) { @@ -71,4 +71,4 @@ assert.throws(function () { eval('/* */--> WScript.Echo("Code after /* */--> is var a = 1; a-->a; WScript.Echo("Code after post-decrement with a greater-than comparison (-->) is reachable"); assert.areEqual(0, a, "Post decrement executes"); -assert.throws(function () { eval('/* */ --->'); }, SyntaxError, "HTMLCloseComment causes syntax error with an extra -", "Syntax error"); +assert.throws(function () { eval('/* */ --->'); }, SyntaxError, "HTMLCloseComment causes syntax error with an extra -", "Unexpected token '>' after '-'"); diff --git a/test/es6/default.js b/test/es6/default.js index 683dbeae64c..9ded3ae4fc9 100644 --- a/test/es6/default.js +++ b/test/es6/default.js @@ -10,15 +10,15 @@ var tests = [ name: "Default argument parsing", body: function () { // Incomplete expressions - assert.throws(function () { eval("function foo(a =) { return a; }"); }, SyntaxError, "Incomplete default expression throws in a function", "Syntax error"); - assert.throws(function () { eval("var x = function(a =) { return a; }"); }, SyntaxError, "Incomplete default expression throws in a function expression", "Syntax error"); - assert.throws(function () { eval("(a =) => a"); }, SyntaxError, "Incomplete default expression throws in a lambda", "Syntax error"); - assert.throws(function () { eval("var x = { foo(a =) { return a; } }"); }, SyntaxError, "Incomplete default expression throws in an object method", "Syntax error"); - assert.throws(function () { eval("var x = class { foo(a =) { return a; } }"); }, SyntaxError, "Incomplete default expression throws in a class method", "Syntax error"); - assert.throws(function () { eval("var x = { foo: function (a =) { return a; } }"); }, SyntaxError, "Incomplete default expression throws in an object member function", "Syntax error"); - assert.throws(function () { eval("function * foo(a =) { return a; }"); }, SyntaxError, "Incomplete default expression throws in a generator function", "Syntax error"); - assert.throws(function () { eval("var x = function*(a =) { return a; }"); }, SyntaxError, "Incomplete default expression throws in a generator function", "Syntax error"); - assert.throws(function () { eval("var x = class { * foo(a =) { return a; } }"); }, SyntaxError, "Incomplete default expression throws in a class generator method", "Syntax error"); + assert.throws(function () { eval("function foo(a =) { return a; }"); }, SyntaxError, "Incomplete default expression throws in a function", "Unexpected token ')' after '='"); + assert.throws(function () { eval("var x = function(a =) { return a; }"); }, SyntaxError, "Incomplete default expression throws in a function expression", "Unexpected token ')' after '='"); + assert.throws(function () { eval("(a =) => a"); }, SyntaxError, "Incomplete default expression throws in a lambda", "Unexpected token ')' after '='"); + assert.throws(function () { eval("var x = { foo(a =) { return a; } }"); }, SyntaxError, "Incomplete default expression throws in an object method", "Unexpected token ')' after '='"); + assert.throws(function () { eval("var x = class { foo(a =) { return a; } }"); }, SyntaxError, "Incomplete default expression throws in a class method", "Unexpected token ')' after '='"); + assert.throws(function () { eval("var x = { foo: function (a =) { return a; } }"); }, SyntaxError, "Incomplete default expression throws in an object member function", "Unexpected token ')' after '='"); + assert.throws(function () { eval("function * foo(a =) { return a; }"); }, SyntaxError, "Incomplete default expression throws in a generator function", "Unexpected token ')' after '='"); + assert.throws(function () { eval("var x = function*(a =) { return a; }"); }, SyntaxError, "Incomplete default expression throws in a generator function", "Unexpected token ')' after '='"); + assert.throws(function () { eval("var x = class { * foo(a =) { return a; } }"); }, SyntaxError, "Incomplete default expression throws in a class generator method", "Unexpected token ')' after '='"); // Duplicate parameters assert.throws(function () { eval("function f(a, b, a, c = 10) { }"); }, SyntaxError, "Duplicate parameters are not allowed before the default argument", "Duplicate formal parameter names not allowed in this context"); diff --git a/test/es6/generators-syntax.js b/test/es6/generators-syntax.js index c72564d9414..e964d293732 100644 --- a/test/es6/generators-syntax.js +++ b/test/es6/generators-syntax.js @@ -71,7 +71,7 @@ var tests = [ assert.throws(function () { eval("function* gf() { +yield 2; }"); }, SyntaxError, "yield with operand cannot appear in UnaryExpression -- e.g. operand of unary +", "Syntax error"); assert.throws(function () { eval("function* gf() { +yield* 'foo'; }"); }, SyntaxError, "yield* with operand cannot appear in UnaryExpression -- e.g. operand of unary +", "Syntax error"); - assert.throws(function () { eval("function* gf() { yield++; }"); }, SyntaxError, "yield cannot appear in PostfixExpression -- e.g. operand of postfix ++", "Syntax error"); + assert.throws(function () { eval("function* gf() { yield++; }"); }, SyntaxError, "yield cannot appear in PostfixExpression -- e.g. operand of postfix ++", "Unexpected token ';' after '++'"); } }, { @@ -193,7 +193,7 @@ var tests = [ { name: "yield is a keyword and disallowed within arrow function parameter syntax", body: function () { - assert.throws(function () { eval("function* gf() { var a = yield => { }; }"); }, SyntaxError, "yield cannot appear as the formal name of an unparenthesized arrow function parameter list", "Syntax error"); + assert.throws(function () { eval("function* gf() { var a = yield => { }; }"); }, SyntaxError, "yield cannot appear as the formal name of an unparenthesized arrow function parameter list", "Unexpected token '=>' after 'yield'"); assert.throws(function () { eval("function* gf() { var a = (yield) => { }; }"); }, SyntaxError, "yield cannot appear as a formal name within parenthesized arrow function parameter list (single formal)", "The use of a keyword for an identifier is invalid"); assert.throws(function () { eval("function* gf() { var a = (x, y, yield) => { }; }"); }, SyntaxError, "yield cannot appear as a formal name within parenthesized arrow function parameter list (middle formal)", "The use of a keyword for an identifier is invalid"); assert.throws(function () { eval("function* gf() { var a = (x, yield, y) => { }; }"); }, SyntaxError, "yield cannot appear as a formal name within parenthesized arrow function parameter list (last formal)", "The use of a keyword for an identifier is invalid"); diff --git a/test/es6/lambda1.js b/test/es6/lambda1.js index 2c6230325d1..959f2fd1c31 100644 --- a/test/es6/lambda1.js +++ b/test/es6/lambda1.js @@ -170,7 +170,7 @@ var tests = [ name: "Interesting valid and invalid syntax", body: function () { assert.doesNotThrow(() => { eval('(x, ) => {};'); }, SyntaxError, "Trailing comma is valid syntax"); - assert.throws(() => { eval('(var x) => {};'); }, SyntaxError, "var not used in formals declaration", "Syntax error"); + assert.throws(() => { eval('(var x) => {};'); }, SyntaxError, "var not used in formals declaration", "Unexpected keyword 'var' after '('"); assert.throws(() => { eval('a.x => {};'); }, SyntaxError, "valid expression syntax that is invalid parameter list syntax on lhs of =>", "Syntax error"); assert.throws(() => { eval('(x, y)[7] => {};'); }, SyntaxError, "valid expression syntax that is invalid parameter list syntax on lhs of =>", "Expected '=>'"); assert.throws(() => { eval('x() => {};'); }, SyntaxError, "valid expression syntax that is invalid parameter list syntax on lhs of =>", "Syntax error"); @@ -444,19 +444,19 @@ var tests = [ { name: "New line characters are not allowed between arrow parameters and =>", body: function () { - assert.throws(function () { eval('x \n => d;'); }, SyntaxError, "Arrow with simple expression body and simple parameter", "Syntax error"); - assert.throws(function () { eval('var a = x \n => d;'); }, SyntaxError, "Arrow with simple expression body and simple parameter assigned to a var", "Syntax error"); - assert.throws(function () { eval('(x) \n => d;'); }, SyntaxError, "Arrow with simple expression body and single parameter", "Syntax error"); - assert.throws(function () { eval('var a = (x) \n => d;'); }, SyntaxError, "Arrow with simple expression body and single parameter assigned to a var", "Syntax error"); - assert.throws(function () { eval('() \n => d;'); }, SyntaxError, "Arrow with simple expression body and empty parameter list", "Syntax error"); - assert.throws(function () { eval('var a = () \n => d;'); }, SyntaxError, "Arrow with simple expression body and empty parameter list assigned to a var", "Syntax error"); - - assert.throws(function () { eval('x \n => { return d };'); }, SyntaxError, "Arrow with block body and simple parameter", "Syntax error"); - assert.throws(function () { eval('var a = x \n => { return d };'); }, SyntaxError, "Arrow with block body and simple parameter assigned to a var", "Syntax error"); - assert.throws(function () { eval('(x) \n => { return d };'); }, SyntaxError, "Arrow with block body and single parameter", "Syntax error"); - assert.throws(function () { eval('var a = (x) \n => { return d };'); }, SyntaxError, "Arrow with block body and single parameter assigned to a var", "Syntax error"); - assert.throws(function () { eval('() \n => { return d };'); }, SyntaxError, "Arrow with block body and empty parameter list", "Syntax error"); - assert.throws(function () { eval('var a = () \n => { return d };'); }, SyntaxError, "Arrow with block body and empty parameter list assigned to a var", "Syntax error"); + assert.throws(function () { eval('x \n => d;'); }, SyntaxError, "Arrow with simple expression body and simple parameter", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('var a = x \n => d;'); }, SyntaxError, "Arrow with simple expression body and simple parameter assigned to a var", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('(x) \n => d;'); }, SyntaxError, "Arrow with simple expression body and single parameter", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('var a = (x) \n => d;'); }, SyntaxError, "Arrow with simple expression body and single parameter assigned to a var", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('() \n => d;'); }, SyntaxError, "Arrow with simple expression body and empty parameter list", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('var a = () \n => d;'); }, SyntaxError, "Arrow with simple expression body and empty parameter list assigned to a var", "Lambda parameter list is only valid if followed by '=>' on the same line"); + + assert.throws(function () { eval('x \n => { return d };'); }, SyntaxError, "Arrow with block body and simple parameter", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('var a = x \n => { return d };'); }, SyntaxError, "Arrow with block body and simple parameter assigned to a var", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('(x) \n => { return d };'); }, SyntaxError, "Arrow with block body and single parameter", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('var a = (x) \n => { return d };'); }, SyntaxError, "Arrow with block body and single parameter assigned to a var", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('() \n => { return d };'); }, SyntaxError, "Arrow with block body and empty parameter list", "Lambda parameter list is only valid if followed by '=>' on the same line"); + assert.throws(function () { eval('var a = () \n => { return d };'); }, SyntaxError, "Arrow with block body and empty parameter list assigned to a var", "Lambda parameter list is only valid if followed by '=>' on the same line"); assert.throws(function () { eval('var a = {}; a.x \n => d;'); }, SyntaxError, "Verify that badly formed arrow functions return correct error even if a newline is before the => token", "Syntax error"); assert.throws(function () { eval('var a = {}; a\n.x => d;'); }, SyntaxError, "Verify that badly formed arrow functions return correct error even if a newline is before the => token", "Syntax error"); diff --git a/test/es7/asyncawait-syntax.js b/test/es7/asyncawait-syntax.js index 89c2350742c..5966bd2cdd8 100644 --- a/test/es7/asyncawait-syntax.js +++ b/test/es7/asyncawait-syntax.js @@ -42,7 +42,7 @@ var tests = [ assert.throws(function () { eval("async function method() { var await = 1; }"); }, SyntaxError, "'await' cannot be used as an identifier in an async function.", "The use of a keyword for an identifier is invalid"); assert.throws(function () { eval("async function method(await;) { }"); }, SyntaxError, "'await' cannot be used as an identifier in an async function.", "The use of a keyword for an identifier is invalid"); - assert.throws(function () { eval("async function method() { var x = await; }"); }, SyntaxError, "'await' cannot be used as an identifier in an async function.", "Syntax error"); + assert.throws(function () { eval("async function method() { var x = await; }"); }, SyntaxError, "'await' cannot be used as an identifier in an async function.", "Unexpected token ';' after 'await'"); } }, { @@ -198,10 +198,10 @@ var tests = [ { name: "await is a keyword and disallowed within arrow function parameter syntax", body: function () { - assert.throws(function () { eval("async function af() { var a = await => { }; }"); }, SyntaxError, "await cannot appear as the formal name of an unparenthesized arrow function parameter list", "Syntax error"); - assert.throws(function () { eval("async function af() { var a = (await) => { }; }"); }, SyntaxError, "await cannot appear as a formal name within parenthesized arrow function parameter list (single formal)", "Syntax error"); - assert.throws(function () { eval("async function af() { var a = (x, y, await) => { }; }"); }, SyntaxError, "await cannot appear as a formal name within parenthesized arrow function parameter list (middle formal)", "Syntax error"); - assert.throws(function () { eval("async function af() { var a = (x, await, y) => { }; }"); }, SyntaxError, "await cannot appear as a formal name within parenthesized arrow function parameter list (last formal)", "Syntax error"); + assert.throws(function () { eval("async function af() { var a = await => { }; }"); }, SyntaxError, "await cannot appear as the formal name of an unparenthesized arrow function parameter list", "Unexpected token '=>' after 'await'"); + assert.throws(function () { eval("async function af() { var a = (await) => { }; }"); }, SyntaxError, "await cannot appear as a formal name within parenthesized arrow function parameter list (single formal)", "Unexpected token ')' after 'await'"); + assert.throws(function () { eval("async function af() { var a = (x, y, await) => { }; }"); }, SyntaxError, "await cannot appear as a formal name within parenthesized arrow function parameter list (middle formal)", "Unexpected token ')' after 'await'"); + assert.throws(function () { eval("async function af() { var a = (x, await, y) => { }; }"); }, SyntaxError, "await cannot appear as a formal name within parenthesized arrow function parameter list (last formal)", "Unexpected token ',' after 'await'"); assert.throws(function () { eval("async function af() { var a = (x = await 0) => { }; }"); }, SyntaxError, "await expression is disallowed within arrow function default parameter expression (single formal)", "'await' expression not allowed in this context"); assert.throws(function () { eval("async function af() { var a = (x, y = await 0, z = 0) => { }; }"); }, SyntaxError, "await expression is disallowed within arrow function default parameter expression (middle formal)", "'await' expression not allowed in this context"); @@ -211,10 +211,10 @@ var tests = [ assert.throws(function () { eval("async await => { }"); }, SyntaxError, "await cannot appear as the formal name of a unparathensized async arrow function", "The use of a keyword for an identifier is invalid"); assert.throws(function () { eval("function () { a = async await => { } }"); }, SyntaxError, "await cannot appear as the formal name of a unparathensized async arrow function expression", "Expected identifier"); assert.throws(function () { eval("async (a, b = await 1) => {}"); }, SyntaxError, "await expression cannot appear in the formals of an async arrow function", "Expected ')'"); - assert.throws(function () { eval("async () => { await => { }; }"); }, SyntaxError, "await cannot appear as the formal name of an unparathensized arrow function within an async arrow function", "Syntax error"); - assert.throws(function () { eval("async () => { (a, await) => { }; }"); }, SyntaxError, "await cannot appear as the formal name of a parathensized arrow function within an async arrow function", "Syntax error"); + assert.throws(function () { eval("async () => { await => { }; }"); }, SyntaxError, "await cannot appear as the formal name of an unparathensized arrow function within an async arrow function", "Unexpected token '=>' after 'await'"); + assert.throws(function () { eval("async () => { (a, await) => { }; }"); }, SyntaxError, "await cannot appear as the formal name of a parathensized arrow function within an async arrow function", "Unexpected token ')' after 'await'"); assert.throws(function () { eval("async () => { (x, y, z = await 0) => { }; }"); }, SyntaxError, "await expression is disallowed within default parameter expression of an arrow function which is inside an async arrow function", "'await' expression not allowed in this context"); - 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", "Syntax error"); + 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'"); } }, ];