Skip to content

Commit

Permalink
Fix false positive FunctionTypeReferenceSpacingRule in anonymous func…
Browse files Browse the repository at this point in the history
…tion (#1443)

* Fix false positive FunctionTypeReferenceSpacingRule in anonymous function

Rule should not depend on the existence of the function identifier
as it is also possible to define anonymous functions. Checking of
anonymous and named function should be identical.

Closes #1440
  • Loading branch information
paul-dingemans authored Apr 27, 2022
1 parent c82be37 commit 3472532
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ An AssertJ style API for testing KtLint rules ([#1444](https://github.com/pinter
- Add experimental rule for consistent spacing before the start of the function body (`function-start-of-body-spacing`) ([#1341](https://github.com/pinterest/ktlint/issues/1341))

### Fixed
- Fix check of spacing in the receiver type of an anonymous function ([#1440](https://github.com/pinterest/ktlint/issues/1440))

### Changed
* Set Kotlin development version to `1.6.21` and Kotlin version to `1.6.21`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package com.pinterest.ktlint.ruleset.experimental

import com.pinterest.ktlint.core.Rule
import com.pinterest.ktlint.core.ast.ElementType.FUN
import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
import com.pinterest.ktlint.core.ast.ElementType.NULLABLE_TYPE
import com.pinterest.ktlint.core.ast.ElementType.TYPE_REFERENCE
import com.pinterest.ktlint.core.ast.ElementType.VALUE_PARAMETER_LIST
import com.pinterest.ktlint.core.ast.ElementType.WHITE_SPACE
import com.pinterest.ktlint.core.ast.nextSibling
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
Expand All @@ -17,26 +17,26 @@ public class FunctionTypeReferenceSpacingRule : Rule("function-type-reference-sp
) {
if (node.elementType == FUN) {
node
.findTypeReferenceBeforeFunctionIdentifier()
.findFunctionReceiverTypeReference()
?.let { typeReference ->
typeReference
.firstChildNode
.takeIf { it.elementType == NULLABLE_TYPE }
?.let { nullableTypeElement ->
visitNodesUntilIdentifier(nullableTypeElement.firstChildNode, emit, autoCorrect)
visitNodesUntilStartOfValueParameterList(nullableTypeElement.firstChildNode, emit, autoCorrect)
}

if (typeReference.elementType != NULLABLE_TYPE) {
visitNodesUntilIdentifier(typeReference, emit, autoCorrect)
visitNodesUntilStartOfValueParameterList(typeReference, emit, autoCorrect)
}
}
}
}

private fun ASTNode.findTypeReferenceBeforeFunctionIdentifier(): ASTNode? {
private fun ASTNode.findFunctionReceiverTypeReference(): ASTNode? {
require(elementType == FUN)
var currentNode: ASTNode? = firstChildNode
while (currentNode != null && currentNode.elementType != IDENTIFIER) {
while (currentNode != null && currentNode.elementType != VALUE_PARAMETER_LIST) {
if (currentNode.elementType == TYPE_REFERENCE) {
return currentNode
}
Expand All @@ -45,13 +45,13 @@ public class FunctionTypeReferenceSpacingRule : Rule("function-type-reference-sp
return null
}

private fun visitNodesUntilIdentifier(
private fun visitNodesUntilStartOfValueParameterList(
node: ASTNode,
emit: (offset: Int, errorMessage: String, canBeAutoCorrected: Boolean) -> Unit,
autoCorrect: Boolean
) {
var currentNode: ASTNode? = node
while (currentNode != null && currentNode.elementType != IDENTIFIER) {
while (currentNode != null && currentNode.elementType != VALUE_PARAMETER_LIST) {
val nextNode = currentNode.nextSibling { true }
removeIfNonEmptyWhiteSpace(currentNode, emit, autoCorrect)
currentNode = nextNode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,40 @@ class FunctionTypeReferenceSpacingRuleTest {
""".trimIndent()
functionTypeReferenceSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Issue 1440 - Given an anonymous function without receiver type then do not reformat`() {
val code =
"""
val anonymousFunction = fun(foo: Boolean): String? = if (foo) "Test string" else null
""".trimIndent()
functionTypeReferenceSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given an anonymous function with receiver type then do not reformat`() {
val code =
"""
val anonymousFunction = fun Boolean.(): String? = if (this) "Test string" else null
""".trimIndent()
functionTypeReferenceSpacingRuleAssertThat(code).hasNoLintViolations()
}

@Test
fun `Given an anonymous function with receiver type followed by an unexpected space then do reformat`() {
val code =
"""
val anonymousFunction = fun Boolean ? . (): String? = this?.let { "Test string" }
""".trimIndent()
val formattedCode =
"""
val anonymousFunction = fun Boolean?.(): String? = this?.let { "Test string" }
""".trimIndent()
functionTypeReferenceSpacingRuleAssertThat(code)
.hasLintViolations(
LintViolation(1, 36, "Unexpected whitespace"),
LintViolation(1, 38, "Unexpected whitespace"),
LintViolation(1, 40, "Unexpected whitespace")
).isFormattedAs(formattedCode)
}
}

0 comments on commit 3472532

Please sign in to comment.