Skip to content

Commit dc884d2

Browse files
committed
[MERGE #5489 @akroshg] Trailing comma in an arrow function should be valid.
Merge pull request #5489 from akroshg:comma Trailing commas is allowed for all function including arrow function. Since we don't know that an expression can be formals of the lambda function we need to defer the error till we determine that we indeed are going to have the arrow function.
2 parents 9390649 + 4e83c3f commit dc884d2

File tree

4 files changed

+64
-18
lines changed

4 files changed

+64
-18
lines changed

lib/Parser/Parse.cpp

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator
8484
m_isInParsingArgList(false),
8585
m_hasDestructuringPattern(false),
8686
m_hasDeferredShorthandInitError(false),
87+
m_deferCommaError(false),
8788
m_pnestedCount(nullptr),
8889

8990
wellKnownPropertyPids(), // should initialize to nullptrs
@@ -99,7 +100,7 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator
99100
m_funcParenExprDepth(0),
100101
m_deferEllipsisError(false),
101102
m_deferEllipsisErrorLoc(), // calls default initializer
102-
103+
m_deferCommaErrorLoc(),
103104
m_tryCatchOrFinallyDepth(0),
104105

105106
m_pstmtCur(nullptr),
@@ -3077,9 +3078,7 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
30773078
uint saveCurrBlockId = GetCurrentBlock()->blockId;
30783079
GetCurrentBlock()->blockId = m_nextBlockId++;
30793080

3080-
// Push the deferred error state for ellipsis errors. It is possible that another syntax error will occur before we undefer this one.
3081-
bool deferEllipsisErrorSave = m_deferEllipsisError;
3082-
RestorePoint ellipsisErrorLocSave = m_deferEllipsisErrorLoc;
3081+
AutoDeferErrorsRestore deferErrorRestore(this);
30833082

30843083
this->m_funcParenExprDepth++;
30853084
pnode = ParseExpr<buildAST>(koplNo, &fCanAssign, TRUE, FALSE, nullptr, nullptr /*nameLength*/, nullptr /*pShortNameOffset*/, &term, true, nullptr, plastRParen);
@@ -3099,22 +3098,22 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
30993098
// Put back the original next-block-ID so the existing pid ref stacks will be correct.
31003099
m_nextBlockId = saveNextBlockId;
31013100
}
3102-
3103-
// Emit a deferred ... error if one was parsed.
3104-
if (m_deferEllipsisError && m_token.tk != tkDArrow)
3105-
{
3106-
this->GetScanner()->SeekTo(m_deferEllipsisErrorLoc);
3107-
Error(ERRInvalidSpreadUse);
3108-
}
31093101
else
31103102
{
3111-
m_deferEllipsisError = false;
3103+
// Emit a deferred ... error if one was parsed.
3104+
if (m_deferEllipsisError)
3105+
{
3106+
this->GetScanner()->SeekTo(m_deferEllipsisErrorLoc);
3107+
Error(ERRInvalidSpreadUse);
3108+
}
3109+
else if (m_deferCommaError)
3110+
{
3111+
// Emit a comma error if that was deferred.
3112+
this->GetScanner()->SeekTo(m_deferCommaErrorLoc);
3113+
Error(ERRsyntax);
3114+
}
31123115
}
31133116

3114-
// We didn't error out, so restore the deferred error state.
3115-
m_deferEllipsisError = deferEllipsisErrorSave;
3116-
m_deferEllipsisErrorLoc = ellipsisErrorLocSave;
3117-
31183117
break;
31193118
}
31203119

@@ -8801,6 +8800,14 @@ ParseNodePtr Parser::ParseExpr(int oplMin,
88018800
}
88028801
else // a binary operator
88038802
{
8803+
if (nop == knopComma && m_token.tk == tkRParen)
8804+
{
8805+
// Trailing comma
8806+
this->GetScanner()->Capture(&m_deferCommaErrorLoc);
8807+
m_deferCommaError = true;
8808+
break;
8809+
}
8810+
88048811
ParseNode* pnode1 = pnode;
88058812

88068813
// Parse the operand, make a new node, and look for more

lib/Parser/Parse.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,8 @@ class Parser
455455
bool m_hasDestructuringPattern;
456456
// This bool is used for deferring the shorthand initializer error ( {x = 1}) - as it is allowed in the destructuring grammar.
457457
bool m_hasDeferredShorthandInitError;
458+
bool m_deferEllipsisError;
459+
bool m_deferCommaError;
458460
uint * m_pnestedCount; // count of functions nested at one level below the current node
459461

460462
struct WellKnownPropertyPids
@@ -495,8 +497,8 @@ class Parser
495497

496498
// Used for issuing spread and rest errors when there is ambiguity with lambda parameter lists and parenthesized expressions
497499
uint m_funcParenExprDepth;
498-
bool m_deferEllipsisError;
499500
RestorePoint m_deferEllipsisErrorLoc;
501+
RestorePoint m_deferCommaErrorLoc;
500502

501503
uint m_tryCatchOrFinallyDepth; // Used to determine if parsing is currently in a try/catch/finally block in order to throw error on yield expressions inside them
502504

@@ -585,6 +587,33 @@ class Parser
585587
#endif
586588
};
587589

