Repository: analysis-dev/diktat (kotlin)

Issue:
FileStructureRule doesn't group typealiases together
## Environment information
* diktat version: 0.6.2

Pull request:
### What's done:
* Fixed logic
* Added tests
* Updated docs

## Which rule and warnings did you add?

This pull request closes #924

Patch:
diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/TopLevelOrderRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/TopLevelOrderRule.kt
index 473d3b3627..d539bfddb3 100644
--- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/TopLevelOrderRule.kt
+++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/chapter3/files/TopLevelOrderRule.kt
@@ -15,6 +15,7 @@ import com.pinterest.ktlint.core.ast.ElementType.OVERRIDE_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.PRIVATE_KEYWORD
 import com.pinterest.ktlint.core.ast.ElementType.PROPERTY
 import com.pinterest.ktlint.core.ast.ElementType.PROTECTED_KEYWORD
+import com.pinterest.ktlint.core.ast.ElementType.TYPEALIAS
 import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
 import com.pinterest.ktlint.core.ast.isPartOfComment
 import org.jetbrains.kotlin.com.intellij.lang.ASTNode
@@ -44,18 +45,19 @@ class TopLevelOrderRule(configRules: List<RulesConfig>) : DiktatRule(
         }
         val properties = Properties(children.filter { it.elementType == PROPERTY }).sortElements()
         val functions = children.filter { it.elementType == FUN }
+        val typealiases = children.filter { it.elementType == TYPEALIAS }
         val classes = children.filter { it.elementType == CLASS || it.elementType == OBJECT_DECLARATION }
