diff --git a/diktat-analysis.yml b/diktat-analysis.yml index d6f4e18929..c139b4c94b 100644 --- a/diktat-analysis.yml +++ b/diktat-analysis.yml @@ -115,6 +115,11 @@ # Checks that KDoc does not contain single line with words 'return', 'get' or 'set' - name: KDOC_TRIVIAL_KDOC_ON_FUNCTION enabled: true +# Checks that kdoc does not contain @author tag or date +- name: KDOC_CONTAINS_DATE_OR_AUTHOR + enabled: true + configuration: + versionRegex: \d+\.\d+\.\d+[-.\w\d]* # Checks that there is newline after header KDoc - name: HEADER_WRONG_FORMAT enabled: true @@ -130,9 +135,6 @@ # Checks that header kdoc is located before package directive - name: HEADER_NOT_BEFORE_PACKAGE enabled: true -# Checks that header kdoc does not contain @author tag ar date -- name: HEADER_CONTAINS_DATE_OR_AUTHOR - enabled: true # Checks that file does not contain lines > maxSize - name: FILE_IS_TOO_LONG enabled: true diff --git a/diktat-rules/src/main/kotlin/generated/WarningNames.kt b/diktat-rules/src/main/kotlin/generated/WarningNames.kt index 10842cf258..826f16ba87 100644 --- a/diktat-rules/src/main/kotlin/generated/WarningNames.kt +++ b/diktat-rules/src/main/kotlin/generated/WarningNames.kt @@ -86,14 +86,14 @@ public object WarningNames { public const val KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT: String = "KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT" + public const val KDOC_CONTAINS_DATE_OR_AUTHOR: String = "KDOC_CONTAINS_DATE_OR_AUTHOR" + public const val HEADER_WRONG_FORMAT: String = "HEADER_WRONG_FORMAT" public const val HEADER_MISSING_OR_WRONG_COPYRIGHT: String = "HEADER_MISSING_OR_WRONG_COPYRIGHT" public const val WRONG_COPYRIGHT_YEAR: String = "WRONG_COPYRIGHT_YEAR" - public const val HEADER_CONTAINS_DATE_OR_AUTHOR: String = "HEADER_CONTAINS_DATE_OR_AUTHOR" - public const val HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE: String = "HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE" diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt index 156d662dae..0d43046e44 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/constants/Warnings.kt @@ -67,10 +67,10 @@ enum class Warnings( KDOC_NO_CONSTRUCTOR_PROPERTY(true, "2.1.1", "all properties from the primary constructor should be documented in a @property tag in KDoc"), KDOC_EXTRA_PROPERTY(false, "2.1.1", "There is property in KDoc which is not present in the class"), KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT(true, "2.1.1", "replace comment before property with @property tag in class KDoc"), + KDOC_CONTAINS_DATE_OR_AUTHOR(false, "2.1.3", "KDoc should not contain creation date and author name"), HEADER_WRONG_FORMAT(true, "2.2.1", "file header comments should be properly formatted"), HEADER_MISSING_OR_WRONG_COPYRIGHT(true, "2.2.1", "file header comment must include copyright information inside a block comment"), WRONG_COPYRIGHT_YEAR(true, "2.2.1", "year defined in copyright and current year are different"), - HEADER_CONTAINS_DATE_OR_AUTHOR(false, "2.2.1", "file header comment should not contain creation date and author name"), HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE(false, "2.2.1", "files that contain multiple or no classes should contain description of what is inside of this file"), HEADER_NOT_BEFORE_PACKAGE(true, "2.2.1", "header KDoc should be placed before package and imports"), COMMENTED_OUT_CODE(false, "2.4.2", "you should not comment out code, use VCS to save it in history and delete this block"), diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt index cb37fd2937..a393f46bcc 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/IdentifierNaming.kt @@ -451,7 +451,7 @@ class IdentifierNaming(private val configRules: List) : Rule("ident // FixMe: this should be moved to properties val oneCharIdentifiers = setOf("i", "j", "k", "x", "y", "z") - val booleanMethodPrefixes = setOf("has", "is", "are", "have", "should") + val booleanMethodPrefixes = setOf("has", "is", "are", "have", "should", "can") val confusingIdentifierNames = setOf("O", "D", "I", "l", "Z", "S", "e", "B", "h", "n", "m", "rn") } } diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt index d31bc93ee3..ffe278ba89 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/comments/HeaderCommentRule.kt @@ -4,17 +4,18 @@ import org.cqfn.diktat.common.config.rules.RuleConfiguration import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.common.config.rules.getRuleConfig import org.cqfn.diktat.ruleset.constants.EmitType -import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_CONTAINS_DATE_OR_AUTHOR import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_NOT_BEFORE_PACKAGE import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_WRONG_FORMAT +import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR import org.cqfn.diktat.ruleset.constants.Warnings.WRONG_COPYRIGHT_YEAR import org.cqfn.diktat.ruleset.utils.copyrightWords import org.cqfn.diktat.ruleset.utils.findChildAfter import org.cqfn.diktat.ruleset.utils.findChildBefore import org.cqfn.diktat.ruleset.utils.getAllChildrenWithType import org.cqfn.diktat.ruleset.utils.getFirstChildWithType +import org.cqfn.diktat.ruleset.utils.kDocTags import org.cqfn.diktat.ruleset.utils.moveChildBefore import com.pinterest.ktlint.core.Rule @@ -29,8 +30,12 @@ import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafElement 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.parser.KDocKnownTag +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag import java.time.LocalDate +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoField /** * Visitor for header comment in .kt file: @@ -59,18 +64,7 @@ class HeaderCommentRule(private val configRules: List) : Rule("head } private fun checkHeaderKdoc(node: ASTNode) { - val headerKdoc = node.findChildBefore(PACKAGE_DIRECTIVE, KDOC) - headerKdoc?.let { - // fixme we should also check date of creation, but it can be in different formats - headerKdoc - .text - .split('\n') - .filter { it.contains("@author") } - .forEach { - HEADER_CONTAINS_DATE_OR_AUTHOR.warn(configRules, emitWarn, isFixMode, - it.trim(), headerKdoc.startOffset, headerKdoc) - } - + node.findChildBefore(PACKAGE_DIRECTIVE, KDOC)?.let { headerKdoc -> if (headerKdoc.treeNext != null && headerKdoc.treeNext.elementType == WHITE_SPACE && headerKdoc.treeNext.text.count { it == '\n' } != 2) { HEADER_WRONG_FORMAT.warnAndFix(configRules, emitWarn, isFixMode, diff --git a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt index 1696aa5e62..fefdfe34cc 100644 --- a/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt +++ b/diktat-rules/src/main/kotlin/org/cqfn/diktat/ruleset/rules/kdoc/KdocFormatting.kt @@ -1,7 +1,10 @@ package org.cqfn.diktat.ruleset.rules.kdoc +import org.cqfn.diktat.common.config.rules.RuleConfiguration import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.common.config.rules.getRuleConfig import org.cqfn.diktat.ruleset.constants.EmitType +import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_EMPTY_KDOC import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NEWLINES_BEFORE_BASIC_TAGS import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_DEPRECATED_TAG @@ -43,6 +46,9 @@ import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.psi.psiUtil.startOffset +import java.time.format.DateTimeFormatter +import java.time.temporal.ChronoField + /** * Formatting visitor for Kdoc: * 1) removing all blank lines between Kdoc and the code it's declaring @@ -50,12 +56,15 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset * 3) ensuring there is only one white space between tag and it's body * 4) ensuring tags @apiNote, @implSpec, @implNote have one empty line after their body * 5) ensuring tags @param, @return, @throws are arranged in this order + * 6) ensuring @author tag is not used + * 7) ensuring @since tag contains only versions and not dates */ @Suppress("ForbiddenComment") class KdocFormatting(private val configRules: List) : Rule("kdoc-formatting") { private val basicTagsList = listOf(KDocKnownTag.PARAM, KDocKnownTag.RETURN, KDocKnownTag.THROWS) private val specialTagNames = setOf("implSpec", "implNote", "apiNote") private var isFixMode: Boolean = false + private var versionRegex: Regex? = null private lateinit var emitWarn: EmitType /** @@ -69,6 +78,13 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo isFixMode = autoCorrect emitWarn = emit + versionRegex ?: run { + versionRegex = KdocFormatConfiguration( + configRules.getRuleConfig(KDOC_CONTAINS_DATE_OR_AUTHOR)?.configuration ?: emptyMap() + ) + .versionRegex + } + if (node.elementType == KDOC && isKdocNotEmpty(node)) { checkNoDeprecatedTag(node) checkEmptyTags(node.kDocTags()) @@ -77,6 +93,7 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo node.kDocBasicTags()?.let { checkEmptyLinesBetweenBasicTags(it) } checkBasicTagsOrder(node) checkNewLineAfterSpecialTags(node) + checkAuthorAndDate(node) } } @@ -307,6 +324,17 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo } } + private fun checkAuthorAndDate(node: ASTNode) { + node.kDocTags() + ?.filter { + it.knownTag == KDocKnownTag.AUTHOR || + it.knownTag == KDocKnownTag.SINCE && it.hasInvalidVersion() + } + ?.forEach { + KDOC_CONTAINS_DATE_OR_AUTHOR.warn(configRules, emitWarn, isFixMode, it.text.trim(), it.startOffset, it.node) + } + } + // fixme this method can be improved and extracted to utils private fun ASTNode.hasEmptyLineAfter(): Boolean { require(this.elementType == KDOC_TAG) { "This check is only for KDOC_TAG" } @@ -321,4 +349,43 @@ class KdocFormatting(private val configRules: List) : Rule("kdoc-fo private fun ASTNode.applyToPrevSibling(elementType: IElementType, consumer: ASTNode.() -> Unit) { prevSibling { it.elementType == elementType }?.apply(consumer) } + + /** + * Checks whether this tag's content represents an invalid version + */ + private fun KDocTag.hasInvalidVersion(): Boolean { + val content = getContent().trim() + if (' ' in content || '/' in content) { + // Filter based on symbols that are not allowed in versions. Assuming that people put either version or date in `@since` tag. + return true + } + return versionRegex?.matches(content)?.not() + ?: dateFormats.mapNotNull { + // try to parse using some standard date patterns + runCatching { + it.parse(content).get(ChronoField.YEAR) + } + .getOrNull() + } + .isNotEmpty() + } + + /** + * A [RuleConfiguration] for KDoc formatting + */ + class KdocFormatConfiguration(config: Map) : RuleConfiguration(config) { + /** + * Regular expression, if present, against which a version should be matched in `@since` tag. + */ + val versionRegex: Regex? by lazy { + config["versionRegex"]?.let { Regex(it) } + } + } + + companion object { + val dateFormats: List = listOf("yyyy-dd-mm", "yy-dd-mm", "yyyy-mm-dd", "yy-mm-dd", "yyyy.mm.dd", "yyyy.dd.mm") + .map { + DateTimeFormatter.ofPattern(it) + } + } } diff --git a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml index 2681f665db..dc4b98a7df 100644 --- a/diktat-rules/src/main/resources/diktat-analysis-huawei.yml +++ b/diktat-rules/src/main/resources/diktat-analysis-huawei.yml @@ -112,6 +112,11 @@ # Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after - name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS enabled: true +# Checks that kdoc does not contain @author tag or date +- name: KDOC_CONTAINS_DATE_OR_AUTHOR + enabled: true + configuration: + versionRegex: \d+\.\d+\.\d+[-.\w\d]* # Checks that KDoc does not contain single line with words 'return', 'get' or 'set' - name: KDOC_TRIVIAL_KDOC_ON_FUNCTION enabled: true @@ -130,9 +135,6 @@ # Checks that header kdoc is located before package directive - name: HEADER_NOT_BEFORE_PACKAGE enabled: true -# Checks that header kdoc does not contain @author tag ar date -- name: HEADER_CONTAINS_DATE_OR_AUTHOR - enabled: true # Checks that file does not contain lines > maxSize - name: FILE_IS_TOO_LONG enabled: true diff --git a/diktat-rules/src/main/resources/diktat-analysis.yml b/diktat-rules/src/main/resources/diktat-analysis.yml index cf353c0ce0..0c9e986f18 100644 --- a/diktat-rules/src/main/resources/diktat-analysis.yml +++ b/diktat-rules/src/main/resources/diktat-analysis.yml @@ -111,6 +111,11 @@ # Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after - name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS enabled: true +# Checks that kdoc does not contain @author tag or date +- name: KDOC_CONTAINS_DATE_OR_AUTHOR + enabled: true + configuration: + versionRegex: \d+\.\d+\.\d+[-.\w\d]* # Checks that KDoc does not contain single line with words 'return', 'get' or 'set' - name: KDOC_TRIVIAL_KDOC_ON_FUNCTION enabled: true @@ -129,9 +134,6 @@ # Checks that header kdoc is located before package directive - name: HEADER_NOT_BEFORE_PACKAGE enabled: true -# Checks that header kdoc does not contain @author tag ar date -- name: HEADER_CONTAINS_DATE_OR_AUTHOR - enabled: true # Checks that file does not contain lines > maxSize - name: FILE_IS_TOO_LONG enabled: true diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt index a427a349f8..52493bbdfd 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/HeaderCommentRuleTest.kt @@ -2,11 +2,11 @@ package org.cqfn.diktat.ruleset.chapter2 import org.cqfn.diktat.common.config.rules.RulesConfig import org.cqfn.diktat.ruleset.constants.Warnings -import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_CONTAINS_DATE_OR_AUTHOR import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_MISSING_OR_WRONG_COPYRIGHT import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_NOT_BEFORE_PACKAGE import org.cqfn.diktat.ruleset.constants.Warnings.HEADER_WRONG_FORMAT +import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR import org.cqfn.diktat.ruleset.rules.DIKTAT_RULE_SET_ID import org.cqfn.diktat.ruleset.rules.comments.HeaderCommentRule import org.cqfn.diktat.util.LintTestBase @@ -256,26 +256,6 @@ class HeaderCommentRuleTest : LintTestBase(::HeaderCommentRule) { ) } - @Test - @Tag(WarningNames.HEADER_CONTAINS_DATE_OR_AUTHOR) - fun `@author tag is not allowed in header comment`() { - lintMethod( - """ - |$copyrightBlock - |/** - | * Description of this file - | * @author anonymous - | */ - | - |package org.cqfn.diktat.example - | - |class Example { } - """.trimMargin(), - LintError(4, 1, ruleId, "${HEADER_CONTAINS_DATE_OR_AUTHOR.warnText()} * @author anonymous"), - rulesConfigList = rulesConfigList - ) - } - @Test @Tag(WarningNames.HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE) fun `file with zero classes should have header KDoc`() { diff --git a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/KdocFormattingTest.kt b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/KdocFormattingTest.kt index 732797b566..02f2f092cb 100644 --- a/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/KdocFormattingTest.kt +++ b/diktat-rules/src/test/kotlin/org/cqfn/diktat/ruleset/chapter2/KdocFormattingTest.kt @@ -1,5 +1,7 @@ package org.cqfn.diktat.ruleset.chapter2 +import org.cqfn.diktat.common.config.rules.RulesConfig +import org.cqfn.diktat.ruleset.constants.Warnings import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_EMPTY_KDOC import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NEWLINES_BEFORE_BASIC_TAGS import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_DEPRECATED_TAG @@ -321,4 +323,99 @@ class KdocFormattingTest : LintTestBase(::KdocFormatting) { LintError(2, 16, ruleId, "${KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS.warnText()} @implSpec, @apiNote, @implNote", true)) } + + @Test + @Tag(WarningNames.KDOC_CONTAINS_DATE_OR_AUTHOR) + fun `@author tag is not allowed in header comment`() { + lintMethod( + """ + |/** + | * Description of this file + | * @author anonymous + | */ + | + |package org.cqfn.diktat.example + | + |/** + | * Description of this class + | * @author anonymous + | */ + |class Example { } + """.trimMargin(), + LintError(3, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @author anonymous"), + LintError(10, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @author anonymous"), + ) + } + + @Test + @Tag(WarningNames.KDOC_CONTAINS_DATE_OR_AUTHOR) + fun `@since tag should only contain versions`() { + lintMethod( + """ + |/** + | * Description of this file + | * @since 2019-10-11 + | * @since 19-10-11 + | * @since 2019.10.11 + | * @since 2019/10/11 + | * @since 11 Oct 2019 + | * @since 1.2.3 + | * @since 1.2.3-1 + | * @since 1.2.3-SNAPSHOT + | * @since 1.2.3-rc-1 + | * @since 1.2.3.RELEASE + | */ + | + |package org.cqfn.diktat.example + | + |/** + | * Description of this file + | * @since 2019-10-11 + | * @since 1.2.3 + | */ + |class Example { } + """.trimMargin(), + LintError(3, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019-10-11"), + LintError(4, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 19-10-11"), + LintError(5, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019.10.11"), + LintError(6, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019/10/11"), + LintError(7, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 11 Oct 2019"), + LintError(19, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019-10-11"), + rulesConfigList = emptyList() + ) + } + + @Test + @Tag(WarningNames.KDOC_CONTAINS_DATE_OR_AUTHOR) + fun `@since tag should only contain versions - with configured regex`() { + lintMethod( + """ + |/** + | * Description of this file + | * @since 2019-10-11 + | * @since 1.2.3-rc-1 + | * @since 1.2.3.RELEASE + | */ + | + |package org.cqfn.diktat.example + | + |/** + | * Description of this file + | * @since 2019-10-11 + | * @since 1.2.3 + | */ + |class Example { } + """.trimMargin(), + LintError(3, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019-10-11"), + LintError(5, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 1.2.3.RELEASE"), + LintError(12, 4, ruleId, "${Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.warnText()} @since 2019-10-11"), + rulesConfigList = listOf( + RulesConfig( + Warnings.KDOC_CONTAINS_DATE_OR_AUTHOR.name, true, mapOf( + "versionRegex" to "\\d+\\.\\d+\\.\\d+[-\\w\\d]*" + ) + ) + ) + ) + } } diff --git a/examples/gradle-groovy-dsl/diktat-analysis.yml b/examples/gradle-groovy-dsl/diktat-analysis.yml index cf353c0ce0..e3fcb8b435 100644 --- a/examples/gradle-groovy-dsl/diktat-analysis.yml +++ b/examples/gradle-groovy-dsl/diktat-analysis.yml @@ -111,6 +111,9 @@ # Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after - name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS enabled: true +# Checks that kdoc does not contain @author tag or date +- name: KDOC_CONTAINS_DATE_OR_AUTHOR + enabled: true # Checks that KDoc does not contain single line with words 'return', 'get' or 'set' - name: KDOC_TRIVIAL_KDOC_ON_FUNCTION enabled: true @@ -129,9 +132,6 @@ # Checks that header kdoc is located before package directive - name: HEADER_NOT_BEFORE_PACKAGE enabled: true -# Checks that header kdoc does not contain @author tag ar date -- name: HEADER_CONTAINS_DATE_OR_AUTHOR - enabled: true # Checks that file does not contain lines > maxSize - name: FILE_IS_TOO_LONG enabled: true diff --git a/examples/gradle-kotlin-dsl-multiproject/diktat-analysis.yml b/examples/gradle-kotlin-dsl-multiproject/diktat-analysis.yml index 1dff285a5f..7c503cf769 100644 --- a/examples/gradle-kotlin-dsl-multiproject/diktat-analysis.yml +++ b/examples/gradle-kotlin-dsl-multiproject/diktat-analysis.yml @@ -71,6 +71,8 @@ enabled: true - name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS enabled: true +- name: KDOC_CONTAINS_DATE_OR_AUTHOR + enabled: true - name: KDOC_TRIVIAL_KDOC_ON_FUNCTION enabled: 'true' - name: HEADER_WRONG_FORMAT @@ -84,8 +86,6 @@ copyrightText: 'Copyright (c) Your Company Name Here. 2010-2021' - name: HEADER_NOT_BEFORE_PACKAGE enabled: true -- name: HEADER_CONTAINS_DATE_OR_AUTHOR - enabled: true - name: FILE_IS_TOO_LONG enabled: true configuration: diff --git a/examples/gradle-kotlin-dsl/diktat-analysis.yml b/examples/gradle-kotlin-dsl/diktat-analysis.yml index cf353c0ce0..e3fcb8b435 100644 --- a/examples/gradle-kotlin-dsl/diktat-analysis.yml +++ b/examples/gradle-kotlin-dsl/diktat-analysis.yml @@ -111,6 +111,9 @@ # Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after - name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS enabled: true +# Checks that kdoc does not contain @author tag or date +- name: KDOC_CONTAINS_DATE_OR_AUTHOR + enabled: true # Checks that KDoc does not contain single line with words 'return', 'get' or 'set' - name: KDOC_TRIVIAL_KDOC_ON_FUNCTION enabled: true @@ -129,9 +132,6 @@ # Checks that header kdoc is located before package directive - name: HEADER_NOT_BEFORE_PACKAGE enabled: true -# Checks that header kdoc does not contain @author tag ar date -- name: HEADER_CONTAINS_DATE_OR_AUTHOR - enabled: true # Checks that file does not contain lines > maxSize - name: FILE_IS_TOO_LONG enabled: true diff --git a/examples/maven/diktat-analysis.yml b/examples/maven/diktat-analysis.yml index cf353c0ce0..e3fcb8b435 100644 --- a/examples/maven/diktat-analysis.yml +++ b/examples/maven/diktat-analysis.yml @@ -111,6 +111,9 @@ # Checks that special tags `@apiNote`, `@implNote`, `@implSpec` have exactly one empty line after - name: KDOC_NO_NEWLINE_AFTER_SPECIAL_TAGS enabled: true +# Checks that kdoc does not contain @author tag or date +- name: KDOC_CONTAINS_DATE_OR_AUTHOR + enabled: true # Checks that KDoc does not contain single line with words 'return', 'get' or 'set' - name: KDOC_TRIVIAL_KDOC_ON_FUNCTION enabled: true @@ -129,9 +132,6 @@ # Checks that header kdoc is located before package directive - name: HEADER_NOT_BEFORE_PACKAGE enabled: true -# Checks that header kdoc does not contain @author tag ar date -- name: HEADER_CONTAINS_DATE_OR_AUTHOR - enabled: true # Checks that file does not contain lines > maxSize - name: FILE_IS_TOO_LONG enabled: true diff --git a/info/available-rules.md b/info/available-rules.md index 085763b398..550983f0cb 100644 --- a/info/available-rules.md +++ b/info/available-rules.md @@ -1,9 +1,9 @@ | Chap | Standard | Rule name | Description | Fix | Config | FixMe | | ----- | ---------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --- | ------ | ----- | | 1 | 1.1.1 | CONFUSING_IDENTIFIER_NAMING | Check: warns if identifier has inappropriate name (See table of rule 1.2 part 6). | no | no | no | -| 1 | 1.2.1 | PACKAGE_NAME_MISSING | Check: warns if package name is missing in a file
Fix: automatically adds package directive with the name that starts from the domain name (in example - com.huawei) and contains the real directory | yes | no | Domain name should not be hardcoded. It should be moved to extra configuration.Recursively fix all imports in project.Fix the directory where the code is stored.Make this check isolated from domain name addition| +| 1 | 1.2.1 | PACKAGE_NAME_MISSING | Check: warns if package name is missing in a file
Fix: automatically adds package directive with the name that starts from the domain name (in example - com.huawei) and contains the real directory | yes | no | Recursively fix all imports in project.
Fix the directory where the code is stored.
Make this check isolated from domain name addition | | 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_CASE | Check: warns if package name is in incorrect (non-lower) case
Fix: automatically update the case in package name | yes | no | Recursively update all imports in the project.| -| 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_PREFIX | Check: warns if package name does not start with the company's domain
Fix: automatically update the prefix in the package name | yes | no | Fix the directory where the code is stored.Recursively update all imports in the project.Domain name should not be hardcoded. It should be moved to extra configuration.| +| 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_PREFIX | Check: warns if package name does not start with the company's domain
Fix: automatically update the prefix in the package name | yes | no | Fix the directory where the code is stored.
Recursively update all imports in the project.| | 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_SYMBOLS | Check: warns if package name has incorrect symbols like underscore or non-ASCII letters/digits.Exception: underscores that are used for differentiating of keywords in a name.
Fix: no but will be | no | no | Add autofix for at least converting underscore to a dot or replacing itFix the directory where the code is stored.Cover autofix with tests| | 1 | 1.2.1 | PACKAGE_NAME_INCORRECT_PATH | Check: warns if the path for a file does not match with a package name
Fix: replacing incorrect package name with the name constructed from a path to the file. | yes | no | Make this check isolated from domain name creationRecursively update all imports in the project.Fix the directory where the code is stored.Add test mechanism to test checker| | 1 | 1.2.1 | INCORRECT_PACKAGE_SEPARATOR | Check: warns if underscore is incorrectly used to split package naming
Fix: fixing all nodes in AST and the package name to remove all underscores | no | no | Recursively update usages of this class in the project| @@ -38,12 +38,12 @@ | 2 | 2.2.1 | KDOC_NO_EMPTY_TAGS | Check: warns if KDoc tags have empty content | no | no | | | 2 | 2.1.3 | KDOC_NO_DEPRECATED_TAG | Check: warns if `@deprecated` is used in KDoc
Fix: adds `@Deprecated` annotation with message, removes tag | yes | no | Annotation's `replaceWith` field can be filled too| | 2 | 2.1.1 | KDOC_NO_CONSTRUCTOR_PROPERTY | Check: warns if there is no property tag inside KDoc before constructor | yes | no | -| 2 | 2.1.1 | KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT | Check: warns if there is comment before property in constructor | yes | no | +| 2 | 2.1.1 | KDOC_NO_CONSTRUCTOR_PROPERTY_WITH_COMMENT| Check: warns if there is comment before property in constructor | yes | no | +| 2 | 2.2.1 | KDOC_CONTAINS_DATE_OR_AUTHOR | Check: warns if header KDoc contains `@author` tag.
Warns if `@since` tag contains version and not date. | no | no | Detect author by other patterns (e.g. 'created by' etc.)| | 2 | 2.3.1 | KDOC_TRIVIAL_KDOC_ON_FUNCTION | Check: warns if KDoc contains single line with words 'return', 'get' or 'set' | no | no | | | 2 | 2.2.1 | HEADER_WRONG_FORMAT | Checks: warns if there is no newline after header KDoc
Fix: adds newline | yes | no | Check if header is on the very top of file. It's hard to determine when it's not.| | 2 | 2.2.1 | HEADER_MISSING_OR_WRONG_COPYRIGHT | Checks: copyright exists on top of file and is properly formatted (as a block comment)
Fix: adds copyright if it is missing and required | yes | mandatoryCopyright | Allow setting copyright patterns via configuration| | 2 | 2.2.1 | WRONG_COPYRIGHT_YEAR | Checks: copyright have a valid year
Fix: makes a year valid | yes | no | - | -| 2 | 2.2.1 | HEADER_CONTAINS_DATE_OR_AUTHOR | Check: warns if header KDoc contains `@author` tag | no | no | Detect author by other patterns (e.g. 'created by' etc.)Detect creation date (no standard tag even in javadoc)| | 2 | 2.2.1 | HEADER_MISSING_IN_NON_SINGLE_CLASS_FILE | Check: warns if file with zero or >1 classes doesn't have header KDoc | no | no | | | 2 | 2.4.2 | COMMENTED_OUT_CODE | Check: warns if commented code is detected (when un-commented, can be parsed) | no | no | Offset is lost when joined EOL comments are split again| | 2 | 2.4.1 | COMMENT_WHITE_SPACE | Check: warns if there is no space between // and comment, if there is no space between code and comment
Fix: adds a white space | yes | maxSpaces | - | diff --git a/info/guide/guide-chapter-2.md b/info/guide/guide-chapter-2.md index afe3550248..8a7c5c9d82 100644 --- a/info/guide/guide-chapter-2.md +++ b/info/guide/guide-chapter-2.md @@ -94,7 +94,7 @@ When the method has such details as arguments, return value, or can throw except /** * This is the short overview comment for the example interface. * / * Add a blank line between the comment text and each KDoc tag underneath * / - * @since 2019-01-01 + * @since 1.6 */ protected abstract class Sample { /** @@ -136,8 +136,11 @@ Therefore, Kdoc should contain the following: Kdoc should not contain: - Empty descriptions in tag blocks. It is better not to write Kdoc than waste code line space. - There should be no empty lines between the method/class declaration and the end of Kdoc (`*/` symbols). -Important note: KDoc does not support the `@deprecated` tag. Instead, use the `@Deprecated` annotation. - +- `@author` tag. It doesn't matter who originally created a class when you can use `git blame` or VCS of your choice to look through the changes history. +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). **Exception:** @@ -172,20 +175,38 @@ Other KDoc tags (such as @param type parameters and @see.) can be added as follo ### 2.2 Adding comments on the file header This section describes the general rules of adding comments on the file header. -Comments on the file header should be placed before the package name and imports. If you need to add more content to the comment, subsequently add it in the same format. -Comments on the file header must include copyright information, without the creation date and author's name (use VCS for history management). Also, describe the content inside files that contain multiple or no classes. +### 2.2.1 Formatting of comments in the file header -Place comments on the file header before the package name and imports. If you need to add more content to the comment, subsequently add it in the same format. +Comments on the file header should be placed before the package name and imports. If you need to add more content to the comment, subsequently add it in the same format. + +Comments on the file header must include copyright information, without the creation date and author's name (use VCS for history management). +Also, describe the content inside files that contain multiple or no classes. The following examples for Huawei describe the format of the *copyright license*: \ Chinese version: `版权所有 (c) 华为技术有限公司 2012-2020` \ English version: `Copyright (c) Huawei Technologies Co., Ltd. 2012-2020. All rights reserved.` +`2012` and `2020` are the years the file was first created and the current year, respectively. + +Do not place **release notes** in header, use VCS to keep track of changes in file. Notable changes can be marked in individual KDocs using `@since` tag with version. + +Invalid example: +```kotlin +/** + * Release notes: + * 2019-10-11: added class Foo + */ -Regarding the **release notes**, see examples below: +class Foo +``` -- `2012-2020` can be modified according to your actual situation. `2012` and `2020` are the years the file was first created and last modified, respectively. -These two years can be the same (for example, `2020–2020`). When the file is substantially changed (for example, through feature extensions and major refactorings), the subsequent years must be updated. +Valid example: +```kotlin +/** + * @since 2.4.0 + */ +class Foo +``` - The **copyright statement** can use your company's subsidiaries, as shown in the below examples: \ Chinese version: `版权所有 (c) 海思半导体 2012-2020` \