From d8e8b2ddec800c8f6c766a58141f1e8405c114c2 Mon Sep 17 00:00:00 2001 From: Peter Trifanov Date: Tue, 20 Oct 2020 16:09:24 +0300 Subject: [PATCH] Recommendation 3.1 - recommended order of imports (#380) ### What's done: * Implementation * Tests --- diktat-analysis.yml | 5 +- .../src/test/resources/test-rules-config.yml | 4 +- .../ruleset/rules/files/FileStructureRule.kt | 132 ++++++++++++++---- .../cqfn/diktat/ruleset/utils/ASTConstants.kt | 7 + .../org/cqfn/diktat/ruleset/utils/PsiUtils.kt | 1 - .../main/resources/diktat-analysis-huawei.yml | 9 +- .../src/main/resources/diktat-analysis.yml | 6 +- .../chapter3/FileStructureRuleTestFix.kt | 31 +++- .../ReorderingImportsRecommendedExpected.kt | 24 ++++ .../ReorderingImportsRecommendedTest.kt | 20 +++ .../smoke/src/main/kotlin/Example2Expected.kt | 7 + .../smoke/src/main/kotlin/Example2Test.kt | 5 + info/diktat-kotlin-coding-style-guide-en.md | 62 +++----- 13 files changed, 236 insertions(+), 77 deletions(-) create mode 100644 diktat-rules/src/test/resources/test/paragraph3/file_structure/ReorderingImportsRecommendedExpected.kt create mode 100644 diktat-rules/src/test/resources/test/paragraph3/file_structure/ReorderingImportsRecommendedTest.kt diff --git a/diktat-analysis.yml b/diktat-analysis.yml index e3f311d5c0..7b28f66c83 100644 --- a/diktat-analysis.yml +++ b/diktat-analysis.yml @@ -136,9 +136,12 @@ - name: FILE_CONTAINS_ONLY_COMMENTS enabled: true configuration: {} + # order imports alphabetically - name: FILE_UNORDERED_IMPORTS enabled: true - configuration: {} + configuration: + # use logical imports grouping with sorting inside of a group + useRecommendedImportsOrder: true - name: FILE_INCORRECT_BLOCKS_ORDER enabled: true configuration: {} diff --git a/diktat-common/src/test/resources/test-rules-config.yml b/diktat-common/src/test/resources/test-rules-config.yml index 10c371af45..e719cdd85b 100644 --- a/diktat-common/src/test/resources/test-rules-config.yml +++ b/diktat-common/src/test/resources/test-rules-config.yml @@ -149,7 +149,9 @@ configuration: {} - name: FILE_WILDCARD_IMPORTS enabled: true - configuration: {} + configuration: + allowedWildcards: "" # Allowed wildcards for imports (e.g. "import org.cqfn.diktat.*, import org.jetbrains.kotlin.*") + useRecommendedImportsOrder: true - name: NO_BRACES_IN_CONDITIONALS_AND_LOOPS enabled: true configuration: {} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt index f1cfa73f9b..fedd39c60b 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/files/FileStructureRule.kt @@ -12,12 +12,15 @@ import com.pinterest.ktlint.core.ast.ElementType.PACKAGE_DIRECTIVE import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE import org.cqfn.diktat.common.config.rules.RuleConfiguration import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.common.config.rules.getCommonConfiguration import org.cqfn.diktat.common.config.rules.getRuleConfig import org.cqfn.diktat.ruleset.constants.Warnings.FILE_CONTAINS_ONLY_COMMENTS import org.cqfn.diktat.ruleset.constants.Warnings.FILE_INCORRECT_BLOCKS_ORDER import org.cqfn.diktat.ruleset.constants.Warnings.FILE_NO_BLANK_LINE_BETWEEN_BLOCKS import org.cqfn.diktat.ruleset.constants.Warnings.FILE_UNORDERED_IMPORTS import org.cqfn.diktat.ruleset.constants.Warnings.FILE_WILDCARD_IMPORTS +import org.cqfn.diktat.ruleset.rules.PackageNaming.Companion.PACKAGE_SEPARATOR +import org.cqfn.diktat.ruleset.utils.StandardPlatforms import org.cqfn.diktat.ruleset.utils.findChildBefore import org.cqfn.diktat.ruleset.utils.getFileName import org.cqfn.diktat.ruleset.utils.handleIncorrectOrder @@ -26,6 +29,7 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtImportDirective @@ -43,6 +47,9 @@ class FileStructureRule(private val configRules: List) : Rule("file private lateinit var emitWarn: ((offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit) private var isFixMode: Boolean = false private var fileName: String = "" + private val domainName by lazy { + configRules.getCommonConfiguration().value.domainName + } override fun visit(node: ASTNode, autoCorrect: Boolean, @@ -52,10 +59,14 @@ class FileStructureRule(private val configRules: List) : Rule("file if (node.elementType == ElementType.FILE) { fileName = node.getFileName() - val configuration = WildCardImportsConfig( - this.configRules.getRuleConfig(FILE_WILDCARD_IMPORTS)?.configuration ?: mapOf() + val wildcardImportsConfig = WildCardImportsConfig( + this.configRules.getRuleConfig(FILE_WILDCARD_IMPORTS)?.configuration ?: emptyMap() + ) + val importsGroupingConfig = ImportsGroupingConfig( + this.configRules.getRuleConfig(FILE_UNORDERED_IMPORTS)?.configuration ?: emptyMap() ) - node.findChildByType(IMPORT_LIST)?.let { checkImportsOrder(it, configuration) } + node.findChildByType(IMPORT_LIST) + ?.let { checkImportsOrder(it, wildcardImportsConfig, importsGroupingConfig) } if (checkFileHasCode(node)) { checkCodeBlocksOrderAndEmptyLines(node) } @@ -64,7 +75,10 @@ class FileStructureRule(private val configRules: List) : Rule("file } private fun checkFileHasCode(node: ASTNode): Boolean { - val codeTokens = TokenSet.andNot(TokenSet.ANY, TokenSet.create(WHITE_SPACE, KDOC, BLOCK_COMMENT, EOL_COMMENT, PACKAGE_DIRECTIVE, IMPORT_LIST)) + val codeTokens = TokenSet.andNot( + TokenSet.ANY, + TokenSet.create(WHITE_SPACE, KDOC, BLOCK_COMMENT, EOL_COMMENT, PACKAGE_DIRECTIVE, IMPORT_LIST) + ) val hasCode = node.getChildren(codeTokens).isNotEmpty() if (!hasCode) { FILE_CONTAINS_ONLY_COMMENTS.warn(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) @@ -88,7 +102,14 @@ class FileStructureRule(private val configRules: List) : Rule("file listOfNotNull(copyrightComment, headerKdoc, fileAnnotations).handleIncorrectOrder({ getSiblingBlocks(copyrightComment, headerKdoc, fileAnnotations, packageDirectiveNode) }) { astNode, beforeThisNode -> - FILE_INCORRECT_BLOCKS_ORDER.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset, astNode) { + FILE_INCORRECT_BLOCKS_ORDER.warnAndFix( + configRules, + emitWarn, + isFixMode, + astNode.text.lines().first(), + astNode.startOffset, + astNode + ) { node.moveChildBefore(astNode, beforeThisNode, true) astNode.treeNext?.let { node.replaceChild(it, PsiWhiteSpaceImpl("\n\n")) } } @@ -98,7 +119,8 @@ class FileStructureRule(private val configRules: List) : Rule("file arrayOf(copyrightComment, headerKdoc, fileAnnotations, packageDirectiveNode, importsList).forEach { astNode -> astNode?.treeNext?.apply { if (elementType == WHITE_SPACE && text.count { it == '\n' } != 2) { - FILE_NO_BLANK_LINE_BETWEEN_BLOCKS.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), astNode.startOffset, astNode) { + FILE_NO_BLANK_LINE_BETWEEN_BLOCKS.warnAndFix(configRules, emitWarn, isFixMode, astNode.text.lines().first(), + astNode.startOffset, astNode) { (this as LeafPsiElement).replaceWithText("\n\n${text.replace("\n", "")}") } } @@ -107,48 +129,68 @@ class FileStructureRule(private val configRules: List) : Rule("file } @Suppress("UnsafeCallOnNullableType") - private fun checkImportsOrder(node: ASTNode, configuration: WildCardImportsConfig) { + private fun checkImportsOrder( + node: ASTNode, + wildCardImportsConfig: WildCardImportsConfig, + importsGroupingConfig: ImportsGroupingConfig + ) { val imports = node.getChildren(TokenSet.create(IMPORT_DIRECTIVE)).toList() // importPath can be null if import name cannot be parsed, which should be a very rare case, therefore !! should be safe here imports .filter { (it.psi as KtImportDirective).importPath!!.run { - isAllUnder && toString() !in configuration.allowedWildcards + isAllUnder && toString() !in wildCardImportsConfig.allowedWildcards } } .forEach { FILE_WILDCARD_IMPORTS.warn(configRules, emitWarn, isFixMode, it.text, it.startOffset, it) } - val sortedImports = imports.sortedBy { it.text } - if (sortedImports != imports) { + val sortedImportsGroups = if (importsGroupingConfig.useRecommendedImportsOrder) { + regroupImports(imports.map { it.psi as KtImportDirective }) + .map { group -> group.map { it.node } } + } else { + listOf(imports) + } + .map { group -> group.sortedBy { it.text } } + + if (sortedImportsGroups.flatten() != imports) { FILE_UNORDERED_IMPORTS.warnAndFix(configRules, emitWarn, isFixMode, fileName, node.startOffset, node) { - rearrangeImports(node, imports, sortedImports) + rearrangeImports(node, imports, sortedImportsGroups) } } } - private fun rearrangeImports(node: ASTNode, imports: List, sortedImports: List) { + private fun rearrangeImports(node: ASTNode, imports: List, sortedImportsGroups: List>) { require(node.elementType == IMPORT_LIST) // move all commented lines among import before imports block node.getChildren(TokenSet.create(EOL_COMMENT)) - .forEach { - node.treeParent.addChild(it.clone() as ASTNode, node) - node.treeParent.addChild(PsiWhiteSpaceImpl("\n"), node) - } + .forEach { + node.treeParent.addChild(it.clone() as ASTNode, node) + node.treeParent.addChild(PsiWhiteSpaceImpl("\n"), node) + } node.removeRange(imports.first(), imports.last()) - sortedImports.forEachIndexed { index, importNode -> - if (index != 0) { - node.addChild(PsiWhiteSpaceImpl("\n"), null) + sortedImportsGroups.filterNot { it.isEmpty() } + .run { + forEachIndexed { groupIndex, group -> + group.forEachIndexed { index, importNode -> + node.addChild(importNode, null) + if (index != group.size - 1) { + node.addChild(PsiWhiteSpaceImpl("\n"), null) + } + } + if (groupIndex != size - 1) { + node.addChild(PsiWhiteSpaceImpl("\n\n"), null) + } + } } - node.addChild(importNode, null) - } } - private fun ASTNode.getSiblingBlocks(copyrightComment: ASTNode?, - headerKdoc: ASTNode?, - fileAnnotations: ASTNode?, - packageDirectiveNode: ASTNode + private fun ASTNode.getSiblingBlocks( + copyrightComment: ASTNode?, + headerKdoc: ASTNode?, + fileAnnotations: ASTNode?, + packageDirectiveNode: ASTNode ): Pair = when (elementType) { BLOCK_COMMENT -> null to listOfNotNull(headerKdoc, fileAnnotations, packageDirectiveNode).first() KDOC -> copyrightComment to (fileAnnotations ?: packageDirectiveNode) @@ -156,7 +198,47 @@ class FileStructureRule(private val configRules: List) : Rule("file else -> error("Only BLOCK_COMMENT, KDOC and FILE_ANNOTATION_LIST are valid inputs.") } + private fun regroupImports(imports: List): List> { + val (android, notAndroid) = imports.partition { + it.isStandard(StandardPlatforms.ANDROID) + } + + val (ownDomain, tmp) = notAndroid.partition { import -> + import.importPath?.fqName?.pathSegments() + ?.zip(domainName.split(PACKAGE_SEPARATOR).map(Name::identifier)) + ?.all { it.first == it.second } + ?: false + } + + val (others, javaAndKotlin) = tmp.partition { + !it.isStandard(StandardPlatforms.JAVA) && !it.isStandard(StandardPlatforms.KOTLIN) + } + + val (java, kotlin) = javaAndKotlin.partition { it.isStandard(StandardPlatforms.JAVA) } + + return listOf(android, ownDomain, others, java, kotlin) + } + + private val standardImportsAsName = StandardPlatforms.values() + .map { it to it.packages } + .toMap() + .mapValues { (_, value) -> + value.map { it.split(PACKAGE_SEPARATOR).map(Name::identifier) } + } + + private fun KtImportDirective.isStandard(platformName: StandardPlatforms) = standardImportsAsName[platformName]?.any { names -> + names.zip(importPath?.fqName?.pathSegments() ?: emptyList()) + .all { it.first == it.second } + } ?: false + class WildCardImportsConfig(config: Map) : RuleConfiguration(config) { val allowedWildcards = config["allowedWildcards"]?.split(",")?.map { it.trim() } ?: listOf() } + + class ImportsGroupingConfig(config: Map) : RuleConfiguration(config) { + /** + * Use imports grouping according to recommendation 3.1 + */ + val useRecommendedImportsOrder = config["useRecommendedImportsOrder"]?.toBoolean() ?: true + } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/ASTConstants.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/ASTConstants.kt index 9cbab1b334..1d4c8c40cf 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/ASTConstants.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/ASTConstants.kt @@ -18,3 +18,10 @@ internal const val SET_PREFIX = "set" val emptyBlockList = listOf(LBRACE, WHITE_SPACE, RBRACE) internal const val EMPTY_BLOCK_TEXT = "{}" + +enum class StandardPlatforms(val packages: List) { + ANDROID(listOf("android", "androidx", "com.android")), + JAVA(listOf("java", "javax")), + KOTLIN(listOf("kotlin", "kotlinx")), + ; +} diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/PsiUtils.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/PsiUtils.kt index bc9ea43406..d253ca51d3 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/PsiUtils.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/utils/PsiUtils.kt @@ -8,7 +8,6 @@ import org.jetbrains.kotlin.psi.KtCallExpression import org.jetbrains.kotlin.psi.KtConstantExpression import org.jetbrains.kotlin.psi.KtDotQualifiedExpression import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtFunctionLiteral import org.jetbrains.kotlin.psi.KtIfExpression import org.jetbrains.kotlin.psi.KtNameReferenceExpression import org.jetbrains.kotlin.psi.KtProperty diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml index 5c7e89d7fe..ae60e46a70 100644 --- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml +++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml @@ -135,9 +135,12 @@ - name: FILE_CONTAINS_ONLY_COMMENTS enabled: true configuration: {} + # order imports alphabetically - name: FILE_UNORDERED_IMPORTS enabled: true - configuration: {} + configuration: + # use logical imports grouping with sorting inside of a group + useRecommendedImportsOrder: true - name: FILE_INCORRECT_BLOCKS_ORDER enabled: true configuration: {} @@ -146,7 +149,9 @@ configuration: {} - name: FILE_WILDCARD_IMPORTS enabled: true - configuration: {} + configuration: + allowedWildcards: "" # Allowed wildcards for imports (e.g. "import org.cqfn.diktat.*, import org.jetbrains.kotlin.*") + useRecommendedImportsOrder: true - name: NO_BRACES_IN_CONDITIONALS_AND_LOOPS enabled: true configuration: {} diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml index 98a236b47c..fa368a8de8 100644 --- a/diktat-rules/src/main/resources/diktat-analysis.yml +++ b/diktat-rules/src/main/resources/diktat-analysis.yml @@ -136,9 +136,12 @@ - name: FILE_CONTAINS_ONLY_COMMENTS enabled: true configuration: {} + # order imports alphabetically - name: FILE_UNORDERED_IMPORTS enabled: true - configuration: {} + configuration: + # use logical imports grouping with sorting inside of a group + useRecommendedImportsOrder: true - name: FILE_INCORRECT_BLOCKS_ORDER enabled: true configuration: {} @@ -150,6 +153,7 @@ enabled: true configuration: allowedWildcards: "" # Allowed wildcards for imports (e.g. "import org.cqfn.diktat.*, import org.jetbrains.kotlin.*") + useRecommendedImportsOrder: true - name: NO_BRACES_IN_CONDITIONALS_AND_LOOPS enabled: true configuration: {} diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/FileStructureRuleTestFix.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/FileStructureRuleTestFix.kt index 59206747b6..04e246a200 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/FileStructureRuleTestFix.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter3/FileStructureRuleTestFix.kt @@ -1,9 +1,11 @@ package org.cqfn.diktat.ruleset.chapter3 import generated.WarningNames +import org.cqfn.diktat.common.config.rules.DIKTAT_COMMON +import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.ruleset.constants.Warnings import org.cqfn.diktat.ruleset.rules.files.FileStructureRule import org.cqfn.diktat.util.FixTestBase -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test @@ -35,11 +37,32 @@ class FileStructureRuleTestFix : FixTestBase("test/paragraph3/file_structure", : @Test @Tag(WarningNames.FILE_UNORDERED_IMPORTS) fun `should reorder imports alphabetically with saving of EOL comments`() { - fixAndCompare("ReorderingImportsExpected.kt", "ReorderingImportsTest.kt") + fixAndCompare( + "ReorderingImportsExpected.kt", "ReorderingImportsTest.kt", + overrideRulesConfigList = listOf( + RulesConfig( + Warnings.FILE_UNORDERED_IMPORTS.name, true, + mapOf("useRecommendedImportsOrder" to "false") + ) + ) + ) } @Test @Tag(WarningNames.FILE_UNORDERED_IMPORTS) - @Disabled("not yet implemented") - fun `should reorder imports according to recommendation 3_1`() = Unit + fun `should reorder imports according to recommendation 3_1`() { + fixAndCompare( + "ReorderingImportsRecommendedExpected.kt", "ReorderingImportsRecommendedTest.kt", + overrideRulesConfigList = listOf( + RulesConfig( + DIKTAT_COMMON, true, + mapOf("domainName" to "org.cqfn.diktat") + ), + RulesConfig( + Warnings.FILE_UNORDERED_IMPORTS.name, true, + mapOf("useRecommendedImportsOrder" to "true") + ) + ) + ) + } } diff --git a/diktat-rules/src/test/resources/test/paragraph3/file_structure/ReorderingImportsRecommendedExpected.kt b/diktat-rules/src/test/resources/test/paragraph3/file_structure/ReorderingImportsRecommendedExpected.kt new file mode 100644 index 0000000000..0b0f1391a0 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/file_structure/ReorderingImportsRecommendedExpected.kt @@ -0,0 +1,24 @@ +package test.paragraph3.file_structure + +// comment about java imports +import android.* +import androidx.* +import com.android.* + +import org.cqfn.* +import org.cqfn.diktat.* + +import com.fasterxml.jackson.databind.ObjectMapper +import com.google.common.base.CaseFormat +import io.gitlab.arturbosch.detekt.Detekt +import org.junit.jupiter.api.Assertions +import org.slf4j.Logger +import org.springframework.context.annotation.Bean + +import java.io.IOException +import java.net.URL +import java.nio.charset.Charset + +import kotlin.system.exitProcess + +class Example diff --git a/diktat-rules/src/test/resources/test/paragraph3/file_structure/ReorderingImportsRecommendedTest.kt b/diktat-rules/src/test/resources/test/paragraph3/file_structure/ReorderingImportsRecommendedTest.kt new file mode 100644 index 0000000000..c364456b95 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph3/file_structure/ReorderingImportsRecommendedTest.kt @@ -0,0 +1,20 @@ +package test.paragraph3.file_structure + +import com.android.* +import kotlin.system.exitProcess +// comment about java imports +import java.io.IOException +import java.net.URL +import org.cqfn.* +import com.fasterxml.jackson.databind.ObjectMapper +import android.* +import org.cqfn.diktat.* +import org.junit.jupiter.api.Assertions +import androidx.* +import org.springframework.context.annotation.Bean +import com.google.common.base.CaseFormat +import java.nio.charset.Charset +import io.gitlab.arturbosch.detekt.Detekt +import org.slf4j.Logger + +class Example diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt index d3dc99dc08..38e7103c3d 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Expected.kt @@ -4,6 +4,13 @@ package your.name.here +import org.slf4j.LoggerFactory + +import java.io.IOException +import java.util.Properties + +import kotlin.system.exitProcess + /** * @property foo * @property bar diff --git a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Test.kt b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Test.kt index 77e4c3d022..53abaac0d2 100644 --- a/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Test.kt +++ b/diktat-rules/src/test/resources/test/smoke/src/main/kotlin/Example2Test.kt @@ -1,5 +1,10 @@ package test.smoke +import java.io.IOException +import java.util.Properties +import kotlin.system.exitProcess +import org.slf4j.LoggerFactory + data @ExperimentalStdlibApi public class Example(val foo:Int, val bar:Double):SuperExample("lorem ipsum") private class Test : Exception() \ No newline at end of file diff --git a/info/diktat-kotlin-coding-style-guide-en.md b/info/diktat-kotlin-coding-style-guide-en.md index 54d06279dd..20d934c882 100644 --- a/info/diktat-kotlin-coding-style-guide-en.md +++ b/info/diktat-kotlin-coding-style-guide-en.md @@ -656,56 +656,34 @@ b) Each of the above code blocks should be separated by a blank line. c) Import statements are ordered alphabetically, without line breaks, and are not using wildcards * d) Recommendation: in one .kt source file there should be only one class declaration and its name should match with the filename -### Recommendation 3.1: import statements should appear in the following order: Android, internal company imports, external commercial organizations, other open source third parties, net/org open source organizations, and finally java core dependencies. Each group should be separated by a blank line. +### Recommendation 3.1: import statements should appear in the following order: Android, internal company imports, external imports, java core dependencies and finally, kotlin standard library. Each group should be separated by a blank line. -Note: Static imports are placed above all other imports (using the same sorting method as for regular imports). From top to bottom, the order is the following: -1. static imports -2. Android -3. Imports of packages used internally in your organization -4. Other commercial organizations -5. Open source imports -6. net/org open source dependencies -7. javacard -8. java core packages +1. Android +2. Imports of packages used internally in your organization +3. Imports from other non-core dependencies +4. java core packages +5. kotlin stdlib -Each category sorted in alphabetical order. This style is compatible with [Android import order] (https://source.android.com/setup/contribute/code-style#order-import-statements). +Each category should be sorted in alphabetical order. This style is compatible with [Android import order](https://source.android.com/setup/contribute/code-style#order-import-statements). -Recommended example: - -```java -import static all.other.imports; // static imports - -import android.*; // android - -import androidx.*; // android - -import com.android.*; // android - -import your.company.*; // your company's libs - -import com.your.company.*; // your company's libs - -import com.google.common.io.Files; // other business organizations - -import lombok.extern.slf4j.Sl4j; // Other open source third parties - -import maven.*; // Other open source third parties - -import net.sf.json.*; // open source organization starting with .net - -import org.linux.apache.server.SoapServer; // open source organization starting with .org +Recommended example: +```kotlin +import android.* // android +import androidx.* // android +import com.android.* // android -import javacard.*; +import com.your.company.* // your company's libs +import your.company.* // your company's libs -import java.io.IOException; // java core packages -import java.net.URL; +import com.fasterxml.jackson.databind.ObjectMapper // other third-party dependencies +import org.junit.jupiter.api.Assertions -import java.rmi.RmiServer; // java core packages -import java.rmi.server.Server; +import java.io.IOException // java core packages +import java.net.URL -import javax.swing.JPanel; // java extensions -import javax.swing.event.ActionEvent; +import kotlin.system.exitProcess // kotlin standard library +import kotlinx.coroutines.* // official kotlin extension library ``` ### >Recommendation 3.2: The declaration part of a class-like code structures (class/interface/e.t.c) should be in the following order: compile-time constants (for objects), class properties, late-init class properties, init-blocks, constructors, public methods, internal methods, protected methods, private methods, companion object. Their declaration should be separated by blank lines.