diff --git a/docs/rules/no-sequences.md b/docs/rules/no-sequences.md index 602a412f16a..af4a7a0edcf 100644 --- a/docs/rules/no-sequences.md +++ b/docs/rules/no-sequences.md @@ -17,7 +17,7 @@ while (a = next(), a && a.length); This rule forbids the use of the comma operator, with the following exceptions: * In the initialization or update portions of a `for` statement. -* If the expression sequence is explicitly wrapped in parentheses. +* By default, if the expression sequence is explicitly wrapped in parentheses. This exception can be removed with the `allowInParentheses` option. Examples of **incorrect** code for this rule: @@ -63,10 +63,48 @@ while ((val = foo(), val < 42)); with ((doSomething(), val)) {} ``` +## Options + +This rule takes one option, an object, with the following properties: + +* `"allowInParentheses"`: If set to `true` (default), this rule allows expression sequences that are explicitly wrapped in parentheses. + +### allowInParentheses + +Examples of **incorrect** code for this rule with the `{ "allowInParentheses": false }` option: + +```js +/*eslint no-sequences: ["error", { "allowInParentheses": false }]*/ + +foo = (doSomething(), val); + +(0, eval)("doSomething();"); + +do {} while ((doSomething(), !!test)); + +for (; (doSomething(), !!test); ); + +if ((doSomething(), !!test)); + +switch ((val = foo(), val)) {} + +while ((val = foo(), val < 42)); + +with ((doSomething(), val)) {} +``` + +Examples of **correct** code for this rule with the `{ "allowInParentheses": false }` option: + +```js +/*eslint no-sequences: ["error", { "allowInParentheses": false }]*/ + +for (i = 0, j = 10; i < j; i++, j--); +``` + ## When Not To Use It Disable this rule if sequence expressions with the comma operator are acceptable. -Another case is where you might want to report all usages of the comma operator, even if they are wrapped in parentheses or in a for loop. You can achieve this using rule `no-restricted-syntax`: +Another case is where you might want to report all usages of the comma operator, even in a for loop. You can achieve this using rule `no-restricted-syntax`: ```js { diff --git a/lib/rules/no-sequences.js b/lib/rules/no-sequences.js index d67635d1175..fe516975fbc 100644 --- a/lib/rules/no-sequences.js +++ b/lib/rules/no-sequences.js @@ -11,6 +11,14 @@ const astUtils = require("./utils/ast-utils"); +//------------------------------------------------------------------------------ +// Helpers +//------------------------------------------------------------------------------ + +const DEFAULT_OPTIONS = { + allowInParentheses: true +}; + //------------------------------------------------------------------------------ // Rule Definition //------------------------------------------------------------------------------ @@ -26,7 +34,15 @@ module.exports = { url: "https://eslint.org/docs/rules/no-sequences" }, - schema: [], + schema: [{ + properties: { + allowInParentheses: { + type: "boolean", + default: true + } + }, + additionalProperties: false + }], messages: { unexpectedCommaExpression: "Unexpected use of comma operator." @@ -34,6 +50,7 @@ module.exports = { }, create(context) { + const options = Object.assign({}, DEFAULT_OPTIONS, context.options[0]); const sourceCode = context.getSourceCode(); /** @@ -99,13 +116,15 @@ module.exports = { } // Wrapping a sequence in extra parens indicates intent - if (requiresExtraParens(node)) { - if (isParenthesisedTwice(node)) { - return; - } - } else { - if (isParenthesised(node)) { - return; + if (options.allowInParentheses) { + if (requiresExtraParens(node)) { + if (isParenthesisedTwice(node)) { + return; + } + } else { + if (isParenthesised(node)) { + return; + } } } diff --git a/tests/lib/rules/no-sequences.js b/tests/lib/rules/no-sequences.js index ae988ee3dae..f9eb8a6a30f 100644 --- a/tests/lib/rules/no-sequences.js +++ b/tests/lib/rules/no-sequences.js @@ -46,10 +46,20 @@ ruleTester.run("no-sequences", rule, { "do {} while ((doSomething(), !!test));", "for ((doSomething(), somethingElse()); (doSomething(), !!test); );", "if ((doSomething(), !!test));", - "switch ((doSomething(), !!test)) {}", + "switch ((doSomething(), val)) {}", "while ((doSomething(), !!test));", "with ((doSomething(), val)) {}", - { code: "a => ((doSomething(), a))", env: { es6: true } } + { code: "a => ((doSomething(), a))", env: { es6: true } }, + + // options object without "allowInParentheses" property + { code: "var foo = (1, 2);", options: [{}] }, + + // explicitly set option "allowInParentheses" to default value + { code: "var foo = (1, 2);", options: [{ allowInParentheses: true }] }, + + // valid code with "allowInParentheses" set to `false` + { code: "for ((i = 0, j = 0); test; );", options: [{ allowInParentheses: false }] }, + { code: "for (; test; (i++, j++));", options: [{ allowInParentheses: false }] } ], // Examples of code that should trigger the rule @@ -75,6 +85,18 @@ ruleTester.run("no-sequences", rule, { { code: "a => (doSomething(), a)", env: { es6: true }, errors: errors(20) }, { code: "(1), 2", errors: errors(4) }, { code: "((1)) , (2)", errors: errors(7) }, - { code: "while((1) , 2);", errors: errors(11) } + { code: "while((1) , 2);", errors: errors(11) }, + + // option "allowInParentheses": do not allow sequence in parentheses + { code: "var foo = (1, 2);", options: [{ allowInParentheses: false }], errors: errors(13) }, + { code: "(0,eval)(\"foo()\");", options: [{ allowInParentheses: false }], errors: errors(3) }, + { code: "foo(a, (b, c), d);", options: [{ allowInParentheses: false }], errors: errors(10) }, + { code: "do {} while ((doSomething(), !!test));", options: [{ allowInParentheses: false }], errors: errors(28) }, + { code: "for (; (doSomething(), !!test); );", options: [{ allowInParentheses: false }], errors: errors(22) }, + { code: "if ((doSomething(), !!test));", options: [{ allowInParentheses: false }], errors: errors(19) }, + { code: "switch ((doSomething(), val)) {}", options: [{ allowInParentheses: false }], errors: errors(23) }, + { code: "while ((doSomething(), !!test));", options: [{ allowInParentheses: false }], errors: errors(22) }, + { code: "with ((doSomething(), val)) {}", options: [{ allowInParentheses: false }], errors: errors(21) }, + { code: "a => ((doSomething(), a))", options: [{ allowInParentheses: false }], env: { es6: true }, errors: errors(21) } ] });