diff --git a/diktat-cli/src/test/kotlin/com/saveourtool/diktat/smoke/DiktatSmokeTestBase.kt b/diktat-cli/src/test/kotlin/com/saveourtool/diktat/smoke/DiktatSmokeTestBase.kt index ba0afd4364..591c4666a6 100644 --- a/diktat-cli/src/test/kotlin/com/saveourtool/diktat/smoke/DiktatSmokeTestBase.kt +++ b/diktat-cli/src/test/kotlin/com/saveourtool/diktat/smoke/DiktatSmokeTestBase.kt @@ -21,6 +21,7 @@ import com.saveourtool.diktat.ruleset.constants.Warnings.MISSING_KDOC_ON_FUNCTIO import com.saveourtool.diktat.ruleset.constants.Warnings.MISSING_KDOC_TOP_LEVEL import com.saveourtool.diktat.ruleset.constants.Warnings.WRONG_INDENTATION import com.saveourtool.diktat.ruleset.constants.Warnings.WRONG_NEWLINES +import com.saveourtool.diktat.ruleset.constants.Warnings.KDOC_NO_CONSTRUCTOR_PROPERTY import com.saveourtool.diktat.ruleset.rules.chapter1.FileNaming import com.saveourtool.diktat.ruleset.rules.chapter2.comments.CommentsRule import com.saveourtool.diktat.ruleset.rules.chapter2.comments.HeaderCommentRule @@ -28,7 +29,6 @@ import com.saveourtool.diktat.ruleset.rules.chapter2.kdoc.KdocComments import com.saveourtool.diktat.ruleset.rules.chapter2.kdoc.KdocFormatting import com.saveourtool.diktat.ruleset.rules.chapter2.kdoc.KdocMethods import com.saveourtool.diktat.ruleset.rules.chapter3.EmptyBlock -import com.saveourtool.diktat.ruleset.rules.chapter3.files.SemicolonsRule import com.saveourtool.diktat.ruleset.rules.chapter6.classes.InlineClassesRule import com.saveourtool.diktat.ruleset.utils.indentation.IndentationConfig import com.saveourtool.diktat.ruleset.utils.indentation.IndentationConfig.Companion.EXTENDED_INDENT_AFTER_OPERATORS @@ -40,6 +40,7 @@ import com.charleskorn.kaml.YamlConfiguration import generated.WarningNames import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Test import org.junit.jupiter.api.Timeout @@ -258,6 +259,24 @@ abstract class DiktatSmokeTestBase { } } + @Test + @Tag("DiktatRuleSetProvider") + @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) + @Disabled("https://github.com/saveourtool/diktat/issues/1889") + fun `regression - kdoc for classes`() { + val configFilePath = prepareOverriddenRulesConfig( + rulesToDisable = emptyList(), + rulesToOverride = mapOf( + KDOC_NO_CONSTRUCTOR_PROPERTY.name to mapOf( + "isParamTagsForParameters" to "true", + "isParamTagsForPrivateProperties" to "true", + "isParamTagsForGenericTypes" to "true" + ) + ) + ) + fixAndCompare(configFilePath, "ClassKdocExpected.kt", "ClassKdocTest.kt") + } + @Test @Tag("DiktatRuleSetProvider") @Timeout(TEST_TIMEOUT_SECONDS, unit = SECONDS) diff --git a/diktat-cli/src/test/resources/test/smoke/src/main/kotlin/ClassKdocExpected.kt b/diktat-cli/src/test/resources/test/smoke/src/main/kotlin/ClassKdocExpected.kt new file mode 100644 index 0000000000..ab832f6109 --- /dev/null +++ b/diktat-cli/src/test/resources/test/smoke/src/main/kotlin/ClassKdocExpected.kt @@ -0,0 +1,332 @@ +package com.saveourtool.diktat + +/** + * kdoc + * class + * comment + * + * @param name property info + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param name property info + * single-line comment + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param name property info + * block + * comment + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param name property info + * kdoc property + * comment + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + name: String +) {} + +/** + * kdoc + * class + * comment + * + * @property name property info + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @property name property info + * single-line comment + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @property name property info + * block + * comment + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @property name property info + * kdoc property + * comment + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @property name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param name property info + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param name property info + * single-line comment + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param name property info + * block + * comment + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param name property info + * kdoc property + * comment + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @property name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + private val name: String +) {} + +/** + * kdoc + * class + * comment + * + * @param K + * @property openName open property info + * single-line comment + * @property openLastName open last property + * info + * block + * comment + * @property openAddr property + * info + * @property openBirthDate kdoc property + * comment + */ +open class B constructor( + open val openName: String, + open val openLastName: String, + open val openBirthDate: String, + /** + * @property openAddr property + * comment + */ + open val openAddr: String +) {} + +/** + * kdoc + * class + * comment + * + * @param P generic type + * @param K generic type + * @param privateLastName private + * property info + * block + * comment + * @param G + * @param privateName single-line comment + * @param paramName single-line comment + * @param paramLastName block + * comment + * @param privateBirthDate kdoc property + * comment + * @param paramBirthDate kdoc property + * comment + * @property internalName internal + * property info + * single-line comment + * @property openName override + * property info + * single-line comment + * @property openAddr override + * property info + * @property protectedName single-line comment + * @property name single-line comment + * @property protectedLastName block + * comment + * @property internalLastName block + * comment + * @property openLastName block + * comment + * @property lastName block + * comment + * @property protectedBirthDate kdoc property + * comment + * @property internalBirthDate kdoc property + * comment + * @property openBirthDate kdoc property + * comment + * @property birthDate kdoc property + * comment + */ +class A constructor( + private val privateName: String, + protected val protectedName: String, + internal val internalName: String, + override val openName: String, + val name: String, + paramName: String, + private val privateLastName: String, + protected val protectedLastName: String, + internal val internalLastName: String, + override val openLastName: String, + val lastName: String, + paramLastName: String, + private val privateBirthDate: String, + protected val protectedBirthDate: String, + internal val internalBirthDate: String, + override val openBirthDate: String, + val birthDate: String, + paramBirthDate: String, + /** + * @property privateAddr property + * comment + */ + private val privateAddr: String, + /** + * @property protectedAddr property + * comment + */ + protected val protectedAddr: String, + /** + * @property internalAddr property + * comment + */ + internal val internalAddr: String, + /** + * @property openAddr property + * comment + */ + override val openAddr: String, + /** + * @property addr property + * comment + */ + val addr: String, + /** + * @property paramAddr property + * comment + */ + paramAddr: String, +) : B() {} + +/** + * kdoc + * class + * comment + * + * @property keyAs + * @property as + */ +actual annotation class JsonSerialize( + actual val `as`: KClass<*>, + actual val keyAs: KClass<*>, +) diff --git a/diktat-cli/src/test/resources/test/smoke/src/main/kotlin/ClassKdocTest.kt b/diktat-cli/src/test/resources/test/smoke/src/main/kotlin/ClassKdocTest.kt new file mode 100644 index 0000000000..b5b6ab915b --- /dev/null +++ b/diktat-cli/src/test/resources/test/smoke/src/main/kotlin/ClassKdocTest.kt @@ -0,0 +1,351 @@ +package test.smoke.src.main.kotlin + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + // single-line comment + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + /* + * block + * comment + */ + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + /** + * kdoc property + * comment + */ + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + // single-line comment + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /* + * block + * comment + */ + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * kdoc property + * comment + */ + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + // single-line comment + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /* + * block + * comment + */ + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * kdoc property + * comment + */ + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @param openName open property info + * @param openLastName open last property + * info + * @property openAddr property + * info + */ +open class B constructor( + // single-line comment + open val openName: String, + /* + * block + * comment + */ + open val openLastName: String, + /** + * kdoc property + * comment + */ + open val openBirthDate: String, + /** + * @property openAddr property + * comment + */ + open val openAddr: String +) {} + +/** + * kdoc + * class + * comment + * @property P generic type + * @param K generic type + * @property internalName internal + * property info + * @param openName override + * property info + * @property privateLastName private + * property info + * @property openAddr override + * property info + */ +class A constructor( + // single-line comment + private val privateName: String, + // single-line comment + protected val protectedName: String, + // single-line comment + internal val internalName: String, + // single-line comment + override val openName: String, + // single-line comment + val name: String, + // single-line comment + paramName: String, + /* + * block + * comment + */ + private val privateLastName: String, + /* + * block + * comment + */ + protected val protectedLastName: String, + /* + * block + * comment + */ + internal val internalLastName: String, + /* + * block + * comment + */ + override val openLastName: String, + /* + * block + * comment + */ + val lastName: String, + /* + * block + * comment + */ + paramLastName: String, + /** + * kdoc property + * comment + */ + private val privateBirthDate: String, + /** + * kdoc property + * comment + */ + protected val protectedBirthDate: String, + /** + * kdoc property + * comment + */ + internal val internalBirthDate: String, + /** + * kdoc property + * comment + */ + override val openBirthDate: String, + /** + * kdoc property + * comment + */ + val birthDate: String, + /** + * kdoc property + * comment + */ + paramBirthDate: String, + /** + * @property privateAddr property + * comment + */ + private val privateAddr: String, + /** + * @property protectedAddr property + * comment + */ + protected val protectedAddr: String, + /** + * @property internalAddr property + * comment + */ + internal val internalAddr: String, + /** + * @property openAddr property + * comment + */ + override val openAddr: String, + /** + * @property addr property + * comment + */ + val addr: String, + /** + * @property paramAddr property + * comment + */ + paramAddr: String, +) : B() {} + +/** + * kdoc + * class + * comment + * @property keyAs + */ +actual annotation class JsonSerialize( + actual val `as`: KClass<*>, + actual val keyAs: KClass<*>, +) diff --git a/diktat-ktlint-engine/src/main/kotlin/com/saveourtool/diktat/ktlint/KtLintRuleWrapper.kt b/diktat-ktlint-engine/src/main/kotlin/com/saveourtool/diktat/ktlint/KtLintRuleWrapper.kt index 3929c24f9d..b77db15f54 100644 --- a/diktat-ktlint-engine/src/main/kotlin/com/saveourtool/diktat/ktlint/KtLintRuleWrapper.kt +++ b/diktat-ktlint-engine/src/main/kotlin/com/saveourtool/diktat/ktlint/KtLintRuleWrapper.kt @@ -13,6 +13,7 @@ private typealias EmitType = (offset: Int, errorMessage: String, canBeAutoCorrec /** * This is a wrapper around __KtLint__'s [Rule] which adjusts visitorModifiers to keep order with prevRule. + * * @property rule */ class KtLintRuleWrapper( diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/config/RuleConfiguration.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/config/RuleConfiguration.kt index 7d371eb585..5f301d6864 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/config/RuleConfiguration.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/config/RuleConfiguration.kt @@ -2,6 +2,7 @@ package com.saveourtool.diktat.ruleset.config /** * Configuration that allows customizing additional options of particular rules. + * * @property config a map of strings with configuration options for a particular rule */ open class RuleConfiguration(protected val config: Map) diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt index 9dc43f5ca3..0fb36399ce 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter2/kdoc/KdocComments.kt @@ -35,7 +35,6 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet import org.jetbrains.kotlin.kdoc.lexer.KDocTokens import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.KDOC -import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag import org.jetbrains.kotlin.lexer.KtTokens @@ -67,9 +66,6 @@ class KdocComments(configRules: List) : DiktatRule( KdocCommentsConfiguration(configRules.getRuleConfig(KDOC_NO_CONSTRUCTOR_PROPERTY)?.configuration ?: emptyMap()) } - /** - * @param node - */ override fun logic(node: ASTNode) { val filePath = node.getFilePath() val config = configRules.getCommonConfiguration() @@ -191,15 +187,11 @@ class KdocComments(configRules: List) : DiktatRule( val correctTag = if (isParamTagNeeded) KDocKnownTag.PARAM else KDocKnownTag.PROPERTY if (parameterTagInClassKdoc.knownTag != correctTag) { - val warningText = if (isParamTagNeeded) { - "change `@property` tag to `@param` tag for <$parameterName> to KDoc" - } else { - "change `@param` tag to `@property` tag for <$parameterName> to KDoc" - } + val paramOrPropertySwitchText = if (isParamTagNeeded) "@property" to "@param" else "@param" to "@property" + val warningText = "change `${paramOrPropertySwitchText.first}` tag to `${paramOrPropertySwitchText.second}` tag for <$parameterName> to KDoc" KDOC_NO_CONSTRUCTOR_PROPERTY.warnAndFix(configRules, emitWarn, isFixMode, warningText, node.startOffset, node) { - val isFirstTagInKdoc = parameterTagInClassKdoc.node == kdocBeforeClass.kDocTags().first().node - replaceWrongTagInClassKdoc(kdocBeforeClass, parameterName, isParamTagNeeded, isFirstTagInKdoc) + kdocBeforeClass.replaceWrongTagInClassKdoc(parameterName, isParamTagNeeded) } } } ?: run { @@ -207,8 +199,9 @@ class KdocComments(configRules: List) : DiktatRule( val warningText = if (isParamTagNeeded) "add param <$parameterName> to KDoc" else "add property <$parameterName> to KDoc" KDOC_NO_CONSTRUCTOR_PROPERTY.warnAndFix(configRules, emitWarn, isFixMode, warningText, node.startOffset, node) { - val newKdocText = if (isParamTagNeeded) "* @param $parameterName\n " else "* @property $parameterName\n " - insertTextInKdoc(kdocBeforeClass, checkOneNewLineAfterKdocClassDescription(kdocBeforeClass, newKdocText, false)) + val paramOrPropertyTagText = if (isParamTagNeeded) "@param" else "@property" + val newKdocText = "* $paramOrPropertyTagText $parameterName\n " + insertTextInKdoc(kdocBeforeClass, newKdocText) } } } @@ -225,65 +218,6 @@ class KdocComments(configRules: List) : DiktatRule( (isTypeParameterNode && configuration.isParamTagsForGenericTypes) } - private fun replaceWrongTagInClassKdoc( - kdocBeforeClass: ASTNode, - parameterName: String, - isParamTagNeeded: Boolean, - isFirstTagInKdoc: Boolean - ) { - val wrongTagText = if (isParamTagNeeded) "* @property $parameterName" else "* @param $parameterName" - val replaceText = if (isParamTagNeeded) "* @param $parameterName" else "* @property $parameterName" - - changeTagInKdoc(kdocBeforeClass, wrongTagText, checkOneNewLineAfterKdocClassDescription(kdocBeforeClass, replaceText, isFirstTagInKdoc)) - } - - @Suppress("UnsafeCallOnNullableType") - private fun changeTagInKdoc( - kdocBeforeClass: ASTNode, - wrongTagText: String, - correctTagText: String - ) { - val allKdocText = kdocBeforeClass.text - val newKdocText = allKdocText.replaceFirst(wrongTagText, correctTagText) - kdocBeforeClass.treeParent.replaceChild(kdocBeforeClass, KotlinParser().createNode(newKdocText).findChildByType(KDOC)!!) - } - - @Suppress("UnsafeCallOnNullableType") - private fun checkOneNewLineAfterKdocClassDescription( - kdocBeforeClass: ASTNode, - newKdocText: String, - isFirstTagInKdoc: Boolean - ): String { - val firstDescriptionSection = kdocBeforeClass - .findChildrenMatching { it.elementType == KDocElementTypes.KDOC_SECTION } - .firstOrNull() - val lastTextInDescription = firstDescriptionSection - ?.findChildrenMatching { it.elementType == KDocTokens.TEXT || it.elementType == KDocTokens.CODE_BLOCK_TEXT || it.elementType == KDocTokens.MARKDOWN_LINK } - ?.lastOrNull { it.text.trim().isNotEmpty() } - - val isHasDescription = lastTextInDescription != null - - return newKdocText.let {text -> - if (isHasDescription && (kdocBeforeClass.kDocTags().isEmpty() || isFirstTagInKdoc)) { - // if we have any existing tags and current is first of them, we need to save last three nodes which are KDOC_LEADING_ASTERISK and two WHITE_SPACE around it - // this is necessary so that first tag is on new line immediately after description - val beforeChild = if (isFirstTagInKdoc) firstDescriptionSection!!.lastChildNode.treePrev.treePrev.treePrev else firstDescriptionSection!!.lastChildNode - - // remove all KDOC_LEADING_ASTERISK and WHITE_SPACE between last text in description and end of description - firstDescriptionSection - .findChildrenMatching { - firstDescriptionSection.isChildAfterAnother(it, lastTextInDescription!!) && - (firstDescriptionSection.isChildBeforeAnother(it, beforeChild) || it == beforeChild) - } - .forEach { firstDescriptionSection.removeChild(it) } - - "*\n $text" - } else { - text - } - } - } - private fun checkKdocBeforeClass( node: ASTNode, kdocBeforeClass: ASTNode, @@ -306,7 +240,8 @@ class KdocComments(configRules: List) : DiktatRule( KDOC_NO_CONSTRUCTOR_PROPERTY.warnAndFix(configRules, emitWarn, isFixMode, warningText, node.startOffset, node) { val classNode = node.parent { it.elementType == CLASS }!! - val newKdocText = if (isParamTagNeeded) "/**\n * @param $parameterName\n */" else "/**\n * @property $parameterName\n */" + val paramOrPropertyTagText = if (isParamTagNeeded) "@param" else "@property" + val newKdocText = "/**\n * $paramOrPropertyTagText $parameterName\n */" val newKdoc = KotlinParser().createNode(newKdocText) classNode.addChild(PsiWhiteSpaceImpl("\n"), classNode.firstChildNode) @@ -348,34 +283,36 @@ class KdocComments(configRules: List) : DiktatRule( parameterName: String, paramOrPropertyTagText: String ) = when (prevComment.elementType) { - KDOC -> "/**\n * $paramOrPropertyTagText $parameterName${createClassKdocTextFromKdocComment(prevComment)}\n */" + KDOC -> "/**\n * $paramOrPropertyTagText $parameterName ${createClassKdocTextFromKdocComment(prevComment)}\n */" EOL_COMMENT -> "/**\n * $paramOrPropertyTagText $parameterName ${createClassKdocTextFromEolComment(prevComment)}\n */" - else -> "/**\n * $paramOrPropertyTagText $parameterName${createClassKdocTextFromBlockComment(prevComment)}\n */" + else -> "/**\n * $paramOrPropertyTagText $parameterName ${createClassKdocTextFromBlockComment(prevComment)}\n */" } private fun createClassKdocTextFromKdocComment(prevComment: ASTNode) = prevComment.text .removePrefix("/**") .removeSuffix("*/") - .replace("\n( )*\\*( )*".toRegex(), "\n * ") - .trimStart(' ') + .replace("\\*+".toRegex(), "") + .replace("( )+".toRegex(), " ") + .trimStart(' ', '\n') .trimEnd(' ', '\n') - .let { if (!it.startsWith("\n")) " $it" else it } + .replace("( )*\n( )*".toRegex(), "\n * ") private fun createClassKdocTextFromEolComment(prevComment: ASTNode) = prevComment.text .removePrefix("//") - .trimStart(' ') + .trimStart(' ', '\n') .trimEnd(' ', '\n') private fun createClassKdocTextFromBlockComment(prevComment: ASTNode) = prevComment.text .removePrefix("/*") .removeSuffix("*/") - .replace("\n( )*\\*( )*".toRegex(), "\n * ") - .trimStart(' ') + .replace("\\*+".toRegex(), "") + .replace("( )+".toRegex(), " ") + .trimStart(' ', '\n') .trimEnd(' ', '\n') - .let { if (!it.startsWith("\n")) " $it" else it } + .replace("( )*\n( )*".toRegex(), "\n * ") @Suppress( "UnsafeCallOnNullableType", @@ -404,15 +341,14 @@ class KdocComments(configRules: List) : DiktatRule( val isFixable = !isHasTagsInConstructorKdoc val (isHasWrongTag, warningText) = checkWrongTagAndMakeWarningText(parameterTagInClassKdoc, parameterName, isParamTagNeeded) - val isFirstTagInKdoc = parameterTagInClassKdoc?.node != null && parameterTagInClassKdoc.node == kdocBeforeClass.kDocTags().first().node parameterInClassKdoc?.let { KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT.warnOnlyOrWarnAndFix(configRules, emitWarn, warningText, prevComment.startOffset, node, isFixable, isFixMode) { // local docs should be appended to docs in class - appendKdocTagContent(parameterInClassKdoc, commentText) + appendKdocTagContent(parameterInClassKdoc, parameterName, commentText) if (isHasWrongTag) { - replaceWrongTagInClassKdoc(kdocBeforeClass, parameterName, isParamTagNeeded, isFirstTagInKdoc) + kdocBeforeClass.replaceWrongTagInClassKdoc(parameterName, isParamTagNeeded) } node.removeWithWhiteSpace(prevComment) @@ -421,8 +357,9 @@ class KdocComments(configRules: List) : DiktatRule( ?: run { if (isNeedToWarn(node, isParamTagNeeded)) { KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT.warnOnlyOrWarnAndFix(configRules, emitWarn, warningText, prevComment.startOffset, node, isFixable, isFixMode) { - val newKdocText = if (isParamTagNeeded) "* @param $parameterName$commentText\n " else "* @property $parameterName$commentText\n " - insertTextInKdoc(kdocBeforeClass, checkOneNewLineAfterKdocClassDescription(kdocBeforeClass, newKdocText, isFirstTagInKdoc)) + val paramOrPropertyTagText = if (isParamTagNeeded) "@param" else "@property" + val newKdocText = "* $paramOrPropertyTagText $parameterName $commentText\n " + insertTextInKdoc(kdocBeforeClass, newKdocText) node.removeWithWhiteSpace(prevComment) } @@ -446,21 +383,14 @@ class KdocComments(configRules: List) : DiktatRule( val parameterInClassKdoc = parameterTagInClassKdoc?.node val (isHasWrongTag, warningText) = checkWrongTagAndMakeWarningText(parameterTagInClassKdoc, parameterName, isParamTagNeeded) - val isFirstTagInKdoc = parameterTagInClassKdoc?.node != null && parameterTagInClassKdoc.node == kdocBeforeClass.kDocTags().first().node parameterInClassKdoc?.let { KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT.warnAndFix(configRules, emitWarn, isFixMode, warningText, prevComment.startOffset, node) { - if (parameterInClassKdoc.hasChildOfType(KDocTokens.TEXT)) { - val newKdocText = parameterInClassKdoc - .findChildrenMatching { it.elementType == KDocTokens.TEXT || it.elementType == KDocTokens.CODE_BLOCK_TEXT } - .lastOrNull() - (newKdocText as LeafPsiElement).rawReplaceWithText("${newKdocText.text}\n * ${createClassKdocTextFromEolComment(prevComment)}") - } else { - parameterInClassKdoc.addChild(LeafPsiElement(KDocTokens.TEXT, " ${createClassKdocTextFromEolComment(prevComment)}"), null) - } + // local docs should be appended to docs in class + appendKdocTagContent(parameterInClassKdoc, parameterName, createClassKdocTextFromEolComment(prevComment)) if (isHasWrongTag) { - replaceWrongTagInClassKdoc(kdocBeforeClass, parameterName, isParamTagNeeded, isFirstTagInKdoc) + kdocBeforeClass.replaceWrongTagInClassKdoc(parameterName, isParamTagNeeded) } node.treeParent.removeChildMergingSurroundingWhitespaces(prevComment.treePrev, prevComment, prevComment.treeNext) @@ -469,13 +399,10 @@ class KdocComments(configRules: List) : DiktatRule( ?: run { if (isNeedToWarn(node, isParamTagNeeded)) { KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT.warnAndFix(configRules, emitWarn, isFixMode, warningText, prevComment.startOffset, node) { - val newKdocText = if (isParamTagNeeded) { - "* @param $parameterName ${createClassKdocTextFromEolComment(prevComment)}\n " - } else { - "* @property $parameterName ${createClassKdocTextFromEolComment(prevComment)}\n " - } + val paramOrPropertyTagText = if (isParamTagNeeded) "@param" else "@property" + val newKdocText = "* $paramOrPropertyTagText $parameterName ${createClassKdocTextFromEolComment(prevComment)}\n " - insertTextInKdoc(kdocBeforeClass, checkOneNewLineAfterKdocClassDescription(kdocBeforeClass, newKdocText, isFirstTagInKdoc)) + insertTextInKdoc(kdocBeforeClass, newKdocText) node.treeParent.removeChildMergingSurroundingWhitespaces(prevComment.treePrev, prevComment, prevComment.treeNext) } @@ -498,18 +425,13 @@ class KdocComments(configRules: List) : DiktatRule( ): Pair { val correctTag = if (isParamTagNeeded) KDocKnownTag.PARAM else KDocKnownTag.PROPERTY val isHasWrongTag = parameterTagInClassKdoc != null && parameterTagInClassKdoc.knownTag != correctTag + + val paramOrPropertySwitchText = if (isParamTagNeeded) "@property" to "@param" else "@param" to "@property" + val paramOrPropertyText = if (isParamTagNeeded) "param" else "property" val warningText = if (isHasWrongTag) { - if (isParamTagNeeded) { - "change `@property` tag to `@param` tag for <$parameterName> and add comment to KDoc" - } else { - "change `@param` tag to `@property` tag for <$parameterName> and add comment to KDoc" - } + "change `${paramOrPropertySwitchText.first}` tag to `${paramOrPropertySwitchText.second}` tag for <$parameterName> and add comment to KDoc" } else { - if (isParamTagNeeded) { - "add comment for param <$parameterName> to KDoc" - } else { - "add comment for property <$parameterName> to KDoc" - } + "add comment for $paramOrPropertyText <$parameterName> to KDoc" } return Pair(isHasWrongTag, warningText) @@ -539,15 +461,21 @@ class KdocComments(configRules: List) : DiktatRule( * Append [content] to [kdocTagNode], e.g. * (`@property foo bar`, "baz") -> `@property foo bar baz` */ - private fun appendKdocTagContent(kdocTagNode: ASTNode, content: String) { - kdocTagNode.findChildrenMatching { it.elementType == KDocTokens.TEXT || it.elementType == KDocTokens.CODE_BLOCK_TEXT } + private fun appendKdocTagContent( + kdocTagNode: ASTNode, + parameterName: String, + content: String + ) { + kdocTagNode.findChildrenMatching { + it.elementType == KDocTokens.TEXT || it.elementType == KDocTokens.CODE_BLOCK_TEXT || (it.elementType == KDocTokens.MARKDOWN_LINK && it.text != parameterName) + } .lastOrNull() ?.let { kdocTagNode.replaceChild( it, - LeafPsiElement(KDocTokens.TEXT, "${it.text}$content"), + LeafPsiElement(KDocTokens.TEXT, "${it.text}\n * $content"), ) - } ?: kdocTagNode.addChild(LeafPsiElement(KDocTokens.TEXT, content), null) + } ?: kdocTagNode.addChild(LeafPsiElement(KDocTokens.TEXT, " $content"), null) } private fun checkClassElements(classNode: ASTNode) { @@ -567,7 +495,7 @@ class KdocComments(configRules: List) : DiktatRule( ?.findChildrenMatching { it.elementType == VALUE_PARAMETER } ?.forEach { checkValueParameter(classNode, it) } - // if parent class is public or internal than we can check it's internal code elements + // if parent class is public or internal than we can check its internal code elements if (classBody != null && modifierList.isAccessibleOutside()) { classBody .getChildren(statementsToDocument) diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter2/kdoc/KdocFormatting.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter2/kdoc/KdocFormatting.kt index 34027f5616..777765e930 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter2/kdoc/KdocFormatting.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter2/kdoc/KdocFormatting.kt @@ -237,6 +237,19 @@ class KdocFormatting(configRules: List) : DiktatRule( private fun checkEmptyLineBeforeBasicTags(basicTags: List) { val firstBasicTag = basicTags.firstOrNull() ?: return + // Unlike other tags, @property and @constructor tags are inside separate KDOC_SECTION, + // so we need to move all children from current KDOC_SECTION to previous one and delete current KDOC_SECTION + if (firstBasicTag.knownTag == KDocKnownTag.PROPERTY || firstBasicTag.knownTag == KDocKnownTag.CONSTRUCTOR) { + val currentSection = firstBasicTag.node.treeParent + val prevSection = currentSection.prevSibling { it.elementType == KDocElementTypes.KDOC_SECTION } + + prevSection?.let { + prevSection.addChildren(currentSection.firstChildNode, currentSection.lastChildNode, null) + prevSection.addChild(currentSection.lastChildNode, null) + currentSection.treeParent.removeChild(currentSection) + } + } + val hasContentBefore = firstBasicTag .node .allSiblings(true) diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter3/LineLength.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter3/LineLength.kt index 0473592285..5ff6c23c4b 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter3/LineLength.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter3/LineLength.kt @@ -545,6 +545,7 @@ class LineLength(configRules: List) : DiktatRule( /** * Class Comment show that long line should be split in comment + * * @property hasNewLineBefore flag to handle type of comment: ordinary comment (long part of which should be moved to the next line) * and inline comments (which should be moved entirely to the previous line) * @property indexLastSpace index of last space to substring comment @@ -593,6 +594,7 @@ class LineLength(configRules: List) : DiktatRule( /** * Class StringTemplate show that long line should be split in string template + * * @property delimiterIndex * @property isOneLineString */ @@ -643,6 +645,7 @@ class LineLength(configRules: List) : DiktatRule( /** * Class LongBinaryExpression show that long line should be split between other parts long binary expression, * after one of operation reference + * * @property maximumLineLength is number of maximum line length * @property leftOffset is offset before start [node] * @property binList is list of Binary Expression which are children of [node] @@ -798,6 +801,7 @@ class LineLength(configRules: List) : DiktatRule( /** * Class ValueArgumentList show that line should be split in ValueArgumentList: + * * @property maximumLineLength - max line length * @property positionByOffset */ diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter6/ExtensionFunctionsSameNameRule.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter6/ExtensionFunctionsSameNameRule.kt index d4a61a6da5..e4332f14f4 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter6/ExtensionFunctionsSameNameRule.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/rules/chapter6/ExtensionFunctionsSameNameRule.kt @@ -124,6 +124,7 @@ class ExtensionFunctionsSameNameRule(configRules: List) : DiktatRul /** * Class that represents a function's signature + * * @property name function name * @property parameters function parameters as strings * @property returnType return type of a function if it is explicitly set @@ -138,6 +139,7 @@ class ExtensionFunctionsSameNameRule(configRules: List) : DiktatRul /** * Class that represents an extension function + * * @property className name of receiver class * @property signature a [FunctionSignature] of a function * @property node a [ASTNode] that represents this function diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/AstConstants.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/AstConstants.kt index 58b07c189c..ec046049e1 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/AstConstants.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/AstConstants.kt @@ -66,6 +66,7 @@ internal val ignoreImports = setOf("invoke", "get", "set", "getValue", "setValue /** * Enum that represents some standard platforms that can appear in kotlin code + * * @property packages beginnings of fully qualified names of packages belonging to a particular platform */ enum class StandardPlatforms(val packages: List) { diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/KdocUtils.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/KdocUtils.kt index 6bfa3aeea4..9c6f474d5b 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/KdocUtils.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/KdocUtils.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.CompositeElement 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.kdoc.lexer.KDocTokens +import org.jetbrains.kotlin.kdoc.lexer.KDocTokens.KDOC import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag @@ -77,3 +78,36 @@ inline fun ASTNode.insertTagBefore( kdocSection.addChild(newTag, beforeTagLineStart) consumer(newTag) } + +/** + * This method replaces wrong tag in class KDoc leaving [parameterName] unchanged. + * + * @param parameterName name of class parameter + * @param isParamTagNeeded true, in case we need to change the tag to `@param` + */ +fun ASTNode.replaceWrongTagInClassKdoc( + parameterName: String, + isParamTagNeeded: Boolean +) { + val paramOrPropertySwitchText = if (isParamTagNeeded) "@property" to "@param" else "@param" to "@property" + val wrongTagText = "* ${paramOrPropertySwitchText.first} $parameterName" + val replaceText = "* ${paramOrPropertySwitchText.second} $parameterName" + + this.replaceFirstTextInKdoc(wrongTagText, replaceText) +} + +/** + * This method replaces the first occurrence of text in class KDoc. + * + * @param wrongText text to replace + * @param correctText replacement text + */ +@Suppress("UnsafeCallOnNullableType") +fun ASTNode.replaceFirstTextInKdoc( + wrongText: String, + correctText: String +) { + val allKdocText = this.text + val newKdocText = allKdocText.replaceFirst(wrongText, correctText) + this.treeParent.replaceChild(this, KotlinParser().createNode(newKdocText).findChildByType(KDOC)!!) +} diff --git a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/StringCaseUtils.kt b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/StringCaseUtils.kt index a6502527ff..1ee1680b8b 100644 --- a/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/StringCaseUtils.kt +++ b/diktat-rules/src/main/kotlin/com/saveourtool/diktat/ruleset/utils/StringCaseUtils.kt @@ -11,6 +11,7 @@ private val log = KotlinLogging.logger {} /** * Available cases to name enum members + * * @property str */ enum class Style(val str: String) { diff --git a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter2/KdocCommentsFixTest.kt b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter2/KdocCommentsFixTest.kt index 3922c3440b..d846e91a9d 100644 --- a/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter2/KdocCommentsFixTest.kt +++ b/diktat-rules/src/test/kotlin/com/saveourtool/diktat/ruleset/chapter2/KdocCommentsFixTest.kt @@ -24,7 +24,7 @@ class KdocCommentsFixTest : FixTestBase("test/paragraph2/kdoc/", ::KdocComments) @Test @Tags(Tag(WarningNames.KDOC_NO_CONSTRUCTOR_PROPERTY), Tag(WarningNames.KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT)) fun `check fix with class kdoc`() { - fixAndCompare("ConstructorCommentExpected.kt", "ConstructorCommentTest.kt") + fixAndCompare("ConstructorCommentKDocExpected.kt", "ConstructorCommentKDocTest.kt") } @Test @@ -33,6 +33,12 @@ class KdocCommentsFixTest : FixTestBase("test/paragraph2/kdoc/", ::KdocComments) fixAndCompare("ConstructorCommentPropertiesExpected.kt", "ConstructorCommentPropertiesTest.kt") } + @Test + @Tags(Tag(WarningNames.KDOC_NO_CONSTRUCTOR_PROPERTY), Tag(WarningNames.KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT)) + fun `check fix with class kdoc and properties`() { + fixAndCompare("ConstructorCommentAllExpected.kt", "ConstructorCommentAllTest.kt") + } + @Test @Tags(Tag(WarningNames.KDOC_NO_CONSTRUCTOR_PROPERTY), Tag(WarningNames.KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT)) fun `should preserve newlines when moving comments from value parameters`() { diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/BasicTagsEmptyLineBeforeExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/BasicTagsEmptyLineBeforeExpected.kt index 55f56378ea..749e319f0b 100644 --- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/BasicTagsEmptyLineBeforeExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/BasicTagsEmptyLineBeforeExpected.kt @@ -15,14 +15,55 @@ class Example { /** * Description - * @see test2 + * Description 2 * * @param a integer parameter */ fun test3(a: Int) = Unit /** + * Description + * @see test2 + * * @param a integer parameter */ fun test4(a: Int) = Unit + + /** + * @param a integer parameter + */ + fun test5(a: Int) = Unit } + +/** + * @property a integer parameter + */ +class Example2(val a: Int) {} + +/** + * Description + * + * @property a integer parameter + */ +class Example3(val a: Int) {} + +/** + * Description + * Description 2 + * + * @property a integer parameter + */ +class Example4(val a: Int) {} + +/** + * Description + * @see test2 + * + * @property a integer parameter + */ +class Example5(val a: Int) {} + +/** + * @property a integer parameter + */ +class Example6(val a: Int) {} diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/BasicTagsEmptyLineBeforeTest.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/BasicTagsEmptyLineBeforeTest.kt index c1c9dae12d..496876c44f 100644 --- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/BasicTagsEmptyLineBeforeTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/BasicTagsEmptyLineBeforeTest.kt @@ -14,14 +14,52 @@ class Example { /** * Description - * @see test2 + * Description 2 * @param a integer parameter */ fun test3(a: Int) = Unit /** - * + * Description + * @see test2 * @param a integer parameter */ fun test4(a: Int) = Unit + + /** + * + * @param a integer parameter + */ + fun test5(a: Int) = Unit } + +/** + * @property a integer parameter + */ +class Example2(val a: Int) {} + +/** + * Description + * @property a integer parameter + */ +class Example3(val a: Int) {} + +/** + * Description + * Description 2 + * @property a integer parameter + */ +class Example4(val a: Int) {} + +/** + * Description + * @see test2 + * @property a integer parameter + */ +class Example5(val a: Int) {} + +/** + * + * @property a integer parameter + */ +class Example6(val a: Int) {} diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentAllExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentAllExpected.kt new file mode 100644 index 0000000000..0b2cbc7e40 --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentAllExpected.kt @@ -0,0 +1,314 @@ +package test.paragraph2.kdoc + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + * single-line comment + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + * block + * comment + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + * kdoc property + * comment + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + * single-line comment + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + * block + * comment + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + * kdoc property + * comment + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + * single-line comment + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + * block + * comment + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + * kdoc property + * comment + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @property openName open property info + * single-line comment + * @property openLastName open last property + * info + * block + * comment + * @property openAddr property + * info + * @param K + * @property openBirthDate kdoc property + * comment + */ +open class B constructor( + open val openName: String, + open val openLastName: String, + open val openBirthDate: String, + /** + * @property openAddr property + * comment + */ + open val openAddr: String +) {} + +/** + * kdoc + * class + * comment + * @param P generic type + * @param K generic type + * @property internalName internal + * property info + * single-line comment + * @property openName override + * property info + * single-line comment + * @param privateLastName private + * property info + * block + * comment + * @property openAddr override + * property info + * @param G + * @param privateName single-line comment + * @property protectedName single-line comment + * @property name single-line comment + * @param paramName single-line comment + * @property protectedLastName block + * comment + * @property internalLastName block + * comment + * @property openLastName block + * comment + * @property lastName block + * comment + * @param paramLastName block + * comment + * @param privateBirthDate kdoc property + * comment + * @property protectedBirthDate kdoc property + * comment + * @property internalBirthDate kdoc property + * comment + * @property openBirthDate kdoc property + * comment + * @property birthDate kdoc property + * comment + * @param paramBirthDate kdoc property + * comment + */ +class A constructor( + private val privateName: String, + protected val protectedName: String, + internal val internalName: String, + override val openName: String, + val name: String, + paramName: String, + private val privateLastName: String, + protected val protectedLastName: String, + internal val internalLastName: String, + override val openLastName: String, + val lastName: String, + paramLastName: String, + private val privateBirthDate: String, + protected val protectedBirthDate: String, + internal val internalBirthDate: String, + override val openBirthDate: String, + val birthDate: String, + paramBirthDate: String, + /** + * @property privateAddr property + * comment + */ + private val privateAddr: String, + /** + * @property protectedAddr property + * comment + */ + protected val protectedAddr: String, + /** + * @property internalAddr property + * comment + */ + internal val internalAddr: String, + /** + * @property openAddr property + * comment + */ + override val openAddr: String, + /** + * @property addr property + * comment + */ + val addr: String, + /** + * @property paramAddr property + * comment + */ + paramAddr: String, +) : B(), C

