Repository: checkstyle/checkstyle

Problem statement:
EmptyLineSeparator: Inability to use xpath for violation on empty line
Split from #7956
https://github.com/checkstyle/checkstyle/blob/a8a90cf2423c27fb3fe227fc1906fdfc255f5d9b/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheck.java#L419
There is no AST node associated an empty line for which the violations are logged. This results the inability of such violations to be xpath suppressed. We may need to change the violation a bit to make it compliant with xpath.

```
C:\Users\Shashwat\Documents>cat config.xml
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
          "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
          "https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
  <module name="TreeWalker">
        <module name="EmptyLineSeparator">
        <property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
        <property name="allowMultipleEmptyLines" value="false"/>
        </module>
  </module>
</module>
C:\Users\Shashwat\Documents>cat Test.java
class A {



    void foo() { // line 5



    } // line 9
}
C:\Users\Shashwat\Documents>java -jar checkstyle-8.31-all.jar -c config.xml Test.java
Starting audit...
[ERROR] C:\Users\Shashwat\Documents\Test.java:5: 'METHOD_DEF' has more than 1 empty lines before. [EmptyLineSeparator]
[ERROR] C:\Users\Shashwat\Documents\Test.java:7: There is more than 1 empty line one after another. [EmptyLineSeparator]
[ERROR] C:\Users\Shashwat\Documents\Test.java:8: There is more than 1 empty line one after another. [EmptyLineSeparator]
Audit done.
Checkstyle ends with 3 errors.
```

In the above example it is clear that when we have to log violation for a similar case, outside the class members, we do it in a single go.
```Test.java:5: 'METHOD_DEF' has more than 1 empty lines before. [EmptyLineSeparator]```
This can be extended to solve this issue as well, where instead of logging for every empty line, we can log violation for the above example as follows:
```Test.java:9: There is more than 1 empty line above. [EmptyLineSeparator]```
However, the node to be logged is a bit uncertain. This needs to be dicussed.



Hints:
it is not critical on what node to print violation as in java you always have some character after - end of class scope. It would be good to report on previous token always, as we always know it existed and we do NOT know what will be on next line, this will be more reliable approach I think.
@romani it' ok to move violation report on previous token, right? If yes, I will plan to solve this issue.
>it' ok to move violation report on previous token, right?

Right. You are welcome to send PR
I am on it.

Patch:
diff --git a/src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionEmptyLineSeparatorTest.java b/src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionEmptyLineSeparatorTest.java
--- a/src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionEmptyLineSeparatorTest.java
+++ b/src/it/java/org/checkstyle/suppressionxpathfilter/XpathRegressionEmptyLineSeparatorTest.java
@@ -122,21 +122,38 @@ public void testFour() throws Exception {
         moduleConfig.addAttribute("allowMultipleEmptyLines", "false");
 
         final String[] expectedViolation = {
-            "12:5: " + getCheckMessage(EmptyLineSeparatorCheck.class,
-                    EmptyLineSeparatorCheck.MSG_MULTIPLE_LINES_AFTER, "METHOD_DEF"),
+            "12:25: " + getCheckMessage(EmptyLineSeparatorCheck.class,
+                    EmptyLineSeparatorCheck.MSG_MULTIPLE_LINES_AFTER, "}"),
         };
 
-        final List<String> expectedXpathQueries = Arrays.asList(
+        final List<String> expectedXpathQueries = Collections.singletonList(
                 "/CLASS_DEF[./IDENT[@text='SuppressionXpathRegressionEmptyLineSeparator4']]"
-                        + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='foo1']]",
+                        + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='foo1']]/SLIST/RCURLY"
+        );
 
-                "/CLASS_DEF[./IDENT[@text='SuppressionXpathRegressionEmptyLineSeparator4']]"
-                        + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='foo1']]"
-                        + "/MODIFIERS",
+        runVerifications(moduleConfig, fileToProcess, expectedViolation, expectedXpathQueries);
+    }
 
-                "/CLASS_DEF[./IDENT[@text='SuppressionXpathRegressionEmptyLineSeparator4']]"
-                        + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='foo1']]"
-                        + "/MODIFIERS/LITERAL_PUBLIC"
+    @Test
+    public void testFive() throws Exception {
+        final File fileToProcess = new File(
+                getPath("SuppressionXpathRegressionEmptyLineSeparator5.java")
+        );
+
+        final DefaultConfiguration moduleConfig =
+                createModuleConfig(EmptyLineSeparatorCheck.class);
+        moduleConfig.addAttribute("allowMultipleEmptyLines", "false");
+        moduleConfig.addAttribute("allowMultipleEmptyLinesInsideClassMembers", "false");
+
+        final String[] expectedViolation = {
+            "14:15: " + getCheckMessage(EmptyLineSeparatorCheck.class,
+                    EmptyLineSeparatorCheck.MSG_MULTIPLE_LINES_INSIDE),
+        };
+
+        final List<String> expectedXpathQueries = Collections.singletonList(
+                "/CLASS_DEF[./IDENT[@text='SuppressionXpathRegressionEmptyLineSeparator5']]"
+                        + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='foo1']]/SLIST/LITERAL_TRY/SLIST"
+                        + "/SINGLE_LINE_COMMENT/COMMENT_CONTENT"
         );
 
         runVerifications(moduleConfig, fileToProcess, expectedViolation, expectedXpathQueries);
diff --git a/src/it/resources/org/checkstyle/suppressionxpathfilter/emptylineseparator/SuppressionXpathRegressionEmptyLineSeparator5.java b/src/it/resources/org/checkstyle/suppressionxpathfilter/emptylineseparator/SuppressionXpathRegressionEmptyLineSeparator5.java
new file mode 100644
--- /dev/null
+++ b/src/it/resources/org/checkstyle/suppressionxpathfilter/emptylineseparator/SuppressionXpathRegressionEmptyLineSeparator5.java
@@ -0,0 +1,22 @@
+package org.checkstyle.suppressionxpathfilter.emptylineseparator;
+
+import java.io.IOException;
+
+public class SuppressionXpathRegressionEmptyLineSeparator5 {
+
+    int foo1() throws Exception {
+        int a = 1;
+        int b = 2;
+        try {
+            if (a != b) {
+                throw new IOException();
+            }
+            // warn
+
+
+        } catch(IOException e) {
+            return 1;
+        }
+        return 0;
+    }
+}
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheck.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheck.java
@@ -444,7 +444,9 @@ private void checkToken(DetailAST ast, DetailAST nextToken) {
             default:
                 if (nextToken.getType() == TokenTypes.RCURLY) {
                     if (hasNotAllowedTwoEmptyLinesBefore(nextToken)) {
-                        log(ast, MSG_MULTIPLE_LINES_AFTER, ast.getText());
+                        final DetailAST result = getLastElementBeforeEmptyLines(ast,
+                                nextToken.getLineNo());
+                        log(result, MSG_MULTIPLE_LINES_AFTER, result.getText());
                     }
                 }
                 else if (!hasEmptyLineAfter(ast)) {
@@ -477,12 +479,65 @@ private void processMultipleLinesInside(DetailAST ast) {
         if (isClassMemberBlock(astType)) {
             final List<Integer> emptyLines = getEmptyLines(ast);
             final List<Integer> emptyLinesToLog = getEmptyLinesToLog(emptyLines);
-
             for (Integer lineNo : emptyLinesToLog) {
-                // Checkstyle counts line numbers from 0 but IDE from 1
-                log(lineNo + 1, MSG_MULTIPLE_LINES_INSIDE);
+                log(getLastElementBeforeEmptyLines(ast, lineNo), MSG_MULTIPLE_LINES_INSIDE);
+            }
+        }
+    }
+
+    /**
+     * Returns the element after which empty lines exist.
+     *
+     * @param ast the ast to check.
+     * @param line the empty line which gives violation.
+     * @return The DetailAST after which empty lines are present.
+     */
+    private static DetailAST getLastElementBeforeEmptyLines(DetailAST ast, int line) {
+        DetailAST result = ast;
+        if (ast.getFirstChild().getLineNo() <= line) {
+            result = ast.getFirstChild();
+            while (result.getNextSibling() != null
+                    && result.getNextSibling().getLineNo() <= line) {
+                result = result.getNextSibling();
+            }
+            if (result.hasChildren()) {
+                result = getLastElementBeforeEmptyLines(result, line);
+            }
+        }
+
+        if (result.getNextSibling() != null) {
+            final Optional<DetailAST> postFixNode = getPostFixNode(result.getNextSibling());
+            if (postFixNode.isPresent()) {
+                // A post fix AST will always have a sibling METHOD CALL
+                // METHOD CALL will at least have two children
+                // The first first child is DOT in case of POSTFIX which have at least 2 children
+                // First child of DOT again puts us back to normal AST tree which will
+                // recurse down below from here
+                final DetailAST firstChildAfterPostFix = postFixNode.get();
+                result = getLastElementBeforeEmptyLines(firstChildAfterPostFix, line);
             }
         }
+        return result;
+    }
+
+    /**
+     * Gets postfix Node from AST if present.
+     *
+     * @param ast the AST used to get postfix Node.
+     * @return Optional postfix node.
+     */
+    private static Optional<DetailAST> getPostFixNode(DetailAST ast) {
+        Optional<DetailAST> result = Optional.empty();
+        if (ast.getType() == TokenTypes.EXPR
+            // EXPR always has at least one child
+            && ast.getFirstChild().getType() == TokenTypes.METHOD_CALL) {
+            // METHOD CALL always has at two least child
+            final DetailAST node = ast.getFirstChild().getFirstChild();
+            if (node.getType() == TokenTypes.DOT) {
+                result = Optional.of(node);
+            }
+        }
+        return result;
     }
 
     /**
@@ -534,7 +589,7 @@ private static List<Integer> getEmptyLinesToLog(List<Integer> emptyLines) {
             int previousEmptyLineNo = emptyLines.get(0);
             for (int emptyLineNo : emptyLines) {
                 if (previousEmptyLineNo + 1 == emptyLineNo) {
-                    emptyLinesToLog.add(emptyLineNo);
+                    emptyLinesToLog.add(previousEmptyLineNo);
                 }
                 previousEmptyLineNo = emptyLineNo;
             }


Test patch:
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheckTest.java
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheckTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/whitespace/EmptyLineSeparatorCheckTest.java
@@ -103,7 +103,7 @@ public void testMultipleEmptyLinesBetweenClassMembers() throws Exception {
             "33:5: " + getCheckMessage(MSG_MULTIPLE_LINES, "VARIABLE_DEF"),
             "38:5: " + getCheckMessage(MSG_MULTIPLE_LINES, "VARIABLE_DEF"),
             "43:5: " + getCheckMessage(MSG_MULTIPLE_LINES, "METHOD_DEF"),
-            "45:5: " + getCheckMessage(MSG_MULTIPLE_LINES_AFTER, "METHOD_DEF"),
+            "45:33: " + getCheckMessage(MSG_MULTIPLE_LINES_AFTER, "}"),
         };
         verify(checkConfig, getPath("InputEmptyLineSeparatorMultipleEmptyLines.java"), expected);
     }
@@ -256,10 +256,12 @@ public void testPreviousLineEmptiness() throws Exception {
         final DefaultConfiguration checkConfig = createModuleConfig(EmptyLineSeparatorCheck.class);
         checkConfig.addAttribute("allowMultipleEmptyLinesInsideClassMembers", "false");
         final String[] expected = {
-            "11: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "16: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "22: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "31: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "13:30: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "18:5: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "24:67: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "33:48: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "43:21: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+
         };
         verify(checkConfig,
                 getPath("InputEmptyLineSeparatorPreviousLineEmptiness.java"), expected);
@@ -270,12 +272,11 @@ public void testDisAllowMultipleEmptyLinesInsideClassMembers() throws Exception
         final DefaultConfiguration checkConfig = createModuleConfig(EmptyLineSeparatorCheck.class);
         checkConfig.addAttribute("allowMultipleEmptyLinesInsideClassMembers", "false");
         final String[] expected = {
-            "27: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "39: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "45: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "50: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "55: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "56: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "25:11: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "37:11: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "43:11: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "48:31: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "53:11: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
             "60:1: " + getCheckMessage(MSG_SHOULD_BE_SEPARATED, "CLASS_DEF"),
         };
         verify(checkConfig,
@@ -464,8 +465,8 @@ public void testEmptyLineSeparatorRecordsAndCompactCtorsNoEmptyLines() throws Ex
 
         final String[] expected = {
             "2:1: " + getCheckMessage(MSG_SHOULD_BE_SEPARATED, "package"),
-            "16: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
-            "22: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "14:27: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "20:29: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
         };
 
         verify(checkConfig,
@@ -486,4 +487,55 @@ public void testEmptyLineSeparatorMultipleSingleTypeVariables() throws Exception
                 expected);
     }
 
+    @Test
+    public void testEmptyLineSeparatorEmptyLinesInsideClassMembersRecursive() throws Exception {
+        final DefaultConfiguration checkConfig = createModuleConfig(EmptyLineSeparatorCheck.class);
+        checkConfig.addAttribute("allowMultipleEmptyLinesInsideClassMembers", "false");
+        final String[] expected = {
+            "19:15: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+        };
+        verify(checkConfig,
+                getPath("InputEmptyLineSeparatorRecursive.java"),
+                expected);
+    }
+
+    @Test
+    public void testEmptyLineSeparatorNewMethodDef() throws Exception {
+        final DefaultConfiguration checkConfig = createModuleConfig(EmptyLineSeparatorCheck.class);
+        checkConfig.addAttribute("allowMultipleEmptyLinesInsideClassMembers", "false");
+        final String[] expected = {
+            "21:34: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "30:26: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+        };
+        verify(checkConfig,
+                getPath("InputEmptyLineSeparatorNewMethodDef.java"),
+                expected);
+    }
+
+    @Test
+    public void testEmptyLineSeparatorPostFixCornerCases() throws Exception {
+        final DefaultConfiguration checkConfig = createModuleConfig(EmptyLineSeparatorCheck.class);
+        checkConfig.addAttribute("allowMultipleEmptyLinesInsideClassMembers", "false");
+        final String[] expected = {
+            "10:19: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "24:29: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+            "35:29: " + getCheckMessage(MSG_MULTIPLE_LINES_INSIDE),
+        };
+        verify(checkConfig,
+                getPath("InputEmptyLineSeparatorPostFixCornerCases.java"),
+                expected);
+    }
+
+    @Test
+    public void testEmptyLineSeparatorAnnotation() throws Exception {
+        final DefaultConfiguration checkConfig = createModuleConfig(EmptyLineSeparatorCheck.class);
+        checkConfig.addAttribute("allowMultipleEmptyLines", "false");
+        final String[] expected = {
+            "10:22: " + getCheckMessage(MSG_MULTIPLE_LINES_AFTER, "}"),
+        };
+        verify(checkConfig,
+                getPath("InputEmptyLineSeparatorAnnotations.java"),
+                expected);
+    }
+
 }
diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorRecordsAndCompactCtorsNoEmptyLines.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorRecordsAndCompactCtorsNoEmptyLines.java
--- a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorRecordsAndCompactCtorsNoEmptyLines.java
+++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorRecordsAndCompactCtorsNoEmptyLines.java
@@ -11,15 +11,15 @@
  *
  */
 public class InputEmptyLineSeparatorRecordsAndCompactCtorsNoEmptyLines {
-    public void foo() {
+    public void foo() { // violation
 
 
-    } // ^ violation
+    }
 
     public record MyRecord1(){
-        public MyRecord1{
+        public MyRecord1{ // violation
 
 
-        } // ^ violation
+        }
     }
 }
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorAnnotations.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorAnnotations.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorAnnotations.java
@@ -0,0 +1,15 @@
+package com.puppycrawl.tools.checkstyle.checks.whitespace.emptylineseparator;
+
+/**
+ * Config:
+ * allowMultipleEmptyLines = false
+ */
+public class InputEmptyLineSeparatorAnnotations {
+    @Deprecated
+    @SuppressWarnings("unchecked")
+    class SomeClass {} // violation
+
+
+
+
+}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorNewMethodDef.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorNewMethodDef.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorNewMethodDef.java
@@ -0,0 +1,35 @@
+package com.puppycrawl.tools.checkstyle.checks.whitespace.emptylineseparator;
+
+import java.util.ArrayList;
+
+/**
+ * Config:
+ * allowMultipleEmptyLinesInsideClassMembers = false
+ */
+public class InputEmptyLineSeparatorNewMethodDef {
+    private static final int MULTIPLICATOR;
+
+    void processOne() { // ok
+
+        int a;
+
+        int b;
+
+        new ArrayList<String>() {
+            {
+                add("String One");
+                add("String Two"); // violation
+
+
+                add("String Three");
+            }
+        }.add("String Four");
+    }
+
+    static { // violation at line below
+        MULTIPLICATOR = 5;
+
+
+    }
+
+}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorPostFixCornerCases.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorPostFixCornerCases.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorPostFixCornerCases.java
@@ -0,0 +1,41 @@
+package com.puppycrawl.tools.checkstyle.checks.whitespace.emptylineseparator;
+
+/**
+ * Config:
+ * allowMultipleEmptyLinesInsideClassMembers = false
+ */
+public class InputEmptyLineSeparatorPostFixCornerCases {
+
+    protected void foo1() throws Exception {
+        int a = 25;
+        // violation
+
+
+        foo(a);
+    }
+
+    void foo(int a) {
+        System.out.println(a);
+    }
+
+    protected void foo() {
+
+        Object[] defaults = new Object[] {
+                "String One",
+                // violation
+
+
+                "String Two",
+
+        };
+    }
+
+    protected void foo2() {
+        Object[] defaults = new Object[] {
+            "String One", 13,
+
+
+            "String Two", // violation above this line.
+        };
+    }
+}
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorPreviousLineEmptiness.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorPreviousLineEmptiness.java
--- a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorPreviousLineEmptiness.java
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorPreviousLineEmptiness.java
@@ -1,12 +1,16 @@
 package com.puppycrawl.tools.checkstyle.checks.whitespace.emptylineseparator;
 
+/**
+ * Config:
+ * allowMultipleEmptyLinesInsideClassMembers = false
+ */
 public class InputEmptyLineSeparatorPreviousLineEmptiness {
     private static final int MULTIPLICATOR;
 
     private int base;
 
     static {
-        MULTIPLICATOR = 5;
+        MULTIPLICATOR = 5; // violation
 
 
     }
@@ -17,7 +21,7 @@ public class InputEmptyLineSeparatorPreviousLineEmptiness {
         base = 33;
     }
 
-    public InputEmptyLineSeparatorPreviousLineEmptiness(int base) {
+    public InputEmptyLineSeparatorPreviousLineEmptiness(int base) { // violation
 
 
         this.base = base;
@@ -26,9 +30,18 @@ public InputEmptyLineSeparatorPreviousLineEmptiness(int base) {
     public InputEmptyLineSeparatorPreviousLineEmptiness() {
     }
 
-    public static int increment(int value) {
+    public static int increment(int value) { // violation
 
 
         return value * MULTIPLICATOR + 1;
     }
+
+    void foo2() { // ok
+    }
+
+
+    void foo3() { // violation
+
+
+    }
 }
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorRecursive.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorRecursive.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/whitespace/emptylineseparator/InputEmptyLineSeparatorRecursive.java
@@ -0,0 +1,28 @@
+package com.puppycrawl.tools.checkstyle.checks.whitespace.emptylineseparator;
+
+import java.io.IOException;
+
+/**
+ * Config:
+ * allowMultipleEmptyLinesInsideClassMembers = false
+ */
+public class InputEmptyLineSeparatorRecursive {
+
+    int foo1() throws Exception {
+        int a = 1;
+        int b = 2;
+        try {
+            if (a != b) {
+                throw new IOException();
+            }
+            // empty lines below should cause a violation
+            // violation
+
+
+        } catch(IOException e) {
+            System.out.println("IO error: " + e.getMessage());
+            return 1;
+        }
+        return 0;
+    }
+}