Skip to content

Commit

Permalink
Prevent exception in binary-expression-wrapping rule in case it is …
Browse files Browse the repository at this point in the history
…preceded on the same line by a block comment containing a newline character (#2607)

Function `leavesOnLine` calls function `getFirstLeafOnLineOrSelf` which finds the first leaf on the line by looking for newline character. A block comment containing a newline character resulted in considering that block comment to be the first element on the line, even in case it was preceded by other elements on the same line.

Due to this change, the `max-line-length` rule failed, which has been resolved by refactoring the calculation of the line length.

Closes #2601
  • Loading branch information
paul-dingemans authored Mar 17, 2024
1 parent 2b396bd commit a0763b8
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ private fun Sequence<ASTNode>.dropTrailingEolComment(): Sequence<ASTNode> =
}

internal fun ASTNode.getFirstLeafOnLineOrSelf() =
prevLeaf { it.textContains('\n') || it.prevLeaf() == null }
prevLeaf { (it.textContains('\n') && !it.isPartOfComment()) || it.prevLeaf() == null }
?: this

internal fun ASTNode.getLastLeafOnLineOrNull() = nextLeaf { it.textContains('\n') }?.prevLeaf()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,25 @@ class ASTNodeExtensionTest {
assertThat(actual).contains(code)
}

@Test
fun `Issue 2602 - Given that getFirstLeafOnLineOrSelf is called for a block comment containing a new line then do not throw a null pointer exception`() {
val code =
"""
val foo = /* some
comment */ "foo"
""".trimIndent()

val actual =
transformCodeToAST(code)
.lastChildLeafOrSelf()
.getFirstLeafOnLineOrSelf()
.text

// The newline (and indentation spaces) before the word "comment" inside the block comment is entirely ignored. As there are no
// whitespace nodes containing a newline, this piece of code is considered to be a oneliner starting with the word "val".
assertThat(actual).contains("val")
}

@Nested
inner class HasModifier {
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import com.pinterest.ktlint.rule.engine.core.api.isPartOf
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpace
import com.pinterest.ktlint.rule.engine.core.api.isWhiteSpaceWithNewline
import com.pinterest.ktlint.rule.engine.core.api.leavesOnLine
import com.pinterest.ktlint.rule.engine.core.api.lineLengthWithoutNewlinePrefix
import com.pinterest.ktlint.rule.engine.core.api.nextLeaf
import com.pinterest.ktlint.rule.engine.core.api.parent
import com.pinterest.ktlint.rule.engine.core.api.prevLeaf
Expand Down Expand Up @@ -102,25 +103,12 @@ public class MaxLineLengthRule :
}

private fun ASTNode.lineLength() =
leavesOnLine(excludeEolComment = false)
.sumOf {
when {
it.isWhiteSpaceWithNewline() -> {
it.text.substringAfterLast('\n').length
}

leavesOnLine(false)
.filterNot {
ignoreBackTickedIdentifier &&
it.elementType == IDENTIFIER &&
it.text.matches(BACKTICKED_IDENTIFIER_REGEX) &&
ignoreBackTickedIdentifier
-> {
0
}

else -> {
it.textLength
}
}
}
it.text.matches(BACKTICKED_IDENTIFIER_REGEX)
}.lineLengthWithoutNewlinePrefix()

private fun ASTNode.isPartOfRawMultiLineString() =
parent(STRING_TEMPLATE, strict = false)
Expand Down

0 comments on commit a0763b8

Please # to comment.