Bug 1212719 - Throw SyntaxError immediately for unexpected TOK_TRIPLEDOT. r=Waldo
authorTooru Fujisawa <arai_a@mac.com>
Thu, 08 Oct 2015 17:31:23 +0900
changeset 304546 d90b11b242df8dc9295341c56000b05c1dee6013
parent 304545 b227e94179e245747b321ec4bc106960afd731b4
child 304547 00dac1d05d6097e885f1115ec74a64a9f92d044d
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1212719
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1212719 - Throw SyntaxError immediately for unexpected TOK_TRIPLEDOT. r=Waldo
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jit-test/tests/parser/arrow-rest.js
js/src/jit-test/tests/parser/modifier-arrow-rest.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1225,17 +1225,17 @@ Parser<ParseHandler>::functionBody(InHan
     Node pn;
     if (type == StatementListBody) {
         pn = statements(yieldHandling);
         if (!pn)
             return null();
     } else {
         MOZ_ASSERT(type == ExpressionBody);
 
-        Node kid = assignExpr(inHandling, yieldHandling);
+        Node kid = assignExpr(inHandling, yieldHandling, TripledotProhibited);
         if (!kid)
             return null();
 
         pn = handler.newReturnStatement(kid, null(), handler.getPosition(kid));
         if (!pn)
             return null();
     }
 
@@ -2425,17 +2425,17 @@ Parser<SyntaxParseHandler>::checkFunctio
     return true;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::addExprAndGetNextTemplStrToken(YieldHandling yieldHandling, Node nodeList,
                                                      TokenKind* ttp)
 {
-    Node pn = expr(InAllowed, yieldHandling);
+    Node pn = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!pn)
         return false;
     handler.addList(nodeList, pn);
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return false;
     if (tt != TOK_RC) {
@@ -3973,17 +3973,17 @@ Parser<SyntaxParseHandler>::checkDestruc
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::destructuringExpr(YieldHandling yieldHandling, BindData<ParseHandler>* data,
                                         TokenKind tt)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     pc->inDeclDestructuring = true;
-    Node pn = primaryExpr(yieldHandling, tt);
+    Node pn = primaryExpr(yieldHandling, TripledotProhibited, tt);
     pc->inDeclDestructuring = false;
     if (!pn)
         return null();
     if (!checkDestructuringPattern(data, pn))
         return null();
     return pn;
 }
 
@@ -4224,17 +4224,17 @@ Parser<ParseHandler>::variables(YieldHan
             TokenKind tt;
             if (!tokenStream.getToken(&tt))
                 return null();
             if (tt == TOK_LB || tt == TOK_LC) {
                 if (psimple)
                     *psimple = false;
 
                 pc->inDeclDestructuring = true;
-                pn2 = primaryExpr(yieldHandling, tt);
+                pn2 = primaryExpr(yieldHandling, TripledotProhibited, tt);
                 pc->inDeclDestructuring = false;
                 if (!pn2)
                     return null();
 
                 bool parsingForInOrOfInit = false;
                 if (location == InForInit) {
                     bool isForIn, isForOf;
                     if (!matchInOrOf(&isForIn, &isForOf))
@@ -4253,17 +4253,17 @@ Parser<ParseHandler>::variables(YieldHan
                     tokenStream.ungetToken();
                     handler.addList(pn, pn2);
                     break;
                 }
 
                 MUST_MATCH_TOKEN(TOK_ASSIGN, JSMSG_BAD_DESTRUCT_DECL);
 
                 Node init = assignExpr(location == InForInit ? InProhibited : InAllowed,
-                                       yieldHandling);
+                                       yieldHandling, TripledotProhibited);
                 if (!init)
                     return null();
 
                 // Ban the nonsensical |for (var V = E1 in E2);| where V is a
                 // destructuring pattern.  See bug 1164741 for background.
                 if (location == InForInit && kind == PNK_VAR) {
                     TokenKind afterInit;
                     if (!tokenStream.peekToken(&afterInit))
@@ -4339,17 +4339,17 @@ Parser<ParseHandler>::variables(YieldHan
                 // If we are not parsing a let declaration, bind the name
                 // now. Otherwise we must wait until after parsing the initializing
                 // assignment.
                 bool bindBeforeInitializer = kind != PNK_LET && kind != PNK_CONST;
                 if (bindBeforeInitializer && !data.bind(name, this))
                     return null();
 
                 Node init = assignExpr(location == InForInit ? InProhibited : InAllowed,
-                                       yieldHandling);
+                                       yieldHandling, TripledotProhibited);
                 if (!init)
                     return null();
 
                 // Ignore an initializer if we have a for-in loop declaring a
                 // |var| with an initializer: |for (var v = ... in ...);|.
                 // Warn that this syntax is invalid so that developers looking
                 // in the console know to fix this.  ES<6 permitted the
                 // initializer while ES6 doesn't; ignoring it seems the best
@@ -5050,17 +5050,17 @@ Parser<FullParseHandler>::exportDeclarat
                 return null();
             break;
           default:
             tokenStream.ungetToken();
             RootedPropertyName name(context, context->names().starDefaultStar);
             binding = makeInitializedLexicalBinding(name, true, pos());
             if (!binding)
                 return null();
-            kid = assignExpr(InAllowed, YieldIsKeyword);
+            kid = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
             if (!kid)
                 return null();
             if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
                 return null();
             break;
         }
 
         return handler.newExportDefaultDeclaration(kid, binding, TokenPos(begin, pos().end));
@@ -5089,17 +5089,17 @@ Parser<SyntaxParseHandler>::exportDeclar
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expressionStatement(YieldHandling yieldHandling, InvokedPrediction invoked)
 {
     tokenStream.ungetToken();
-    Node pnexpr = expr(InAllowed, yieldHandling, invoked);
+    Node pnexpr = expr(InAllowed, yieldHandling, TripledotProhibited, invoked);
     if (!pnexpr)
         return null();
     if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <typename ParseHandler>
@@ -5333,17 +5333,17 @@ Parser<FullParseHandler>::forStatement(Y
                 blockObj->initEnclosingScopeFromParser(pc->innermostStaticScope());
                 pn1 = variables(yieldHandling, constDecl ? PNK_CONST : PNK_LET, InForInit,
                                 nullptr, blockObj, DontHoistVars);
             } else {
                 // Pass |InProhibited| when parsing an expression so that |in|
                 // isn't parsed in a RelationalExpression as a binary operator.
                 // In this context, |in| is part of a for-in loop -- *not* part
                 // of a binary expression.
-                pn1 = expr(InProhibited, yieldHandling);
+                pn1 = expr(InProhibited, yieldHandling, TripledotProhibited);
             }
             if (!pn1)
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     MOZ_ASSERT_IF(isForDecl, pn1->isArity(PN_LIST));
@@ -5466,18 +5466,18 @@ Parser<FullParseHandler>::forStatement(Y
             pn2 = pn1;
             pn1 = nullptr;
 
             if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment))
                 return null();
         }
 
         pn3 = (headKind == PNK_FOROF)
-              ? assignExpr(InAllowed, yieldHandling)
-              : expr(InAllowed, yieldHandling);
+              ? assignExpr(InAllowed, yieldHandling, TripledotProhibited)
+              : expr(InAllowed, yieldHandling, TripledotProhibited);
         if (!pn3)
             return null();
         modifier = TokenStream::None;
 
         if (blockObj) {
             /*
              * Now that the pn3 has been parsed, push the let scope. To hold
              * the blockObj for the emitter, wrap the PNK_LEXICALSCOPE node
@@ -5566,31 +5566,31 @@ Parser<FullParseHandler>::forStatement(Y
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_INIT);
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_SEMI) {
             pn2 = nullptr;
             modifier = TokenStream::Operand;
         } else {
-            pn2 = expr(InAllowed, yieldHandling);
+            pn2 = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!pn2)
                 return null();
             modifier = TokenStream::None;
         }
 
         /* Parse the update expression or null into pn3. */
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_COND);
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_RP) {
             pn3 = nullptr;
             modifier = TokenStream::Operand;
         } else {
-            pn3 = expr(InAllowed, yieldHandling);
+            pn3 = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!pn3)
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     MUST_MATCH_TOKEN_MOD(TOK_RP, modifier, JSMSG_PAREN_AFTER_FOR_CTRL);
 
@@ -5666,17 +5666,17 @@ Parser<SyntaxParseHandler>::forStatement
                 tokenStream.consumeKnownToken(tt, TokenStream::Operand);
                 lhsNode = variables(yieldHandling, PNK_VAR, InForInit, &simpleForDecl);
             }
             else if (tt == TOK_CONST || tt == TOK_LET) {
                 JS_ALWAYS_FALSE(abortIfSyntaxParser());
                 return null();
             }
             else {
-                lhsNode = expr(InProhibited, yieldHandling);
+                lhsNode = expr(InProhibited, yieldHandling, TripledotProhibited);
             }
             if (!lhsNode)
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     // If there's an |in| keyword here, it's a for-in loop, by dint of careful
@@ -5702,39 +5702,43 @@ Parser<SyntaxParseHandler>::forStatement
         if (!simpleForDecl) {
             JS_ALWAYS_FALSE(abortIfSyntaxParser());
             return null();
         }
 
         if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment))
             return null();
 
