diff --git a/CHANGELOG.md b/CHANGELOG.md index fdca506bbd..45465255d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Fixed - KtLint CLI 0.43 doesn't work with JDK 1.8 ([#1271](https://github.com/pinterest/ktlint/issues/1271)) - +- Fix false positive in rule spacing-between-declarations-with-annotations ([#1281] (https://github.com/pinterest/ktlint/issues/1281)) +- ### Changed - Update Kotlin version to `1.6.0` release diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithAnnotationsRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithAnnotationsRule.kt index 3da9d2c4fc..cc903893da 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithAnnotationsRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithAnnotationsRule.kt @@ -4,11 +4,15 @@ import com.pinterest.ktlint.core.Rule import com.pinterest.ktlint.core.ast.ElementType.MODIFIER_LIST import com.pinterest.ktlint.core.ast.children import org.jetbrains.kotlin.com.intellij.lang.ASTNode +import org.jetbrains.kotlin.com.intellij.psi.PsiComment +import org.jetbrains.kotlin.com.intellij.psi.PsiElement import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.psi.KtAnnotationEntry import org.jetbrains.kotlin.psi.KtDeclaration import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments +import org.jetbrains.kotlin.psi.psiUtil.prevLeaf +import org.jetbrains.kotlin.psi.psiUtil.prevLeafs /** * @see https://youtrack.jetbrains.com/issue/KT-35106 @@ -24,20 +28,41 @@ class SpacingBetweenDeclarationsWithAnnotationsRule : Rule("spacing-between-decl val declaration = node.psi.parent as? KtDeclaration val prevDeclaration = declaration?.getPrevSiblingIgnoringWhitespaceAndComments(withItself = false) as? KtDeclaration - val prevWhiteSpace = prevDeclaration?.nextSibling as? PsiWhiteSpace - if (declaration != null && prevDeclaration != null && prevWhiteSpace?.text?.count { it == '\n' } == 1) { + val whiteSpaceAfterPreviousDeclaration = prevDeclaration?.nextSibling as? PsiWhiteSpace + val startOfDeclarationIncludingLeadingComment = node.psi.parent.getPrevLeafIgnoringCommentAndWhitespaceExceptBlankLines() + if (whiteSpaceAfterPreviousDeclaration?.text != null && + startOfDeclarationIncludingLeadingComment?.text?.count { it == '\n' } == 1 + ) { emit( node.startOffset, "Declarations and declarations with annotations should have an empty space between.", true ) if (autoCorrect) { - val indent = prevWhiteSpace.text.substringAfter('\n') - (prevWhiteSpace.node as LeafPsiElement).rawReplaceWithText("\n\n$indent") + val indent = whiteSpaceAfterPreviousDeclaration.text.substringAfter('\n') + (whiteSpaceAfterPreviousDeclaration.node as LeafPsiElement).rawReplaceWithText("\n\n$indent") } } } } + /** + * Gets the previous element but ignores white whitespaces (excluding blank lines) and comments. Note the difference + * with method [PsiElement.getPrevSiblingIgnoringWhitespaceAndComments] which excludes blank lines as well. + */ + private fun PsiElement.getPrevLeafIgnoringCommentAndWhitespaceExceptBlankLines(): PsiElement? { + var prevLeaf: PsiElement? = this.prevLeaf() + val iterator = prevLeafs.iterator() + while (iterator.hasNext()) { + val psiElement = iterator.next() + if (psiElement is PsiComment || (psiElement is PsiWhiteSpace && psiElement.text?.count { it == '\n' } == 1)) { + prevLeaf = psiElement + } else { + break + } + } + return prevLeaf + } + private fun ASTNode.hasAnnotationsAsChildren(): Boolean = children().find { it.psi is KtAnnotationEntry } != null } diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithAnnotationsRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithAnnotationsRuleTest.kt index 3b30306862..a5a7daf134 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithAnnotationsRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/SpacingBetweenDeclarationsWithAnnotationsRuleTest.kt @@ -234,7 +234,7 @@ class SpacingBetweenDeclarationsWithAnnotationsRuleTest { } @Test - fun `missing space after comment with previous variable should do nothing`() { + fun `No blank line is required between comment and an annotated declaration`() { Assertions.assertThat( SpacingBetweenDeclarationsWithAnnotationsRule().lint( """ @@ -250,4 +250,27 @@ class SpacingBetweenDeclarationsWithAnnotationsRuleTest { ) ).isEmpty() } + + @Test + fun `Issue 1281 - No blank line is required between comment and an annotated declaration when previous declaration end with a comment`() { + Assertions.assertThat( + SpacingBetweenDeclarationsWithAnnotationsRule().lint( + """ + class KotlinPluginTest { + // tag::setUp[] + @BeforeEach + fun setUp() { + } + // end::setUp[] + + // tag::testQuery[] + @Test + fun testFindById() { + } + // end::testQuery[] + } + """.trimIndent() + ) + ).isEmpty() + } }