590+
class AutoDeferErrorsRestore
591+
{
592+
public:
593+
AutoDeferErrorsRestore(Parser *p)
594+
: m_parser(p)
595+
{
596+
m_deferEllipsisErrorSave = m_parser->m_deferEllipsisError;
597+
m_deferCommaError = m_parser->m_deferCommaError;
598+
m_ellipsisErrorLocSave = m_parser->m_deferEllipsisErrorLoc;
599+
m_commaErrorLocSave = m_parser->m_deferCommaErrorLoc;
600+
}
601+
602+
~AutoDeferErrorsRestore()
603+
{
604+
m_parser->m_deferEllipsisError = m_deferEllipsisErrorSave;
605+
m_parser->m_deferCommaError = m_deferCommaError;
606+
m_parser->m_deferEllipsisErrorLoc = m_ellipsisErrorLocSave;
607+
m_parser->m_deferCommaErrorLoc = m_commaErrorLocSave;
608+
}
609+
private:
610+
Parser *m_parser;
611+
RestorePoint m_ellipsisErrorLocSave;
612+
RestorePoint m_commaErrorLocSave;
613+
bool m_deferEllipsisErrorSave;
614+
bool m_deferCommaError;
615+
};
616+
588617
// This function is going to capture some of the important current state of the parser to an object. Once we learn
589618
// that we need to reparse the grammar again we could use RestoreStateFrom to restore that state to the parser.
590619
void CaptureState(ParserState *state);

test/es6/lambda1.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@ var tests = [
169169
{
170170
name: "Interesting valid and invalid syntax",
171171
body: function () {
172+
assert.doesNotThrow(() => { eval('(x, ) => {};'); }, SyntaxError, "Trailing comma is valid syntax");
172173
assert.throws(() => { eval('(var x) => {};'); }, SyntaxError, "var not used in formals declaration", "Syntax error");
173-
assert.throws(() => { eval('(x, ) => {};'); }, SyntaxError, "missing formal identifier in parameter list", "Syntax error");
174174
assert.throws(() => { eval('a.x => {};'); }, SyntaxError, "valid expression syntax that is invalid parameter list syntax on lhs of =>", "Syntax error");
175175
assert.throws(() => { eval('(x, y)[7] => {};'); }, SyntaxError, "valid expression syntax that is invalid parameter list syntax on lhs of =>", "Expected '=>'");
176176
assert.throws(() => { eval('x() => {};'); }, SyntaxError, "valid expression syntax that is invalid parameter list syntax on lhs of =>", "Syntax error");

test/es6/trailingcomma.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ var tests = [
2424
assert.doesNotThrow(function () { eval("function foo() {}; foo(1, 2,);"); }, "Trailing comma in a user function call is a valid syntax");
2525
assert.doesNotThrow(function () { eval("Math.min(1, 2, );"); }, "Trailing comma in built-in function call is a valid syntax");
2626
}
27+
},
28+
{
29+
name: "Trailing comma in arrow function",
30+
body: function () {
31+
assert.doesNotThrow(function () { eval("(a,) => {}"); }, "Trailing comma in an arrow function is a valid syntax");
32+
assert.doesNotThrow(function () { eval("let f = (a,) => {}"); }, "Trailing comma in an arrow function is a valid syntax");
33+
assert.doesNotThrow(function () { eval("(b = (a,) => {}) => {}"); }, "Trailing comma in an nested arrow function is a valid syntax");
34+
assert.doesNotThrow(function () { eval("({b = (a,) => {}}) => {}"); }, "Trailing comma in an nested arrow function in destructuring is a valid syntax");
35+
assert.throws(function () { eval("(b = (a,)) => {}"); }, SyntaxError, "Trailing comma in a comma expression is not valid syntax");
36+
}
2737
}
2838
];
2939

0 commit comments

Comments
 (0)