diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule.kt index b37377246b..4202876c69 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRule.kt @@ -12,6 +12,7 @@ import com.pinterest.ktlint.rule.engine.core.api.nextLeaf import com.pinterest.ktlint.ruleset.standard.StandardRule import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement +import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl /** * Lints and formats the spacing after the fun keyword @@ -26,14 +27,30 @@ public class FunKeywordSpacingRule : StandardRule("fun-keyword-spacing") { node .takeIf { it.elementType == FUN_KEYWORD } ?.nextLeaf(includeEmpty = true) - ?.takeIf { it.elementType == ElementType.WHITE_SPACE && it.text != " " } - ?.let { whiteSpaceAfterFunKeyword -> - emit( - whiteSpaceAfterFunKeyword.startOffset, - "Single space expected after the fun keyword", - true, - ).ifAutocorrectAllowed { - (whiteSpaceAfterFunKeyword as LeafPsiElement).rawReplaceWithText(" ") + ?.let { leafAfterFunKeyword -> + when { + leafAfterFunKeyword.elementType == ElementType.WHITE_SPACE && leafAfterFunKeyword.text != " " -> { + emit( + leafAfterFunKeyword.startOffset, + "Single space expected after the fun keyword", + true, + ).ifAutocorrectAllowed { + (leafAfterFunKeyword as LeafPsiElement).rawReplaceWithText(" ") + } + } + + leafAfterFunKeyword.elementType == ElementType.IDENTIFIER -> { + // Identifier can only be adjacent to fun keyword in case the identifier is wrapped between backticks: + // fun`foo`() {} + emit(leafAfterFunKeyword.startOffset, "Space expected between the fun keyword and backtick", true) + .ifAutocorrectAllowed { + leafAfterFunKeyword.treeParent.addChild(PsiWhiteSpaceImpl(" "), leafAfterFunKeyword) + } + } + + else -> { + Unit + } } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRuleTest.kt index 54d2f75001..47f5b6a5b4 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/rules/FunKeywordSpacingRuleTest.kt @@ -36,4 +36,19 @@ class FunKeywordSpacingRuleTest { .hasLintViolation(1, 4, "Single space expected after the fun keyword") .isFormattedAs(formattedCode) } + + @Test + fun `Issue 2879 - Given a function with name between backticks then the fun keyword and name should be separated by a space`() { + val code = + """ + fun`foo or bar`() = "foo" + """.trimIndent() + val formattedCode = + """ + fun `foo or bar`() = "foo" + """.trimIndent() + funKeywordSpacingRuleAssertThat(code) + .hasLintViolation(1, 4, "Space expected between the fun keyword and backtick") + .isFormattedAs(formattedCode) + } }