From 10cd56b5f86fe8624ddc7b8ec4ca66f6e93fe19d Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 2 Sep 2016 22:43:09 -0700 Subject: [PATCH] Update: improve `max-statements-per-line` message (fixes #6287) --- lib/rules/max-statements-per-line.js | 26 +++++-- tests/lib/rules/max-statements-per-line.js | 84 +++++++++++----------- 2 files changed, 63 insertions(+), 47 deletions(-) diff --git a/lib/rules/max-statements-per-line.js b/lib/rules/max-statements-per-line.js index 2ff7a4ee960f..9d0a71bb7c02 100644 --- a/lib/rules/max-statements-per-line.js +++ b/lib/rules/max-statements-per-line.js @@ -35,11 +35,12 @@ module.exports = { const sourceCode = context.getSourceCode(), options = context.options[0] || {}, maxStatementsPerLine = typeof options.max !== "undefined" ? options.max : 1, - message = "This line has too many statements. Maximum allowed is {{maxStatementsPerLine}}.", - data = { maxStatementsPerLine }; + emptyBlockMessage = "This line has an empty block, but no statements are allowed.", + message = "This line has {{numberOfStatementsOnThisLine}} statement(s). Maximum allowed is {{maxStatementsPerLine}}."; let lastStatementLine = 0, - numberOfStatementsOnThisLine = 0; + numberOfStatementsOnThisLine = 0, + firstExtraStatement; //-------------------------------------------------------------------------- // Helpers @@ -47,6 +48,18 @@ module.exports = { const SINGLE_CHILD_ALLOWED = /^(?:(?:DoWhile|For|ForIn|ForOf|If|Labeled|While)Statement|Export(?:Default|Named)Declaration)$/; + /** + * Reports with the first extra statement, and clears it. + * + * @returns {void} + */ + function reportFirstExtraStatementAndClear() { + if (firstExtraStatement) { + context.report({node: firstExtraStatement, message, data: {numberOfStatementsOnThisLine, maxStatementsPerLine}}); + } + firstExtraStatement = null; + } + /** * Gets the actual last token of a given node. * @@ -85,13 +98,14 @@ module.exports = { if (line === lastStatementLine) { numberOfStatementsOnThisLine += 1; } else { + reportFirstExtraStatementAndClear(); numberOfStatementsOnThisLine = 1; lastStatementLine = line; } // Reports if the node violated this rule. if (numberOfStatementsOnThisLine === maxStatementsPerLine + 1) { - context.report({node, message, data}); + firstExtraStatement = firstExtraStatement || node; } } @@ -106,6 +120,7 @@ module.exports = { // Update state. if (line !== lastStatementLine) { + reportFirstExtraStatementAndClear(); numberOfStatementsOnThisLine = 1; lastStatementLine = line; } @@ -163,12 +178,13 @@ module.exports = { "ExportNamedDeclaration:exit": leaveStatement, "ExportDefaultDeclaration:exit": leaveStatement, "ExportAllDeclaration:exit": leaveStatement, + "Program:exit": reportFirstExtraStatementAndClear, // For backward compatibility. // Empty blocks should be warned if `{max: 0}` was given. BlockStatement: function reportIfZero(node) { if (maxStatementsPerLine === 0 && node.body.length === 0) { - context.report({node, message, data}); + context.report({node, message: emptyBlockMessage}); } } }; diff --git a/tests/lib/rules/max-statements-per-line.js b/tests/lib/rules/max-statements-per-line.js index 9aebd2abbd38..a7f48e035811 100644 --- a/tests/lib/rules/max-statements-per-line.js +++ b/tests/lib/rules/max-statements-per-line.js @@ -117,47 +117,47 @@ ruleTester.run("max-statements-per-line", rule, { } ], invalid: [ - { code: "{ }", options: [{ max: 0 }], errors: [{ message: "This line has too many statements. Maximum allowed is 0." }] }, - { code: "var bar = 1;", options: [{ max: 0 }], errors: [{ message: "This line has too many statements. Maximum allowed is 0." }] }, - { code: "var bar = 1; var baz = 2;", errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "var bar = 1; var baz = 2;", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "if (condition) var bar = 1; if (condition) var baz = 2;", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "if (condition) var bar = 1; else var baz = 1;", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "if (condition) { } if (condition) { }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "if (condition) { var bar = 1; } else { }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "if (condition) { } else { var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "if (condition) { var bar = 1; } else { var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "for (var i = 0; i < length; ++i) { var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "switch (discriminant) { default: break; }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "function foo() { var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "function foo() { if (condition) var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "function foo() { if (condition) { var bar = 1; } }", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "(function() { var bar = 1; })();", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "var foo = function foo() { var bar = 1; };", options: [{ max: 1 }], errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "var foo = { prop: () => { var bar = 1; } };", options: [{ max: 1 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "var bar = 1; var baz = 2; var qux = 3;", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "if (condition) { var bar = 1; var baz = 2; }", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "if (condition) { var bar = 1; } else { var bar = 1; }", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "if (condition) { var bar = 1; var baz = 2; } else { var bar = 1; var baz = 2; }", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "for (var i = 0; i < length; ++i) { var bar = 1; var baz = 2; }", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "switch (discriminant) { case 'test': break; default: break; }", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "function foo() { var bar = 1; var baz = 2; }", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "function foo() { if (condition) { var bar = 1; } }", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "(function() { var bar = 1; var baz = 2; })();", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "var foo = function foo() { var bar = 1; var baz = 2; };", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "var foo = { prop: () => { var bar = 1; var baz = 2; } };", options: [{ max: 2 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "var bar = 1; var baz = 2; var qux = 3; var waldo = 4;", options: [{ max: 3 }], errors: [{ message: "This line has too many statements. Maximum allowed is 3." }] }, - { code: "if (condition) { var bar = 1; var baz = 2; var qux = 3; }", options: [{ max: 3 }], errors: [{ message: "This line has too many statements. Maximum allowed is 3." }] }, - { code: "if (condition) { var bar = 1; var baz = 2; } else { var bar = 1; var baz = 2; }", options: [{ max: 3 }], errors: [{ message: "This line has too many statements. Maximum allowed is 3." }] }, - { code: "switch (discriminant) { case 'test': var bar = 1; break; default: var bar = 1; break; }", options: [{ max: 3 }], errors: [{ message: "This line has too many statements. Maximum allowed is 3." }] }, - { code: "let bar = bar => { a; }, baz = baz => { b; }, qux = qux => { c; };", options: [{ max: 3 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has too many statements. Maximum allowed is 3." }] }, - { code: "(bar => { a; }) ? (baz => { b; }) : (qux => { c; });", options: [{ max: 3 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has too many statements. Maximum allowed is 3." }] }, - { code: "bar => { a; }, baz => { b; }, qux => { c; }, quux => { d; };", options: [{ max: 4 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has too many statements. Maximum allowed is 4." }] }, - { code: "[bar => { a; }, baz => { b; }, qux => { c; }, quux => { d; }];", options: [{ max: 4 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has too many statements. Maximum allowed is 4." }] }, - { code: "foo(bar => { a; }, baz => { b; }, qux => { c; }, quux => { d; });", options: [{ max: 4 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has too many statements. Maximum allowed is 4." }] }, - { code: "({ bar: bar => { a; }, baz: baz => { b; }, qux: qux => { c; }, quux: quux => { d; }});", options: [{ max: 4 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has too many statements. Maximum allowed is 4." }] }, - { code: "a; if (b) { c; d; }\nz;", options: [{ max: 2 }], errors: [{ message: "This line has too many statements. Maximum allowed is 2." }] }, - { code: "export default function foo() { console.log('test') }", options: [{ max: 1 }], parserOptions: {ecmaVersion: 6, sourceType: "module"}, errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] }, - { code: "export function foo() { console.log('test') }", options: [{ max: 1 }], parserOptions: {ecmaVersion: 6, sourceType: "module"}, errors: [{ message: "This line has too many statements. Maximum allowed is 1." }] } + { code: "{ }", options: [{ max: 0 }], errors: [{ message: "This line has an empty block, but the maximum allowed is 0." }] }, + { code: "var bar = 1;", options: [{ max: 0 }], errors: [{ message: "This line has 1 statement(s). Maximum allowed is 0." }] }, + { code: "var bar = 1; var baz = 2;", errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "var bar = 1; var baz = 2;", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "if (condition) var bar = 1; if (condition) var baz = 2;", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "if (condition) var bar = 1; else var baz = 1;", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "if (condition) { } if (condition) { }", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "if (condition) { var bar = 1; } else { }", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "if (condition) { } else { var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "if (condition) { var bar = 1; } else { var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 1." }] }, + { code: "for (var i = 0; i < length; ++i) { var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "switch (discriminant) { default: break; }", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "function foo() { var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "function foo() { if (condition) var bar = 1; }", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "function foo() { if (condition) { var bar = 1; } }", options: [{ max: 1 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 1." }] }, + { code: "(function() { var bar = 1; })();", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "var foo = function foo() { var bar = 1; };", options: [{ max: 1 }], errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "var foo = { prop: () => { var bar = 1; } };", options: [{ max: 1 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "var bar = 1; var baz = 2; var qux = 3;", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "if (condition) { var bar = 1; var baz = 2; }", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "if (condition) { var bar = 1; } else { var bar = 1; }", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "if (condition) { var bar = 1; var baz = 2; } else { var bar = 1; var baz = 2; }", options: [{ max: 2 }], errors: [{ message: "This line has 5 statement(s). Maximum allowed is 2." }] }, + { code: "for (var i = 0; i < length; ++i) { var bar = 1; var baz = 2; }", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "switch (discriminant) { case 'test': break; default: break; }", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "function foo() { var bar = 1; var baz = 2; }", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "function foo() { if (condition) { var bar = 1; } }", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "(function() { var bar = 1; var baz = 2; })();", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "var foo = function foo() { var bar = 1; var baz = 2; };", options: [{ max: 2 }], errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "var foo = { prop: () => { var bar = 1; var baz = 2; } };", options: [{ max: 2 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has 3 statement(s). Maximum allowed is 2." }] }, + { code: "var bar = 1; var baz = 2; var qux = 3; var waldo = 4;", options: [{ max: 3 }], errors: [{ message: "This line has 4 statement(s). Maximum allowed is 3." }] }, + { code: "if (condition) { var bar = 1; var baz = 2; var qux = 3; }", options: [{ max: 3 }], errors: [{ message: "This line has 4 statement(s). Maximum allowed is 3." }] }, + { code: "if (condition) { var bar = 1; var baz = 2; } else { var bar = 1; var baz = 2; }", options: [{ max: 3 }], errors: [{ message: "This line has 5 statement(s). Maximum allowed is 3." }] }, + { code: "switch (discriminant) { case 'test': var bar = 1; break; default: var bar = 1; break; }", options: [{ max: 3 }], errors: [{ message: "This line has 5 statement(s). Maximum allowed is 3." }] }, + { code: "let bar = bar => { a; }, baz = baz => { b; }, qux = qux => { c; };", options: [{ max: 3 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has 4 statement(s). Maximum allowed is 3." }] }, + { code: "(bar => { a; }) ? (baz => { b; }) : (qux => { c; });", options: [{ max: 3 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has 4 statement(s). Maximum allowed is 3." }] }, + { code: "bar => { a; }, baz => { b; }, qux => { c; }, quux => { d; };", options: [{ max: 4 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has 5 statement(s). Maximum allowed is 4." }] }, + { code: "[bar => { a; }, baz => { b; }, qux => { c; }, quux => { d; }];", options: [{ max: 4 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has 5 statement(s). Maximum allowed is 4." }] }, + { code: "foo(bar => { a; }, baz => { b; }, qux => { c; }, quux => { d; });", options: [{ max: 4 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has 5 statement(s). Maximum allowed is 4." }] }, + { code: "({ bar: bar => { a; }, baz: baz => { b; }, qux: qux => { c; }, quux: quux => { d; }});", options: [{ max: 4 }], parserOptions: { ecmaVersion: 6 }, errors: [{ message: "This line has 5 statement(s). Maximum allowed is 4." }] }, + { code: "a; if (b) { c; d; }\nz;", options: [{ max: 2 }], errors: [{ message: "This line has 4 statement(s). Maximum allowed is 2." }] }, + { code: "export default function foo() { console.log('test') }", options: [{ max: 1 }], parserOptions: {ecmaVersion: 6, sourceType: "module"}, errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] }, + { code: "export function foo() { console.log('test') }", options: [{ max: 1 }], parserOptions: {ecmaVersion: 6, sourceType: "module"}, errors: [{ message: "This line has 2 statement(s). Maximum allowed is 1." }] } ] });