From 12eb6881c64a02d1cd27cd76b0f65c3353364497 Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Sat, 2 Apr 2022 16:28:57 +0200 Subject: [PATCH] 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 --- .../FunctionTypeReferenceSpacingRule.kt | 16 +++--- .../FunctionTypeReferenceSpacingRuleTest.kt | 50 +++++++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionTypeReferenceSpacingRule.kt b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionTypeReferenceSpacingRule.kt index 4955b11fa8..ce7bacb00b 100644 --- a/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionTypeReferenceSpacingRule.kt +++ b/ktlint-ruleset-experimental/src/main/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionTypeReferenceSpacingRule.kt @@ -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 @@ -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 } @@ -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 diff --git a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionTypeReferenceSpacingRuleTest.kt b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionTypeReferenceSpacingRuleTest.kt index 70a78a5541..e5009bddae 100644 --- a/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionTypeReferenceSpacingRuleTest.kt +++ b/ktlint-ruleset-experimental/src/test/kotlin/com/pinterest/ktlint/ruleset/experimental/FunctionTypeReferenceSpacingRuleTest.kt @@ -92,4 +92,54 @@ class FunctionTypeReferenceSpacingRuleTest { FunctionTypeReferenceSpacingRule().format(code) ).isEqualTo(code) } + + @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() + Assertions.assertThat( + FunctionTypeReferenceSpacingRule().lint(code) + ).isEmpty() + Assertions.assertThat( + FunctionTypeReferenceSpacingRule().format(code) + ).isEqualTo(code) + } + + @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() + Assertions.assertThat( + FunctionTypeReferenceSpacingRule().lint(code) + ).isEmpty() + Assertions.assertThat( + FunctionTypeReferenceSpacingRule().format(code) + ).isEqualTo(code) + } + + @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() + Assertions.assertThat( + FunctionTypeReferenceSpacingRule().lint(code) + ).containsExactly( + LintError(1, 36, "function-type-reference-spacing", "Unexpected whitespace"), + LintError(1, 38, "function-type-reference-spacing", "Unexpected whitespace"), + LintError(1, 40, "function-type-reference-spacing", "Unexpected whitespace") + ) + Assertions.assertThat( + FunctionTypeReferenceSpacingRule().format(code) + ).isEqualTo(formattedCode) + } }