420857 - Syntax error for missing brace should mention the line number of the opening brace.

This commit is contained in:
Fedor 2019-09-05 20:07:00 +03:00
parent d3e6886052
commit 713888bd05
5 changed files with 180 additions and 27 deletions

View File

@ -66,29 +66,32 @@ using UsedNamePtr = UsedNameTracker::UsedNameMap::Ptr;
// Read a token. Report an error and return null() if that token doesn't match
// to the condition. Do not use MUST_MATCH_TOKEN_INTERNAL directly.
#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorNumber) \
#define MUST_MATCH_TOKEN_INTERNAL(cond, modifier, errorReport) \
JS_BEGIN_MACRO \
TokenKind token; \
if (!tokenStream.getToken(&token, modifier)) \
return null(); \
if (!(cond)) { \
error(errorNumber); \
errorReport; \
return null(); \
} \
JS_END_MACRO
#define MUST_MATCH_TOKEN_MOD(tt, modifier, errorNumber) \
MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorNumber)
MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, error(errorNumber))
#define MUST_MATCH_TOKEN(tt, errorNumber) \
MUST_MATCH_TOKEN_MOD(tt, TokenStream::None, errorNumber)
#define MUST_MATCH_TOKEN_FUNC_MOD(func, modifier, errorNumber) \
MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, errorNumber)
MUST_MATCH_TOKEN_INTERNAL((func)(token), modifier, error(errorNumber))
#define MUST_MATCH_TOKEN_FUNC(func, errorNumber) \
MUST_MATCH_TOKEN_FUNC_MOD(func, TokenStream::None, errorNumber)
#define MUST_MATCH_TOKEN_MOD_WITH_REPORT(tt, modifier, errorReport) \
MUST_MATCH_TOKEN_INTERNAL(token == tt, modifier, errorReport)
template <class T, class U>
static inline void
PropagateTransitiveParseFlags(const T* inner, U* outer)
@ -1012,6 +1015,35 @@ Parser<ParseHandler>::hasValidSimpleStrictParameterNames()
return true;
}
template <typename ParseHandler>
void
Parser<ParseHandler>::reportMissingClosing(unsigned errorNumber, unsigned noteNumber,
uint32_t openedPos)
{
auto notes = MakeUnique<JSErrorNotes>();
if (!notes)
return;
uint32_t line, column;
tokenStream.srcCoords.lineNumAndColumnIndex(openedPos, &line, &column);
const size_t MaxWidth = sizeof("4294967295");
char columnNumber[MaxWidth];
SprintfLiteral(columnNumber, "%" PRIu32, column);
char lineNumber[MaxWidth];
SprintfLiteral(lineNumber, "%" PRIu32, line);
if (!notes->addNoteASCII(pc->sc()->context,
getFilename(), line, column,
GetErrorMessage, nullptr,
noteNumber, lineNumber, columnNumber))
{
return;
}
errorWithNotes(Move(notes), errorNumber);
}
template <typename ParseHandler>
void
Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind,
@ -1039,11 +1071,11 @@ Parser<ParseHandler>::reportRedeclaration(HandlePropertyName name, DeclarationKi
char lineNumber[MaxWidth];
SprintfLiteral(lineNumber, "%" PRIu32, line);
if (!notes->addNoteLatin1(pc->sc()->context,
getFilename(), line, column,
GetErrorMessage, nullptr,
JSMSG_REDECLARED_PREV,
lineNumber, columnNumber))
if (!notes->addNoteASCII(pc->sc()->context,
getFilename(), line, column,
GetErrorMessage, nullptr,
JSMSG_REDECLARED_PREV,
lineNumber, columnNumber))
{
return;
}
@ -3613,6 +3645,7 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
TokenKind tt;
if (!tokenStream.getToken(&tt, TokenStream::Operand))
return false;
uint32_t openedPos = 0;
if (tt != TOK_LC) {
if ((funbox->isStarGenerator() && !funbox->isAsync()) || kind == Method ||
kind == GetterNoExpressionClosure || kind == SetterNoExpressionClosure ||
@ -3634,6 +3667,8 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
tokenStream.ungetToken();
bodyType = ExpressionBody;
funbox->setIsExprBody();
} else {
openedPos = pos().begin;
}
// Arrow function parameters inherit yieldHandling from the enclosing
@ -3671,13 +3706,9 @@ Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
}
if (bodyType == StatementListBody) {
bool matched;
if (!tokenStream.matchToken(&matched, TOK_RC, TokenStream::Operand))
return false;
if (!matched) {
error(JSMSG_CURLY_AFTER_BODY);
return false;
}
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_BODY,
JSMSG_CURLY_OPENED, openedPos));
funbox->setEnd(pos().end);
} else {
#if !JS_HAS_EXPR_CLOSURES
@ -4449,6 +4480,7 @@ typename ParseHandler::Node
Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned errorNumber)
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
uint32_t openedPos = pos().begin;
ParseContext::Statement stmt(pc, StatementKind::Block);
ParseContext::Scope scope(this);
@ -4459,7 +4491,9 @@ Parser<ParseHandler>::blockStatement(YieldHandling yieldHandling, unsigned error
if (!list)
return null();
MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, errorNumber);
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
reportMissingClosing(errorNumber, JSMSG_CURLY_OPENED,
openedPos));
return finishLexicalScope(scope, list);
}
@ -6711,6 +6745,8 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
{
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_TRY);
uint32_t openedPos = pos().begin;
ParseContext::Statement stmt(pc, StatementKind::Try);
ParseContext::Scope scope(this);
if (!scope.init(pc))
@ -6724,7 +6760,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
if (!innerBlock)
return null();
MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_TRY);
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_TRY,
JSMSG_CURLY_OPENED, openedPos));
}
bool hasUnconditionalCatch = false;
@ -6840,6 +6878,8 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
if (tt == TOK_FINALLY) {
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_FINALLY);
uint32_t openedPos = pos().begin;
ParseContext::Statement stmt(pc, StatementKind::Finally);
ParseContext::Scope scope(this);
if (!scope.init(pc))
@ -6853,7 +6893,9 @@ Parser<ParseHandler>::tryStatement(YieldHandling yieldHandling)
if (!finallyBlock)
return null();
MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_FINALLY);
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_FINALLY,
JSMSG_CURLY_OPENED, openedPos));
} else {
tokenStream.ungetToken();
}
@ -6870,6 +6912,8 @@ typename ParseHandler::Node
Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
ParseContext::Scope& catchParamScope)
{
uint32_t openedPos = pos().begin;
ParseContext::Statement stmt(pc, StatementKind::Block);
// ES 13.15.7 CatchClauseEvaluation
@ -6889,7 +6933,9 @@ Parser<ParseHandler>::catchBlockStatement(YieldHandling yieldHandling,
if (!list)
return null();
MUST_MATCH_TOKEN_MOD(TOK_RC, TokenStream::Operand, JSMSG_CURLY_AFTER_CATCH);
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::Operand,
reportMissingClosing(JSMSG_CURLY_AFTER_CATCH,
JSMSG_CURLY_OPENED, openedPos));
// The catch parameter names are not bound in the body scope, so remove
// them before generating bindings.
@ -9243,7 +9289,9 @@ Parser<ParseHandler>::arrayInitializer(YieldHandling yieldHandling, PossibleErro
}
}
MUST_MATCH_TOKEN_MOD(TOK_RB, modifier, JSMSG_BRACKET_AFTER_LIST);
MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RB, modifier,
reportMissingClosing(JSMSG_BRACKET_AFTER_LIST,
JSMSG_BRACKET_OPENED, begin));
}
handler.setEndPosition(literal, pos().end);
return literal;
@ -9478,6 +9526,8 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
{
MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
uint32_t openedPos = pos().begin;
Node literal = handler.newObjectLiteral(pos().begin);
if (!literal)
return null();
@ -9640,7 +9690,7 @@ Parser<ParseHandler>::objectLiteral(YieldHandling yieldHandling, PossibleError*
if (tt == TOK_RC)
break;
if (tt != TOK_COMMA) {
error(JSMSG_CURLY_AFTER_LIST);
reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos);
return null();
}
}