-        val sortOrder = Blocks(properties, functions, classes).sortElements().map { astNode ->
-            Pair(astNode, astNode.siblings(false).takeWhile { it.elementType == WHITE_SPACE || it.isPartOfComment() }.toList())
+        val sortedElementsWithTrailingNonCodeNodes = Blocks(properties, typealiases, functions, classes).sortElements().map { astNode ->
+            astNode to astNode.siblings(false).takeWhile { it.elementType == WHITE_SPACE || it.isPartOfComment() }.toList()
         }
         val lastNonSortedChildren = initialElementsOrder.last().siblings(true).toList()
-        sortOrder.filterIndexed { index, pair -> initialElementsOrder[index] != pair.first }
+        sortedElementsWithTrailingNonCodeNodes.filterIndexed { index, pair -> initialElementsOrder[index] != pair.first }
             .forEach { listOfChildren ->
                 val wrongNode = listOfChildren.first
                 TOP_LEVEL_ORDER.warnAndFix(configRules, emitWarn, isFixMode, wrongNode.text, wrongNode.startOffset, wrongNode) {
                     node.removeRange(node.findChildByType(IMPORT_LIST)!!.treeNext, node.lastChildNode)
                     node.removeChild(node.lastChildNode)
-                    sortOrder.map { (sortedNode, sortedNodePrevSibling) ->
+                    sortedElementsWithTrailingNonCodeNodes.map { (sortedNode, sortedNodePrevSibling) ->
                         sortedNodePrevSibling.reversed().map { node.addChild(it, null) }
                         node.addChild(sortedNode, null)
                     }
@@ -94,16 +96,17 @@ class TopLevelOrderRule(configRules: List<RulesConfig>) : DiktatRule(
      */
     private data class Blocks(
         private val properties: List<ASTNode>,
+        private val typealiases: List<ASTNode>,
         private val functions: List<ASTNode>,
         private val classes: List<ASTNode>) : Elements {
         override fun sortElements(): MutableList<ASTNode> {
             val (extensionFun, nonExtensionFun) = functions.partition { (it.psi as KtFunction).isExtensionDeclaration() }
-            return (properties + listOf(classes, extensionFun, nonExtensionFun).map { nodes ->
+            return (properties + listOf(typealiases, classes, extensionFun, nonExtensionFun).flatMap { nodes ->
                 val (privatePart, notPrivatePart) = nodes.partition { it.hasModifier(PRIVATE_KEYWORD) }
                 val (protectedPart, notProtectedPart) = notPrivatePart.partition { it.hasModifier(PROTECTED_KEYWORD) || it.hasModifier(OVERRIDE_KEYWORD) }
                 val (internalPart, publicPart) = notProtectedPart.partition { it.hasModifier(INTERNAL_KEYWORD) }
                 listOf(publicPart, internalPart, protectedPart, privatePart).flatten()
-            }.flatten()).toMutableList()
+            }).toMutableList()
         }
     }
 
@@ -111,6 +114,6 @@ class TopLevelOrderRule(configRules: List<RulesConfig>) : DiktatRule(
         /**
          * List of children that should be sort
          */
-        val sortedType = listOf(PROPERTY, FUN, CLASS, OBJECT_DECLARATION)
+        val sortedType = listOf(PROPERTY, FUN, CLASS, OBJECT_DECLARATION, TYPEALIAS)
     }
 }
diff --git a/info/guide/guide-chapter-3.md b/info/guide/guide-chapter-3.md
index 95099f8152..30d94ab193 100644
--- a/info/guide/guide-chapter-3.md
+++ b/info/guide/guide-chapter-3.md
@@ -83,6 +83,7 @@ Kotlin allows several top-level declaration types: classes, objects, interfaces,
 When declaring more than one class or zero classes (e.g. only functions), as per rule [2.2.1](#r2.2.1), you should document the whole file in the header KDoc.
 When declaring top-level structures, keep the following order:
 1. Top-level constants and properties (following same order as properties inside a class: `const val`,`val`, `lateinit var`, `var`)
+2. typealiases (grouped by their visibility modifiers)
 2. Interfaces, classes and objects (grouped by their visibility modifiers)
 3. Extension functions
 4. Other functions
@@ -98,6 +99,8 @@ const val CONSTANT = 42
 
 val topLevelProperty = "String constant"
 
+internal typealias ExamplesHandler = (IExample) -> Unit
+
 interface IExample
 
 class Example : IExample


Test patch:
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/TopLevelOrderRuleFixTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/TopLevelOrderRuleFixTest.kt
index cd5a6c8862..e58c846aa6 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/TopLevelOrderRuleFixTest.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/files/TopLevelOrderRuleFixTest.kt
@@ -22,7 +22,7 @@ class TopLevelOrderRuleFixTest : FixTestBase("test/paragraph3/top_level", ::TopL
     }
 
     // FixMe: should be considered this case (swapped order of kdoc and package directive)
-    @Disabled("Isn't working yet")
+    @Disabled("Isn't working yet, because KDoc is bound to class declaration here. Also, should be moved to FileStructureRuleFixTest.")
     @Test
     @Tag(WarningNames.TOP_LEVEL_ORDER)
     fun `should fix top level order with header kdoc`() {
diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt
index a0e0a61bbf..e5090ad883 100644
--- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt
+++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/util/TestUtils.kt
@@ -21,8 +21,6 @@ import java.util.concurrent.atomic.AtomicInteger
 
 internal const val TEST_FILE_NAME = "TestFileName.kt"
 
-typealias LintErrorCallback = (LintError, Boolean) -> Unit
-
 private val log = LoggerFactory.getLogger("TestUtils")
 
 @Suppress("TYPE_ALIAS")
@@ -30,6 +28,8 @@ internal val defaultCallback: (lintError: LintError, corrected: Boolean) -> Unit
     log.warn("Received linting error: $lintError")
 }
 
+typealias LintErrorCallback = (LintError, Boolean) -> Unit
+
 /**
  * Compare [LintError]s from [this] with [expectedLintErrors]
  *
diff --git a/diktat-rules/src/test/resources/test/paragraph3/top_level/TopLevelSortExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/top_level/TopLevelSortExpected.kt
index 3a6cfe5722..7cb3b7a8bc 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/top_level/TopLevelSortExpected.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/top_level/TopLevelSortExpected.kt
@@ -6,6 +6,12 @@ val heh = 10
 
 private var t = 1
 
+typealias StringConsumer = (String) -> Unit
+
+internal typealias ExamplesList = List<IExample>
+
+private typealias UsersMap = Map<User, UserData>
+
 class Qwe() {}
 
 fun String.qq() {}
diff --git a/diktat-rules/src/test/resources/test/paragraph3/top_level/TopLevelSortTest.kt b/diktat-rules/src/test/resources/test/paragraph3/top_level/TopLevelSortTest.kt
index 3bec663066..b2e388a175 100644
--- a/diktat-rules/src/test/resources/test/paragraph3/top_level/TopLevelSortTest.kt
+++ b/diktat-rules/src/test/resources/test/paragraph3/top_level/TopLevelSortTest.kt
@@ -4,6 +4,8 @@ const val q = "1"
 
 private fun foo() {}
 
+private typealias UsersMap = Map<User, UserData>
+
 class Qwe() {}
 
 fun String.qq() {}
@@ -12,4 +14,8 @@ private var t = 1
 
 internal fun kl() {}
 
+internal typealias ExamplesList = List<IExample>
+
 val heh = 10
+
+typealias StringConsumer = (String) -> Unit