diff --git a/lib/rules/indent.js b/lib/rules/indent.js index 9c534cd58f0..bc6aa9e5c0c 100644 --- a/lib/rules/indent.js +++ b/lib/rules/indent.js @@ -916,18 +916,6 @@ module.exports = { } offsets.setDesiredOffsets([firstBodyToken.range[0], lastBodyToken.range[1]], lastParentToken, 1); - - /* - * For blockless nodes with semicolon-first style, don't indent the semicolon. - * e.g. - * if (foo) bar() - * ; [1, 2, 3].map(foo) - */ - const lastToken = sourceCode.getLastToken(node); - - if (node.type !== "EmptyStatement" && astUtils.isSemicolonToken(lastToken)) { - offsets.setDesiredOffset(lastToken, lastParentToken, 0); - } } } @@ -1271,6 +1259,50 @@ module.exports = { } }, + /* + * For blockless nodes with semicolon-first style, don't indent the semicolon. + * e.g. + * if (foo) + * bar() + * ; [1, 2, 3].map(foo) + * + * Traversal into the node sets indentation of the semicolon, so we need to override it on exit. + */ + ":matches(DoWhileStatement, ForStatement, ForInStatement, ForOfStatement, IfStatement, WhileStatement):exit"(node) { + let nodesToCheck; + + if (node.type === "IfStatement") { + nodesToCheck = [node.consequent]; + if (node.alternate) { + nodesToCheck.push(node.alternate); + } + } else { + nodesToCheck = [node.body]; + } + + for (const nodeToCheck of nodesToCheck) { + const lastToken = sourceCode.getLastToken(nodeToCheck); + + if (astUtils.isSemicolonToken(lastToken)) { + const tokenBeforeLast = sourceCode.getTokenBefore(lastToken); + const tokenAfterLast = sourceCode.getTokenAfter(lastToken); + + // override indentation of `;` only if its line looks like a semicolon-first style line + if ( + !astUtils.isTokenOnSameLine(tokenBeforeLast, lastToken) && + tokenAfterLast && + astUtils.isTokenOnSameLine(lastToken, tokenAfterLast) + ) { + offsets.setDesiredOffset( + lastToken, + sourceCode.getFirstToken(node), + 0 + ); + } + } + } + }, + ImportDeclaration(node) { if (node.specifiers.some(specifier => specifier.type === "ImportSpecifier")) { const openingCurly = sourceCode.getFirstToken(node, astUtils.isOpeningBraceToken); diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js index 39476791420..4d7d6104663 100644 --- a/tests/lib/rules/indent.js +++ b/tests/lib/rules/indent.js @@ -6082,6 +6082,262 @@ ruleTester.run("indent", rule, { `, options: [4, { FunctionExpression: { body: 2 }, StaticBlock: { body: 2 } }], parserOptions: { ecmaVersion: 2022 } + }, + + // https://github.com/eslint/eslint/issues/15930 + { + code: unIndent` + if (2 > 1) + \tconsole.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: ["tab"] + }, + { + code: unIndent` + if (2 > 1) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4] + }, + { + code: unIndent` + if (foo) bar(); + baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo) bar() + ;baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + bar(); + baz(); + `, + options: [4] + }, + { + code: unIndent` + if (foo) + bar() + ; baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + bar() + ;baz() + qux() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + bar() + ;else + baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + bar() + else + baz() + ;qux() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + if (bar) + baz() + ;qux() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + bar() + else if (baz) + qux() + ;quux() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + if (bar) + baz() + else + qux() + ;quux() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + bar() + ; + baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + ; + baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + ;baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo); + else + baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + ; + else + baz() + `, + options: [4] + }, + { + code: unIndent` + if (foo) + ;else + baz() + `, + options: [4] + }, + { + code: unIndent` + do foo(); + while (bar) + `, + options: [4] + }, + { + code: unIndent` + do foo() + ;while (bar) + `, + options: [4] + }, + { + code: unIndent` + do + foo(); + while (bar) + `, + options: [4] + }, + { + code: unIndent` + do + foo() + ;while (bar) + `, + options: [4] + }, + { + code: unIndent` + do; + while (foo) + `, + options: [4] + }, + { + code: unIndent` + do + ; + while (foo) + `, + options: [4] + }, + { + code: unIndent` + do + ;while (foo) + `, + options: [4] + }, + { + code: unIndent` + while (2 > 1) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4] + }, + { + code: unIndent` + for (;;) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4] + }, + { + code: unIndent` + for (a in b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4] + }, + { + code: unIndent` + for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4] + }, + { + code: unIndent` + label: for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4] + }, + { + code: unIndent` + label: + for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4] } ], @@ -12623,6 +12879,457 @@ ruleTester.run("indent", rule, { [6, 12, 0, "Identifier"], [7, 4, 0, "Punctuator"] ]) + }, + + // https://github.com/eslint/eslint/issues/15930 + { + code: unIndent` + if (2 > 1) + \tconsole.log('a') + \t;[1, 2, 3].forEach(x=>console.log(x)) + `, + output: unIndent` + if (2 > 1) + \tconsole.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: ["tab"], + errors: expectedErrors("tab", [3, 0, 1, "Punctuator"]) + }, + { + code: unIndent` + if (2 > 1) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + output: unIndent` + if (2 > 1) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo) bar(); + baz() + `, + output: unIndent` + if (foo) bar(); + baz() + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Identifier"]) + }, + { + code: unIndent` + if (foo) bar() + ;baz() + `, + output: unIndent` + if (foo) bar() + ;baz() + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + bar(); + baz(); + `, + output: unIndent` + if (foo) + bar(); + baz(); + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Identifier"]) + }, + { + code: unIndent` + if (foo) + bar() + ; baz() + `, + output: unIndent` + if (foo) + bar() + ; baz() + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + bar() + ;baz() + qux() + `, + output: unIndent` + if (foo) + bar() + ;baz() + qux() + `, + options: [4], + errors: expectedErrors([ + [3, 0, 4, "Punctuator"], + [4, 0, 4, "Identifier"] + ]) + }, + { + code: unIndent` + if (foo) + bar() + ;else + baz() + `, + output: unIndent` + if (foo) + bar() + ;else + baz() + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + bar() + else + baz() + ;qux() + `, + output: unIndent` + if (foo) + bar() + else + baz() + ;qux() + `, + options: [4], + errors: expectedErrors([5, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + if (bar) + baz() + ;qux() + `, + output: unIndent` + if (foo) + if (bar) + baz() + ;qux() + `, + options: [4], + errors: expectedErrors([4, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + bar() + else if (baz) + qux() + ;quux() + `, + output: unIndent` + if (foo) + bar() + else if (baz) + qux() + ;quux() + `, + options: [4], + errors: expectedErrors([5, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + if (bar) + baz() + else + qux() + ;quux() + `, + output: unIndent` + if (foo) + if (bar) + baz() + else + qux() + ;quux() + `, + options: [4], + errors: expectedErrors([6, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + bar() + ; + baz() + `, + output: unIndent` + if (foo) + bar() + ; + baz() + `, + options: [4], + errors: expectedErrors([3, 4, 0, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + ; + baz() + `, + output: unIndent` + if (foo) + ; + baz() + `, + options: [4], + errors: expectedErrors([2, 4, 0, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + ;baz() + `, + output: unIndent` + if (foo) + ;baz() + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + if (foo); + else + baz() + `, + output: unIndent` + if (foo); + else + baz() + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Keyword"]) + }, + { + code: unIndent` + if (foo) + ; + else + baz() + `, + output: unIndent` + if (foo) + ; + else + baz() + `, + options: [4], + errors: expectedErrors([2, 4, 0, "Punctuator"]) + }, + { + code: unIndent` + if (foo) + ;else + baz() + `, + output: unIndent` + if (foo) + ;else + baz() + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + do foo(); + while (bar) + `, + output: unIndent` + do foo(); + while (bar) + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Keyword"]) + }, + { + code: unIndent` + do foo() + ;while (bar) + `, + output: unIndent` + do foo() + ;while (bar) + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + do + foo(); + while (bar) + `, + output: unIndent` + do + foo(); + while (bar) + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Keyword"]) + }, + { + code: unIndent` + do + foo() + ;while (bar) + `, + output: unIndent` + do + foo() + ;while (bar) + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + do; + while (foo) + `, + output: unIndent` + do; + while (foo) + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Keyword"]) + }, + { + code: unIndent` + do + ; + while (foo) + `, + output: unIndent` + do + ; + while (foo) + `, + options: [4], + errors: expectedErrors([2, 4, 0, "Punctuator"]) + }, + { + code: unIndent` + do + ;while (foo) + `, + output: unIndent` + do + ;while (foo) + `, + options: [4], + errors: expectedErrors([2, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + while (2 > 1) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + output: unIndent` + while (2 > 1) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + for (;;) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + output: unIndent` + for (;;) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + for (a in b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + output: unIndent` + for (a in b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + output: unIndent` + for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + label: for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + output: unIndent` + label: for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4], + errors: expectedErrors([3, 0, 4, "Punctuator"]) + }, + { + code: unIndent` + label: + for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + output: unIndent` + label: + for (a of b) + console.log('a') + ;[1, 2, 3].forEach(x=>console.log(x)) + `, + options: [4], + errors: expectedErrors([4, 0, 4, "Punctuator"]) } ] });