Repository: checkstyle/checkstyle (Java)

Issue:
WhitespaceAround: new property allowEmptySwitchBlockStatements
I have read check documentation: https://checkstyle.org/config_xxxxxx.html#NameOfAffectedCheck
I have downloaded the latest checkstyle from: https://checkstyle.org/cmdline.html#Download_and_Run
I have executed the cli and showed it below, as cli describes the problem better than 1,000 words

```bash
/var/tmp $ javac YOUR_FILE.java
#[[MAKE SURE THERE IS SUCCESSFUL COMPILATION]]
```

/var/tmp $ cat config.xml
```xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">

<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">

<module name="Checker">
    <property name="charset" value="UTF-8"/>
    <property name="haltOnException" value="true"/>
    <property name="severity" value="error"/>

    <module name="FileTabCharacter">
        <property name="eachLine" value="true"/>
    </module>
    <module name="NewlineAtEndOfFile"/>
    <module name="SuppressWarningsFilter"/>
    <module name="UniqueProperties"/>

    <module name="TreeWalker">
        <module name="SuppressWarningsHolder"/>

        <module name="WhitespaceAround">
            <property name="allowEmptyCatches" value="true"/>
            <property name="allowEmptyConstructors" value="true"/>
            <property name="allowEmptyMethods" value="true"/>
            <property name="allowEmptyLambdas" value="true"/>
            <property name="allowEmptyLoops" value="true"/>
            <property name="allowEmptyTypes" value="true"/>
            <property name="ignoreEnhancedForColon" value="false"/>
        </module>
    </module>
</module>
```

/var/tmp $ cat YOUR_FILE.java
```java
public class WhitespaceAround {
    private void switchTest() {
        switch ("foo") {
            case "A" -> {}
            case "B" -> System.out.println("B");
            default -> System.out.println("other");
        }
    }
}
```
/var/tmp $ RUN_LOCALE="-Duser.language=en -Duser.country=US"
/var/tmp $ java $RUN_LOCALE -jar checkstyle-X.XX-all.jar -c config.xml YOUR_FILE.java
```bash
Starting audit...
[ERROR] /private/var/tmp/WhitespaceAround.java:4:25: '{' is not followed by whitespace. [WhitespaceAround]
[ERROR] /private/var/tmp/WhitespaceAround.java:4:26: '}' is not preceded with whitespace. [WhitespaceAround]
Audit done.
Checkstyle ends with 2 errors.
```

For Windows users, please use `type` instead of `cat` and run

```cmd
set RUN_LOCALE="-Duser.language=en -Duser.country=US"
java %RUN_LOCALE% -jar checkstyle-X.XX-all.jar -c config.xml YOUR_FILE.java
```

in place of the last 2 commands above.

---
My expectation was that one of the allowEmpty* properties would stop the violation  on the line 
```java
case "A" -> {}
```

---

*Solution:* is new property `allowEmptySwitchBlockStatements` to let users control behavior on empty blocks.


