Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Rule COMMENTS_BY_KDOC was implemented #1350

Merged
merged 14 commits into from
Jun 14, 2022
3 changes: 3 additions & 0 deletions diktat-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@
# Checks that underscore is correctly used to split package naming
- name: INCORRECT_PACKAGE_SEPARATOR
enabled: true
# Checks that code block doesn't contain kdoc comments
- name: COMMENTED_BY_KDOC
enabled: true
# Checks that there is no @deprecated tag in kdoc
- name: KDOC_NO_DEPRECATED_TAG
enabled: true
Expand Down
2 changes: 2 additions & 0 deletions diktat-rules/src/main/kotlin/generated/WarningNames.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ public object WarningNames {

public const val COMMENTED_OUT_CODE: String = "COMMENTED_OUT_CODE"

public const val COMMENTED_BY_KDOC: String = "COMMENTED_BY_KDOC"

public const val WRONG_NEWLINES_AROUND_KDOC: String = "WRONG_NEWLINES_AROUND_KDOC"

public const val FIRST_COMMENT_NO_BLANK_LINE: String = "FIRST_COMMENT_NO_BLANK_LINE"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ enum class Warnings(
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"),
COMMENTED_BY_KDOC(true, "2.1.1", "you should not comment inside code blocks using kdoc syntax"),
WRONG_NEWLINES_AROUND_KDOC(true, "2.4.1", "there should be a blank line above the kDoc and there should not be no blank lines after kDoc"),
FIRST_COMMENT_NO_BLANK_LINE(true, "2.4.1", "there should not be any blank lines before first comment"),
COMMENT_WHITE_SPACE(true, "2.4.1", "there should be a white space between code and comment also between code start token and comment text"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import org.cqfn.diktat.ruleset.rules.DiktatRule
import org.cqfn.diktat.ruleset.utils.*

import com.pinterest.ktlint.core.ast.ElementType
import com.pinterest.ktlint.core.ast.ElementType.BLOCK
import com.pinterest.ktlint.core.ast.ElementType.BLOCK_COMMENT
import com.pinterest.ktlint.core.ast.ElementType.CLASS
import com.pinterest.ktlint.core.ast.ElementType.CLASS_BODY
Expand All @@ -35,6 +36,7 @@ import com.pinterest.ktlint.core.ast.parent
import com.pinterest.ktlint.core.ast.prevSibling
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiCommentImpl
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.parser.KDocKnownTag
Expand Down Expand Up @@ -67,13 +69,30 @@ class KdocComments(configRules: List<RulesConfig>) : DiktatRule(
CLASS -> checkClassElements(node)
VALUE_PARAMETER -> checkValueParameter(node)
PRIMARY_CONSTRUCTOR -> checkParameterList(node.findChildByType(VALUE_PARAMETER_LIST))
BLOCK -> checkKdocPresent(node)
else -> {
// this is a generated else block
}
}
}
}

private fun checkKdocPresent(node: ASTNode) {
node.findChildrenMatching { it.elementType == KDOC }.forEach {
warnKdocInsideBlock(it)
}
}

private fun warnKdocInsideBlock(kdocNode: ASTNode) {
Warnings.COMMENTED_BY_KDOC.warnAndFix(
configRules, emitWarn, isFixMode,
"Redundant asterisk in block comment: \\**", kdocNode.startOffset, kdocNode
) {
kdocNode.treeParent.addChild(PsiCommentImpl(BLOCK_COMMENT, kdocNode.text.replace("/**", "/*")), kdocNode)
kdocNode.treeParent.removeChild(kdocNode)
}
}

private fun checkParameterList(node: ASTNode?) {
val kdocBeforeClass = node
?.parent({ it.elementType == CLASS })
Expand Down Expand Up @@ -299,10 +318,11 @@ class KdocComments(configRules: List<RulesConfig>) : DiktatRule(
}
}

private fun checkTopLevelDoc(node: ASTNode) =
private fun checkTopLevelDoc(node: ASTNode) {
// checking that all top level class declarations and functions have kDoc
(node.getAllChildrenWithType(CLASS) + node.getAllChildrenWithType(FUN))
return (node.getAllChildrenWithType(CLASS) + node.getAllChildrenWithType(FUN))
.forEach { checkDoc(it, MISSING_KDOC_TOP_LEVEL) }
}

/**
* raises warning if protected, public or internal code element does not have a Kdoc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class ExtensionFunctionsSameNameRule(configRules: List<RulesConfig>) : DiktatRul
listOf(EXTENSION_FUNCTION_SAME_SIGNATURE)
) {
override fun logic(node: ASTNode) {
/**
/*
* 1) Collect all classes that extend other classes (collect related classes)
* 2) Collect all extension functions with same signature
* 3) Check if classes of functions are related
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -845,9 +845,9 @@ private fun ASTNode.findOffsetByLine(line: Int, positionByOffset: (Int) -> Pair<
val currentOffset = this.startOffset

var forwardDirection = true

// We additionaly consider the offset and line for current node in aim to speed up the search
// and not start any time from beginning of file, if possible
//
// If current line is closer to the requested line, start search from it
// otherwise search from the beginning of file
var lineOffset = if (line < currentLine) {
Expand Down
3 changes: 3 additions & 0 deletions diktat-rules/src/main/resources/diktat-analysis-huawei.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
# Checks that underscore is correctly used to split package naming
- name: INCORRECT_PACKAGE_SEPARATOR
enabled: true
# Checks that code block doesn't contain kdoc comments
- name: COMMENTED_BY_KDOC
enabled: true
# Checks that there is no @deprecated tag in kdoc
- name: KDOC_NO_DEPRECATED_TAG
enabled: true
Expand Down
3 changes: 3 additions & 0 deletions diktat-rules/src/main/resources/diktat-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
# Checks that underscore is correctly used to split package naming
- name: INCORRECT_PACKAGE_SEPARATOR
enabled: true
# Checks that code block doesn't contain kdoc comments
- name: COMMENTED_BY_KDOC
enabled: true
# Checks that there is no @deprecated tag in kdoc
- name: KDOC_NO_DEPRECATED_TAG
enabled: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ import org.junit.jupiter.api.Tag
import org.junit.jupiter.api.Test

class KdocCommentsFixTest : FixTestBase("test/paragraph2/kdoc/", ::KdocComments) {
@Test
@Tag(WarningNames.COMMENTED_BY_KDOC)
fun `check fix code block with kdoc comment`() {
fixAndCompare("KdocBlockCommentExpected.kt", "KdocBlockCommentTest.kt")
}

@Test
@Tag(WarningNames.KDOC_NO_CONSTRUCTOR_PROPERTY)
fun `check fix with class kdoc`() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.cqfn.diktat.ruleset.chapter2

import org.cqfn.diktat.ruleset.constants.Warnings
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_EXTRA_PROPERTY
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_CLASS_BODY_PROPERTIES_IN_HEADER
import org.cqfn.diktat.ruleset.constants.Warnings.KDOC_NO_CONSTRUCTOR_PROPERTY
Expand All @@ -19,6 +20,43 @@ import org.junit.jupiter.api.Test
class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
private val ruleId: String = "$DIKTAT_RULE_SET_ID:${KdocComments.NAME_ID}"

@Test
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
@Tag(WarningNames.COMMENTED_BY_KDOC)
fun `Should warn if kdoc comment is inside code block`() {
lintMethod(
"""
|package org.cqfn.diktat.example
|
|/**
| * right place for kdoc
| */
|class Example {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please also check top-level methods (in Kotlin you can do it without a class and from AST perspective they can differ)?

fun foo() {
    /**
    */
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

|/**
| * right place for kdoc
| */
| fun doGood(){
| /**
| * wrong place for kdoc
| */
| /*
| * right place for block comment
| */
| // right place for eol comment
| 1+2
| /**
| * right place for kdoc
| */
| fun prettyPrint(level: Int = 0, maxLevel: Int = -1): String {
| return "test"
| }
| }
|}
""".trimMargin(),
LintError( 11, 9, ruleId,"${Warnings.COMMENTED_BY_KDOC.warnText()} " +
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
"Redundant asterisk in block comment: \\**", true)
Fixed Show fixed Hide fixed
)
}

@Test
@Tag(WarningNames.MISSING_KDOC_TOP_LEVEL)
fun `all public classes should be documented with KDoc`() {
Expand All @@ -39,7 +77,8 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
}

""".trimIndent()
lintMethod(code,
lintMethod(
code,
LintError(1, 1, ruleId, "${MISSING_KDOC_TOP_LEVEL.warnText()} SomeGoodName"),
LintError(6, 1, ruleId, "${MISSING_KDOC_TOP_LEVEL.warnText()} SomeOtherGoodName"),
LintError(9, 1, ruleId, "${MISSING_KDOC_TOP_LEVEL.warnText()} SomeNewGoodName"),
Expand All @@ -55,8 +94,10 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
internal class SomeGoodName {
}
""".trimIndent()
lintMethod(code, LintError(
1, 1, ruleId, "${MISSING_KDOC_TOP_LEVEL.warnText()} SomeGoodName")
lintMethod(
code, LintError(
1, 1, ruleId, "${MISSING_KDOC_TOP_LEVEL.warnText()} SomeGoodName"
)
)
}

Expand All @@ -71,10 +112,11 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
internal fun someGoodNameNew(): String {
return " ";
}

fun main() {}
""".trimIndent()
lintMethod(code,
lintMethod(
code,
LintError(1, 1, ruleId, "${MISSING_KDOC_TOP_LEVEL.warnText()} someGoodName"),
LintError(4, 1, ruleId, "${MISSING_KDOC_TOP_LEVEL.warnText()} someGoodNameNew")
)
Expand Down Expand Up @@ -124,11 +166,12 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {

private class InternalClass {
}

public fun main() {}
}
""".trimIndent()
lintMethod(code,
lintMethod(
code,
LintError(5, 5, ruleId, "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} variable"),
LintError(7, 5, ruleId, "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} perfectFunction"),
LintError(13, 5, ruleId, "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} InternalClass")
Expand Down Expand Up @@ -158,11 +201,12 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {

private class InternalClass {
}

public fun main() {}
}
""".trimIndent()
lintMethod(code,
lintMethod(
code,
LintError(5, 5, ruleId, "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} variable"),
LintError(8, 5, ruleId, "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} perfectFunction"),
LintError(14, 5, ruleId, "${MISSING_KDOC_CLASS_ELEMENTS.warnText()} InternalClass")
Expand Down Expand Up @@ -193,7 +237,8 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
private class InternalClass {
}
}
""".trimIndent())
""".trimIndent()
)
}

@Test
Expand Down Expand Up @@ -374,7 +419,7 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
| * some descriptions
| * @return fdv
| */
|
|
| val name: String,
| anotherName: String,
| OneMoreName: String
Expand All @@ -398,7 +443,7 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
| * sdcjkh
| * @property name text2
| */
| val name: String,
| val name: String,
| ) {
|}
""".trimMargin(),
Expand All @@ -415,7 +460,7 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
| * text
| */
|class Example (
| private val name: String,
| private val name: String,
| ) {
|}
""".trimMargin()
Expand All @@ -432,7 +477,7 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
| * @property
| */
|class Example (
| val name: String,
| val name: String,
| ) {
|}
""".trimMargin(),
Expand All @@ -447,7 +492,7 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
lintMethod(
"""
|class Example (
| val name: String,
| val name: String,
| private val surname: String
| ) {
|}
Expand All @@ -467,7 +512,7 @@ class KdocCommentsWarnTest : LintTestBase(::KdocComments) {
| * @property kek
| */
|class Example (
| val name: String,
| val name: String,
| private val surname: String
| ) {
|}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package test.paragraph2.kdoc

/**
* Converts this AST node and all its children to pretty string representation
*/
@Suppress("AVOID_NESTED_FUNCTIONS")
fun Example.prettyPrint(level: Int = 0, maxLevel: Int = -1): String {
/**
* AST operates with \n only, so we need to build the whole string representation and then change line separator
*/
fun Example.doPrettyPrint(level: Int, maxLevel: Int): String {
return "test" + level + maxLevel
}
return doPrettyPrint(level, maxLevel).replace("\n", System.lineSeparator())
}

/**
* right place for kdoc
*/
class Example {
/**
* right place for kdoc
*/
fun doGood() {
/*
* wrong place for kdoc
*/
/*
* right place for block comment
*/
// right place for eol comment
1 + 2
/**
* Converts this AST node and all its children to pretty string representation
*/
fun Example.prettyPrint(level: Int = 0, maxLevel: Int = -1): String {
return "test"
}
}
}
Loading