Repository: eslint/eslint
Pull request #15289 (2021-11-11 12:46:31)

feat: update keyword-spacing 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 `keyword-spacing`.

<!--
    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 `keyword-spacing` rule to check spacing around `static` of class static blocks.

Also added test to confirm that this doesn't conflict with `semi-spacing` and `space-before-blocks`.


#### 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/keyword-spacing.md b/docs/rules/keyword-spacing.md
index 0cc952dc9..8ca673937 100644
--- a/docs/rules/keyword-spacing.md
+++ b/docs/rules/keyword-spacing.md
@@ -243,13 +243,14 @@ if(foo) {
 
 ### overrides
 
-Examples of **correct** code for this rule with the `{ "overrides": { "if": { "after": false }, "for": { "after": false }, "while": { "after": false } } }` option:
+Examples of **correct** code for this rule with the `{ "overrides": { "if": { "after": false }, "for": { "after": false }, "while": { "after": false }, "static": { "after": false } } }` option:
 
 ```js
 /*eslint keyword-spacing: ["error", { "overrides": {
   "if": { "after": false },
   "for": { "after": false },
-  "while": { "after": false }
+  "while": { "after": false },
+  "static": { "after": false }
 } }]*/
 
 if(foo) {
@@ -263,7 +264,13 @@ if(foo) {
 for(;;);
 
 while(true) {
-  //...
+    //...
+}
+
+class C {
+    static{
+        //...
+    }
 }
 ```
 
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/docs/rules/space-before-blocks.md b/docs/rules/space-before-blocks.md
index 83e9b3d6a..35a4c16c3 100644
--- a/docs/rules/space-before-blocks.md
+++ b/docs/rules/space-before-blocks.md
@@ -63,6 +63,9 @@ if (a) {
     c();
 }
 
+class C {
+    static{} /*no error. this is checked by `keyword-spacing` rule.*/
+}
 
 function a() {}
 
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/keyword-spacing.js b/lib/rules/keyword-spacing.js
index d860ae0f0..44222e1a4 100644
--- a/lib/rules/keyword-spacing.js
+++ b/lib/rules/keyword-spacing.js
@@ -590,6 +590,7 @@ module.exports = {
             ImportNamespaceSpecifier: checkSpacingForImportNamespaceSpecifier,
             MethodDefinition: checkSpacingForProperty,
             PropertyDefinition: checkSpacingForProperty,
+            StaticBlock: checkSpacingAroundFirstToken,
             Property: checkSpacingForProperty,
 
             // To avoid conflicts with `space-infix-ops`, e.g. `a > this.b`
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/keyword-spacing.js b/tests/lib/rules/keyword-spacing.js
index 05e58d7e1..35c8a06e4 100644
--- a/tests/lib/rules/keyword-spacing.js
+++ b/tests/lib/rules/keyword-spacing.js
@@ -978,6 +978,11 @@ ruleTester.run("keyword-spacing", rule, {
         { code: "class A { a;static[b]; }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
         { code: "class A { a; static #b; }", parserOptions: { ecmaVersion: 2022 } },
         { code: "class A { a;static#b; }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {} static {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {}static{} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {} static {} }", options: [override("static", BOTH)], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {}static{} }", options: [override("static", NEITHER)], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { a() {}\nstatic\n{} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
 
         // not conflict with `generator-star-spacing`
         { code: "class A { static* [a]() {} }", parserOptions: { ecmaVersion: 6 } },
@@ -986,6 +991,10 @@ ruleTester.run("keyword-spacing", rule, {
         // not conflict with `semi-spacing`
         { code: "class A { ;static a() {} }", parserOptions: { ecmaVersion: 6 } },
         { code: "class A { ; static a() {} }", options: [NEITHER], parserOptions: { ecmaVersion: 6 } },
+        { code: "class A { ;static a; }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { ; static a ; }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { ;static {} }", parserOptions: { ecmaVersion: 2022 } },
+        { code: "class A { ; static{} }", options: [NEITHER], parserOptions: { ecmaVersion: 2022 } },
 
         //----------------------------------------------------------------------
         // super
@@ -3043,6 +3052,59 @@ ruleTester.run("keyword-spacing", rule, {
             parserOptions: { ecmaVersion: 2022 },
             errors: unexpectedAfter("static")
         },
+        {
+            code: "class A { a() {}static{} }",
+            output: "class A { a() {} static {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedBeforeAndAfter("static")
+        },
+        {
+            code: "class A { a() {}static{} }",
+            output: "class A { a() {} static {} }",
+            options: [override("static", BOTH)],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedBeforeAndAfter("static")
+        },
+        {
+            code: "class A {  a() {}static {} }",
+            output: "class A {  a() {} static {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedBefore("static")
+        },
+        {
+            code: "class A {  a() {} static{} }",
+            output: "class A {  a() {} static {} }",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: expectedAfter("static")
+        },
+        {
+            code: "class A { a() {} static {} }",
+            output: "class A { a() {}static{} }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedBeforeAndAfter("static")
+        },
+        {
+            code: "class A { a() {} static {} }",
+            output: "class A { a() {}static{} }",
+            options: [override("static", NEITHER)],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedBeforeAndAfter("static")
+        },
+        {
+            code: "class A { a() {} static{} }",
+            output: "class A { a() {}static{} }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedBefore("static")
+        },
+        {
+            code: "class A { a() {}static {} }",
+            output: "class A { a() {}static{} }",
+            options: [NEITHER],
+            parserOptions: { ecmaVersion: 2022 },
+            errors: unexpectedAfter("static")
+        },
 
         //----------------------------------------------------------------------
         // super
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-spacing.js b/tests/lib/rules/semi-spacing.js
index c9cccddd5..ffd67854a 100644
--- a/tests/lib/rules/semi-spacing.js
+++ b/tests/lib/rules/semi-spacing.js
@@ -483,6 +483,19 @@ ruleTester.run("semi-spacing", rule, {
                     endColumn: 16
                 }
             ]
+        },
+        {
+            code: "class C { foo;static {}}",
+            output: "class C { foo; static {}}",
+            parserOptions: { ecmaVersion: 2022 },
+            errors: [{
+                messageId: "missingWhitespaceAfter",
+                type: "PropertyDefinition",
+                line: 1,
+                column: 14,
+                endLine: 1,
+                endColumn: 15
+            }]
         }
     ]
 });
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"
diff --git a/tests/lib/rules/space-before-blocks.js b/tests/lib/rules/space-before-blocks.js
index 79961dd4d..49af96116 100644
--- a/tests/lib/rules/space-before-blocks.js
+++ b/tests/lib/rules/space-before-blocks.js
@@ -202,7 +202,19 @@ ruleTester.run("space-before-blocks", rule, {
         { code: "switch(x) { case (9):{ break; } }", options: alwaysArgs },
         { code: "switch(x){ case (9): { break; } }", options: neverArgs },
         { code: "switch(x) { default:{ break; } }", options: alwaysArgs },
-        { code: "switch(x){ default: { break; } }", options: neverArgs }
+        { code: "switch(x){ default: { break; } }", options: neverArgs },
+
+        // not conflict with `keyword-spacing`
+        {
+            code: "(class{ static{} })",
+            options: ["always"],
+            parserOptions: { ecmaVersion: 2022 }
+        },
+        {
+            code: "(class { static {} })",
+            options: ["never"],
+            parserOptions: { ecmaVersion: 2022 }
+        }
     ],
     invalid: [
         {