Repository: checkstyle/checkstyle

Problem statement:
ExecutableStatementCount should allow LAMBDA token
from discussion at https://github.com/checkstyle/checkstyle/issues/8966

https://checkstyle.org/config_sizes.html#ExecutableStatementCount

```
$ javac Test.java 
$ cat Test.java 
 public class Test {
  java.util.function.Consumer a = (o) -> {
    o.toString(); // 1
    o.toString(); // 2
    o.toString(); // 3
    o.toString(); // 4
    o.toString(); // 5
    o.toString(); // 6
  };
 }

$ cat config.xml 
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
          "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
          "https://checkstyle.org/dtds/configuration_1_3.dtd">

<module name="Checker">
  <module name="TreeWalker">
    <module name="ExecutableStatementCount">
      <property name="max" value="5"/>
    </module>
  </module>
</module>

$ java -jar checkstyle-8.36.2-all.jar -c config.xml Test.java 
Starting audit...
Audit done.
```

Expected: violation over lambda on line 2


Hints:
abandoned PR - https://github.com/checkstyle/checkstyle/pull/9067 , everyone is welcome to pickup code from this PR and create new PR to fix issue.

Patch:
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/sizes/ExecutableStatementCountCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/sizes/ExecutableStatementCountCheck.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/sizes/ExecutableStatementCountCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/sizes/ExecutableStatementCountCheck.java
@@ -26,6 +26,7 @@
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
+import com.puppycrawl.tools.checkstyle.utils.TokenUtil;
 
 /**
  * <p>
@@ -51,7 +52,9 @@
  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#STATIC_INIT">
  * STATIC_INIT</a>,
  * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF">
- * COMPACT_CTOR_DEF</a>.
+ * COMPACT_CTOR_DEF</a>,
+ * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#LAMBDA">
+ * LAMBDA</a>.
  * </li>
  * </ul>
  * <p>
@@ -119,6 +122,7 @@ public int[] getDefaultTokens() {
             TokenTypes.STATIC_INIT,
             TokenTypes.SLIST,
             TokenTypes.COMPACT_CTOR_DEF,
+            TokenTypes.LAMBDA,
         };
     }
 
@@ -136,6 +140,7 @@ public int[] getAcceptableTokens() {
             TokenTypes.STATIC_INIT,
             TokenTypes.SLIST,
             TokenTypes.COMPACT_CTOR_DEF,
+            TokenTypes.LAMBDA,
         };
     }
 
@@ -156,56 +161,43 @@ public void beginTree(DetailAST rootAST) {
 
     @Override
     public void visitToken(DetailAST ast) {
-        switch (ast.getType()) {
-            case TokenTypes.CTOR_DEF:
-            case TokenTypes.METHOD_DEF:
-            case TokenTypes.INSTANCE_INIT:
-            case TokenTypes.STATIC_INIT:
-            case TokenTypes.COMPACT_CTOR_DEF:
-                visitMemberDef(ast);
-                break;
-            case TokenTypes.SLIST:
-                visitSlist(ast);
-                break;
-            default:
-                throw new IllegalStateException(ast.toString());
+        if (isContainerNode(ast)) {
+            visitContainerNode(ast);
+        }
+        else if (TokenUtil.isOfType(ast, TokenTypes.SLIST)) {
+            visitSlist(ast);
+        }
+        else {
+            throw new IllegalStateException(ast.toString());
         }
     }
 
     @Override
     public void leaveToken(DetailAST ast) {
-        switch (ast.getType()) {
-            case TokenTypes.CTOR_DEF:
-            case TokenTypes.METHOD_DEF:
-            case TokenTypes.INSTANCE_INIT:
-            case TokenTypes.STATIC_INIT:
-            case TokenTypes.COMPACT_CTOR_DEF:
-                leaveMemberDef(ast);
-                break;
-            case TokenTypes.SLIST:
-                // Do nothing
-                break;
-            default:
-                throw new IllegalStateException(ast.toString());
+        if (isContainerNode(ast)) {
+            leaveContainerNode(ast);
+        }
+        else if (!TokenUtil.isOfType(ast, TokenTypes.SLIST)) {
+            throw new IllegalStateException(ast.toString());
         }
     }
 
     /**
-     * Process the start of the member definition.
+     * Process the start of the container node.
      *
-     * @param ast the token representing the member definition.
+     * @param ast the token representing the container node.
      */
