From 7501f72f1b32a9d03aae8552cd729ca23683b8bb Mon Sep 17 00:00:00 2001 From: Paul Dingemans Date: Sun, 11 Feb 2024 20:08:15 +0100 Subject: [PATCH] Add new rule for disallowing KDoc at non-whitelisted locations (#2548) KDocs should only be used at locations for which it makes sense to provide (e.g. generate) documentation to be used by other consumers. Closes #2547 --- .../main/kotlin/ktlint-publication.gradle.kts | 2 +- .../snapshot/docs/rules/experimental.md | 57 +++++ documentation/snapshot/docs/rules/standard.md | 47 ++-- .../MaxLineLengthEditorConfigProperty.kt | 2 +- .../rule/engine/api/KtLintRuleEngine.kt | 6 +- .../ktlint/rule/engine/api/KtLintTest.kt | 2 +- .../api/ktlint-ruleset-standard.api | 9 + .../standard/StandardRuleSetProvider.kt | 2 + .../ktlint/ruleset/standard/rules/KdocRule.kt | 80 +++++++ .../standard/rules/KdocWrappingRule.kt | 73 ++---- .../ruleset/standard/rules/KdocRuleTest.kt | 212 ++++++++++++++++++ .../standard/rules/KdocWrappingRuleTest.kt | 23 -- 12 files changed, 399 insertions(+), 116 deletions(-) create mode 100644 ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRule.kt create mode 100644 ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRuleTest.kt diff --git a/build-logic/src/main/kotlin/ktlint-publication.gradle.kts b/build-logic/src/main/kotlin/ktlint-publication.gradle.kts index ccd506d04b..0dcd85eaf6 100644 --- a/build-logic/src/main/kotlin/ktlint-publication.gradle.kts +++ b/build-logic/src/main/kotlin/ktlint-publication.gradle.kts @@ -80,7 +80,7 @@ publishing { } } -/** +/* * Following signing parameters must be configured in `$HOME/.gradle/gradle.properties`: * ```properties * signing.keyId=12345678 diff --git a/documentation/snapshot/docs/rules/experimental.md b/documentation/snapshot/docs/rules/experimental.md index 28730e37ac..7359e7eafb 100644 --- a/documentation/snapshot/docs/rules/experimental.md +++ b/documentation/snapshot/docs/rules/experimental.md @@ -541,6 +541,63 @@ Conditions should not use a both `&&` and `||` operators between operators at th Rule id: `mixed-condition-operators` (`standard` rule set) +## KDoc + +KDoc's should only be used on elements for which KDoc is to be transformed to documentation. Normal block comments should be used in other cases. + +!!! note: + Access modifiers are ignored. Strictly speaking, one could argue that private declarations should not have a KDoc as no documentation will be generated for it. However, for internal use of developers the KDoc still serves documentation purposes. + +=== "[:material-heart:](#) Ktlint" + + ```kotlin + /** some KDoc */ + class FooBar( + /** some KDoc */ + val foo: Foo + ) { + /** + * Some bar KDoc + */ + constructor() : this() + + /** some KDoc */ + val bar: Bar + } + + enum class Foo { + /** some KDoc */ + BAR + } + + /** some KDoc */ + interface Foo + /** some KDoc */ + fun foo() + /** some KDoc */ + val foo: Foo + /** some KDoc */ + object foo: Foo + /** some KDoc */ + typealias FooBar = (Foo) -> Bar + ``` + +=== "[:material-heart-off-outline:](#) Disallowed" + + ```kotlin + /** + * Some dangling Kdoc (e.g. not followed by a declaration) + */ + + val foo /** Some KDoc */ = "foo" + + class Foo( + /** some dangling KDoc inside a parameter list */ + ) + ``` + +Rule id: `kdoc` (`standard` rule set) + ## Multiline loop Braces required for multiline for, while, and do statements. diff --git a/documentation/snapshot/docs/rules/standard.md b/documentation/snapshot/docs/rules/standard.md index b66ec43626..3c97fa2e08 100644 --- a/documentation/snapshot/docs/rules/standard.md +++ b/documentation/snapshot/docs/rules/standard.md @@ -2122,7 +2122,7 @@ Rule id: `trailing-comma-on-declaration-site` (`standard` rule set) ## Type argument comment -Disallows comments to be placed at certain locations inside a type argument (list). A KDoc is not allowed. +Disallows comments to be placed at certain locations inside a type argument. === "[:material-heart:](#) Ktlint" @@ -2144,11 +2144,6 @@ Disallows comments to be placed at certain locations inside a type argument (lis fun Foo< out Any, // some comment >.foo() {} - val fooBar: FooBar< - /** some comment */ - Foo, - Bar - > ``` !!! note @@ -2165,16 +2160,16 @@ Rule id: `type-argument-comment` (`standard` rule set) ## Type parameter comment -Disallows comments to be placed at certain locations inside a type parameter (list). A KDoc is not allowed. +Disallows comments to be placed at certain locations inside a type parameter. === "[:material-heart:](#) Ktlint" ```kotlin - class Foo2< + class Foo1< /* some comment */ out Bar > - class Foo3< + class Foo2< // some comment out Bar > @@ -2183,12 +2178,8 @@ Disallows comments to be placed at certain locations inside a type parameter (li === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - class Foo1< - /** some comment */ - in Bar - > - class Foo2 - class Foo3< + class Foo1 + class Foo2< in Bar, // some comment > ``` @@ -2225,17 +2216,17 @@ Rule id: `unnecessary-parentheses-before-trailing-lambda` (`standard` rule set) ## Value argument comment -Disallows comments to be placed at certain locations inside a value argument (list). A KDoc is not allowed. +Disallows comments to be placed at certain locations inside a value argument. === "[:material-heart:](#) Ktlint" ```kotlin - val foo2 = + val foo1 = foo( /* some comment */ bar = "bar" ) - val foo3 = + val foo2 = foo( // some comment bar = "bar" @@ -2245,9 +2236,8 @@ Disallows comments to be placed at certain locations inside a value argument (li === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - val foo1 = foo(bar /** some comment */ = "bar") - val foo2 = foo(bar /* some comment */ = "bar") - val foo3 = + val foo1 = foo(bar /* some comment */ = "bar") + val foo2 = foo( bar = // some comment "bar" @@ -2268,20 +2258,16 @@ Rule id: `value-argument-comment` (`standard` rule set) ## Value parameter comment -Disallows comments to be placed at certain locations inside a value argument (list). A KDoc is allowed but must start on a separate line. +Disallows comments to be placed at certain locations inside a value argument. === "[:material-heart:](#) Ktlint" ```kotlin class Foo1( - /** some comment */ - bar = "bar" - ) - class Foo2( /* some comment */ bar = "bar" ) - class Foo3( + class Foo2( // some comment bar = "bar" ) @@ -2290,13 +2276,10 @@ Disallows comments to be placed at certain locations inside a value argument (li === "[:material-heart-off-outline:](#) Disallowed" ```kotlin - class Foo2( - bar /** some comment */ = "bar" - ) - class Foo2( + class Foo1( bar = /* some comment */ "bar" ) - class Foo3( + class Foo2( bar = // some comment "bar" diff --git a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/MaxLineLengthEditorConfigProperty.kt b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/MaxLineLengthEditorConfigProperty.kt index 93e23c9748..59c6890118 100644 --- a/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/MaxLineLengthEditorConfigProperty.kt +++ b/ktlint-rule-engine-core/src/main/kotlin/com/pinterest/ktlint/rule/engine/core/api/editorconfig/MaxLineLengthEditorConfigProperty.kt @@ -30,7 +30,7 @@ public val MAX_LINE_LENGTH_PROPERTY: EditorConfigProperty = codeStyleValue.defaultValue() } - /** + /* * Internally, Ktlint uses integer 'Int.MAX_VALUE' to indicate that the max line length has to be ignored as this is easier * in comparisons to check whether the maximum length of a line is exceeded. */ diff --git a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt index c7b2ca6cbd..82af41d9d8 100644 --- a/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt +++ b/ktlint-rule-engine/src/main/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintRuleEngine.kt @@ -146,9 +146,9 @@ public class KtLintRuleEngine( ruleExecutionContext.executeRule(rule, true) { offset, errorMessage, canBeAutoCorrected -> if (canBeAutoCorrected) { mutated = true - /** - * Rebuild the suppression locator after each change in the AST as the offsets of the - * suppression hints might have changed. + /* + * Rebuild the suppression locator after each change in the AST as the offsets of the suppression hints might + * have changed. */ ruleExecutionContext.rebuildSuppressionLocator() } diff --git a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt index cda60584d5..920ec92ce9 100644 --- a/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt +++ b/ktlint-rule-engine/src/test/kotlin/com/pinterest/ktlint/rule/engine/api/KtLintTest.kt @@ -428,7 +428,7 @@ class KtLintTest { @Test fun `Given that format is started using the ruleProviders parameter then NO exception is thrown`() { - /** + /* * Formatting some code with the [WithStateRule] does not result in a [KtLintRuleException] because [KtLintRuleEngine.format] is * able to request a new instance of the rule whenever the instance has been used before to traverse the AST. */ diff --git a/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api b/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api index 5c5484e02c..aa6a95aa80 100644 --- a/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api +++ b/ktlint-ruleset-standard/api/ktlint-ruleset-standard.api @@ -360,6 +360,15 @@ public final class com/pinterest/ktlint/ruleset/standard/rules/IndentationRuleKt public static final fun getINDENTATION_RULE_ID ()Lcom/pinterest/ktlint/rule/engine/core/api/RuleId; } +public final class com/pinterest/ktlint/ruleset/standard/rules/KdocRule : com/pinterest/ktlint/ruleset/standard/StandardRule { + public fun ()V + public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V +} + +public final class com/pinterest/ktlint/ruleset/standard/rules/KdocRuleKt { + public static final fun getKDOC_RULE_ID ()Lcom/pinterest/ktlint/rule/engine/core/api/RuleId; +} + public final class com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule : com/pinterest/ktlint/ruleset/standard/StandardRule { public fun ()V public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider.kt index 6bd36f8d3d..05ddf82b98 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StandardRuleSetProvider.kt @@ -35,6 +35,7 @@ import com.pinterest.ktlint.ruleset.standard.rules.IfElseBracingRule import com.pinterest.ktlint.ruleset.standard.rules.IfElseWrappingRule import com.pinterest.ktlint.ruleset.standard.rules.ImportOrderingRule import com.pinterest.ktlint.ruleset.standard.rules.IndentationRule +import com.pinterest.ktlint.ruleset.standard.rules.KdocRule import com.pinterest.ktlint.ruleset.standard.rules.KdocWrappingRule import com.pinterest.ktlint.ruleset.standard.rules.MaxLineLengthRule import com.pinterest.ktlint.ruleset.standard.rules.MixedConditionOperatorsRule @@ -132,6 +133,7 @@ public class StandardRuleSetProvider : RuleSetProviderV3(RuleSetId.STANDARD) { RuleProvider { IfElseWrappingRule() }, RuleProvider { ImportOrderingRule() }, RuleProvider { IndentationRule() }, + RuleProvider { KdocRule() }, RuleProvider { KdocWrappingRule() }, RuleProvider { MaxLineLengthRule() }, RuleProvider { MixedConditionOperatorsRule() }, diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRule.kt new file mode 100644 index 0000000000..2760efc3cd --- /dev/null +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRule.kt @@ -0,0 +1,80 @@ +package com.pinterest.ktlint.ruleset.standard.rules + +import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS +import com.pinterest.ktlint.rule.engine.core.api.ElementType.ENUM_ENTRY +import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE +import com.pinterest.ktlint.rule.engine.core.api.ElementType.FUN +import com.pinterest.ktlint.rule.engine.core.api.ElementType.KDOC +import com.pinterest.ktlint.rule.engine.core.api.ElementType.OBJECT_DECLARATION +import com.pinterest.ktlint.rule.engine.core.api.ElementType.PROPERTY +import com.pinterest.ktlint.rule.engine.core.api.ElementType.SECONDARY_CONSTRUCTOR +import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPEALIAS +import com.pinterest.ktlint.rule.engine.core.api.ElementType.VALUE_PARAMETER +import com.pinterest.ktlint.rule.engine.core.api.RuleId +import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint +import com.pinterest.ktlint.rule.engine.core.api.SinceKtlint.Status.EXPERIMENTAL +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPERTY +import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY +import com.pinterest.ktlint.ruleset.standard.StandardRule +import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.com.intellij.psi.tree.TokenSet + +/** + * Disallow KDoc except of classes, functions and xxx + */ +@SinceKtlint("1.2.0", EXPERIMENTAL) +public class KdocRule : + StandardRule( + id = "kdoc", + usesEditorConfigProperties = + setOf( + INDENT_SIZE_PROPERTY, + INDENT_STYLE_PROPERTY, + ), + ) { + override fun beforeVisitChildNodes( + node: ASTNode, + autoCorrect: Boolean, + emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, + ) { + node + .takeIf { it.elementType == KDOC } + ?.let { + if (it.treeParent.elementType in allowedParentElementTypes) { + if (it.treeParent.firstChildNode != it) { + emit( + node.startOffset, + "A KDoc is allowed only at start of '${it.treeParent.elementType.debugName.lowercase()}'", + false, + ) + } + } else { + if (it.treeParent.elementType == FILE) { + emit(node.startOffset, "A dangling toplevel KDoc is not allowed", false) + } else { + emit( + node.startOffset, + "A KDoc is not allowed inside '${it.treeParent.elementType.debugName.lowercase()}'", + false, + ) + } + } + } + } + + private companion object { + val allowedParentElementTypes = + TokenSet.create( + CLASS, + ENUM_ENTRY, + FUN, + OBJECT_DECLARATION, + PROPERTY, + SECONDARY_CONSTRUCTOR, + TYPEALIAS, + VALUE_PARAMETER, + ) + } +} + +public val KDOC_RULE_ID: RuleId = KdocRule().ruleId diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule.kt index 152c049be4..3aa6005e63 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRule.kt @@ -12,7 +12,6 @@ import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_SIZE_PROPER import com.pinterest.ktlint.rule.engine.core.api.editorconfig.INDENT_STYLE_PROPERTY import com.pinterest.ktlint.rule.engine.core.api.indent import com.pinterest.ktlint.rule.engine.core.api.nextLeaf -import com.pinterest.ktlint.rule.engine.core.api.noNewLineInClosedRange import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe import com.pinterest.ktlint.ruleset.standard.StandardRule @@ -38,66 +37,30 @@ public class KdocWrappingRule : emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, ) { if (node.elementType == KDOC) { - val nonIndentLeafOnSameLinePrecedingKdocComment = - node - .findChildByType(KDOC_START) - ?.prevLeaf() - ?.takeIf { isNonIndentLeafOnSameLine(it) } - val nonIndentLeafOnSameLineFollowingKdocComment = - node - .findChildByType(KDOC_END) - ?.nextLeaf() - ?.takeIf { isNonIndentLeafOnSameLine(it) } - - if (nonIndentLeafOnSameLinePrecedingKdocComment != null && - nonIndentLeafOnSameLineFollowingKdocComment != null - ) { - if (noNewLineInClosedRange(nonIndentLeafOnSameLinePrecedingKdocComment, nonIndentLeafOnSameLineFollowingKdocComment)) { - // Do not try to fix constructs like below: - // val foo /** some comment */ = "foo" + node + .findChildByType(KDOC_START) + ?.prevLeaf() + ?.takeIf { isNonIndentLeafOnSameLine(it) } + ?.let { + // It can not be autocorrected as it might depend on the situation and code style what is + // preferred. emit( node.startOffset, - "A KDoc comment in between other elements on the same line is disallowed", - false, - ) - } else { - // Do not try to fix constructs like below: - // val foo = "foo" /* - // some comment* - // */ val bar = "bar" - emit( - node.startOffset, - "A KDoc comment starting on same line as another element and ending on another line before another element is " + - "disallowed", + "A KDoc comment after any other element on the same line must be separated by a new line", false, ) } - return - } - - if (nonIndentLeafOnSameLinePrecedingKdocComment != null) { - // It can not be autocorrected as it might depend on the situation and code style what is - // preferred. - emit( - node.startOffset, - "A KDoc comment after any other element on the same line must be separated by a new line", - false, - ) - } - nonIndentLeafOnSameLineFollowingKdocComment - ?.followsKdocCommentOnSameLine(node, emit, autoCorrect) - } - } - - private fun ASTNode.followsKdocCommentOnSameLine( - kdocCommentNode: ASTNode, - emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit, - autoCorrect: Boolean, - ) { - emit(startOffset, "A KDoc comment may not be followed by any other element on that same line", true) - if (autoCorrect) { - kdocCommentNode.upsertWhitespaceAfterMe(kdocCommentNode.indent()) + node + .findChildByType(KDOC_END) + ?.nextLeaf() + ?.takeIf { isNonIndentLeafOnSameLine(it) } + ?.let { nextLeaf -> + emit(nextLeaf.startOffset, "A KDoc comment may not be followed by any other element on that same line", true) + if (autoCorrect) { + node.upsertWhitespaceAfterMe(node.indent()) + } + } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRuleTest.kt new file mode 100644 index 0000000000..50109decd5 --- /dev/null +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocRuleTest.kt @@ -0,0 +1,212 @@ +package com.pinterest.ktlint.ruleset.standard.rules + +import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +class KdocRuleTest { + private val kdocRuleAssertThat = assertThatRule { KdocRule() } + + @ParameterizedTest(name = "foo: {0}") + @ValueSource( + strings = [ + "class Foo", + "enum class Foo", + "interface Foo", + "fun foo()", + "val foo: Foo", + "object foo: Foo", + "typealias FooBar = (Foo) -> Bar", + ], + ) + fun `A KDoc is allowed`(annotatedConstruct: String) { + val code = + """ + /** + * Some Foo Kdoc + */ + $annotatedConstruct + """.trimIndent() + kdocRuleAssertThat(code).hasNoLintViolations() + } + + @Test + fun `A KDoc is allowed on a class parameter`() { + val code = + """ + class Foo( + /** + * Some bar Kdoc + */ + val bar: Bar + ) + """.trimIndent() + kdocRuleAssertThat(code).hasNoLintViolations() + } + + @Test + fun `A KDoc is allowed on a secondary class constructor`() { + val code = + """ + class Foo { + /** + * Some bar KDoc + */ + constructor() : this() + } + """.trimIndent() + kdocRuleAssertThat(code).hasNoLintViolations() + } + + @Test + fun `A KDoc is allowed on a class property`() { + val code = + """ + class Foo { + /** + * Some bar KDoc + */ + val bar: Bar + } + """.trimIndent() + kdocRuleAssertThat(code).hasNoLintViolations() + } + + @Test + fun `A KDoc is allowed on an enum entry`() { + val code = + """ + enum class Foo { + /** + * Some bar Kdoc + */ + BAR + } + """.trimIndent() + kdocRuleAssertThat(code).hasNoLintViolations() + } + + @Test + fun `A dangling KDoc is not allowed`() { + val code = + """ + /** + * Some Foo Kdoc + */ + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(1, 1, "A dangling toplevel KDoc is not allowed") + } + + @Test + fun `A KDoc comment in between code elements on the same line should start and end on a new line but is not autocorrected`() { + val code = + """ + val foo /** Some KDoc comment */ = "foo" + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(1, 9, "A KDoc is allowed only at start of 'property'") + } + + @Test + fun `Given a KDoc comment containing a new line and the block is preceded and followed by other code elements then raise lint errors but do not autocorrect`() { + val code = + """ + val foo /** + some KDoc comment + */ = "foo" + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(1, 9, "A KDoc is allowed only at start of 'property'") + } + + @Test + fun `Given a kdoc as child of type argument list`() { + val code = + """ + val fooBar: FooBar< + /** some comment */ + Foo, Bar> + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(2, 5, "A KDoc is not allowed inside 'type_argument_list'") + } + + @Test + fun `Given a kdoc inside a type projection`() { + val code = + """ + fun Foo.foo() {} + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(1, 13, "A KDoc is not allowed inside 'type_projection'") + } + + @Test + fun `Given a kdoc as child of type parameter list`() { + val code = + """ + class Foo< + /** some comment */ + Bar> + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(2, 5, "A KDoc is not allowed inside 'type_parameter_list'") + } + + @Test + fun `Given a kdoc inside a type parameter`() { + val code = + """ + class Foo + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(1, 14, "A KDoc is not allowed inside 'type_parameter'") + } + + @Test + fun `Given a kdoc as child of value argument list`() { + val code = + """ + val foo = foo( + /** some comment */ + ) + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(2, 5, "A KDoc is not allowed inside 'value_argument_list'") + } + + @Test + fun `Given a kdoc inside a value argument`() { + val code = + """ + val foo = foo( + bar /** some comment */ = "bar" + ) + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(2, 9, "A KDoc is not allowed inside 'value_argument'") + } + + @Test + fun `Given a comment inside value parameter ast node`() { + val code = + """ + class Foo1( + val bar: + // some comment + Bar + ) + class Foo2( + val bar: /* some comment */ Bar + ) + class Foo3( + val bar: /** some comment */ Bar + ) + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(10, 14, "A KDoc is allowed only at start of 'value_parameter'") + } + + @Test + fun `Given a kdoc as child of value parameter list`() { + val code = + """ + class Foo( + /** some comment */ + ) + """.trimIndent() + kdocRuleAssertThat(code).hasLintViolationWithoutAutoCorrect(2, 5, "A KDoc is not allowed inside 'value_parameter_list'") + } +} diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRuleTest.kt index 18008f2174..c3e38716c0 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/KdocWrappingRuleTest.kt @@ -69,29 +69,6 @@ class KdocWrappingRuleTest { .hasLintViolationWithoutAutoCorrect(1, 17, "A KDoc comment after any other element on the same line must be separated by a new line") } - @Test - fun `A KDoc comment in between code elements on the same line should start and end on a new line but is not autocorrected`() { - val code = - """ - val foo /** Some KDoc comment */ = "foo" - """.trimIndent() - kdocWrappingRuleAssertThat(code) - .hasLintViolationWithoutAutoCorrect(1, 9, "A KDoc comment in between other elements on the same line is disallowed") - } - - @Test - fun `Given a KDoc comment containing a new line and the block is preceded and followed by other code elements then raise lint errors but do not autocorrect`() { - val code = - """ - val foo /** - some KDoc comment - */ = "foo" - """.trimIndent() - @Suppress("ktlint:standard:argument-list-wrapping", "ktlint:standard:max-line-length") - kdocWrappingRuleAssertThat(code) - .hasLintViolationWithoutAutoCorrect(1, 9, "A KDoc comment starting on same line as another element and ending on another line before another element is disallowed") - } - @Test fun `Given a KDoc comment which is indented then keep that indent when wrapping the line`() { val code =