diff --git a/src/jshint.js b/src/jshint.js index f8e5db97f1..5b3bc88473 100644 --- a/src/jshint.js +++ b/src/jshint.js @@ -3172,10 +3172,9 @@ var JSHINT = (function() { return; } - exprs[exprs.length - 1].parent = true; - if (exprs.length > 1) { - ret = Object.create(state.syntax[","], { exprs: { value: exprs } }); + ret = Object.create(state.syntax[","]); + ret.exprs = exprs; first = exprs[0]; last = exprs[exprs.length - 1]; @@ -3219,6 +3218,8 @@ var JSHINT = (function() { } if (ret) { + ret.paren = true; + // The operator may be necessary to override the default binding power of // neighboring operators (whenever there is an operator in use within the // first expression *or* the current group contains multiple expressions) @@ -3862,15 +3863,37 @@ var JSHINT = (function() { state.funct["(metrics)"].ComplexityCount += 1; } - // Parse assignments that were found instead of conditionals. - // For example: if (a = 1) { ... } - + /** + * Detect and warn about assignment that occurs within conditional clauses, + * e.g. + * + * if (a = 1) { ... } + * + * This check is disabled in response to parenthesized expressions so that + * users may opt out of the warning on a case-by-case bases, e.g. + * + * if ((a = 1)) { ... } + * + * @param {object} expr - the parsed expression within the conditional clause + */ function checkCondAssignment(expr) { + if (!expr || expr.paren || state.option.boss) { + return; + } + var id = expr.id; + + // If the expression is composed of multiple sub-expressions via a comma + // operator, then check the final sub-expression. if (id === ",") { expr = expr.exprs[expr.exprs.length - 1]; id = expr.id; + + if (expr.paren) { + return; + } } + switch (id) { case "=": case "+=": @@ -3881,9 +3904,7 @@ var JSHINT = (function() { case "|=": case "^=": case "/=": - if (!expr.paren && !state.option.boss) { - warning("W084"); - } + warning("W084"); } } @@ -5222,7 +5243,7 @@ var JSHINT = (function() { if (!state.option.asi) nolinebreak(this); - if (state.tokens.next.id !== ";" && + if (state.tokens.next.identifier && state.tokens.curr.line === startLine(state.tokens.next)) { if (!state.funct["(scope)"].funct.hasLabel(v)) { warning("W090", state.tokens.next, v); @@ -5250,7 +5271,7 @@ var JSHINT = (function() { if (!state.option.asi) nolinebreak(this); - if (state.tokens.next.id !== ";") { + if (state.tokens.next.identifier) { if (state.tokens.curr.line === startLine(state.tokens.next)) { if (!state.funct["(scope)"].funct.hasLabel(v)) { warning("W090", state.tokens.next, v); diff --git a/tests/unit/parser.js b/tests/unit/parser.js index c34866d624..9ba5099bfd 100644 --- a/tests/unit/parser.js +++ b/tests/unit/parser.js @@ -7508,10 +7508,20 @@ exports["test for GH-1105"] = function (test) { "}" ]; - var run = TestRun(test) - .addError(2, 22, "Missing semicolon."); + TestRun(test) + .addError(2, 22, "Missing semicolon.") + .test(code); + + code = [ + "while (true) {", + " if (true) { continue }", + "}" + ]; + + TestRun(test) + .addError(2, 25, "Missing semicolon.") + .test(code); - run.test(code); test.done(); };