зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1005110 - Warn about unreachable expression after semicolon-less return. r=efaust
This commit is contained in:
Родитель
567240287f
Коммит
01a24c421f
|
@ -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 "
|
||||
|
|
Загрузка…
Ссылка в новой задаче