, D {} + +/** + * kdoc + * class + * comment + * @property keyAs + * @property as + */ +actual annotation class JsonSerialize( + actual val `as`: KClass<*>, + actual val keyAs: KClass<*>, +) diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentAllTest.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentAllTest.kt new file mode 100644 index 0000000000..895fb7d3bf --- /dev/null +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentAllTest.kt @@ -0,0 +1,351 @@ +package test.paragraph2.kdoc + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + // single-line comment + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + /* + * block + * comment + */ + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + /** + * kdoc property + * comment + */ + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + // single-line comment + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /* + * block + * comment + */ + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * kdoc property + * comment + */ + val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @param name property info + */ +class A constructor( + // single-line comment + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /* + * block + * comment + */ + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * kdoc property + * comment + */ + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @property name property info + */ +class A constructor( + /** + * @property name property + * comment + */ + private val name: String +) {} + +/** + * kdoc + * class + * comment + * @param openName open property info + * @param openLastName open last property + * info + * @property openAddr property + * info + */ +open class B constructor( + // single-line comment + open val openName: String, + /* + * block + * comment + */ + open val openLastName: String, + /** + * kdoc property + * comment + */ + open val openBirthDate: String, + /** + * @property openAddr property + * comment + */ + open val openAddr: String +) {} + +/** + * kdoc + * class + * comment + * @property P generic type + * @param K generic type + * @property internalName internal + * property info + * @param openName override + * property info + * @property privateLastName private + * property info + * @property openAddr override + * property info + */ +class A constructor( + // single-line comment + private val privateName: String, + // single-line comment + protected val protectedName: String, + // single-line comment + internal val internalName: String, + // single-line comment + override val openName: String, + // single-line comment + val name: String, + // single-line comment + paramName: String, + /* + * block + * comment + */ + private val privateLastName: String, + /* + * block + * comment + */ + protected val protectedLastName: String, + /* + * block + * comment + */ + internal val internalLastName: String, + /* + * block + * comment + */ + override val openLastName: String, + /* + * block + * comment + */ + val lastName: String, + /* + * block + * comment + */ + paramLastName: String, + /** + * kdoc property + * comment + */ + private val privateBirthDate: String, + /** + * kdoc property + * comment + */ + protected val protectedBirthDate: String, + /** + * kdoc property + * comment + */ + internal val internalBirthDate: String, + /** + * kdoc property + * comment + */ + override val openBirthDate: String, + /** + * kdoc property + * comment + */ + val birthDate: String, + /** + * kdoc property + * comment + */ + paramBirthDate: String, + /** + * @property privateAddr property + * comment + */ + private val privateAddr: String, + /** + * @property protectedAddr property + * comment + */ + protected val protectedAddr: String, + /** + * @property internalAddr property + * comment + */ + internal val internalAddr: String, + /** + * @property openAddr property + * comment + */ + override val openAddr: String, + /** + * @property addr property + * comment + */ + val addr: String, + /** + * @property paramAddr property + * comment + */ + paramAddr: String, +) : B(), C

