Browse Source

Improve parsing error messages
- add support for use of %s
- replace some frequntely enocuntered generic messages

rhuanjl 7 năm trước cách đây
mục cha
commit
1f18f53acf

+ 190 - 11
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<buildAST>();
@@ -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<true>(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);
     }

+ 2 - 1
lib/Parser/Parse.h

@@ -313,7 +313,8 @@ protected:
     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);

+ 1 - 0
lib/Parser/Scan.h

@@ -688,6 +688,7 @@ public:
         }
     };
 
+    tokens GetPrevious() { return m_tkPrevious; }
     void Capture(_Out_ RestorePoint* restorePoint);
     void SeekTo(const RestorePoint& restorePoint);
     void SeekToForcingPid(const RestorePoint& restorePoint);

+ 9 - 1
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);
+}

+ 6 - 1
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;
 };

+ 9 - 0
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")

+ 8 - 1
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)

+ 1 - 1
lib/Parser/screrror.h

@@ -72,7 +72,7 @@ public:
 
     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;
 };

+ 30 - 2
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)

+ 7 - 7
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

+ 3 - 3
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")
             }
         }

+ 1 - 1
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 ';'

+ 4 - 4
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'");
         }
     },
     {

+ 3 - 3
test/es6/HTMLComments.js

@@ -56,8 +56,8 @@ WScript.Echo("Code before <!-- LineTerminator --> is reachable"); <!-- WScript.E
 /* Multi
    Line
    Comment */ --> 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 '-'");

+ 9 - 9
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");

+ 2 - 2
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");

+ 14 - 14
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");

+ 8 - 8
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'");
         }
     },
 ];