diff --git a/CHANGELOG.md b/CHANGELOG.md index bca00cc9ab..129c35087e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,6 +106,7 @@ If an `EditorConfigProperty` is defined in a `Rule` that is only provided via a * Prevent cyclic dependency between `modifier-list-spacing` rule and `indent` rule when the super type call entry is annotated ([#2227](https://github.com/pinterest/ktlint/pull/2227)) * Do not remove parenthesis after explicit constructor keyword when it has no parameters ([#2226](https://github.com/pinterest/ktlint/pull/2226)) * Fix indentation of super type list of class in case it is preceded by a comment ([#2228](https://github.com/pinterest/ktlint/pull/2228)) +* Allow a super type list starting with an annotation having a parameter to be on the same line as the closing parenthesis of the enclosing class ([#2230](https://github.com/pinterest/ktlint/pull/2230)) ### Changed diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt index f1f49ed40a..f0c49b6266 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRule.kt @@ -3,10 +3,13 @@ package com.pinterest.ktlint.ruleset.standard.rules import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATED_EXPRESSION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION import com.pinterest.ktlint.rule.engine.core.api.ElementType.ANNOTATION_ENTRY +import com.pinterest.ktlint.rule.engine.core.api.ElementType.CLASS +import com.pinterest.ktlint.rule.engine.core.api.ElementType.COLON import com.pinterest.ktlint.rule.engine.core.api.ElementType.CONSTRUCTOR_KEYWORD import com.pinterest.ktlint.rule.engine.core.api.ElementType.FILE_ANNOTATION_LIST import com.pinterest.ktlint.rule.engine.core.api.ElementType.GT import com.pinterest.ktlint.rule.engine.core.api.ElementType.MODIFIER_LIST +import com.pinterest.ktlint.rule.engine.core.api.ElementType.RPAR import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_ARGUMENT_LIST import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_PROJECTION import com.pinterest.ktlint.rule.engine.core.api.ElementType.TYPE_REFERENCE @@ -32,6 +35,8 @@ import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline import com.pinterest.ktlint.rule.engine.core.api.lastChildLeafOrSelf import com.pinterest.ktlint.rule.engine.core.api.nextCodeLeaf import com.pinterest.ktlint.rule.engine.core.api.nextCodeSibling +import com.pinterest.ktlint.rule.engine.core.api.prevCodeLeaf +import com.pinterest.ktlint.rule.engine.core.api.prevCodeSibling import com.pinterest.ktlint.rule.engine.core.api.prevLeaf import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceAfterMe import com.pinterest.ktlint.rule.engine.core.api.upsertWhitespaceBeforeMe @@ -153,10 +158,19 @@ public class AnnotationRule : .filter { it.isAnnotationEntryWithValueArgumentList() || !it.isPrecededByOtherAnnotationEntryWithoutParametersOnTheSameLine() - }.forEach { annotationEntry -> + }.forEachIndexed { index, annotationEntry -> annotationEntry .prevLeaf() - ?.let { prevLeaf -> + .takeUnless { + // Allow in ktlint_official code style: + // class Foo( + // bar: Bar, + // ) : @Suppress("DEPRECATION") + // FooBar() + index == 0 && + codeStyle == CodeStyleValue.ktlint_official && + it.annotationOnSameLineAsClosingParenthesisOfClassParameterList() + }?.let { prevLeaf -> // Let the indentation rule determine the exact indentation and only report and fix when the line needs to be // wrapped if (!prevLeaf.textContains('\n')) { @@ -369,6 +383,22 @@ public class AnnotationRule : private fun ASTNode.isFollowedByOtherAnnotationEntry() = siblings(forward = true).any { it.elementType == ANNOTATION_ENTRY } + private fun ASTNode?.annotationOnSameLineAsClosingParenthesisOfClassParameterList() = + // Allow: + // class Foo( + // bar: Bar, + // ) : @Suppress("DEPRECATION") + // FooBar() + this + ?.takeIf { it.treeParent?.elementType == CLASS } + ?.prevCodeSibling() + ?.takeIf { it.elementType == COLON } + ?.prevCodeLeaf() + ?.takeIf { it.elementType == RPAR } + ?.prevLeaf() + ?.isWhiteSpaceWithNewline() + ?: false + private fun ASTNode.isOnSameLineAsNextAnnotationEntry() = siblings(forward = true) .takeWhile { it.elementType != ANNOTATION_ENTRY } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleTest.kt index 6a679198c0..ae3d1509bd 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/AnnotationRuleTest.kt @@ -974,4 +974,16 @@ class AnnotationRuleTest { """.trimIndent() annotationRuleAssertThat(code).hasNoLintViolations() } + + @Test + fun `Given a class with a super type list starting with an annotation having parameter`() { + val code = + """ + class Foo( + bar: Bar, + ) : @Suppress("DEPRECATION") + FooBar() + """.trimIndent() + annotationRuleAssertThat(code).hasNoLintViolations() + } }