-        if (!(isForIn ? expr(InAllowed, yieldHandling) : assignExpr(InAllowed, yieldHandling)))
-            return null();
+        if (!(isForIn
+              ? expr(InAllowed, yieldHandling, TripledotProhibited)
+              : assignExpr(InAllowed, yieldHandling, TripledotProhibited)))
+        {
+            return null();
+        }
         modifier = TokenStream::None;
     } else {
         /* Parse the loop condition or null. */
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_INIT);
         TokenKind tt;
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         modifier = TokenStream::Operand;
         if (tt != TOK_SEMI) {
-            if (!expr(InAllowed, yieldHandling))
+            if (!expr(InAllowed, yieldHandling, TripledotProhibited))
                 return null();
             modifier = TokenStream::None;
         }
 
         /* Parse the update expression or null. */
         MUST_MATCH_TOKEN_MOD(TOK_SEMI, modifier, JSMSG_SEMI_AFTER_FOR_COND);
         if (!tokenStream.peekToken(&tt, TokenStream::Operand))
             return null();
         modifier = TokenStream::Operand;
         if (tt != TOK_RP) {
-            if (!expr(InAllowed, yieldHandling))
+            if (!expr(InAllowed, yieldHandling, TripledotProhibited))
                 return null();
             modifier = TokenStream::None;
         }
     }
 
     MUST_MATCH_TOKEN_MOD(TOK_RP, modifier, JSMSG_PAREN_AFTER_FOR_CTRL);
 
     /* Parse the loop body. */
