Skip to content

Commit

Permalink
Fix ClassCastException using ktlintFormat on class with KDoc in versi…
Browse files Browse the repository at this point in the history
…on 0.43.0 (#1276)

* Updated refs to latest (0.43.0) release

* Remove trailing spaces from KDoc

Closes #1270

* Fix violation offset and add test for EOL comments

* Update CHANGELOG.md

Co-authored-by: Sha Sha Chu <shasha@pinterest.com>
Co-authored-by: Paul Dingemans <pdingemans@bol.com>
Co-authored-by: Roman Zavarnitsyn <rom4ek93@gmail.com>
  • Loading branch information
4 people committed Dec 12, 2021
1 parent c9fbab9 commit d234a5b
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
### Fixed
- Fix false positive in rule spacing-between-declarations-with-annotations ([#1281](https://github.com/pinterest/ktlint/issues/1281))
- Fix NoSuchElementException for property accessor (`trailing-comma`) ([#1280](https://github.com/pinterest/ktlint/issues/1280))
- Fix ClassCastException using ktlintFormat on class with KDoc (`no-trailing-spaces`) ([#1270](https://github.com/pinterest/ktlint/issues/1270)

### Changed
- Update Kotlin version to `1.6.0` release
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.pinterest.ktlint.ruleset.standard

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.isPartOfComment
import com.pinterest.ktlint.core.ast.nextLeaf
import com.pinterest.ktlint.core.ast.parent
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.PsiComment
import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.kdoc.psi.api.KDoc

class NoTrailingSpacesRule : Rule("no-trailing-spaces") {

Expand All @@ -14,15 +17,23 @@ class NoTrailingSpacesRule : Rule("no-trailing-spaces") {
autoCorrect: Boolean,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit
) {
if (node is PsiWhiteSpace || node is PsiComment) {
if (node.isPartOfKDoc()) {
if (node.elementType == WHITE_SPACE && node.hasTrailingSpacesBeforeNewline()) {
emit(node.startOffset, "Trailing space(s)", true)
if (autoCorrect) {
node.removeTrailingSpacesBeforeNewline()
}
}
} else if (node.elementType == WHITE_SPACE || node.isPartOfComment()) {
val lines = node.text.split("\n")
var violated = false
var violationOffset = node.startOffset
lines
.head()
.forEach { line ->
if (line.hasTrailingSpace()) {
emit(violationOffset, "Trailing space(s)", true)
val firstTrailingSpaceOffset = violationOffset + line.trimEnd().length
emit(firstTrailingSpaceOffset, "Trailing space(s)", true)
violated = true
}
violationOffset += line.length + 1
Expand All @@ -32,7 +43,8 @@ class NoTrailingSpacesRule : Rule("no-trailing-spaces") {
// Ignore the last line as it contains the indentation of the next element
Unit
lines.last().hasTrailingSpace() -> {
emit(violationOffset, "Trailing space(s)", true)
val firstTrailingSpaceOffset = violationOffset + lines.last().trimEnd().length
emit(firstTrailingSpaceOffset, "Trailing space(s)", true)
violated = true
}
}
Expand All @@ -43,6 +55,21 @@ class NoTrailingSpacesRule : Rule("no-trailing-spaces") {
}
}

private fun ASTNode.isPartOfKDoc() = parent({ it.psi is KDoc }, strict = false) != null

private fun ASTNode.hasTrailingSpacesBeforeNewline() =
text.contains(
regex = Regex("\\s+\\n")
)

private fun ASTNode.removeTrailingSpacesBeforeNewline() {
val newText = text.replace(
regex = Regex("\\s+\\n"),
replacement = "\n"
)
(this as LeafPsiElement).replaceWithText(newText)
}

private fun String.hasTrailingSpace() =
takeLast(1) == " "
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,41 @@ class NoTrailingSpacesRuleTest {
}

@Test
fun `lint trailing spaces inside block comments`() {
fun `trailing spaces inside line comments`() {
val code =
"""
/*${" "}
* Some comment${" "}
*/
//${" "}
// Some comment${" "}
class Foo {
//${" "}${" "}
// Some comment${" "}${" "}
fun bar() = "foobar"
}
""".trimIndent()
val expectedCode =
"""
//
// Some comment
class Foo {
//
// Some comment
fun bar() = "foobar"
}
""".trimIndent()

val actual = NoTrailingSpacesRule().lint(code)

assertThat(actual)
.isEqualTo(
listOf(
LintError(1, 1, "no-trailing-spaces", "Trailing space(s)"),
LintError(2, 1, "no-trailing-spaces", "Trailing space(s)")
)
assertThat(
NoTrailingSpacesRule().format(code)
).isEqualTo(expectedCode)
assertThat(
NoTrailingSpacesRule().lint(code)
).isEqualTo(
listOf(
LintError(1, 3, "no-trailing-spaces", "Trailing space(s)"),
LintError(2, 16, "no-trailing-spaces", "Trailing space(s)"),
LintError(4, 7, "no-trailing-spaces", "Trailing space(s)"),
LintError(5, 20, "no-trailing-spaces", "Trailing space(s)")
)
)
}

@Test
Expand All @@ -52,17 +70,86 @@ class NoTrailingSpacesRuleTest {
/*${" "}
* Some comment${" "}
*/
class Foo {
/*${" "}${" "}
* Some comment${" "}${" "}
*/
fun bar() = "foobar"
}
""".trimIndent()
val expectedCode =
"""
/*
* Some comment
*/
class Foo {
/*
* Some comment
*/
fun bar() = "foobar"
}
""".trimIndent()

val actual = NoTrailingSpacesRule().format(code)
assertThat(
NoTrailingSpacesRule().format(code)
).isEqualTo(expectedCode)
assertThat(
NoTrailingSpacesRule().lint(code)
).isEqualTo(
listOf(
LintError(1, 3, "no-trailing-spaces", "Trailing space(s)"),
LintError(2, 16, "no-trailing-spaces", "Trailing space(s)"),
LintError(5, 7, "no-trailing-spaces", "Trailing space(s)"),
LintError(6, 20, "no-trailing-spaces", "Trailing space(s)")
)
)
}

assertThat(actual)
.isEqualTo(
"""
/*
@Test
fun `trailing spaces inside KDoc`() {
val code =
"""
/**${" "}
* Some comment${" "}
*${" "}
*/
class Foo {
/**${" "}${" "}
* Some comment${" "}${" "}
*${" "}${" "}
*/
fun bar() = "foobar"
}
""".trimIndent()
val codeExpected =
"""
/**
* Some comment
*
*/
class Foo {
/**
* Some comment
*
*/
""".trimIndent()
fun bar() = "foobar"
}
""".trimIndent()

assertThat(
NoTrailingSpacesRule().format(code)
).isEqualTo(codeExpected)
assertThat(
NoTrailingSpacesRule().lint(code)
).isEqualTo(
listOf(
LintError(1, 4, "no-trailing-spaces", "Trailing space(s)"),
LintError(2, 16, "no-trailing-spaces", "Trailing space(s)"),
LintError(3, 3, "no-trailing-spaces", "Trailing space(s)"),
LintError(6, 8, "no-trailing-spaces", "Trailing space(s)"),
LintError(7, 20, "no-trailing-spaces", "Trailing space(s)"),
LintError(8, 7, "no-trailing-spaces", "Trailing space(s)"),
)
)
}
}

0 comments on commit d234a5b

Please sign in to comment.