Bug 1434429 - Use the current offset, not the offset of the start of the current token, when reporting errors for unterminated string/template literals. r=till

--HG--
extra : rebase_source : 102061eec263a03b0eeef9da7159a850234bb216
This commit is contained in:
Jeff Walden 2018-01-18 11:34:26 -08:00
parent 2b933cb9ed
commit d2ef6ad9ca
4 changed files with 145 additions and 11 deletions

View File

@ -984,11 +984,12 @@ TokenStreamSpecific<CharT, AnyCharsAccess>::error(unsigned errorNumber, ...)
va_list args;
va_start(args, errorNumber);
TokenStreamAnyChars& anyChars = anyCharsAccess();
ErrorMetadata metadata;
if (computeErrorMetadata(&metadata, anyChars.currentToken().pos.begin))
if (computeErrorMetadata(&metadata, userbuf.offset())) {
TokenStreamAnyChars& anyChars = anyCharsAccess();
ReportCompileError(anyChars.cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber,
args);
}
va_end(args);
}
@ -1000,11 +1001,12 @@ TokenStreamSpecific<CharT, AnyCharsAccess>::errorAt(uint32_t offset, unsigned er
va_list args;
va_start(args, errorNumber);
TokenStreamAnyChars& anyChars = anyCharsAccess();
ErrorMetadata metadata;
if (computeErrorMetadata(&metadata, offset))
if (computeErrorMetadata(&metadata, offset)) {
TokenStreamAnyChars& anyChars = anyCharsAccess();
ReportCompileError(anyChars.cx, Move(metadata), nullptr, JSREPORT_ERROR, errorNumber,
args);
}
va_end(args);
}
@ -1707,7 +1709,7 @@ TokenStreamSpecific<CharT, AnyCharsAccess>::getTokenInternal(TokenKind* ttp, Mod
// Look for a string or a template string.
//
if (c1kind == String) {
if (!getStringOrTemplateToken(c, &tp))
if (!getStringOrTemplateToken(static_cast<char>(c), &tp))
goto error;
goto out;
}
@ -2131,8 +2133,11 @@ TokenStreamSpecific<CharT, AnyCharsAccess>::getTokenInternal(TokenKind* ttp, Mod
template<typename CharT, class AnyCharsAccess>
bool
TokenStreamSpecific<CharT, AnyCharsAccess>::getStringOrTemplateToken(int untilChar, Token** tp)
TokenStreamSpecific<CharT, AnyCharsAccess>::getStringOrTemplateToken(char untilChar, Token** tp)
{
MOZ_ASSERT(untilChar == '\'' || untilChar == '"' || untilChar == '`',
"unexpected string/template literal delimiter");
int c;
int nc = -1;
@ -2147,7 +2152,8 @@ TokenStreamSpecific<CharT, AnyCharsAccess>::getStringOrTemplateToken(int untilCh
while ((c = getCharIgnoreEOL()) != untilChar) {
if (c == EOF) {
ungetCharIgnoreEOL(c);
error(JSMSG_UNTERMINATED_STRING);
const char delimiters[] = { untilChar, untilChar, '\0' };
error(JSMSG_EOF_BEFORE_END_OF_LITERAL, delimiters);
return false;
}
@ -2159,7 +2165,13 @@ TokenStreamSpecific<CharT, AnyCharsAccess>::getStringOrTemplateToken(int untilCh
if (!getChar(&c))
return false;
switch (c) {
if (c == EOF) {
const char delimiters[] = { untilChar, untilChar, '\0' };
error(JSMSG_EOF_IN_ESCAPE_IN_LITERAL, delimiters);
return false;
}
switch (static_cast<CharT>(c)) {
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
@ -2345,7 +2357,8 @@ TokenStreamSpecific<CharT, AnyCharsAccess>::getStringOrTemplateToken(int untilCh
} else if (TokenBuf::isRawEOLChar(c)) {
if (!parsingTemplate) {
ungetCharIgnoreEOL(c);
error(JSMSG_UNTERMINATED_STRING);
const char delimiters[] = { untilChar, untilChar, '\0' };
error(JSMSG_EOL_BEFORE_END_OF_STRING, delimiters);
return false;
}
if (c == '\r') {

View File

@ -1437,7 +1437,7 @@ class MOZ_STACK_CLASS TokenStreamSpecific
MOZ_MUST_USE bool getTokenInternal(TokenKind* ttp, Modifier modifier);
MOZ_MUST_USE bool getStringOrTemplateToken(int untilChar, Token** tp);
MOZ_MUST_USE bool getStringOrTemplateToken(char untilChar, Token** tp);
// Try to get the next character, normalizing '\r', '\r\n', and '\n' into
// '\n'. Also updates internal line-counter state. Return true on success

View File

@ -338,7 +338,9 @@ MSG_DEF(JSMSG_UNNAMED_CLASS_STMT, 0, JSEXN_SYNTAXERR, "class statement requ
MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT, 0, JSEXN_SYNTAXERR, "function statement requires a name")
MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 0, JSEXN_SYNTAXERR, "unterminated comment")
MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal")
MSG_DEF(JSMSG_UNTERMINATED_STRING, 0, JSEXN_SYNTAXERR, "unterminated string literal")
MSG_DEF(JSMSG_EOF_BEFORE_END_OF_LITERAL,1,JSEXN_SYNTAXERR, "{0} literal not terminated before end of script")
MSG_DEF(JSMSG_EOL_BEFORE_END_OF_STRING,1, JSEXN_SYNTAXERR, "{0} string literal contains an unescaped line break")
MSG_DEF(JSMSG_EOF_IN_ESCAPE_IN_LITERAL,1, JSEXN_SYNTAXERR, "reached end of script in the middle of an escape sequence in a {0} literal")
MSG_DEF(JSMSG_USELESS_EXPR, 0, JSEXN_TYPEERR, "useless expression")
MSG_DEF(JSMSG_USE_ASM_DIRECTIVE_FAIL, 0, JSEXN_SYNTAXERR, "\"use asm\" is only meaningful in the Directive Prologue of a function body")
MSG_DEF(JSMSG_VAR_HIDES_ARG, 1, JSEXN_TYPEERR, "variable {0} redeclares argument")

View File

@ -0,0 +1,119 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var BUGNUMBER = 9999999;
var summary =
"Report unterminated string/template literal errors with the line/column " +
"number of the point of non-termination";
function test(f, quotes, [line, col])
{
var caught = false;
try
{
f();
}
catch (e)
{
caught = true;
assertEq(e.lineNumber, line, "line number");
assertEq(e.columnNumber, col, "column number");
assertEq(e.message.includes(quotes), true,
"message must contain delimiter");
}
assertEq(caught, true);
}
test(function() {
//0123
eval("'hi");
}, "''", [1, 3]);
test(function() {
//0123 4
eval("'hi\\");
}, "''", [1, 4]);
test(function() {
//0123456
eval(" 'hi");
}, "''", [1, 6]);
test(function() {
//0123456 7
eval(" 'hi\\");
}, "''", [1, 7]);
test(function() {
//01234567 01234567
eval('var x =\n "hi');
}, '""', [2, 7]);
test(function() {
//0123456 01234567 8
eval('var x =\n "hi\\');
}, '""', [2, 8]);
test(function() {
// 1
//0123456 01234567 012345678 01234567890123
eval('var x =\n "hi\\\n bye\\\n no really');
}, '""', [4, 13]);
test(function() {
// 1
//0123456 01234567 012345678 01234567890123 4
eval('var x =\n "hi\\\n bye\\\n no really\\');
}, '""', [4, 14]);
test(function() {
//0123456 01234567 012345678
eval('var x =\n "hi\\\n bye\n');
}, '""', [3, 8]);
test(function() {
//0123456 01234567 012345678 9
eval('var x =\n "hi\\\n bye\\');
}, '""', [3, 9]);
test(function() {
//0123456 01234567
eval('var x =\n `');
}, '``', [2, 7]);
test(function() {
//0123456 01234567 8
eval('var x =\n `\\');
}, '``', [2, 8]);
test(function() {
// 1
//0123456 0123456789012345
eval('var x =\n htmlEscape`');
}, '``', [2, 15]);
test(function() {
// 1
//0123456 0123456789012345 6
eval('var x =\n htmlEscape`\\');
}, '``', [2, 16]);
test(function() {
// 1
//0123456 01234567890123 01234
eval('var x =\n htmlEscape\n `');
}, '``', [3, 4]);
test(function() {
// 1
//0123456 01234567890123 01234 5
eval('var x =\n htmlEscape\n `\\');
}, '``', [3, 5]);
if (typeof reportCompare === "function")
reportCompare(0, 0, "ok");
print("Tests complete");