Discussion: The issue lies over here-
This code will parse as ( I modified a bit, but it won't make a difference ) 
```
|--LITERAL_CASE -> case [5:12]
    |       |   |   |   `--EXPR -> EXPR [5:17]
    |       |   |   |       `--NUM_INT -> 4 [5:17]
    |       |   |   |--LAMBDA -> -> [5:19]
    |       |   |   `--SLIST -> { [5:22]
    |       |   |       `--RCURLY -> } [5:23]
```
Here the problem is that `LAMBDA` is not the parent of `SLIST` Token. Whereas in the check-  https://github.com/checkstyle/checkstyle/blob/9df88376340f29050e02bf9d23f6452935651581/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/WhitespaceAroundCheck.java#L823-L838
https://github.com/checkstyle/checkstyle/blob/9df88376340f29050e02bf9d23f6452935651581/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/WhitespaceAroundCheck.java#L928-L930

It checks for `LAMBDA` Token to be parent of `SLIST` Token or grand parent of `RCURLY` Token.
@romani If this issue is approved then I am willing to work on it.
Violations for WhitespaceAround are shown for both types of switch statements:

```bash
➜  src cat MyClass.java
public class MyClass {
    private void switchTest1() {
        switch ("foo") {
            case "A" -> {}
            case "B" -> System.out.println("B");
            default -> System.out.println("other");
        }
    }

    private void switchTest2() {
        switch ("foo") {
            case "A" : {}
            case "B" : System.out.println("B");
            default : System.out.println("other");
        }
    }

    public static void main(String[] args) {
    }

}

➜  src java MyClass.java

➜  src cat config.xml 
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">

<module name="Checker">
    <property name="charset" value="UTF-8"/>
    <property name="haltOnException" value="true"/>
    <property name="severity" value="error"/>

    <module name="FileTabCharacter">
        <property name="eachLine" value="true"/>
    </module>
    <module name="NewlineAtEndOfFile"/>
    <module name="SuppressWarningsFilter"/>
    <module name="UniqueProperties"/>

    <module name="TreeWalker">
        <module name="SuppressWarningsHolder"/>

        <module name="WhitespaceAround">
            <property name="allowEmptyCatches" value="true"/>
            <property name="allowEmptyConstructors" value="true"/>
            <property name="allowEmptyMethods" value="true"/>
            <property name="allowEmptyLambdas" value="true"/>
            <property name="allowEmptyLoops" value="true"/>
            <property name="allowEmptyTypes" value="true"/>
            <property name="ignoreEnhancedForColon" value="false"/>
        </module>
    </module>
</module>

➜  src java -jar checkstyle-8.41-all.jar -c config.xml MyClass.java
Starting audit...
[ERROR] Tester/src/MyClass.java:4:25: '{' is not followed by whitespace. [WhitespaceAround]
[ERROR] Tester/src/MyClass.java:4:26: '}' is not preceded with whitespace. [WhitespaceAround]
[ERROR] Tester/src/MyClass.java:12:24: '{' is not followed by whitespace. [WhitespaceAround]
[ERROR] Tester/src/MyClass.java:12:25: '}' is not preceded with whitespace. [WhitespaceAround]
Audit done.
Checkstyle ends with 4 errors.

```
There is no `allow*` property for empty switch case blocks, so we would need to implement a new property in WhitespaceAround like `allowEmptySwitch`.

Another way to resolve this would be to create a [SuppressionXpathFilter](https://checkstyle.sourceforge.io/config_filters.html#SuppressionXpathFilter) module to suppress these violations:

```bash
➜  src cat config.xml                                              
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
        "http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">

<module name="Checker">
    <property name="charset" value="UTF-8"/>
    <property name="haltOnException" value="true"/>
    <property name="severity" value="error"/>

    <module name="FileTabCharacter">
        <property name="eachLine" value="true"/>
    </module>
    <module name="NewlineAtEndOfFile"/>
    <module name="SuppressWarningsFilter"/>
    <module name="UniqueProperties"/>


    <module name="TreeWalker">
        <module name="SuppressWarningsHolder"/>

        <module name="SuppressionXpathFilter">
            <property name="file" value="suppressions.xml"/>
            <property name="optional" value="false"/>
        </module>

        <module name="WhitespaceAround">
            <property name="allowEmptyCatches" value="true"/>
            <property name="allowEmptyConstructors" value="true"/>
            <property name="allowEmptyMethods" value="true"/>
            <property name="allowEmptyLambdas" value="true"/>
            <property name="allowEmptyLoops" value="true"/>
            <property name="allowEmptyTypes" value="true"/>
            <property name="ignoreEnhancedForColon" value="false"/>
        </module>
    </module>
</module>

➜  src cat suppressions.xml  
<?xml version="1.0"?>

<!DOCTYPE suppressions PUBLIC
        "-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2//EN"
        "https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd">

<suppressions>
    <suppress-xpath checks="WhitespaceAround" query="//LITERAL_SWITCH//SLIST"/>
    <suppress-xpath checks="WhitespaceAround" query="//LITERAL_SWITCH//RCURLY"/>
</suppressions>

➜  src java -jar checkstyle-8.41-all.jar -c config.xml MyClass.java
Starting audit...
Audit done.

```
This might be a bit too general, and cause false negatives, though.


`LAMBDA` token in this case is really the arrow operator (definite assignment/ action, so that `break` is not needed), not really a "lambda" as in a lambda expression, and should not be considered as such when checking for whitespace around tokens. 

> Here the problem is that LAMBDA is not the parent of SLIST Token.

The `LAMBDA` token should not be the parent of  `SLIST`, [they should be siblings.](https://docs.oracle.com/javase/specs/jls/se12/preview/switch-expressions.html)


> > Here the problem is that LAMBDA is not the parent of SLIST Token.
> 
> The `LAMBDA` token should not be the parent of `SLIST`, [they should be siblings.](https://docs.oracle.com/javase/specs/jls/se12/preview/switch-expressions.html)

@nmancus1 I think my wording was wrong, I meant to say HERE `LAMBDA` is not the parent of `SLIST`  token whereas it is the parent in situations like `myAdd add = (int a,  int b) -> {return a + b;};`

Hi, what about this issue?

Is it still planned to add an option `allowEmptySwitchCase`?

Our team uses checkstyle and it works great, but we are facing this issue ~~and I didn't managed to get the workaround working (the `suppress-xpath` thing), I get this (even on latest version):~~
> ~~cannot initialize module SuppressionXpathFilter - Unable to parse suppressions.xml - Element type "suppress-xpath" must be declared.~~

~~We really would like not to have to disable completely the `WhitespaceAround` rule, as it is important, but this false-positive blocks our approval workflow unfortunately.
What could we do?~~

---
EDIT: I found the issue was I had the wrong `DOCTYPE` xml definition:
```diff
 <!DOCTYPE suppressions PUBLIC
-    "-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
+    "-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2//EN"
     "https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd">
```
However it would be great to have an official option rather than this workaround 🙂

Thanks!
@indyteo, 

Can you check behavior on latest CLI 10.18.1 we did a lot of updates for latest java syntax support in last few months. 
This issue might be resolved.
Hi @romani,

Even with the latest version 10.18.1, there's no such option (at least not mentioned in [the documentation](https://checkstyle.org/checks/whitespace/whitespacearound.html#Properties)), and the same violation is reported.
@romani please approve the issue and I am on it



Fix (patch):
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/WhitespaceAroundCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/WhitespaceAroundCheck.java
index 45ded8d193c..29f3a4ee188 100644
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/WhitespaceAroundCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/WhitespaceAroundCheck.java
@@ -47,7 +47,8 @@
  * <p>
  * may optionally be exempted from the policy using the {@code allowEmptyMethods},
  * {@code allowEmptyConstructors}, {@code allowEmptyTypes}, {@code allowEmptyLoops},
- * {@code allowEmptyLambdas} and {@code allowEmptyCatches} properties.
+ * {@code allowEmptyLambdas}, {@code allowEmptyCatches}
+ * and {@code allowEmptySwitchBlockStatements} properties.
  * </p>
  *
  * <p>
@@ -107,6 +108,12 @@
  * Default value is {@code false}.
  * </li>
  * <li>
+ * Property {@code allowEmptySwitchBlockStatements} - Allow empty switch blocks
+ * and block statements.
+ * Type is {@code boolean}.
+ * Default value is {@code false}.
+ * </li>
+ * <li>
  * Property {@code allowEmptyTypes} - Allow empty class, interface and enum bodies.
  * Type is {@code boolean}.
  * Default value is {@code false}.
@@ -275,6 +282,8 @@ public class WhitespaceAroundCheck extends AbstractCheck {
     private boolean allowEmptyLambdas;
     /** Allow empty catch bodies. */
     private boolean allowEmptyCatches;
+    /** Allow empty switch blocks and block statements. */
+    private boolean allowEmptySwitchBlockStatements;
     /**
      * Ignore whitespace around colon in
      * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-14.html#jls-14.14.2">
@@ -480,6 +489,16 @@ public void setAllowEmptyCatches(boolean allow) {
         allowEmptyCatches = allow;
     }
 
+    /**
+     * Setter to allow empty switch blocks and block statements.
+     *
+     * @param allow {@code true} to allow empty switch case and default blocks.
+     * @since 10.19.0
+     */
+    public void setAllowEmptySwitchBlockStatements(boolean allow) {
+        allowEmptySwitchBlockStatements = allow;
+    }
+
     @Override
     public void visitToken(DetailAST ast) {
         final int currentType = ast.getType();
@@ -606,7 +625,8 @@ private boolean isEmptyBlock(DetailAST ast, int parentType) {
                 || isEmptyCtorBlockCheckedFromRcurly(ast)
                 || isEmptyLoop(ast, parentType)
                 || isEmptyLambda(ast, parentType)
-                || isEmptyCatch(ast, parentType);
+                || isEmptyCatch(ast, parentType)
+                || isEmptySwitchBlockStatement(ast);
     }
 
     /**
@@ -727,6 +747,41 @@ private boolean isEmptyCatch(DetailAST ast, int parentType) {
         return allowEmptyCatches && isEmptyBlock(ast, parentType, TokenTypes.LITERAL_CATCH);
     }
 
+    /**
+     * Tests if the given {@code DetailAst} is part of an allowed empty
+     * switch case or default block.
+     *
+     * @param ast the {@code DetailAst} to test.
+     * @return {@code true} if {@code ast} makes up part of an allowed
+     *         empty switch case or default block.
+     */
+    private boolean isEmptySwitchBlockStatement(DetailAST ast) {
+        final boolean isEmptySwitchBlockStatement;
+
+        if (allowEmptySwitchBlockStatements) {
+            final DetailAST parent = ast.getParent();
+            final DetailAST grandParent = parent.getParent();
+
+            final boolean isEmptyCaseInSwitchRule =
+                    isEmptyBlock(ast, parent.getType(), TokenTypes.SWITCH_RULE);
+
+            final boolean isEmptyCaseGroupCheckedFromLcurly =
+                    isEmptyBlock(ast, grandParent.getType(), TokenTypes.CASE_GROUP);
+
+            final boolean isEmptyCaseGroupCheckedFromRcurly =
+                    parent.getFirstChild().getType() == TokenTypes.RCURLY
+                      && grandParent.getParent().getType() == TokenTypes.CASE_GROUP;
+
+            isEmptySwitchBlockStatement = isEmptyCaseInSwitchRule
+                    || isEmptyCaseGroupCheckedFromLcurly || isEmptyCaseGroupCheckedFromRcurly;
+        }
+        else {
+            isEmptySwitchBlockStatement = false;
+        }
+
+        return isEmptySwitchBlockStatement;
+    }
+
     /**
      * Test if the given {@code DetailAST} is part of an empty block.
      * An example empty block might look like the following
diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/meta/checks/whitespace/WhitespaceAroundCheck.xml b/src/main/resources/com/puppycrawl/tools/checkstyle/meta/checks/whitespace/WhitespaceAroundCheck.xml
index 01cb6b8e82e..e2b8815d429 100644
--- a/src/main/resources/com/puppycrawl/tools/checkstyle/meta/checks/whitespace/WhitespaceAroundCheck.xml
+++ b/src/main/resources/com/puppycrawl/tools/checkstyle/meta/checks/whitespace/WhitespaceAroundCheck.xml
@@ -25,7 +25,8 @@
  &lt;p&gt;
  may optionally be exempted from the policy using the {@code allowEmptyMethods},
  {@code allowEmptyConstructors}, {@code allowEmptyTypes}, {@code allowEmptyLoops},
- {@code allowEmptyLambdas} and {@code allowEmptyCatches} properties.
+ {@code allowEmptyLambdas}, {@code allowEmptyCatches}
+ and {@code allowEmptySwitchBlockStatements} properties.
  &lt;/p&gt;
 
  &lt;p&gt;
@@ -74,6 +75,12 @@
             <property default-value="false" name="allowEmptyMethods" type="boolean">
                <description>Allow empty method bodies.</description>
             </property>
+            <property default-value="false"
+                      name="allowEmptySwitchBlockStatements"
+                      type="boolean">
+               <description>Allow empty switch blocks
+ and block statements.</description>
+            </property>
             <property default-value="false" name="allowEmptyTypes" type="boolean">
                <description>Allow empty class, interface and enum bodies.</description>
             </property>
diff --git a/src/xdocs/checks/whitespace/whitespacearound.xml b/src/xdocs/checks/whitespace/whitespacearound.xml
index 3014e3bd992..e5dac027c87 100644
--- a/src/xdocs/checks/whitespace/whitespacearound.xml
+++ b/src/xdocs/checks/whitespace/whitespacearound.xml
@@ -31,7 +31,8 @@ public @interface Beta {} // empty annotation type
           may optionally be exempted from the policy using the <code>
           allowEmptyMethods</code>, <code>allowEmptyConstructors</code>,
           <code>allowEmptyTypes</code>, <code>allowEmptyLoops</code>,
-          <code>allowEmptyLambdas</code> and <code>allowEmptyCatches</code>
+          <code>allowEmptyLambdas</code>, <code>allowEmptyCatches</code> and
+          <code>allowEmptySwitchBlockStatements</code>
           properties.
         </p>
         <p>This check does not flag as violation double brace initialization like:</p>
@@ -108,6 +109,13 @@ try {
               <td><code>false</code></td>
               <td>4.0</td>
             </tr>
+            <tr>
+              <td>allowEmptySwitchBlockStatements</td>
+              <td>Allow empty switch blocks and block statements.</td>
+              <td><a href="../../property_types.html#boolean">boolean</a></td>
+              <td><code>false</code></td>
+              <td>10.19.0</td>
+            </tr>
             <tr>
               <td>allowEmptyTypes</td>
               <td>Allow empty class, interface and enum bodies.</td>
diff --git a/src/xdocs/checks/whitespace/whitespacearound.xml.template b/src/xdocs/checks/whitespace/whitespacearound.xml.template
index c45695167be..cb71275f490 100644
--- a/src/xdocs/checks/whitespace/whitespacearound.xml.template
+++ b/src/xdocs/checks/whitespace/whitespacearound.xml.template
@@ -31,7 +31,8 @@ public @interface Beta {} // empty annotation type
           may optionally be exempted from the policy using the <code>
           allowEmptyMethods</code>, <code>allowEmptyConstructors</code>,
           <code>allowEmptyTypes</code>, <code>allowEmptyLoops</code>,
-          <code>allowEmptyLambdas</code> and <code>allowEmptyCatches</code>
+          <code>allowEmptyLambdas</code>, <code>allowEmptyCatches</code> and
+          <code>allowEmptySwitchBlockStatements</code>
           properties.
         </p>
         <p>This check does not flag as violation double brace initialization like:</p>