Skip to content

Commit

Permalink
Update: improve max-statements-per-line message (fixes #6287)
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Sep 3, 2016
1 parent 4126f12 commit 10cd56b
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 47 deletions.
26 changes: 21 additions & 5 deletions lib/rules/max-statements-per-line.js
Expand Up @@ -35,18 +35,31 @@ 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
//--------------------------------------------------------------------------

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.
*
Expand Down Expand Up @@ -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;
}
}

Expand All @@ -106,6 +120,7 @@ module.exports = {

// Update state.
if (line !== lastStatementLine) {
reportFirstExtraStatementAndClear();
numberOfStatementsOnThisLine = 1;
lastStatementLine = line;
}
Expand Down Expand Up @@ -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});
}
}
};
Expand Down
84 changes: 42 additions & 42 deletions tests/lib/rules/max-statements-per-line.js
Expand Up @@ -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." }] }
]
});

0 comments on commit 10cd56b

Please sign in to comment.