, D {} + +/** + * kdoc + * class + * comment + * @property keyAs + */ +actual annotation class JsonSerialize( + actual val `as`: KClass<*>, + actual val keyAs: KClass<*>, +) diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentKDocExpected.kt similarity index 82% rename from diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentExpected.kt rename to diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentKDocExpected.kt index bb17335968..c1b729a02c 100644 --- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentKDocExpected.kt @@ -4,7 +4,6 @@ package test.paragraph2.kdoc * kdoc * class * comment - * * @param name */ class A constructor( @@ -15,7 +14,6 @@ class A constructor( * kdoc * class * comment - * * @param name single-line comment */ class A constructor( @@ -26,9 +24,7 @@ class A constructor( * kdoc * class * comment - * - * @param name - * block + * @param name block * comment */ class A constructor( @@ -39,9 +35,7 @@ class A constructor( * kdoc * class * comment - * - * @param name - * kdoc property + * @param name kdoc property * comment */ class A constructor( @@ -65,7 +59,6 @@ class A constructor( * kdoc * class * comment - * * @property name */ class A constructor( @@ -76,7 +69,6 @@ class A constructor( * kdoc * class * comment - * * @property name single-line comment */ class A constructor( @@ -87,9 +79,7 @@ class A constructor( * kdoc * class * comment - * - * @property name - * block + * @property name block * comment */ class A constructor( @@ -100,9 +90,7 @@ class A constructor( * kdoc * class * comment - * - * @property name - * kdoc property + * @property name kdoc property * comment */ class A constructor( @@ -126,7 +114,6 @@ class A constructor( * kdoc * class * comment - * * @param name */ class A constructor( @@ -137,7 +124,6 @@ class A constructor( * kdoc * class * comment - * * @param name single-line comment */ class A constructor( @@ -148,9 +134,7 @@ class A constructor( * kdoc * class * comment - * - * @param name - * block + * @param name block * comment */ class A constructor( @@ -161,9 +145,7 @@ class A constructor( * kdoc * class * comment - * - * @param name - * kdoc property + * @param name kdoc property * comment */ class A constructor( @@ -187,14 +169,11 @@ class A constructor( * kdoc * class * comment - * * @param K * @property openName single-line comment - * @property openLastName - * block + * @property openLastName block * comment - * @property openBirthDate - * kdoc property + * @property openBirthDate kdoc property * comment */ open class B constructor( @@ -212,7 +191,6 @@ open class B constructor( * kdoc * class * comment - * * @param K * @param P * @param G @@ -222,44 +200,32 @@ open class B constructor( * @property openName single-line comment * @property name single-line comment * @param paramName single-line comment - * @param privateLastName - * block + * @param privateLastName block * comment - * @property protectedLastName - * block + * @property protectedLastName block * comment - * @property internalLastName - * block + * @property internalLastName block * comment - * @property openLastName - * block + * @property openLastName block * comment - * @property lastName - * block + * @property lastName block * comment - * @param paramLastName - * block + * @param paramLastName block * comment - * @param privateBirthDate - * kdoc property + * @param privateBirthDate kdoc property * comment - * @property protectedBirthDate - * kdoc property + * @property protectedBirthDate kdoc property * comment - * @property internalBirthDate - * kdoc property + * @property internalBirthDate kdoc property * comment - * @property openBirthDate - * kdoc property + * @property openBirthDate kdoc property * comment - * @property birthDate - * kdoc property + * @property birthDate kdoc property * comment - * @param paramBirthDate - * kdoc property + * @param paramBirthDate kdoc property * comment */ -class A constructor( +class A constructor( private val privateName: String, protected val protectedName: String, internal val internalName: String, @@ -314,7 +280,6 @@ class A constructor( * kdoc * class * comment - * * @property as * @property keyAs */ diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentTest.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentKDocTest.kt similarity index 92% rename from diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentTest.kt rename to diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentKDocTest.kt index 86c27b144b..5ee9f10e8a 100644 --- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentKDocTest.kt @@ -15,7 +15,7 @@ class A constructor( * comment */ class A constructor( - //single-line comment + // single-line comment name: String ) {} @@ -73,7 +73,7 @@ class A constructor( * comment */ class A constructor( - //single-line comment + // single-line comment val name: String ) {} @@ -131,7 +131,7 @@ class A constructor( * comment */ class A constructor( - //single-line comment + // single-line comment private val name: String ) {} @@ -180,7 +180,7 @@ class A constructor( * comment */ open class B constructor( - //single-line comment + // single-line comment open val openName: String, /* * block @@ -204,18 +204,18 @@ open class B constructor( * class * comment */ -class A constructor( - //single-line comment +class A constructor( + // single-line comment private val privateName: String, - //single-line comment + // single-line comment protected val protectedName: String, - //single-line comment + // single-line comment internal val internalName: String, - //single-line comment + // single-line comment override val openName: String, - //single-line comment + // single-line comment val name: String, - //single-line comment + // single-line comment paramName: String, /* * block diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentNoKDocExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentNoKDocExpected.kt index fb4c2740a2..ae3616e629 100644 --- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentNoKDocExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentNoKDocExpected.kt @@ -15,8 +15,7 @@ class A constructor( ) {} /** - * @param name - * block + * @param name block * comment */ class A constructor( @@ -24,8 +23,7 @@ class A constructor( ) {} /** - * @param name - * kdoc property + * @param name kdoc property * comment */ class A constructor( @@ -55,8 +53,7 @@ class A constructor( ) {} /** - * @property name - * block + * @property name block * comment */ class A constructor( @@ -64,8 +61,7 @@ class A constructor( ) {} /** - * @property name - * kdoc property + * @property name kdoc property * comment */ class A constructor( @@ -95,8 +91,7 @@ class A constructor( ) {} /** - * @param name - * block + * @param name block * comment */ class A constructor( @@ -104,8 +99,7 @@ class A constructor( ) {} /** - * @param name - * kdoc property + * @param name kdoc property * comment */ class A constructor( @@ -123,11 +117,9 @@ class A constructor( /** * @param K * @property openName single-line comment - * @property openLastName - * block + * @property openLastName block * comment - * @property openBirthDate - * kdoc property + * @property openBirthDate kdoc property * comment */ open class B constructor( @@ -151,44 +143,32 @@ open class B constructor( * @property openName single-line comment * @property name single-line comment * @param paramName single-line comment - * @param privateLastName - * block + * @param privateLastName block * comment - * @property protectedLastName - * block + * @property protectedLastName block * comment - * @property internalLastName - * block + * @property internalLastName block * comment - * @property openLastName - * block + * @property openLastName block * comment - * @property lastName - * block + * @property lastName block * comment - * @param paramLastName - * block + * @param paramLastName block * comment - * @param privateBirthDate - * kdoc property + * @param privateBirthDate kdoc property * comment - * @property protectedBirthDate - * kdoc property + * @property protectedBirthDate kdoc property * comment - * @property internalBirthDate - * kdoc property + * @property internalBirthDate kdoc property * comment - * @property openBirthDate - * kdoc property + * @property openBirthDate kdoc property * comment - * @property birthDate - * kdoc property + * @property birthDate kdoc property * comment - * @param paramBirthDate - * kdoc property + * @param paramBirthDate kdoc property * comment */ -class A constructor( +class A constructor( private val privateName: String, protected val protectedName: String, internal val internalName: String, diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentNoKDocTest.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentNoKDocTest.kt index 7b262ef5ad..2261e20bbd 100644 --- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentNoKDocTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentNoKDocTest.kt @@ -5,7 +5,7 @@ class A constructor( ) {} class A constructor( - //single-line comment + // single-line comment name: String ) {} @@ -38,7 +38,7 @@ class A constructor( ) {} class A constructor( - //single-line comment + // single-line comment val name: String ) {} @@ -71,7 +71,7 @@ class A constructor( ) {} class A constructor( - //single-line comment + // single-line comment private val name: String ) {} @@ -100,7 +100,7 @@ class A constructor( ) {} open class B constructor( - //single-line comment + // single-line comment open val openName: String, /* * block @@ -119,18 +119,18 @@ open class B constructor( open val openAddr: String ) {} -class A constructor( - //single-line comment +class A constructor( + // single-line comment private val privateName: String, - //single-line comment + // single-line comment protected val protectedName: String, - //single-line comment + // single-line comment internal val internalName: String, - //single-line comment + // single-line comment override val openName: String, - //single-line comment + // single-line comment val name: String, - //single-line comment + // single-line comment paramName: String, /* * block diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentPropertiesExpected.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentPropertiesExpected.kt index 01b5afa976..941167a7c1 100644 --- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentPropertiesExpected.kt +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentPropertiesExpected.kt @@ -135,17 +135,14 @@ class A constructor( /** * @property openName open property info * single-line comment - * @property openLastName - * open last property + * @property openLastName open last property * info * block * comment - * @property openAddr - * property + * @property openAddr property * info * @param K - * @property openBirthDate - * kdoc property + * @property openBirthDate kdoc property * comment */ open class B constructor( @@ -179,41 +176,30 @@ open class B constructor( * @property protectedName single-line comment * @property name single-line comment * @param paramName single-line comment - * @property protectedLastName - * block + * @property protectedLastName block * comment - * @property internalLastName - * block + * @property internalLastName block * comment - * @property openLastName - * block + * @property openLastName block * comment - * @property lastName - * block + * @property lastName block * comment - * @param paramLastName - * block + * @param paramLastName block * comment - * @param privateBirthDate - * kdoc property + * @param privateBirthDate kdoc property * comment - * @property protectedBirthDate - * kdoc property + * @property protectedBirthDate kdoc property * comment - * @property internalBirthDate - * kdoc property + * @property internalBirthDate kdoc property * comment - * @property openBirthDate - * kdoc property + * @property openBirthDate kdoc property * comment - * @property birthDate - * kdoc property + * @property birthDate kdoc property * comment - * @param paramBirthDate - * kdoc property + * @param paramBirthDate kdoc property * comment */ -class A constructor( +class A constructor( private val privateName: String, protected val protectedName: String, internal val internalName: String, diff --git a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentPropertiesTest.kt b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentPropertiesTest.kt index 62563bc7ae..627ad27f57 100644 --- a/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentPropertiesTest.kt +++ b/diktat-rules/src/test/resources/test/paragraph2/kdoc/ConstructorCommentPropertiesTest.kt @@ -11,7 +11,7 @@ class A constructor( * @param name property info */ class A constructor( - //single-line comment + // single-line comment name: String ) {} @@ -49,17 +49,17 @@ class A constructor( ) {} /** - * @property name property info + * @param name property info */ class A constructor( val name: String ) {} /** - * @property name property info + * @param name property info */ class A constructor( - //single-line comment + // single-line comment val name: String ) {} @@ -97,17 +97,17 @@ class A constructor( ) {} /** - * @property name property info + * @param name property info */ class A constructor( private val name: String ) {} /** - * @property name property info + * @param name property info */ class A constructor( - //single-line comment + // single-line comment private val name: String ) {} @@ -145,16 +145,14 @@ class A constructor( ) {} /** - * @property openName open property info - * @param openLastName - * open last property + * @param openName open property info + * @param openLastName open last property * info - * @property openAddr - * property + * @property openAddr property * info */ open class B constructor( - //single-line comment + // single-line comment open val openName: String, /* * block @@ -185,18 +183,18 @@ open class B constructor( * @property openAddr override * property info */ -class A constructor( - //single-line comment +class A constructor( + // single-line comment private val privateName: String, - //single-line comment + // single-line comment protected val protectedName: String, - //single-line comment + // single-line comment internal val internalName: String, - //single-line comment + // single-line comment override val openName: String, - //single-line comment + // single-line comment val name: String, - //single-line comment + // single-line comment paramName: String, /* * block diff --git a/info/guide/guide-chapter-2.md b/info/guide/guide-chapter-2.md index 024af112cd..2ff7ee2c93 100644 --- a/info/guide/guide-chapter-2.md +++ b/info/guide/guide-chapter-2.md @@ -6,7 +6,7 @@ Comments should be accurately and clearly expressed, without repeating the name Comments are not a solution to the wrong code. Instead, you should fix the code as soon as you notice an issue or plan to fix it (by entering a TODO comment, including a Jira number). Comments should accurately reflect the code's design ideas and logic and further describe its business logic. As a result, other programmers will be able to save time when trying to understand the code. -Imagine that you are writing the comments to help yourself to understand the original ideas behind the code in the future. +Imagine that you are writing the comments to help yourself to understand the original ideas behind the code in the future. ### 2.1 General form of Kdoc @@ -56,6 +56,7 @@ class Example( ```kotlin /** * Class description + * / * Add a blank line between the comment text and each KDoc tag underneath * / * @property foo property description * @property bar another property description */ @@ -68,7 +69,7 @@ class Example( **Exceptions:** * For setters/getters of properties, obvious comments (like `this getter returns field`) are optional. Note that Kotlin generates simple `get/set` methods under the hood. - + * It is optional to add comments for simple one-line methods, such as shown in the example below: ```kotlin val isEmpty: Boolean @@ -82,7 +83,7 @@ fun isEmptyList(list: List) = list.size == 0 ``` **Note:** You can skip KDocs for a method's override if it is almost the same as the superclass method. -- +- - Don't use Kdoc comments inside code blocks as block comments **Incorrect Example:** @@ -118,18 +119,18 @@ When the method has such details as arguments, return value, or can throw except **Valid examples:** ```kotlin -/** +/** * This is the short overview comment for the example interface. * / * Add a blank line between the comment text and each KDoc tag underneath * / * @since 1.6 */ protected abstract class Sample { /** - * This is a long comment with whitespace that should be split in + * This is a long comment with whitespace that should be split in * comments on multiple lines if the line comment formatting is enabled. * / * Add a blank line between the comment text and each KDoc tag underneath * / * @param fox A quick brown fox jumps over the lazy dog - * @return battle between fox and dog + * @return battle between fox and dog */ protected abstract fun foo(Fox fox) /** @@ -139,10 +140,10 @@ When the method has such details as arguments, return value, or can throw except * @throws ProblemException if lazy dog wins */ protected fun bar() throws ProblemException { - // Some comments / * No need to add a blank line here * / + // Some comments / * No need to add a blank line here * / var aVar = ... - // Some comments / * Add a blank line before the comment * / + // Some comments / * Add a blank line before the comment * / fun doSome() } } @@ -168,15 +169,15 @@ Important notes: - KDoc does not support the `@deprecated` tag. Instead, use the `@Deprecated` annotation. - The `@since` tag should be used for versions only. Do not use dates in `@since` tag, it's confusing and less accurate. -If a tag block cannot be described in one line, indent the content of the new line by *four spaces* from the `@` position to achieve alignment (`@` counts as one + three spaces). - +If a tag block cannot be described in one line, indent the content of the new line by *two spaces* from the `@` position to achieve alignment (`@` counts as one + one space). + **Exception:** - + When the descriptive text in a tag block is too long to wrap, you can indent the alignment with the descriptive text in the last line. The descriptive text of multiple tags does not need to be aligned. See [3.8 Horizontal space](#c3.8). In Kotlin, compared to Java, you can put several classes inside one file, so each class should have a Kdoc formatted comment (as stated in rule 2.1). -This comment should contain @since tag. The right style is to write the application version when its functionality is released. It should be entered after the `@since` tag. +This comment should contain `@since` tag. The right style is to write the application version when its functionality is released. It should be entered after the `@since` tag. **Examples:** @@ -237,7 +238,7 @@ class Foo - The **copyright statement** can use your company's subsidiaries, as shown in the below examples: \ Chinese version: `版权所有 (c) 海思半导体 2012-2020` \ -English version: `Copyright (c) Hisilicon Technologies Co., Ltd. 2012-2020. All rights reserved.` +English version: `Copyright (c) Hisilicon Technologies Co., Ltd. 2012-2020. All rights reserved.` - The copyright information should not be written in KDoc style or use single-line comments. It must start from the beginning of the file. The following example is a copyright statement for Huawei, without other functional comments: @@ -278,9 +279,9 @@ It is a good practice to add a blank line between the body of the comment and Kd **Valid Examples:** ```kotlin -/** +/** * This is the short overview comment for the example interface. - * + * * @since 1.6 */ public interface Example { @@ -291,17 +292,17 @@ It is a good practice to add a blank line between the body of the comment and Kd val bField: String = ... /* Add a blank line above the comment */ /** - * This is a long comment with whitespace that should be split in + * This is a long comment with whitespace that should be split in * multiple line comments in case the line comment formatting is enabled. * /* blank line between description and Kdoc tag */ * @param fox A quick brown fox jumps over the lazy dog - * @return the rounds of battle of fox and dog + * @return the rounds of battle of fox and dog */ fun foo(Fox fox) /* Add a blank line above the comment */ /** * These possibilities include: Formatting of header comments - * + * * @return the rounds of battle of fox and dog * @throws ProblemException if lazy dog wins */ @@ -309,13 +310,13 @@ It is a good practice to add a blank line between the body of the comment and Kd // Some comments /* Since it is the first member definition in this range, there is no need to add a blank line here */ var aVar = ... - // Some comments /* Add a blank line above the comment */ + // Some comments /* Add a blank line above the comment */ fun doSome() } } ``` -- Leave one single space between the comment on the right side of the code and the code. +- Leave one single space between the comment on the right side of the code and the code. If you use conditional comments in the `if-else-if` scenario, put the comments inside the `else-if` branch or in the conditional block, but not before the `else-if`. This makes the code more understandable. When the if-block is used with curly braces, the comment should be placed on the next line after opening the curly braces. Compared to Java, the `if` statement in Kotlin statements returns a value. For this reason, a comment block can describe a whole `if-statement`. @@ -360,7 +361,7 @@ A code is not used to store history. Git, svn, or other VCS tools should be used Unused imports increase the coupling of the code and are not conducive to maintenance. The commented out code cannot be appropriately maintained. In an attempt to reuse the code, there is a high probability that you will introduce defects that are easily missed. The correct approach is to delete the unnecessary code directly and immediately when it is not used anymore. -If you need the code again, consider porting or rewriting it as changes could have occurred since you first commented on the code. +If you need the code again, consider porting or rewriting it as changes could have occurred since you first commented on the code. #### 2.4.3 Code delivered to the client should not contain TODO/FIXME comments