From 3472532b6e413ac302c3f63fc75a32db0169f54c Mon Sep 17 00:00:00 2001 From: paul-dingemans Date: Wed, 27 Apr 2022 19:55:43 +0200 Subject: [PATCH] Fix false positive FunctionTypeReferenceSpacingRule in anonymous function (#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 --- CHANGELOG.md | 1 + .../FunctionTypeReferenceSpacingRule.kt | 16 ++++----- .../FunctionTypeReferenceSpacingRuleTest.kt | 36 +++++++++++++++++++ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cbd71949a..de4f960e9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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`. 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 fbcbe0e93d..bbc360827f 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 @@ -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) + } }