View File

@ -1459,6 +1459,8 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
bool hasValidSimpleStrictParameterNames();
void reportMissingClosing(unsigned errorNumber, unsigned noteNumber, uint32_t openedPos);
void reportRedeclaration(HandlePropertyName name, DeclarationKind prevKind, TokenPos pos,
uint32_t prevPos);
bool notePositionalFormalParameter(Node fn, HandlePropertyName name, uint32_t beginPos,

View File

@ -0,0 +1,90 @@
function test(source, [lineNumber, columnNumber], openType = "{", closeType = "}") {
let caught = false;
try {
Reflect.parse(source, { source: "foo.js" });
} catch (e) {
assertEq(e.message.includes("missing " + closeType + " "), true);
let notes = getErrorNotes(e);
assertEq(notes.length, 1);
let note = notes[0];
assertEq(note.message, openType + " opened at line " + lineNumber + ", column " + columnNumber);
assertEq(note.fileName, "foo.js");
assertEq(note.lineNumber, lineNumber);
assertEq(note.columnNumber, columnNumber);
caught = true;
}
assertEq(caught, true);
}
// Function
test(`
function test1() {
}
function test2() {
if (true) {
//}
}
function test3() {
}
`, [4, 17]);
// Block statement.
test(`
{
if (true) {
}
`, [2, 0]);
test(`
if (true) {
if (true) {
}
`, [2, 10]);
test(`
for (;;) {
if (true) {
}
`, [2, 9]);
test(`
while (true) {
if (true) {
}
`, [2, 13]);
test(`
do {
do {
} while(true);
`, [2, 3]);
// try-catch-finally.
test(`
try {
if (true) {
}
`, [2, 4]);
test(`
try {
} catch (e) {
if (true) {
}
`, [3, 12]);
test(`
try {
} finally {
if (true) {
}
`, [3, 10]);
// Object literal.
test(`
var x = {
foo: {
};
`, [2, 8]);
// Array literal.
test(`
var x = [
[
];
`, [2, 8], "[", "]");

View File

@ -218,6 +218,7 @@ MSG_DEF(JSMSG_BAD_SUPERCALL, 0, JSEXN_SYNTAXERR, "super() is only vali
MSG_DEF(JSMSG_BRACKET_AFTER_ARRAY_COMPREHENSION, 0, JSEXN_SYNTAXERR, "missing ] after array comprehension")
MSG_DEF(JSMSG_BRACKET_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing ] after element list")
MSG_DEF(JSMSG_BRACKET_IN_INDEX, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
MSG_DEF(JSMSG_BRACKET_OPENED, 2, JSEXN_NOTE, "[ opened at line {0}, column {1}")
MSG_DEF(JSMSG_CATCH_AFTER_GENERAL, 0, JSEXN_SYNTAXERR, "catch after unconditional catch")
MSG_DEF(JSMSG_CATCH_IDENTIFIER, 0, JSEXN_SYNTAXERR, "missing identifier in catch")
MSG_DEF(JSMSG_CATCH_OR_FINALLY, 0, JSEXN_SYNTAXERR, "missing catch or finally after try")
@ -228,6 +229,7 @@ MSG_DEF(JSMSG_COLON_IN_COND, 0, JSEXN_SYNTAXERR, "missing : in conditi
MSG_DEF(JSMSG_COMP_PROP_UNTERM_EXPR, 0, JSEXN_SYNTAXERR, "missing ] in computed property name")
MSG_DEF(JSMSG_CONTRARY_NONDIRECTIVE, 1, JSEXN_SYNTAXERR, "'{0}' statement won't be enforced as a directive because it isn't in directive prologue position")
MSG_DEF(JSMSG_CURLY_AFTER_BODY, 0, JSEXN_SYNTAXERR, "missing } after function body")
MSG_DEF(JSMSG_CURLY_OPENED, 2, JSEXN_NOTE, "{ opened at line {0}, column {1}")
MSG_DEF(JSMSG_CURLY_AFTER_CATCH, 0, JSEXN_SYNTAXERR, "missing } after catch block")
MSG_DEF(JSMSG_CURLY_AFTER_FINALLY, 0, JSEXN_SYNTAXERR, "missing } after finally block")
MSG_DEF(JSMSG_CURLY_AFTER_LIST, 0, JSEXN_SYNTAXERR, "missing } after property list")

View File

@ -1013,7 +1013,20 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
if (!out.append(")"))
return nullptr;
}
} else if (fun->isInterpreted() && !fun->isSelfHostedBuiltin()) {
} else if (fun->isInterpreted() &&
(!fun->isSelfHostedBuiltin() ||
fun->infallibleIsDefaultClassConstructor(cx)))
{
// Default class constructors should always haveSource except;
//
// 1. Source has been discarded for the whole compartment.
//
// 2. The source is marked as "lazy", i.e., retrieved on demand, and
// the embedding has not provided a hook to retrieve sources.
MOZ_ASSERT_IF(fun->infallibleIsDefaultClassConstructor(cx),
!cx->runtime()->sourceHook ||
!script->scriptSource()->sourceRetrievable() ||
fun->compartment()->behaviors().discardSource());
if (!AppendPrelude() ||
!out.append("() {\n ") ||
!out.append("[sourceless code]") ||
@ -1022,10 +1035,6 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
return nullptr;
}
} else {
// Default class constructors should always haveSource unless source
// has been discarded for the whole compartment.
MOZ_ASSERT(!fun->infallibleIsDefaultClassConstructor(cx) ||
fun->compartment()->behaviors().discardSource());
if (!AppendPrelude() ||
!out.append("() {\n "))