diff --git a/lib/rules/semi.js b/lib/rules/semi.js index 87086e981b0..8ec650f0678 100644 --- a/lib/rules/semi.js +++ b/lib/rules/semi.js @@ -78,6 +78,8 @@ module.exports = { create(context) { const OPT_OUT_PATTERN = /^[-[(/+`]/u; // One of [(/+-` + const unsafeClassFieldNames = new Set(["get", "set", "static"]); + const unsafeClassFieldFollowers = new Set(["*", "in", "instanceof"]); const options = context.options[1]; const never = context.options[0] === "never"; const exceptOneLine = Boolean(options && options.omitLastInOneLineBlock); @@ -166,6 +168,30 @@ module.exports = { ); } + /** + * Checks if a given PropertyDefinition node followed by a semicolon + * can safely remove that semicolon. It is not to safe to remove if + * the class field name is "get", "set", or "static", or if + * followed by a generator method. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node cannot have the semicolon + * removed. + */ + function maybeClassFieldAsiHazard(node) { + + if (node.type !== "PropertyDefinition") { + return false; + } + + if (unsafeClassFieldNames.has(node.key.name)) { + return true; + } + + const followingToken = sourceCode.getTokenAfter(node); + + return unsafeClassFieldFollowers.has(followingToken.value); + } + /** * Check whether a given node is on the same line with the next token. * @param {Node} node A statement node to check. @@ -233,6 +259,9 @@ module.exports = { * @returns {boolean} whether the semicolon is unnecessary. */ function canRemoveSemicolon(node) { + if (never && maybeClassFieldAsiHazard(node)) { + return false; + } if (isRedundantSemi(sourceCode.getLastToken(node))) { return true; // `;;` or `;}` } diff --git a/tests/lib/rules/semi.js b/tests/lib/rules/semi.js index 092dae15336..3f18e11bdd5 100644 --- a/tests/lib/rules/semi.js +++ b/tests/lib/rules/semi.js @@ -306,6 +306,36 @@ ruleTester.run("semi", rule, { code: "class C { foo() {}; }", // no-extra-semi reports it options: ["never"], parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { a=b;\n*foo() {} }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { get;\nfoo() {} }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { set;\nfoo() {} }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { static;\nfoo() {} }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { a=b;\nin }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { a=b;\ninstanceof }", + options: ["never"], + parserOptions: { ecmaVersion: 2022 } } ], invalid: [