@@ -5787,17 +5791,17 @@ Parser<ParseHandler>::switchStatement(Yi
                 report(ParseError, false, null(), JSMSG_TOO_MANY_DEFAULTS);
                 return null();
             }
             seenDefault = true;
             caseExpr = null();  // The default case has pn_left == nullptr.
             break;
 
           case TOK_CASE:
-            caseExpr = expr(InAllowed, yieldHandling);
+            caseExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!caseExpr)
                 return null();
             break;
 
           default:
             report(ParseError, false, null(), JSMSG_BAD_SWITCH);
             return null();
         }
@@ -5980,17 +5984,17 @@ Parser<ParseHandler>::returnStatement(Yi
       case TOK_EOL:
       case TOK_EOF:
       case TOK_SEMI:
       case TOK_RC:
         exprNode = null();
         pc->funHasReturnVoid = true;
         break;
       default: {
-        exprNode = expr(InAllowed, yieldHandling);
+        exprNode = expr(InAllowed, yieldHandling, TripledotProhibited);
         if (!exprNode)
             return null();
         pc->funHasReturnExpr = true;
       }
     }
 
     if (exprNode) {
         if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
@@ -6078,17 +6082,17 @@ Parser<ParseHandler>::yieldExpression(In
             exprNode = null();
             tokenStream.addModifierException(TokenStream::NoneIsOperand);
             break;
           case TOK_MUL:
             kind = PNK_YIELD_STAR;
             tokenStream.consumeKnownToken(TOK_MUL, TokenStream::Operand);
             // Fall through.
           default:
-            exprNode = assignExpr(inHandling, YieldIsKeyword);
+            exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
             if (!exprNode)
                 return null();
         }
         return newYieldExpression(begin, exprNode, kind == PNK_YIELD_STAR);
       }
 
       case NotGenerator:
         // We are in code that has not seen a yield, but we are in JS 1.7 or
@@ -6137,17 +6141,17 @@ Parser<ParseHandler>::yieldExpression(In
           case TOK_RP:
           case TOK_COLON:
           case TOK_COMMA:
             // No value.
             exprNode = null();
             tokenStream.addModifierException(TokenStream::NoneIsOperand);
             break;
           default:
-            exprNode = assignExpr(inHandling, YieldIsKeyword);
+            exprNode = assignExpr(inHandling, YieldIsKeyword, TripledotProhibited);
             if (!exprNode)
                 return null();
         }
 
         return newYieldExpression(begin, exprNode);
       }
     }
 
@@ -6263,17 +6267,17 @@ Parser<ParseHandler>::throwStatement(Yie
         report(ParseError, false, null(), JSMSG_MISSING_EXPR_AFTER_THROW);
         return null();
     }
     if (tt == TOK_EOL) {
         report(ParseError, false, null(), JSMSG_LINE_BREAK_AFTER_THROW);
         return null();
     }
 
-    Node throwExpr = expr(InAllowed, yieldHandling);
+    Node throwExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
     if (!throwExpr)
         return null();
 
     if (!MatchOrInsertSemicolonAfterExpression(tokenStream))
         return null();
 
     return handler.newThrowStatement(throwExpr, TokenPos(begin, pos().end));
 }
@@ -6408,17 +6412,17 @@ Parser<ParseHandler>::tryStatement(Yield
              * We use 'catch (x if x === 5)' (not 'catch (x : x === 5)')
              * to avoid conflicting with the JS2/ECMAv4 type annotation
              * catchguard syntax.
              */
             bool matched;
             if (!tokenStream.matchToken(&matched, TOK_IF))
                 return null();
             if (matched) {
-                catchGuard = expr(InAllowed, yieldHandling);
+                catchGuard = expr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!catchGuard)
                     return null();
             }
 #endif
             MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_CATCH);
 
             MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CATCH);
             Node catchBody = statements(yieldHandling);
@@ -6591,17 +6595,17 @@ Parser<FullParseHandler>::classDefinitio
 
     ParseNode* classHeritage = null();
     bool hasHeritage;
     if (!tokenStream.matchToken(&hasHeritage, TOK_EXTENDS))
         return null();
     if (hasHeritage) {
         if (!tokenStream.getToken(&tt))
             return null();
-        classHeritage = memberExpr(yieldHandling, tt, true);
+        classHeritage = memberExpr(yieldHandling, TripledotProhibited, tt, true);
         if (!classHeritage)
             return null();
     }
 
     MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_CLASS);
 
     ParseNode* classMethods = handler.newClassMethodList(pos().begin);
 
@@ -6914,19 +6918,19 @@ Parser<ParseHandler>::statement(YieldHan
 
       // NOTE: default case handled in the ExpressionStatement section.
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::expr(InHandling inHandling, YieldHandling yieldHandling,
-                           InvokedPrediction invoked)
-{
-    Node pn = assignExpr(inHandling, yieldHandling, invoked);
+                           TripledotHandling tripledotHandling, InvokedPrediction invoked)
+{
+    Node pn = assignExpr(inHandling, yieldHandling, tripledotHandling, invoked);
     if (!pn)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA))
         return null();
     if (!matched)
         return pn;
