Skip to content

Commit

Permalink
[[FIX]] Allow Expression within for-in head
Browse files Browse the repository at this point in the history
As per the ES2015 language grammar:

> A.3 Statements
>
> [...]
>
> IterationStatement[Yield, Return] :
>
>   [...]
>
>   for ( [lookahead ∉ {let [}] LeftHandSideExpression[?Yield] in Expression[In, ?Yield] ) Statement[?Yield, ?Return]
>   for ( var ForBinding[?Yield] in Expression[In, ?Yield] ) Statement[?Yield, ?Return]
>   for ( ForDeclaration[?Yield] in Expression[In, ?Yield] ) Statement[?Yield, ?Return]
>   for ( [lookahead ≠ let ] LeftHandSideExpression[?Yield] of AssignmentExpression[In, ?Yield] ) Statement[?Yield, ?Return]
>   for ( var ForBinding[?Yield] of AssignmentExpression[In, ?Yield] ) Statement[?Yield, ?Return]
>   for ( ForDeclaration[?Yield] of AssignmentExpression[In, ?Yield] ) Statement[?Yield, ?Return]

http://www.ecma-international.org/ecma-262/6.0/#sec-statements
  • Loading branch information
jugglinmike authored and rwaldron committed Aug 15, 2016
1 parent a801433 commit 56c95d0
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 3 deletions.
18 changes: 15 additions & 3 deletions src/jshint.js
Original file line number Diff line number Diff line change
Expand Up @@ -4204,6 +4204,7 @@ var JSHINT = (function() {
var level = 0; // BindingPattern "level" --- level 0 === no BindingPattern
var comma; // First comma punctuator at level 0
var initializer; // First initializer at level 0
var bindingPower;

// If initial token is a BindingPattern, count it as such.
if (checkPunctuators(state.tokens.next, ["{", "["])) ++level;
Expand All @@ -4222,8 +4223,13 @@ var JSHINT = (function() {

// if we're in a for (… in|of …) statement
if (_.contains(inof, nextop.value)) {
if (!state.inES6() && nextop.value === "of") {
warning("W104", nextop, "for of", "6");
if (nextop.value === "of") {
bindingPower = 20;
if (!state.inES6()) {
warning("W104", nextop, "for of", "6");
}
} else {
bindingPower = 0;
}

var ok = !(initializer || comma);
Expand All @@ -4250,7 +4256,13 @@ var JSHINT = (function() {
Object.create(varstatement).fud({ prefix: true, implied: "for", ignore: !ok });
}
advance(nextop.value);
expression(20);
// The binding power is variable because for-in statements accept any
// Expression in this position, while for-of statements are limited to
// AssignmentExpressions. For example:
//
// for ( LeftHandSideExpression in Expression ) Statement
// for ( LeftHandSideExpression of AssignmentExpression ) Statement
expression(bindingPower);
advance(")", t);

if (nextop.value === "in" && state.option.forin) {
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -7774,3 +7774,22 @@ exports.instanceOfLiterals = function (test) {

test.done();
};

exports.forInExpr = function (test) {
TestRun(test)
.test([
"for (var x in [], []) {}"
]);

TestRun(test)
.addError(2, "Expected ')' to match '(' from line 2 and instead saw ','.")
.addError(2, "Expected an identifier and instead saw ')'.")
.addError(2, "Expected an assignment or function call and instead saw an expression.")
.addError(2, "Missing semicolon.")
.test([
"for (var x in [], []) {}",
"for (var x of {}, {}) {}"
], { esversion: 6 });

test.done();
};

0 comments on commit 56c95d0

Please sign in to comment.