-    private void visitMemberDef(DetailAST ast) {
+    private void visitContainerNode(DetailAST ast) {
         contextStack.push(context);
         context = new Context(ast);
     }
 
     /**
-     * Process the end of a member definition.
+     * Process the end of a container node.
      *
-     * @param ast the token representing the member definition.
+     * @param ast the token representing the container node.
      */
-    private void leaveMemberDef(DetailAST ast) {
+    private void leaveContainerNode(DetailAST ast) {
         final int count = context.getCount();
         if (count > max) {
             log(ast, MSG_KEY, count, max);
@@ -219,33 +211,27 @@ private void leaveMemberDef(DetailAST ast) {
      * @param ast the token representing the statement list.
      */
     private void visitSlist(DetailAST ast) {
-        if (context.getAST() != null) {
-            // find member AST for the statement list
-            final DetailAST contextAST = context.getAST();
-            DetailAST parent = ast.getParent();
-            int type = parent.getType();
-            while (type != TokenTypes.METHOD_DEF
-                && !isConstructorOrInit(type)) {
-                parent = parent.getParent();
-                type = parent.getType();
-            }
-            if (parent == contextAST) {
-                context.addCount(ast.getChildCount() / 2);
-            }
+        final DetailAST contextAST = context.getAST();
+        DetailAST parent = ast.getParent();
+        while (parent != null && !isContainerNode(parent)) {
+            parent = parent.getParent();
+        }
+        if (parent == contextAST) {
+            context.addCount(ast.getChildCount() / 2);
         }
     }
 
     /**
-     * Check if token type is a ctor (compact or canonical) or instance/ static initializer.
+     * Check if the node is of type ctor (compact or canonical),
+     * instance/ static initializer, method definition or lambda.
      *
-     * @param tokenType type of token we are checking
-     * @return true if token type is constructor or initializer
+     * @param node AST node we are checking
+     * @return true if node is of the given types
      */
-    private static boolean isConstructorOrInit(int tokenType) {
-        return tokenType == TokenTypes.CTOR_DEF
-                || tokenType == TokenTypes.INSTANCE_INIT
-                || tokenType == TokenTypes.STATIC_INIT
-                || tokenType == TokenTypes.COMPACT_CTOR_DEF;
+    private static boolean isContainerNode(DetailAST node) {
+        return TokenUtil.isOfType(node, TokenTypes.METHOD_DEF,
+                TokenTypes.LAMBDA, TokenTypes.CTOR_DEF, TokenTypes.INSTANCE_INIT,
+                TokenTypes.STATIC_INIT, TokenTypes.COMPACT_CTOR_DEF);
     }
 
     /**


Test patch:
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ExecutableStatementCountCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ExecutableStatementCountCheckTest.java
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ExecutableStatementCountCheckTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/sizes/ExecutableStatementCountCheckTest.java
@@ -75,6 +75,7 @@ public void testMaxZero() throws Exception {
             "71:5: " + getCheckMessage(MSG_KEY, 2, 0),
             "80:5: " + getCheckMessage(MSG_KEY, 2, 0),
             "83:13: " + getCheckMessage(MSG_KEY, 1, 0),
+            "94:29: " + getCheckMessage(MSG_KEY, 1, 0),
         };
 
         verify(checkConfig, getPath("InputExecutableStatementCountMaxZero.java"), expected);
@@ -210,4 +211,23 @@ public void testExecutableStatementCountRecords() throws Exception {
                 expected);
     }
 
+    @Test
+    public void testExecutableStatementCountLambdas() throws Exception {
+        final DefaultConfiguration checkConfig =
+                createModuleConfig(ExecutableStatementCountCheck.class);
+        checkConfig.addAttribute("max", "1");
+        checkConfig.addAttribute("tokens", "LAMBDA");
+
+        final int max = 1;
+
+        final String[] expected = {
+            "13:22: " + getCheckMessage(MSG_KEY, 6, max),
+            "22:22: " + getCheckMessage(MSG_KEY, 2, max),
+            "23:26: " + getCheckMessage(MSG_KEY, 2, max),
+            "27:26: " + getCheckMessage(MSG_KEY, 4, max),
+        };
+
+        verify(checkConfig, getPath("InputExecutableStatementCountLambdas.java"), expected);
+    }
+
 }
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountRecords.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountRecords.java
--- a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountRecords.java
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountRecords.java
@@ -71,4 +71,20 @@ record R76(int x) {
             return R76.class;
         }
     }
+
+    private int id(int i) {
+        return i;
+    }
+
+    private final int value = 2;
+
+    private final int field = id(switch(value) {
+        case 0 -> -1;
+        case 2 -> {
+            int temp = 0;
+            temp += 3;
+            yield temp;
+        }
+        default -> throw new IllegalStateException();
+    });
 }
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountLambdas.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountLambdas.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountLambdas.java
@@ -0,0 +1,34 @@
+package com.puppycrawl.tools.checkstyle.checks.sizes.executablestatementcount;
+
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/* Config:
+ * max = 1
+ * tokens = LAMBDA
+ */
+
+public class InputExecutableStatementCountLambdas {
+
+    Consumer a = (o) -> { // violation
+        o.toString(); // 1
+        o.toString(); // 2
+        o.toString(); // 3
+        o.toString(); // 4
+        o.toString(); // 5
+        o.toString(); // 6
+    };
+
+    Consumer b = (x) -> { // violation
+        Consumer c = (s) -> { // violation
+            String str = s.toString();
+            str = str + "!";
+        };
+        Consumer d = (s) -> { // violation
+            String str = s.toString();
+            Consumer t = a -> a.toString().trim(); // ok
+            Function x1 = a -> a; // ok
+            Function y = a -> null; // ok
+        };
+    };
+}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountMaxZero.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountMaxZero.java
--- a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountMaxZero.java
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/sizes/executablestatementcount/InputExecutableStatementCountMaxZero.java
@@ -91,7 +91,7 @@ public void run() { // violation
     /** Empty constructor */
     public InputExecutableStatementCountMaxZero(String someString) {} // ok
 
-    static Runnable r1 = () -> { // ok
+    static Runnable r1 = () -> { // violation
         String.valueOf("Hello world one!");
     };
 }