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

Add new rule for disallowing KDoc at non-whitelisted locations #2548

Merged
merged 1 commit into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build-logic/src/main/kotlin/ktlint-publication.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ publishing {
}
}

/**
/*
* Following signing parameters must be configured in `$HOME/.gradle/gradle.properties`:
* ```properties
* signing.keyId=12345678
Expand Down
57 changes: 57 additions & 0 deletions documentation/snapshot/docs/rules/experimental.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
47 changes: 15 additions & 32 deletions documentation/snapshot/docs/rules/standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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
Expand All @@ -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
>
Expand All @@ -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<in /* some comment */ Bar>
class Foo3<
class Foo1<in /* some comment */ Bar>
class Foo2<
in Bar, // some comment
>
```
Expand Down Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -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"
)
Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public val MAX_LINE_LENGTH_PROPERTY: EditorConfigProperty<Int> =
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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
9 changes: 9 additions & 0 deletions ktlint-ruleset-standard/api/ktlint-ruleset-standard.api
Original file line number Diff line number Diff line change
Expand Up @@ -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 <init> ()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 <init> ()V
public fun beforeVisitChildNodes (Lorg/jetbrains/kotlin/com/intellij/lang/ASTNode;ZLkotlin/jvm/functions/Function3;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -132,6 +133,7 @@ public class StandardRuleSetProvider : RuleSetProviderV3(RuleSetId.STANDARD) {
RuleProvider { IfElseWrappingRule() },
RuleProvider { ImportOrderingRule() },
RuleProvider { IndentationRule() },
RuleProvider { KdocRule() },
RuleProvider { KdocWrappingRule() },
RuleProvider { MaxLineLengthRule() },
RuleProvider { MixedConditionOperatorsRule() },
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Loading