Bug 885463 - Warn about 'yield' without operand. r=Waldo.
authorJason Orendorff <jorendorff@mozilla.com>
Tue, 25 Jun 2013 17:39:59 -0500
changeset 136462 818298a5a18322afe655f8303f88f7cf2f9c2186
parent 136461 8255862d8b2dd0d403128b50ef9fc44cbfdedc9a
child 136463 71c097c1ec7ac9909152c7b0a063765d768df4a2
push id24884
push useremorley@mozilla.com
push dateWed, 26 Jun 2013 14:10:39 +0000
treeherdermozilla-central@c3598b276048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs885463
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 885463 - Warn about 'yield' without operand. r=Waldo.
js/src/frontend/Parser.cpp
js/src/jit-test/lib/asserts.js
js/src/jit-test/tests/parser/yield-without-operand.js
js/src/js.msg
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4420,16 +4420,21 @@ Parser<ParseHandler>::returnStatementOrY
     Node exprNode;
     TokenKind next = tokenStream.peekTokenSameLine(TSF_OPERAND);
     if (next == TOK_ERROR)
         return null();
     if (next == TOK_EOF || next == TOK_EOL || next == TOK_SEMI || next == TOK_RC ||
         (isYield && (next == TOK_YIELD || next == TOK_RB || next == TOK_RP ||
                      next == TOK_COLON || next == TOK_COMMA)))
     {
+        if (isYield) {
+            if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
+                return null();
+        }
+
         exprNode = null();
         if (!isYield)
             pc->funHasReturnVoid = true;
     } else {
         exprNode = isYield ? assignExpr() : expr();
         if (!exprNode)
             return null();
         if (!isYield)
--- a/js/src/jit-test/lib/asserts.js
+++ b/js/src/jit-test/lib/asserts.js
@@ -33,8 +33,69 @@ if (typeof assertThrowsValue === 'undefi
         }
         if (fullmsg === undefined)
             fullmsg = "Assertion failed: expected exception " + val + ", no exception thrown";
         if (msg !== undefined)
             fullmsg += " - " + msg;
         throw new Error(fullmsg);
     };
 }
+
+if (typeof assertWarning === 'undefined') {
+    var assertWarning = function assertWarning(f, errorClass, msg) {
+        var hadWerror = options().split(",").indexOf("werror") !== -1;
+
+        // Ensure the "werror" option is disabled.
+        if (hadWerror)
+            options("werror");
+
+        try {
+            f();
+        } catch (exc) {
+            if (hadWerror)
+                options("werror");
+
+            // print() rather than throw a different exception value, in case
+            // the caller wants exc.stack.
+            if (msg)
+                print("assertWarning: " + msg);
+            print("assertWarning: Unexpected exception calling " + f +
+                  " with warnings-as-errors disabled");
+            throw exc;
+        }
+
+        // Enable the "werror" option.
+        options("werror");
+
+        try {
+            assertThrowsInstanceOf(f, errorClass, msg);
+        } catch (exc) {
+            if (msg)
+                print("assertWarning: " + msg);
+            throw exc;
+        } finally {
+            if (!hadWerror)
+                options("werror");
+        }
+    };
+}
+
+if (typeof assertNoWarning === 'undefined') {
+    var assertNoWarning = function assertWarning(f, msg) {
+        // Ensure the "werror" option is enabled.
+        var hadWerror = options().split(",").indexOf("werror") !== -1;
+        if (!hadWerror)
+            options("werror");
+
+        try {
+            f();
+        } catch (exc) {
+            if (msg)
+                print("assertNoWarning: " + msg);
+            print("assertNoWarning: Unexpected exception calling " + f +
+                  "with warnings-as-errors enabled");
+            throw exc;
+        } finally {
+            if (!hadWerror)
+                options("werror");
+        }
+    };
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/yield-without-operand.js
@@ -0,0 +1,22 @@
+// yield without an operand causes a warning. See bug 885463.
+
+load(libdir + "asserts.js");
+
+assertWarning(() => Function("yield"), SyntaxError,
+             "yield followed by EOF should cause a warning");
+assertWarning(() => Function("yield;"), SyntaxError,
+             "yield followed by semicolon should cause a warning");
+assertWarning(() => Function("yield\n  print('ok');"), SyntaxError,
+             "yield followed by newline should cause a warning");
+
+assertWarning(() => eval("(function () { yield; })"), SyntaxError,
+             "yield followed by semicolon in eval code should cause a warning");
+assertWarning(() => eval("(function () { yield })"), SyntaxError,
+             "yield followed by } in eval code should cause a warning");
+
+assertNoWarning(() => Function("yield 0;"),
+                "yield with an operand should be fine");
+assertNoWarning(() => Function("yield 0"),
+                "yield with an operand should be fine, even without a semicolon");
+
+print("\npassed - all those warnings are normal and there's no real way to suppress them");
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -317,17 +317,17 @@ MSG_DEF(JSMSG_UNUSED263,              26
 MSG_DEF(JSMSG_BAD_CLONE_VERSION,      264, 0, JSEXN_ERR, "unsupported structured clone version")
 MSG_DEF(JSMSG_CANT_CLONE_OBJECT,      265, 0, JSEXN_TYPEERR, "can't clone object")
 MSG_DEF(JSMSG_UNUSED266,              266, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
 MSG_DEF(JSMSG_INVALID_FOR_IN_INIT,    268, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer")
 MSG_DEF(JSMSG_CLEARED_SCOPE,          269, 0, JSEXN_TYPEERR, "attempt to run compile-and-go script on a cleared scope")
 MSG_DEF(JSMSG_MALFORMED_ESCAPE,       270, 1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
 MSG_DEF(JSMSG_BAD_GENEXP_BODY,        271, 1, JSEXN_SYNTAXERR, "illegal use of {0} in generator expression")
-MSG_DEF(JSMSG_UNUSED272,              272, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_YIELD_WITHOUT_OPERAND,  272, 0, JSEXN_SYNTAXERR, "yield without a value is deprecated, and illegal in ES6 (use 'yield undefined' instead)")
 MSG_DEF(JSMSG_UNNAMED_FUNCTION_STMT,  273, 0, JSEXN_SYNTAXERR, "function statement requires a name")
 MSG_DEF(JSMSG_CCW_REQUIRED,           274, 1, JSEXN_TYPEERR, "{0}: argument must be an object from a different compartment")
 MSG_DEF(JSMSG_DEBUG_BAD_RESUMPTION,   275, 0, JSEXN_TYPEERR, "debugger resumption value must be undefined, {throw: val}, {return: val}, or null")
 MSG_DEF(JSMSG_ASSIGN_FUNCTION_OR_NULL, 276, 1, JSEXN_TYPEERR, "value assigned to {0} must be a function or null")
 MSG_DEF(JSMSG_DEBUG_NOT_LIVE,         277, 1, JSEXN_ERR, "{0} is not live")
 MSG_DEF(JSMSG_DEBUG_OBJECT_WRONG_OWNER, 278, 0, JSEXN_TYPEERR, "Debugger.Object belongs to a different Debugger")
 MSG_DEF(JSMSG_DEBUG_OBJECT_PROTO,     279, 0, JSEXN_TYPEERR, "Debugger.Object.prototype is not a valid Debugger.Object")
 MSG_DEF(JSMSG_DEBUG_LOOP,             280, 0, JSEXN_TYPEERR, "cannot debug an object in same compartment as debugger or a compartment that is already debugging the debugger")