Repository: eslint/eslint
Pull request #15287 (2021-11-11 10:59:36)

feat: update no-extra-semi for class static blocks
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/HEAD/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[ ] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-proposal.md))
[x] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/HEAD/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

Refs #15016, fixes `no-extra-semi`.

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)

Updated the `no-extra-semi` rule to report semicolons after class static blocks.

```js
/*eslint no-extra-semi: "error"*/

class C {    
    static {        
    }; // error
}
```


#### Is there anything you'd like reviewers to focus on?



Patch:
diff --git a/README.md b/README.md
index 1f6df4ce7..d5f156a74 100644
--- a/README.md
+++ b/README.md
@@ -206,6 +206,7 @@ This means:
 These folks keep the project moving and are resources for help.
 
 <!-- NOTE: This section is autogenerated. Do not manually edit.-->
+
 <!--teamstart-->
 
 ### Technical Steering Committee (TSC)
@@ -288,6 +289,7 @@ Nitin Kumar
 
 <!--teamend-->
 
+
 ## <a name="sponsors"></a>Sponsors
 
 The following companies, organizations, and individuals support ESLint's ongoing maintenance and development. [Become a Sponsor](https://opencollective.com/eslint) to get your logo on our README and website.
diff --git a/docs/developer-guide/contributing/pull-requests.md b/docs/developer-guide/contributing/pull-requests.md
index 9c6f62fff..dde1c3144 100644
--- a/docs/developer-guide/contributing/pull-requests.md
+++ b/docs/developer-guide/contributing/pull-requests.md
@@ -64,7 +64,7 @@ The `tag` is one of the following:
 * `fix` - for a bug fix.
 * `feat` - either for a backwards-compatible enhancement or for a rule change that adds reported problems.
 * `fix!` - for a backwards-incompatible bug fix.
-* `feat!:` - for a backwards-incompatible enhancement or feature.
+* `feat!` - for a backwards-incompatible enhancement or feature.
 * `docs` - changes to documentation only.
 * `chore` - for changes that aren't user-facing.
 * `build` - changes to build process only.
diff --git a/docs/rules/block-scoped-var.md b/docs/rules/block-scoped-var.md
index 75c7db025..9c308feb2 100644
--- a/docs/rules/block-scoped-var.md
+++ b/docs/rules/block-scoped-var.md
@@ -41,6 +41,15 @@ function doFor() {
     }
     console.log(y);
 }
+
+class C {
+    static {
+        if (something) {
+            var build = true;
+        }
+        build = false;
+    }
+}
 ```
 
 Examples of **correct** code for this rule:
@@ -85,6 +94,15 @@ function doFor() {
         console.log(y);
     }
 }
+
+class C {
+    static {
+        var build = false;
+        if (something) {
+            build = true;
+        }
+    }
+}
 ```
 
 ## Further Reading
diff --git a/docs/rules/lines-around-comment.md b/docs/rules/lines-around-comment.md
index 70282513d..ae543bd83 100644
--- a/docs/rules/lines-around-comment.md
+++ b/docs/rules/lines-around-comment.md
@@ -1,7 +1,5 @@
 # require empty lines around comments (lines-around-comment)
 
-This rule was **deprecated** in ESLint v4.0.0 and replaced by the [`padding-line-between-statements`](padding-line-between-statements.md) rule.
-
 Many style guides require empty lines before or after comments. The primary goal
 of these rules is to make the comments easier to read and improve readability of the code.
 
diff --git a/docs/rules/no-extra-semi.md b/docs/rules/no-extra-semi.md
index f4dd8e09c..a33a493c8 100644
--- a/docs/rules/no-extra-semi.md
+++ b/docs/rules/no-extra-semi.md
@@ -17,6 +17,17 @@ function foo() {
     // code
 };
 
+class C {
+    field;;
+
+    method() {
+        // code
+    };
+
+    static {
+        // code
+    };
+};
 ```
 
 Examples of **correct** code for this rule:
@@ -26,10 +37,25 @@ Examples of **correct** code for this rule:
 
 var x = 5;
 
-var foo = function() {
+function foo() {
+    // code
+}
+
+var bar = function() {
     // code
 };
 
+class C {
+    field;
+
+    method() {
+        // code
+    }
+
+    static {
+        // code
+    }
+}
 ```
 
 ## When Not To Use It
