Bug 1005110 - Warn about unreachable expression after semicolon-less return. r=efaust

This commit is contained in:
Tooru Fujisawa 2015-04-01 18:34:03 +09:00
Родитель 567240287f
Коммит 01a24c421f
6 изменённых файлов: 553 добавлений и 3 удалений

Просмотреть файл

@ -5333,8 +5333,20 @@ Parser<ParseHandler>::returnStatement()
if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
return null();
switch (tt) {
case TOK_EOL: {
bool startsExpr;
if (!tokenStream.nextTokenStartsExpr(&startsExpr, TokenStream::Operand))
return null();
if (startsExpr) {
TokenPos pos;
if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
return null();
if (!reportWithOffset(ParseWarning, false, pos.begin, JSMSG_STMT_AFTER_SEMI_LESS))
return null();
}
// Fall through.
}
case TOK_EOF:
case TOK_EOL:
case TOK_SEMI:
case TOK_RC:
exprNode = null();

Просмотреть файл

@ -322,6 +322,33 @@ TokenStream::TokenStream(ExclusiveContext* cx, const ReadOnlyCompileOptions& opt
isExprEnding[TOK_RP] = 1;
isExprEnding[TOK_RB] = 1;
isExprEnding[TOK_RC] = 1;
memset(isExprStarting, 0, sizeof(isExprStarting));
isExprStarting[TOK_INC] = 1;
isExprStarting[TOK_DEC] = 1;
isExprStarting[TOK_LB] = 1;
isExprStarting[TOK_LC] = 1;
isExprStarting[TOK_LP] = 1;
isExprStarting[TOK_NAME] = 1;
isExprStarting[TOK_NUMBER] = 1;
isExprStarting[TOK_STRING] = 1;
isExprStarting[TOK_TEMPLATE_HEAD] = 1;
isExprStarting[TOK_NO_SUBS_TEMPLATE] = 1;
isExprStarting[TOK_REGEXP] = 1;
isExprStarting[TOK_TRUE] = 1;
isExprStarting[TOK_FALSE] = 1;
isExprStarting[TOK_NULL] = 1;
isExprStarting[TOK_THIS] = 1;
isExprStarting[TOK_NEW] = 1;
isExprStarting[TOK_DELETE] = 1;
isExprStarting[TOK_YIELD] = 1;
isExprStarting[TOK_CLASS] = 1;
isExprStarting[TOK_ADD] = 1;
isExprStarting[TOK_SUB] = 1;
isExprStarting[TOK_TYPEOF] = 1;
isExprStarting[TOK_VOID] = 1;
isExprStarting[TOK_NOT] = 1;
isExprStarting[TOK_BITNOT] = 1;
}
#ifdef _MSC_VER

Просмотреть файл

@ -494,6 +494,14 @@ class MOZ_STACK_CLASS TokenStream
return true;
}
bool nextTokenStartsExpr(bool* startsExpr, Modifier modifier = None) {
TokenKind tt;
if (!peekToken(&tt, modifier))
return false;
*startsExpr = isExprStarting[tt];
return true;
}
bool nextTokenEndsExpr(bool* endsExpr) {
TokenKind tt;
if (!peekToken(&tt))
@ -827,6 +835,7 @@ class MOZ_STACK_CLASS TokenStream
mozilla::UniquePtr<char16_t[], JS::FreePolicy> displayURL_; // the user's requested source URL or null
mozilla::UniquePtr<char16_t[], JS::FreePolicy> sourceMapURL_; // source map's filename or null
CharBuffer tokenbuf; // current token string buffer
uint8_t isExprStarting[TOK_LIMIT];// which tokens can start exprs?
uint8_t isExprEnding[TOK_LIMIT];// which tokens definitely terminate exprs?
ExclusiveContext* const cx;
bool mutedErrors;

Просмотреть файл

@ -0,0 +1,501 @@
// Warning should be shown for expression-like statement after semicolon-less
// return (bug 1005110).
if (options().indexOf("werror") == -1)
options("werror");
function testWarn(code, lineNumber, columnNumber) {
var caught = false;
try {
eval(code);
} catch (e) {
caught = true;
assertEq(e.message, "unreachable expression after semicolon-less return statement", code);
assertEq(e.lineNumber, lineNumber);
assertEq(e.columnNumber, columnNumber);
}
assertEq(caught, true, "warning should be caught for " + code);
caught = false;
try {
Reflect.parse(code);
} catch (e) {
caught = true;
assertEq(e.message, "unreachable expression after semicolon-less return statement", code);
}
assertEq(caught, true, "warning should be caught for " + code);
}
function testPass(code) {
var caught = false;
try {
eval(code);
} catch (e) {
caught = true;
}
assertEq(caught, false, "warning should not be caught for " + code);
caught = false;
try {
Reflect.parse(code);
} catch (e) {
caught = true;
}
assertEq(caught, false, "warning should not be caught for " + code);
}
// not EOL
testPass(`
function f() {
return (
1 + 2
);
}
`);
testPass(`
function f() {
return;
1 + 2;
}
`);
// starts expression
// TOK_INC
testWarn(`
function f() {
var i = 0;
return
++i;
}
`, 5, 4);
// TOK_DEC
testWarn(`
function f() {
var i = 0;
return
--i;
}
`, 5, 4);
// TOK_LB
testWarn(`
function f() {
return
[1, 2, 3];
}
`, 4, 4);
// TOK_LC
testWarn(`
function f() {
return
{x: 10};
}
`, 4, 4);
testWarn(`
function f() {
return
{
method()
{
f();
}
};
}
`, 4, 2);
// TOK_LP
testWarn(`
function f() {
return
(1 + 2);
}
`, 4, 4);
// TOK_NAME
testWarn(`
function f() {
return
f;
}
`, 4, 4);
// TOK_NUMBER
testWarn(`
function f() {
return
1 + 2;
}
`, 4, 4);
testWarn(`
function f() {
return
.1 + .2;
}
`, 4, 4);
// TOK_STRING
testWarn(`
function f() {
return
"foo";
}
`, 4, 4);
testWarn(`
function f() {
return
"use struct";
}
`, 4, 4);
testWarn(`
function f() {
return
'foo';
}
`, 4, 4);
// TOK_TEMPLATE_HEAD
testWarn(`
function f() {
return
\`foo\${1 + 2}\`;
}
`, 4, 4);
// TOK_NO_SUBS_TEMPLATE
testWarn(`
function f() {
return
\`foo\`;
}
`, 4, 4);
// TOK_REGEXP
testWarn(`
function f() {
return
/foo/;
}
`, 4, 4);
// TOK_TRUE
testWarn(`
function f() {
return
true;
}
`, 4, 4);
// TOK_FALSE
testWarn(`
function f() {
return
false;
}
`, 4, 4);
// TOK_NULL
testWarn(`
function f() {
return
null;
}
`, 4, 4);
// TOK_THIS
testWarn(`
function f() {
return
this;
}
`, 4, 4);
// TOK_NEW
testWarn(`
function f() {
return
new Array();
}
`, 4, 4);
// TOK_DELETE
testWarn(`
function f() {
var a = {x: 10};
return
delete a.x;
}
`, 5, 4);
// TOK_YIELD
testWarn(`
function* f() {
return
yield 1;
}
`, 4, 4);
// TOK_CLASS
testWarn(`
function f() {
return
class A { constructor() {} };
}
`, 4, 4);
// TOK_ADD
testWarn(`
function f() {
return
+1;
}
`, 4, 4);
// TOK_SUB
testWarn(`
function f() {
return
-1;
}
`, 4, 4);
// TOK_NOT
testWarn(`
function f() {
return
!1;
}
`, 4, 4);
// TOK_BITNOT
testWarn(`
function f() {
return
~1;
}
`, 4, 4);
// don't start expression
// TOK_EOF
testPass(`
var f = new Function("return\\n");
`);
// TOK_SEMI
testPass(`
function f() {
return
;
}
`);
// TOK_RC
testPass(`
function f() {
{
return
}
}
`);
// TOK_FUNCTION
testPass(`
function f() {
g();
return
function g() {}
}
`);
// TOK_IF
testPass(`
function f() {
return
if (true)
1 + 2;
}
`);
// TOK_ELSE
testPass(`
function f() {
if (true)
return
else
1 + 2;
}
`);
// TOK_SWITCH
testPass(`
function f() {
return
switch (1) {
case 1:
break;
}
}
`);
// TOK_CASE
testPass(`
function f() {
switch (1) {
case 0:
return
case 1:
break;
}
}
`);
// TOK_DEFAULT
testPass(`
function f() {
switch (1) {
case 0:
return
default:
break;
}
}
`);
// TOK_WHILE
testPass(`
function f() {
return
while (false)
1 + 2;
}
`);
testPass(`
function f() {
do
return
while (false);
}
`);
// TOK_DO
testPass(`
function f() {
return
do {
1 + 2;
} while (false);
}
`);
// TOK_FOR
testPass(`
function f() {
return
for (;;) {
break;
}
}
`);
// TOK_BREAK
testPass(`
function f() {
for (;;) {
return
break;
}
}
`);
// TOK_CONTINUE
testPass(`
function f() {
for (;;) {
return
continue;
}
}
`);
// TOK_VAR
testPass(`
function f() {
return
var a = 1;
}
`);
// TOK_CONST
testPass(`
function f() {
return
const a = 1;
}
`);
// TOK_WITH
testPass(`
function f() {
return
with ({}) {
1;
}
}
`);
// TOK_RETURN
testPass(`
function f() {
return
return;
}
`);
// TOK_TRY
testPass(`
function f() {
return
try {
} catch (e) {
}
}
`);
// TOK_THROW
testPass(`
function f() {
return
throw 1;
}
`);
// TOK_DEBUGGER
testPass(`
function f() {
return
debugger;
}
`);
// TOK_LET
testPass(`
function f() {
return
let a = 1;
}
`);
// exceptional case
// It's not possible to distinguish between a label statement and an expression
// starts with identifier, by checking a token next to return.
testWarn(`
function f() {
return
a: 1;
}
`, 4, 2);

Просмотреть файл

@ -305,6 +305,7 @@ MSG_DEF(JSMSG_SEMI_AFTER_FOR_COND, 0, JSEXN_SYNTAXERR, "missing ; after for-
MSG_DEF(JSMSG_SEMI_AFTER_FOR_INIT, 0, JSEXN_SYNTAXERR, "missing ; after for-loop initializer")
MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 0, JSEXN_SYNTAXERR, "missing ; before statement")
MSG_DEF(JSMSG_SOURCE_TOO_LONG, 0, JSEXN_RANGEERR, "source is too long")
MSG_DEF(JSMSG_STMT_AFTER_SEMI_LESS, 0, JSEXN_SYNTAXERR, "unreachable expression after semicolon-less return statement")
MSG_DEF(JSMSG_STRICT_CODE_LET_EXPR_STMT, 0, JSEXN_ERR, "strict mode code may not contain unparenthesized let expression statements")
MSG_DEF(JSMSG_STRICT_CODE_WITH, 0, JSEXN_SYNTAXERR, "strict mode code may not contain 'with' statements")
MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")

Просмотреть файл

@ -29,11 +29,11 @@ namespace js {
*
* https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
*/
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 269;
static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 270;
static const uint32_t XDR_BYTECODE_VERSION =
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
static_assert(JSErr_Limit == 390,
static_assert(JSErr_Limit == 391,
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
"removed MSG_DEFs from js.msg, you should increment "
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "