Repository: checkstyle/checkstyle

Problem statement:
InnerTypeLast should handle ctors and static init blocks
https://checkstyle.sourceforge.io/config_design.html#InnerTypeLast
From the Check description:

>>  Checks nested (internal) classes/interfaces are declared at the bottom of the primary (top-level) class after all method and field declarations. 

It is a bit unclear whether **constructors** should be declared before an inner type. I think they should be.

$ javac TestClass.java
```
(no output)
```

$ cat config.xml
```xml
<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
    "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<module name="Checker">
    <module name="TreeWalker">
        <module name="InnerTypeLast">
        </module>
    </module>
</module>
```

$ cat TestClass.java
```java
class TestClass1 {

    public class Inner { // Expected violation
    }

    public TestClass1(Object x) { // ctor
    }

}

class TestClass2 {

    public class Inner { // Expected violation
    }

    static { // static init block
    }

}
```

$ RUN_LOCALE="-Duser.language=en -Duser.country=US"
$ java $RUN_LOCALE -jar checkstyle-8.34-all.jar -c config.xml TestClass.java
```
Starting audit...
Audit done.
```


Hints:
I agree ctors should be included. I question static ctors and instance ctors without the method declaration (just `{}`), but I think it does make some sense they are included too.

Patch:
diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/design/InnerTypeLastCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/design/InnerTypeLastCheck.java
--- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/design/InnerTypeLastCheck.java
+++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/design/InnerTypeLastCheck.java
@@ -19,6 +19,9 @@
 
 package com.puppycrawl.tools.checkstyle.checks.design;
 
+import java.util.Arrays;
+import java.util.List;
+
 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
 import com.puppycrawl.tools.checkstyle.api.DetailAST;
@@ -28,7 +31,8 @@
 /**
  * <p>
  * Checks nested (internal) classes/interfaces are declared at the bottom of the
- * primary (top-level) class after all method and field declarations.
+ * primary (top-level) class after all init and static init blocks,
+ * method, constructor and field declarations.
  * </p>
  * <p>
  * To configure the check:
@@ -45,6 +49,12 @@
  * }
  *
  * class Test2 {
+ *     static {}; // OK
+ *     class InnerTest1 {}
+ *     public Test2() {} // violation, constructor should be declared before inner types.
+ * }
+ *
+ * class Test3 {
  *     private String s; // OK
  *     public void test() {} // OK
  *     class InnerTest1 {}
@@ -73,6 +83,15 @@ public class InnerTypeLastCheck extends AbstractCheck {
      */
     public static final String MSG_KEY = "arrangement.members.before.inner";
 
+    /** List of class member tokens. */
+    private static final List<Integer> CLASS_MEMBER_TOKENS = Arrays.asList(
+            TokenTypes.VARIABLE_DEF,
+            TokenTypes.METHOD_DEF,
+            TokenTypes.CTOR_DEF,
+            TokenTypes.INSTANCE_INIT,
+            TokenTypes.STATIC_INIT
+    );
+
     /** Meet a root class. */
     private boolean rootClass = true;
 
@@ -106,8 +125,7 @@ public void visitToken(DetailAST ast) {
             DetailAST nextSibling = ast.getNextSibling();
             while (nextSibling != null) {
                 if (!ScopeUtil.isInCodeBlock(ast)
-                    && (nextSibling.getType() == TokenTypes.VARIABLE_DEF
-                        || nextSibling.getType() == TokenTypes.METHOD_DEF)) {
+                    && CLASS_MEMBER_TOKENS.contains(nextSibling.getType())) {
                     log(nextSibling, MSG_KEY);
                 }
                 nextSibling = nextSibling.getNextSibling();


Test patch:
diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/design/InnerTypeLastCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/design/InnerTypeLastCheckTest.java
--- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/design/InnerTypeLastCheckTest.java
+++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/design/InnerTypeLastCheckTest.java
@@ -87,6 +87,18 @@ public void testIfRootClassChecked3() throws Exception {
         }, "InputInnerTypeLastClassRootClass.java", expected);
     }
 
+    @Test
+    public void testInnerTypeBeforeCtor() throws Exception {
+        final DefaultConfiguration checkConfig =
+            createModuleConfig(InnerTypeLastCheck.class);
+        final String[] expected = {
+            "8:5: " + getCheckMessage(MSG_KEY),
+            "17:5: " + getCheckMessage(MSG_KEY),
+            "26:5: " + getCheckMessage(MSG_KEY),
+        };
+        verify(checkConfig, getPath("InputInnerTypeLastClassCtorsInitBlocks.java"), expected);
+    }
+
     @Test
     public void testGetAcceptableTokens() {
         final InnerTypeLastCheck obj = new InnerTypeLastCheck();
diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/design/innertypelast/InputInnerTypeLastClassCtorsInitBlocks.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/design/innertypelast/InputInnerTypeLastClassCtorsInitBlocks.java
new file mode 100644
--- /dev/null
+++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/design/innertypelast/InputInnerTypeLastClassCtorsInitBlocks.java
@@ -0,0 +1,27 @@
+package com.puppycrawl.tools.checkstyle.checks.design.innertypelast;
+
+/** Config: default */
+public class InputInnerTypeLastClassCtorsInitBlocks {
+    public class Inner { // violation
+    }
+
+    public InputInnerTypeLastClassCtorsInitBlocks() {
+    }
+}
+
+class BeforeInitBlock {
+
+    public class Inner2 { // violation
+    }
+
+    {}
+
+}
+
+class BeforeStaticInitBlock {
+
+    public interface Inner3 { // violation
+    }
+
+    static {}
+}