diff --git a/docs/rules/semi.md b/docs/rules/semi.md
index 158cd7f53..cb8e6dd0d 100644
--- a/docs/rules/semi.md
+++ b/docs/rules/semi.md
@@ -172,6 +172,14 @@ Examples of additional **correct** code for this rule with the `"always", { "omi
 if (foo) { bar() }
 
 if (foo) { bar(); baz() }
+
+function f() { bar(); baz() }
+
+class C {
+    foo() { bar(); baz() }
+
+    static { bar(); baz() }
+}
 ```
 
 #### beforeStatementContinuationChars
diff --git a/lib/rules/block-scoped-var.js b/lib/rules/block-scoped-var.js
index 10125e61f..d98250b06 100644
--- a/lib/rules/block-scoped-var.js
+++ b/lib/rules/block-scoped-var.js
@@ -112,6 +112,8 @@ module.exports = {
             "SwitchStatement:exit": exitScope,
             CatchClause: enterScope,
             "CatchClause:exit": exitScope,
+            StaticBlock: enterScope,
+            "StaticBlock:exit": exitScope,
 
             // Finds and reports references which are outside of valid scope.
             VariableDeclaration: checkForVariables
diff --git a/lib/rules/no-extra-semi.js b/lib/rules/no-extra-semi.js
index 952869c3e..0e2bcaf07 100644
--- a/lib/rules/no-extra-semi.js
+++ b/lib/rules/no-extra-semi.js
@@ -116,7 +116,7 @@ module.exports = {
              * @param {Node} node A MethodDefinition node of the start point.
              * @returns {void}
              */
-            "MethodDefinition, PropertyDefinition"(node) {
+            "MethodDefinition, PropertyDefinition, StaticBlock"(node) {
                 checkForPartOfClassBody(sourceCode.getTokenAfter(node));
             }
         };
diff --git a/lib/rules/semi.js b/lib/rules/semi.js
index 4124a8c50..c29029ced 100644
--- a/lib/rules/semi.js
+++ b/lib/rules/semi.js
@@ -306,22 +306,31 @@ module.exports = {
         }
 
         /**
-         * Checks a node to see if it's in a one-liner block statement.
+         * Checks a node to see if it's the last item in a one-liner block.
+         * Block is any `BlockStatement` or `StaticBlock` node. Block is a one-liner if its
+         * braces (and consequently everything between them) are on the same line.
          * @param {ASTNode} node The node to check.
-         * @returns {boolean} whether the node is in a one-liner block statement.
+         * @returns {boolean} whether the node is the last item in a one-liner block.
          */
-        function isOneLinerBlock(node) {
+        function isLastInOneLinerBlock(node) {
             const parent = node.parent;
             const nextToken = sourceCode.getTokenAfter(node);
 
             if (!nextToken || nextToken.value !== "}") {
                 return false;
             }
-            return (
-                !!parent &&
-                parent.type === "BlockStatement" &&
-                parent.loc.start.line === parent.loc.end.line
-            );
+
+            if (parent.type === "BlockStatement") {
+                return parent.loc.start.line === parent.loc.end.line;
+            }
+
+            if (parent.type === "StaticBlock") {
+                const openingBrace = sourceCode.getFirstToken(parent, { skip: 1 }); // skip the `static` token
+
+                return openingBrace.loc.start.line === parent.loc.end.line;
+            }
+
+            return false;
         }
 
         /**
@@ -343,7 +352,7 @@ module.exports = {
                     report(node);
                 }
             } else {
-                const oneLinerBlock = (exceptOneLine && isOneLinerBlock(node));
+                const oneLinerBlock = (exceptOneLine && isLastInOneLinerBlock(node));
 
                 if (isSemi && oneLinerBlock) {
                     report(node, true);


Test patch:
diff --git a/tests/lib/rules/block-scoped-var.js b/tests/lib/rules/block-scoped-var.js
index 596c3ba1c..f401332b1 100644
--- a/tests/lib/rules/block-scoped-var.js
+++ b/tests/lib/rules/block-scoped-var.js
@@ -110,7 +110,15 @@ ruleTester.run("block-scoped-var", rule, {
 
         // https://github.com/eslint/eslint/issues/2967
         "(function () { foo(); })(); function foo() {}",
-        { code: "(function () { foo(); })(); function foo() {}", parserOptions: { ecmaVersion: 6, sourceType: "module" } }
+        { code: "(function () { foo(); })(); function foo() {}", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
+
+        { code: "class C { static { var foo; foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo; var foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { if (bar) { foo; } var foo; } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "var foo; class C { static { foo; } } ", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo; } } var foo;", parserOptions: { ecmaVersion: 2022 } },
+        { code: "var foo; class C { static {} [foo]; } ", parserOptions: { ecmaVersion: 2022 } },
+        { code: "foo; class C { static {} } var foo; ", parserOptions: { ecmaVersion: 2022 } }
     ],
     invalid: [
         { code: "function f(){ x; { var x; } }", errors: [{ messageId: "outOfScope", data: { name: "x" }, type: "Identifier" }] },
@@ -165,6 +173,11 @@ ruleTester.run("block-scoped-var", rule, {
                 { messageId: "outOfScope", data: { name: "i" }, type: "Identifier" },
                 { messageId: "outOfScope", data: { name: "i" }, type: "Identifier" }
             ]
+        },
+        {
+            code: "class C { static { if (bar) { var foo; } foo; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "outOfScope", data: { name: "foo" }, type: "Identifier" }]
         }
     ]
 });
diff --git a/tests/lib/rules/no-extra-semi.js b/tests/lib/rules/no-extra-semi.js
index 838925e8c..4f19fb722 100644
--- a/tests/lib/rules/no-extra-semi.js
+++ b/tests/lib/rules/no-extra-semi.js
@@ -40,6 +40,7 @@ ruleTester.run("no-extra-semi", rule, {
         { code: "class A { } a;", parserOptions: { ecmaVersion: 6 } },
         { code: "class A { field; }", parserOptions: { ecmaVersion: 2022 } },
         { code: "class A { field = 0; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { static { foo; } }", parserOptions: { ecmaVersion: 2022 } },
 
         // modules
         { code: "export const x = 42;", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
@@ -112,6 +113,18 @@ ruleTester.run("no-extra-semi", rule, {
             output: "with(foo){}",
             errors: [{ messageId: "unexpected", type: "EmptyStatement" }]
         },
+        {
+            code: "class A { static { ; } }",
+            output: "class A { static {  } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "EmptyStatement", column: 20 }]
+        },
+        {
+            code: "class A { static { a;; } }",
+            output: "class A { static { a; } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "EmptyStatement", column: 22 }]
+        },
 
         // Class body.
         {
@@ -165,6 +178,18 @@ ruleTester.run("no-extra-semi", rule, {
             output: "class A { field; }",
             parserOptions: { ecmaVersion: 2022 },
             errors: [{ messageId: "unexpected", type: "Punctuator", column: 17 }]
+        },
+        {
+            code: "class A { static {}; }",
+            output: "class A { static {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 20 }]
+        },
+        {
+            code: "class A { static { a; }; foo(){} }",
+            output: "class A { static { a; } foo(){} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{ messageId: "unexpected", type: "Punctuator", column: 24 }]
         }
     ]
 });
diff --git a/tests/lib/rules/semi.js b/tests/lib/rules/semi.js
index bc00079d8..d87382182 100644
--- a/tests/lib/rules/semi.js
+++ b/tests/lib/rules/semi.js
@@ -48,14 +48,73 @@ ruleTester.run("semi", rule, {
         { code: "for (let thing of {}) {\n  console.log(thing);\n}", parserOptions: { ecmaVersion: 6 } },
         { code: "do{}while(true)", options: ["never"] },
         { code: "do{}while(true);", options: ["always"] },
+        { code: "class C { static {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static {} }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); } }", options: ["always"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); bar(); } }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); bar(); baz();} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo() } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\nbar() } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\nbar()\nbaz() } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo(); bar() } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n (a) } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;(a) } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n [a] } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;[a] } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n +a } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;+a } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n -a } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;-a } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo();\n /a/ } }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C { static { foo()\n ;/a/} }", options: ["never"], parserOptions: { ecmaVersion: 2022 } },
+        {
+            code: "class C { static { foo();\n (a) } }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { do ; while (foo)\n (a)} }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "class C { static { do ; while (foo)\n ;(a)} }",
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 }
+        },
 
+        // omitLastInOneLineBlock: true
         { code: "if (foo) { bar() }", options: ["always", { omitLastInOneLineBlock: true }] },
         { code: "if (foo) { bar(); baz() }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "if (foo)\n{ bar(); baz() }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "if (foo) {\n bar(); baz(); }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "if (foo) { bar(); baz(); \n}", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "function foo() { bar(); baz() }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "function foo()\n{ bar(); baz() }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "function foo(){\n bar(); baz(); }", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "function foo(){ bar(); baz(); \n}", options: ["always", { omitLastInOneLineBlock: true }] },
+        { code: "() => { bar(); baz() };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "() =>\n { bar(); baz() };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "() => {\n bar(); baz(); };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "() => { bar(); baz(); \n};", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "const obj = { method() { bar(); baz() } };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "const obj = { method()\n { bar(); baz() } };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "const obj = { method() {\n bar(); baz(); } };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "const obj = { method() { bar(); baz(); \n} };", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n method() { bar(); baz() } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n method()\n { bar(); baz() } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n method() {\n bar(); baz(); } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n method() { bar(); baz(); \n} \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 6 } },
+        { code: "class C {\n static { bar(); baz() } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\n static\n { bar(); baz() } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\n static {\n bar(); baz(); } \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class C {\n static { bar(); baz(); \n} \n}", options: ["always", { omitLastInOneLineBlock: true }], parserOptions: { ecmaVersion: 2022 } },
 
-
-        // method definitions don't have a semicolon.
+        // method definitions and static blocks don't have a semicolon.
         { code: "class A { a() {} b() {} }", parserOptions: { ecmaVersion: 6 } },
         { code: "var A = class { a() {} b() {} };", parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { static {} }", parserOptions: { ecmaVersion: 2022 } },
 
         { code: "import theDefault, { named1, named2 } from 'src/mylib';", parserOptions: { ecmaVersion: 6, sourceType: "module" } },
         { code: "import theDefault, { named1, named2 } from 'src/mylib'", options: ["never"], parserOptions: { ecmaVersion: 6, sourceType: "module" } },
@@ -311,6 +370,11 @@ ruleTester.run("semi", rule, {
             options: ["never"],
             parserOptions: { ecmaVersion: 2022 }
         },
+        {
+            code: "class C { static {}; }", // no-extra-semi reports it
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
         {
             code: "class C { a=b;\n*foo() {} }",
             options: ["never"],
@@ -990,7 +1054,158 @@ ruleTester.run("semi", rule, {
                 endColumn: 17
             }]
         },
+        {
+            code: "class C { static { foo() } }",
+            output: "class C { static { foo(); } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
+            }]
+        },
+        {
+            code: "class C { static { foo() } }",
+            output: "class C { static { foo(); } }",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
+            }]
+        },
+        {
+            code: "class C { static { foo(); bar() } }",
+            output: "class C { static { foo(); bar(); } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 32,
+                endLine: 1,
+                endColumn: 33
+            }]
+        },
+        {
+            code: "class C { static { foo()\nbar(); } }",
+            output: "class C { static { foo();\nbar(); } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 2,
+                endColumn: 1
+            }]
+        },
+        {
+            code: "class C { static { foo(); bar()\nbaz(); } }",
+            output: "class C { static { foo(); bar();\nbaz(); } }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 32,
+                endLine: 2,
+                endColumn: 1
+            }]
+        },
+        {
+            code: "class C { static { foo(); } }",
+            output: "class C { static { foo() } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
+            }]
+        },
+        {
+            code: "class C { static { foo();\nbar() } }",
+            output: "class C { static { foo()\nbar() } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                type: "ExpressionStatement",
+                line: 1,
+                column: 25,
+                endLine: 1,
+                endColumn: 26
+            }]
+        },
+        {
+            code: "class C { static { foo()\nbar(); } }",
+            output: "class C { static { foo()\nbar() } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                type: "ExpressionStatement",
+                line: 2,
+                column: 6,
+                endLine: 2,
+                endColumn: 7
+            }]
+        },
+        {
+            code: "class C { static { foo()\nbar();\nbaz() } }",
+            output: "class C { static { foo()\nbar()\nbaz() } }",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                type: "ExpressionStatement",
+                line: 2,
+                column: 6,
+                endLine: 2,
+                endColumn: 7
+            }]
+        },
+        {
+            code: "class C { static { do ; while (foo)\n (a)} }",
+            output: "class C { static { do ; while (foo);\n (a)} }",
+            options: ["never", { beforeStatementContinuationChars: "always" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                type: "DoWhileStatement",
+                line: 1,
+                column: 36,
+                endLine: 2,
+                endColumn: 1
+            }]
+        },
+        {
+            code: "class C { static { do ; while (foo)\n ;(a)} }",
+            output: "class C { static { do ; while (foo)\n (a)} }",
+            options: ["never", { beforeStatementContinuationChars: "never" }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                type: "DoWhileStatement",
+                line: 2,
+                column: 2,
+                endLine: 2,
+                endColumn: 3
+            }]
+        },
 
+        // omitLastInOneLineBlock: true
         {
             code: "if (foo) { bar()\n }",
             output: "if (foo) { bar();\n }",
@@ -1039,6 +1254,158 @@ ruleTester.run("semi", rule, {
                 endColumn: 18
             }]
         },
+        {
+            code: "function foo() { bar(); baz(); }",
+            output: "function foo() { bar(); baz() }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "extraSemi",
+                line: 1,
+                column: 30,
+                endLine: 1,
+                endColumn: 31
+            }]
+        },
+        {
+            code: "function foo()\n{ bar(); baz(); }",
+            output: "function foo()\n{ bar(); baz() }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 15,
+                endLine: 2,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "function foo() {\n bar(); baz() }",
+            output: "function foo() {\n bar(); baz(); }",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 14,
+                endLine: 2,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "function foo() { bar(); baz() \n}",
+            output: "function foo() { bar(); baz(); \n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            errors: [{
+                messageId: "missingSemi",
+                line: 1,
+                column: 30,
+                endLine: 1,
+                endColumn: 31
+            }]
+        },
+        {
+            code: "class C {\nfoo() { bar(); baz(); }\n}",
+            output: "class C {\nfoo() { bar(); baz() }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 21,
+                endLine: 2,
+                endColumn: 22
+            }]
+        },
+        {
+            code: "class C {\nfoo() \n{ bar(); baz(); }\n}",
+            output: "class C {\nfoo() \n{ bar(); baz() }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 15,
+                endLine: 3,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "class C {\nfoo() {\n bar(); baz() }\n}",
+            output: "class C {\nfoo() {\n bar(); baz(); }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 3,
+                column: 14,
+                endLine: 3,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "class C {\nfoo() { bar(); baz() \n}\n}",
+            output: "class C {\nfoo() { bar(); baz(); \n}\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 6 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 21,
+                endLine: 2,
+                endColumn: 22
+            }]
+        },
+        {
+            code: "class C {\nstatic { bar(); baz(); }\n}",
+            output: "class C {\nstatic { bar(); baz() }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 2,
+                column: 22,
+                endLine: 2,
+                endColumn: 23
+            }]
+        },
+        {
+            code: "class C {\nstatic \n{ bar(); baz(); }\n}",
+            output: "class C {\nstatic \n{ bar(); baz() }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "extraSemi",
+                line: 3,
+                column: 15,
+                endLine: 3,
+                endColumn: 16
+            }]
+        },
+        {
+            code: "class C {\nstatic {\n bar(); baz() }\n}",
+            output: "class C {\nstatic {\n bar(); baz(); }\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 3,
+                column: 14,
+                endLine: 3,
+                endColumn: 15
+            }]
+        },
+        {
+            code: "class C {\nfoo() { bar(); baz() \n}\n}",
+            output: "class C {\nfoo() { bar(); baz(); \n}\n}",
+            options: ["always", { omitLastInOneLineBlock: true }],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingSemi",
+                line: 2,
+                column: 21,
+                endLine: 2,
+                endColumn: 22
+            }]
+        },
 
 
         // exports, "always"