@@ -6935,17 +6939,17 @@ Parser<ParseHandler>::expr(InHandling in
     if (!seq)
         return null();
     while (true) {
         if (handler.isUnparenthesizedYieldExpression(pn)) {
             report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
             return null();
         }
 
-        pn = assignExpr(inHandling, yieldHandling);
+        pn = assignExpr(inHandling, yieldHandling, tripledotHandling);
         if (!pn)
             return null();
         handler.addList(seq, pn);
 
         if (!tokenStream.matchToken(&matched, TOK_COMMA))
             return null();
         if (!matched)
             break;
@@ -7035,30 +7039,30 @@ Precedence(ParseNodeKind pnk) {
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler>::orExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                              InvokedPrediction invoked)
+                              TripledotHandling tripledotHandling, InvokedPrediction invoked)
 {
     // Shift-reduce parser for the binary operator part of the JS expression
     // syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
     int depth = 0;
 
     Node pn;
     for (;;) {
-        pn = unaryExpr(yieldHandling, invoked);
+        pn = unaryExpr(yieldHandling, tripledotHandling, invoked);
         if (!pn)
             return pn;
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
         TokenKind tok;
         if (!tokenStream.getToken(&tok))
             return null();
@@ -7098,29 +7102,29 @@ Parser<ParseHandler>::orExpr1(InHandling
 
     MOZ_ASSERT(depth == 0);
     return pn;
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
 Parser<ParseHandler>::condExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                                InvokedPrediction invoked)
-{
-    Node condition = orExpr1(inHandling, yieldHandling, invoked);
+                                TripledotHandling tripledotHandling, InvokedPrediction invoked)
+{
+    Node condition = orExpr1(inHandling, yieldHandling, tripledotHandling, invoked);
     if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
         return condition;
 
-    Node thenExpr = assignExpr(InAllowed, yieldHandling);
+    Node thenExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (!thenExpr)
         return null();
 
     MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND);
 
-    Node elseExpr = assignExpr(inHandling, yieldHandling);
+    Node elseExpr = assignExpr(inHandling, yieldHandling, TripledotProhibited);
     if (!elseExpr)
         return null();
 
     // Advance to the next token; the caller is responsible for interpreting it.
     TokenKind ignored;
     if (!tokenStream.getToken(&ignored))
         return null();
     return handler.newConditional(condition, thenExpr, elseExpr);
@@ -7163,17 +7167,17 @@ Parser<ParseHandler>::checkAndMarkAsAssi
 
     MOZ_ASSERT(handler.isFunctionCall(target));
     return makeSetCall(target, JSMSG_BAD_LEFTSIDE_OF_ASS);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExpr(InHandling inHandling, YieldHandling yieldHandling,
-                                 InvokedPrediction invoked)
+                                 TripledotHandling tripledotHandling, InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
     // (In Parsemark this happens 81.4% of the time;  in code with large
@@ -7215,17 +7219,17 @@ Parser<ParseHandler>::assignExpr(InHandl
 
     tokenStream.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
-    Node lhs = condExpr1(inHandling, yieldHandling, invoked);
+    Node lhs = condExpr1(inHandling, yieldHandling, tripledotHandling, invoked);
     if (!lhs)
         return null();
 
     ParseNodeKind kind;
     JSOp op;
     switch (tokenStream.currentToken().type) {
       case TOK_ASSIGN:       kind = PNK_ASSIGN;       op = JSOP_NOP;    break;
       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    op = JSOP_ADD;    break;
@@ -7314,17 +7318,17 @@ Parser<ParseHandler>::assignExpr(InHandl
     }
 
     AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
     if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
         return null();
 
     bool saved = pc->inDeclDestructuring;
     pc->inDeclDestructuring = false;
-    Node rhs = assignExpr(inHandling, yieldHandling);
+    Node rhs = assignExpr(inHandling, yieldHandling, TripledotProhibited);
     pc->inDeclDestructuring = saved;
     if (!rhs)
         return null();
 
     return handler.newAssignment(kind, lhs, rhs, pc, op);
 }
 
 template <typename ParseHandler>
@@ -7447,25 +7451,26 @@ Parser<ParseHandler>::checkAndMarkAsIncO
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::unaryOpExpr(YieldHandling yieldHandling, ParseNodeKind kind, JSOp op,
                                   uint32_t begin)
 {
-    Node kid = unaryExpr(yieldHandling);
+    Node kid = unaryExpr(yieldHandling, TripledotProhibited);
     if (!kid)
         return null();
     return handler.newUnary(kind, op, begin, kid);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, InvokedPrediction invoked)
+Parser<ParseHandler>::unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                                InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     uint32_t begin = pos().begin;
     switch (tt) {
@@ -7487,59 +7492,60 @@ Parser<ParseHandler>::unaryExpr(YieldHan
         //
         //   // Looks up the name, doesn't find it and so evaluates to
         //   // "undefined".
         //   assertEq(typeof nonExistentName, "undefined");
         //
         //   // Evaluates expression, triggering a runtime ReferenceError for
         //   // the undefined name.
         //   typeof (1, nonExistentName);
-        Node kid = unaryExpr(yieldHandling);
+        Node kid = unaryExpr(yieldHandling, TripledotProhibited);
         if (!kid)
             return null();
 
         return handler.newTypeof(begin, kid);
       }
 
       case TOK_INC:
       case TOK_DEC:
       {
         TokenKind tt2;
         if (!tokenStream.getToken(&tt2, TokenStream::Operand))
             return null();
-        Node pn2 = memberExpr(yieldHandling, tt2, true);
+        Node pn2 = memberExpr(yieldHandling, TripledotProhibited, tt2, true);
         if (!pn2)
             return null();
         AssignmentFlavor flavor = (tt == TOK_INC) ? IncrementAssignment : DecrementAssignment;
         if (!checkAndMarkAsIncOperand(pn2, flavor))
             return null();
         return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
                                 JSOP_NOP,
                                 begin,
                                 pn2);
       }
 
       case TOK_DELETE: {
-        Node expr = unaryExpr(yieldHandling);
+        Node expr = unaryExpr(yieldHandling, TripledotProhibited);
         if (!expr)
             return null();
 
         // Per spec, deleting any unary expression is valid -- it simply
         // returns true -- except for one case that is illegal in strict mode.
         if (handler.maybeNameAnyParentheses(expr)) {
             if (!report(ParseStrictError, pc->sc->strict(), expr, JSMSG_DEPRECATED_DELETE_OPERAND))
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
       default: {
-        Node pn = memberExpr(yieldHandling, tt, /* allowCallSyntax = */ true, invoked);
+        Node pn = memberExpr(yieldHandling, tripledotHandling, tt, /* allowCallSyntax = */ true,
+                             invoked);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt))
             return null();
         if (tt == TOK_INC || tt == TOK_DEC) {
             tokenStream.consumeKnownToken(tt);
@@ -7887,17 +7893,17 @@ Parser<FullParseHandler>::legacyComprehe
 
         RootedPropertyName name(context);
         if (!tokenStream.getToken(&tt))
             return null();
         switch (tt) {
           case TOK_LB:
           case TOK_LC:
             pc->inDeclDestructuring = true;
-            pn3 = primaryExpr(YieldIsKeyword, tt);
+            pn3 = primaryExpr(YieldIsKeyword, TripledotProhibited, tt);
             pc->inDeclDestructuring = false;
             if (!pn3)
                 return null();
             break;
 
           case TOK_NAME:
             name = tokenStream.currentName();
 
@@ -7931,17 +7937,17 @@ Parser<FullParseHandler>::legacyComprehe
                 MOZ_ASSERT(pn2->pn_iflags == (JSITER_FOREACH | JSITER_ENUMERATE));
                 report(ParseError, false, null(), JSMSG_BAD_FOR_EACH_LOOP);
                 return null();
             }
             pn2->pn_iflags = 0;
             headKind = PNK_FOROF;
         }
 
-        ParseNode* pn4 = expr(InAllowed, YieldIsKeyword);
+        ParseNode* pn4 = expr(InAllowed, YieldIsKeyword, TripledotProhibited);
         if (!pn4)
             return null();
         MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_CTRL);
 
         if (isGenexp && pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
@@ -8265,17 +8271,17 @@ Parser<ParseHandler>::comprehensionFor(G
     bool matched;
     if (!tokenStream.matchContextualKeyword(&matched, context->names().of))
         return null();
     if (!matched) {
         report(ParseError, false, null(), JSMSG_OF_AFTER_FOR_NAME);
         return null();
     }
 
-    Node rhs = assignExpr(InAllowed, YieldIsKeyword);
+    Node rhs = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!rhs)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FOR_OF_ITERABLE);
 
     TokenPos headPos(begin, pos().end);
 
     AutoPushStmtInfoPC stmtInfo(*this, StmtType::BLOCK);
@@ -8319,17 +8325,17 @@ template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::comprehensionIf(GeneratorKind comprehensionKind)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_IF));
 
     uint32_t begin = pos().begin;
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
-    Node cond = assignExpr(InAllowed, YieldIsKeyword);
+    Node cond = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!cond)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
     if (handler.isUnparenthesizedAssignment(cond)) {
         if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
             return null();
@@ -8356,17 +8362,17 @@ Parser<ParseHandler>::comprehensionTail(
 
     if (!tokenStream.matchToken(&matched, TOK_IF, TokenStream::Operand))
         return null();
     if (matched)
         return comprehensionIf(comprehensionKind);
 
     uint32_t begin = pos().begin;
 
-    Node bodyExpr = assignExpr(InAllowed, YieldIsKeyword);
+    Node bodyExpr = assignExpr(InAllowed, YieldIsKeyword, TripledotProhibited);
     if (!bodyExpr)
         return null();
 
     if (comprehensionKind == NotGenerator)
         return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
 
     MOZ_ASSERT(comprehensionKind == StarGenerator);
     Node yieldExpr = newYieldExpression(begin, bodyExpr);
@@ -8446,17 +8452,17 @@ Parser<ParseHandler>::generatorComprehen
     return result;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::assignExprWithoutYield(YieldHandling yieldHandling, unsigned msg)
 {
     uint32_t startYieldOffset = pc->lastYieldOffset;
-    Node res = assignExpr(InAllowed, yieldHandling);
+    Node res = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     if (res && pc->lastYieldOffset != startYieldOffset) {
         reportWithOffset(ParseError, false, pc->lastYieldOffset,
                          msg, js_yield_str);
         return null();
     }
     return res;
 }
 
@@ -8481,17 +8487,17 @@ Parser<ParseHandler>::argumentList(Yield
         if (!tokenStream.matchToken(&matched, TOK_TRIPLEDOT, TokenStream::Operand))
             return false;
         if (matched) {
             spread = true;
             begin = pos().begin;
             *isSpread = true;
         }
 
-        Node argNode = assignExpr(InAllowed, yieldHandling);
+        Node argNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
         if (!argNode)
             return false;
         if (spread) {
             argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
             if (!argNode)
                 return false;
         }
 
@@ -8560,18 +8566,18 @@ Parser<ParseHandler>::checkAndMarkSuperS
     if (!pc->sc->allowSuperProperty())
         return false;
     pc->sc->markSuperScopeNeedsHomeObject();
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TokenKind tt, bool allowCallSyntax,
-                                 InvokedPrediction invoked)
+Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                                 TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     Node lhs;
 
     JS_CHECK_RECURSION(context, return null());
 
     /* Check for new expression first. */
@@ -8585,17 +8591,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
             lhs = newTarget;
         } else {
             lhs = handler.newList(PNK_NEW, newBegin, JSOP_NEW);
             if (!lhs)
                 return null();
 
             // Gotten by tryNewTarget
             tt = tokenStream.currentToken().type;
-            Node ctorExpr = memberExpr(yieldHandling, tt, false, PredictInvoked);
+            Node ctorExpr = memberExpr(yieldHandling, TripledotProhibited, tt, false, PredictInvoked);
             if (!ctorExpr)
                 return null();
 
             handler.addList(lhs, ctorExpr);
 
             bool matched;
             if (!tokenStream.matchToken(&matched, TOK_LP))
                 return null();
@@ -8607,17 +8613,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
                     handler.setOp(lhs, JSOP_SPREADNEW);
             }
         }
     } else if (tt == TOK_SUPER) {
         lhs = handler.newSuperBase(pos(), context);
         if (!lhs)
             return null();
     } else {
-        lhs = primaryExpr(yieldHandling, tt, invoked);
+        lhs = primaryExpr(yieldHandling, tripledotHandling, tt, invoked);
         if (!lhs)
             return null();
     }
 
     MOZ_ASSERT_IF(handler.isSuperBase(lhs, context), tokenStream.isCurrentTokenType(TOK_SUPER));
 
     while (true) {
         if (!tokenStream.getToken(&tt))
@@ -8638,17 +8644,17 @@ Parser<ParseHandler>::memberExpr(YieldHa
                 nextMember = handler.newPropertyAccess(lhs, field, pos().end);
                 if (!nextMember)
                     return null();
             } else {
                 report(ParseError, false, null(), JSMSG_NAME_AFTER_DOT);
                 return null();
             }
         } else if (tt == TOK_LB) {
-            Node propExpr = expr(InAllowed, yieldHandling);
+            Node propExpr = expr(InAllowed, yieldHandling, TripledotProhibited);
             if (!propExpr)
                 return null();
 
             MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
 
             if (handler.isSuperBase(lhs, context) && !checkAndMarkSuperScope()) {
                     report(ParseError, false, null(), JSMSG_BAD_SUPERPROP, "member");
                     return null();
@@ -8888,23 +8894,23 @@ Parser<ParseHandler>::arrayInitializer(Y
             if (tt == TOK_COMMA) {
                 tokenStream.consumeKnownToken(TOK_COMMA, TokenStream::Operand);
                 if (!handler.addElision(literal, pos()))
                     return null();
             } else if (tt == TOK_TRIPLEDOT) {
                 spread = true;
                 tokenStream.consumeKnownToken(TOK_TRIPLEDOT, TokenStream::Operand);
                 uint32_t begin = pos().begin;
-                Node inner = assignExpr(InAllowed, yieldHandling);
+                Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!inner)
                     return null();
                 if (!handler.addSpreadElement(literal, begin, inner))
                     return null();
             } else {
-                Node element = assignExpr(InAllowed, yieldHandling);
+                Node element = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
                 if (!element)
                     return null();
                 if (foldConstants && !FoldConstants(context, &element, this))
                     return null();
                 handler.addArrayElement(literal, element);
             }
 
             if (tt != TOK_COMMA) {
@@ -9162,17 +9168,17 @@ Parser<ParseHandler>::computedPropertyNa
     uint32_t begin = pos().begin;
 
     // Turn off the inDeclDestructuring flag when parsing computed property
     // names. In short, when parsing 'let {[x + y]: z} = obj;', noteNameUse()
     // should be called on x and y, but not on z. See the comment on
     // Parser<>::checkDestructuringPattern() for details.
     bool saved = pc->inDeclDestructuring;
     pc->inDeclDestructuring = false;
-    Node assignNode = assignExpr(InAllowed, yieldHandling);
+    Node assignNode = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
     pc->inDeclDestructuring = saved;
     if (!assignNode)
         return null();
 
     MUST_MATCH_TOKEN(TOK_RB, JSMSG_COMP_PROP_UNTERM_EXPR);
     Node propname = handler.newComputedName(assignNode, begin, pos().end);
     if (!propname)
         return null();
@@ -9202,17 +9208,17 @@ Parser<ParseHandler>::objectLiteral(Yiel
         tokenStream.ungetToken();
 
         PropertyType propType;
         Node propName = propertyName(yieldHandling, literal, &propType, &propAtom);
         if (!propName)
             return null();
 
         if (propType == PropertyType::Normal) {
-            Node propExpr = assignExpr(InAllowed, yieldHandling);
+            Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited);
             if (!propExpr)
                 return null();
 
             if (foldConstants && !FoldConstants(context, &propExpr, this))
                 return null();
 
             if (propAtom == context->names().proto) {
                 if (seenPrototypeMutation) {
@@ -9342,18 +9348,18 @@ Parser<ParseHandler>::tryNewTarget(Node 
         return false;
 
     newTarget = handler.newNewTarget(newHolder, targetHolder);
     return !!newTarget;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TokenKind tt,
-                                  InvokedPrediction invoked)
+Parser<ParseHandler>::primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                                  TokenKind tt, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
     JS_CHECK_RECURSION(context, return null());
 
     switch (tt) {
       case TOK_FUNCTION:
         return functionExpr(invoked);
 
@@ -9420,22 +9426,29 @@ Parser<ParseHandler>::primaryExpr(YieldH
       case TOK_THIS:
         if (pc->sc->isFunctionBox())
             pc->sc->asFunctionBox()->usesThis = true;
         return handler.newThisLiteral(pos());
       case TOK_NULL:
         return handler.newNullLiteral(pos());
 
       case TOK_TRIPLEDOT: {
-        TokenKind next;
-
         // This isn't valid expression syntax, but it's valid in an arrow
         // function as a trailing rest param: `(a, b, ...rest) => body`.  Check
-        // for a name, closing parenthesis, and arrow, and allow it only if all
-        // are present.
+        // if it's directly under
+        // CoverParenthesizedExpressionAndArrowParameterList, and check for a
+        // name, closing parenthesis, and arrow, and allow it only if all are
+        // present.
+        if (tripledotHandling != TripledotAllowed) {
+            report(ParseError, false, null(), JSMSG_UNEXPECTED_TOKEN,
+                   "expression", TokenKindToDesc(tt));
+            return null();
+        }
+
+        TokenKind next;
         if (!tokenStream.getToken(&next))
             return null();
         // FIXME: This fails to handle a rest parameter named |yield| correctly
         //        outside of generators: |var f = (...yield) => 42;| should be
         //        valid code!  When this is fixed, make sure to consult both
         //        |yieldHandling| and |checkYieldNameValidity| for correctness
         //        until legacy generator syntax is removed.
         if (next != TOK_NAME) {
@@ -9482,17 +9495,17 @@ Parser<ParseHandler>::parenExprOrGenerat
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR, TokenStream::Operand))
         return null();
     if (matched)
         return generatorComprehension(begin);
 
-    Node pn = expr(InAllowed, yieldHandling, PredictInvoked);
+    Node pn = expr(InAllowed, yieldHandling, TripledotAllowed, PredictInvoked);
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
@@ -9550,17 +9563,17 @@ Parser<ParseHandler>::parenExprOrGenerat
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::exprInParens(InHandling inHandling, YieldHandling yieldHandling)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
     uint32_t begin = pos().begin;
     uint32_t startYieldOffset = pc->lastYieldOffset;
 
-    Node pn = expr(inHandling, yieldHandling, PredictInvoked);
+    Node pn = expr(inHandling, yieldHandling, TripledotProhibited, PredictInvoked);
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -377,16 +377,17 @@ enum class PropertyType {
 
 // Specify a value for an ES6 grammar parametrization.  We have no enum for
 // [Return] because its behavior is exactly equivalent to checking whether
 // we're in a function box -- easier and simpler than passing an extra
 // parameter everywhere.
 enum YieldHandling { YieldIsName, YieldIsKeyword };
 enum InHandling { InAllowed, InProhibited };
 enum DefaultHandling { NameRequired, AllowDefaultName };
+enum TripledotHandling { TripledotAllowed, TripledotProhibited };
 
 template <typename ParseHandler>
 class Parser : private JS::AutoGCRooter, public StrictModeGetter
 {
     class MOZ_STACK_CLASS AutoPushStmtInfoPC
     {
         Parser<ParseHandler>& parser_;
         StmtInfoPC stmt_;
@@ -687,29 +688,34 @@ class Parser : private JS::AutoGCRooter,
     Node expressionStatement(YieldHandling yieldHandling,
                              InvokedPrediction invoked = PredictUninvoked);
     Node variables(YieldHandling yieldHandling,
                    ParseNodeKind kind,
                    ForInitLocation location,
                    bool* psimple = nullptr, StaticBlockObject* blockObj = nullptr,
                    VarContext varContext = HoistVars);
     Node expr(InHandling inHandling, YieldHandling yieldHandling,
+              TripledotHandling tripledotHandling,
               InvokedPrediction invoked = PredictUninvoked);
     Node assignExpr(InHandling inHandling, YieldHandling yieldHandling,
+                    TripledotHandling tripledotHandling,
                     InvokedPrediction invoked = PredictUninvoked);
     Node assignExprWithoutYield(YieldHandling yieldHandling, unsigned err);
     Node yieldExpression(InHandling inHandling);
     Node condExpr1(InHandling inHandling, YieldHandling yieldHandling,
+                   TripledotHandling tripledotHandling,
                    InvokedPrediction invoked = PredictUninvoked);
     Node orExpr1(InHandling inHandling, YieldHandling yieldHandling,
-                 InvokedPrediction invoked = PredictUninvoked);
-    Node unaryExpr(YieldHandling yieldHandling, InvokedPrediction invoked = PredictUninvoked);
-    Node memberExpr(YieldHandling yieldHandling, TokenKind tt, bool allowCallSyntax,
-                    InvokedPrediction invoked = PredictUninvoked);
-    Node primaryExpr(YieldHandling yieldHandling, TokenKind tt,
+                 TripledotHandling tripledotHandling,
+                   InvokedPrediction invoked = PredictUninvoked);
+    Node unaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling,
+                   InvokedPrediction invoked = PredictUninvoked);
+    Node memberExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
+                    bool allowCallSyntax, InvokedPrediction invoked = PredictUninvoked);
+    Node primaryExpr(YieldHandling yieldHandling, TripledotHandling tripledotHandling, TokenKind tt,
                      InvokedPrediction invoked = PredictUninvoked);
     Node parenExprOrGeneratorComprehension(YieldHandling yieldHandling);
     Node exprInParens(InHandling inHandling, YieldHandling yieldHandling);
 
     bool tryNewTarget(Node& newTarget);
     bool checkAndMarkSuperScope();
 
     Node methodDefinition(YieldHandling yieldHandling, PropertyType propType,
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/arrow-rest.js
@@ -0,0 +1,234 @@
+// The parser should throw SyntaxError immediately if it finds "..." in a
+// context where it's not allowed.
+
+load(libdir + "class.js");
+
+function testThrow(code, column) {
+  var caught = false;
+  try {
+    eval(code);
+  } catch (e) {
+    caught = true;
+    assertEq(e.columnNumber, column);
+  }
+  assertEq(caught, true,
+           "failed to throw evaluating <" + code + ">");
+}
+
+// expression statement
+
+testThrow(`
+...a)=>
+`, 0);
+
+// default parameter
+
+testThrow(`
+function f(x=...a) =>
+`, 13);
+
+// rest parameter
+
+testThrow(`
+function f(... ...a) =>
+`, 15);
+
+// destructuring parameter
+
+testThrow(`
+([... ...a)=>
+`, 6);
+
+testThrow(`
+({...a)=>
+`, 2);
+
+testThrow(`
+function f([... ...a)=>
+`, 16);
+
+testThrow(`
+function f({...a)=>
+`, 12);
+
+// arrow
+
+testThrow(`
+x => ...a)=>
+`, 5);
+
+// template literal
+
+testThrow("`${ ...a)=>}`", 4);
+
+// destructing assignment
+
+testThrow(`
+var [... ...a)=>
+`, 9);
+
+testThrow(`
+var {...a)=>
+`, 5);
+
+// initializer
+
+testThrow(`
+var [a] = ...a)=>
+`, 10);
+
+testThrow(`
+var {a:a} = ...a)=>
+`, 12);
+
+testThrow(`
+var a = ...a)=>
+`, 8);
+
+// if statement
+
+testThrow(`
+if (...a) =>
+`, 4);
+
+// for statement
+
+testThrow(`
+for (...a)=>
+`, 5);
+
+testThrow(`
+for (let a in ...a)=>
+`, 14);
+
+testThrow(`
+for (let a of ...a)=>
+`, 14);
+
+testThrow(`
+for (; ...a)=>
+`, 7);
+
+testThrow(`
+for (;; ...a)=>
+`, 8);
+
+// case
+
+testThrow(`
+switch (x) { case ...a)=>
+`, 18);
+
+// return statement
+
+testThrow(`
+function f(x) { return ...a)=>
+`, 23);
+
+// yield expression
+
+testThrow(`
+function* f(x) { yield ...a)=>
+`, 23);
+
+// throw statement
+
+testThrow(`
+throw ...a) =>
+`, 6);
+
+testThrow(`
+try {} catch (x if ...a) =>
+`, 19);
+
+// class
+
+if (classesEnabled()) {
+testThrow(`
+class A extends ...a) =>
+`, 16);
+}
+
+// conditional expression
+
+testThrow(`
+1 ? ...a) =>
+`, 4);
+
+testThrow(`
+1 ? 2 : ...a) =>
+`, 8);
+
+// unary operators
+
+testThrow(`
+void ...a) =>
+`, 5);
+
+testThrow(`
+typeof ...a) =>
+`, 7);
+
+testThrow(`
+++ ...a) =>
+`, 3);
+
+testThrow(`
+delete ...a) =>
+`, 7);
+
+// array comprehension
+
+testThrow(`
+[x for (...a) =>
+`, 8);
+
+testThrow(`
+[x for (x of ...a) =>
+`, 13);
+
+testThrow(`
+[for (...a) =>
+`, 6);
+
+testThrow(`
+[for (x of y) if (...a) =>
+`, 18);
+
+testThrow(`
+[for (x of y) if (x) ...a) =>
+`, 21);
+
+// new
+
+testThrow(`
+new ...a) =>
+`, 4);
+
+// member expression
+
+testThrow(`
+x[...a) =>
+`, 2);
+
+// array literal
+
+testThrow(`
+[... ...a) =>
+`, 5);
+
+// object literal
+
+testThrow(`
+({[...a) =>
+`, 3);
+
+testThrow(`
+({x: ...a) =>
+`, 5);
+
+// assignment
+
+testThrow(`
+x = ...a) =>
+`, 4);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/modifier-arrow-rest.js
@@ -0,0 +1,11 @@
+load(libdir + "syntax.js");
+
+var postfixes = [
+  "...foo) => 1 @",
+];
+
+function check_syntax_error(e, code) {
+  assertEq(e instanceof SyntaxError, true);
+}
+
+test_syntax(postfixes, check_syntax_error, true);