diff --git a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt index 27b779f99a..06ad017816 100644 --- a/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt +++ b/ktlint-ruleset-standard/src/main/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRule.kt @@ -6,7 +6,11 @@ import com.pinterest.ktlint.core.ast.ElementType.DOT import com.pinterest.ktlint.core.ast.ElementType.DOT_QUALIFIED_EXPRESSION import com.pinterest.ktlint.core.ast.ElementType.LITERAL_STRING_TEMPLATE_ENTRY import com.pinterest.ktlint.core.ast.ElementType.LONG_STRING_TEMPLATE_ENTRY +import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_END +import com.pinterest.ktlint.core.ast.ElementType.LONG_TEMPLATE_ENTRY_START +import com.pinterest.ktlint.core.ast.ElementType.REGULAR_STRING_PART import com.pinterest.ktlint.core.ast.ElementType.SUPER_EXPRESSION +import com.pinterest.ktlint.core.ast.children import org.jetbrains.kotlin.com.intellij.lang.ASTNode import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement import org.jetbrains.kotlin.psi.KtBlockStringTemplateEntry @@ -65,9 +69,19 @@ class StringTemplateRule : Rule("string-template") { ) { emit(node.treePrev.startOffset + 2, "Redundant curly braces", true) if (autoCorrect) { - // fixme: a proper way would be to downcast to SHORT_STRING_TEMPLATE_ENTRY - (node.firstChildNode as LeafPsiElement).rawReplaceWithText("$") // entry start - (node.lastChildNode as LeafPsiElement).rawReplaceWithText("") // entry end + val leftCurlyBraceNode = node.findChildByType(LONG_TEMPLATE_ENTRY_START) + val rightCurlyBraceNode = node.findChildByType(LONG_TEMPLATE_ENTRY_END) + if (node.children().count() == 3 && leftCurlyBraceNode != null && rightCurlyBraceNode != null) { + node.removeChild(leftCurlyBraceNode) + node.removeChild(rightCurlyBraceNode) + val remainingNode = node.firstChildNode + val newNode = if (remainingNode.elementType == DOT_QUALIFIED_EXPRESSION) { + LeafPsiElement(REGULAR_STRING_PART, "\$${remainingNode.text}") + } else { + LeafPsiElement(remainingNode.elementType, "\$${remainingNode.text}") + } + node.replaceChild(node.firstChildNode, newNode) + } } } } diff --git a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRuleTest.kt b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRuleTest.kt index dee53082c8..2849be6a9c 100644 --- a/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRuleTest.kt +++ b/ktlint-ruleset-standard/src/test/kotlin/com/pinterest/ktlint/ruleset/standard/StringTemplateRuleTest.kt @@ -2,6 +2,7 @@ package com.pinterest.ktlint.ruleset.standard import com.pinterest.ktlint.test.diffFileFormat import com.pinterest.ktlint.test.diffFileLint +import com.pinterest.ktlint.test.format import org.assertj.core.api.Assertions.assertThat import org.junit.Test @@ -21,4 +22,23 @@ class StringTemplateRuleTest { ) ).isEmpty() } + + @Test + fun testFormatIssue996() { + assertThat( + StringTemplateRule().format( + """ + fun getDrafts(val draftsIds: List) { + println("draftIds=[${'$'}{draftsIds.toString()}]") + } + """.trimIndent() + ) + ).isEqualTo( + """ + fun getDrafts(val draftsIds: List) { + println("draftIds=[${'$'}draftsIds]") + } + """.trimIndent() + ) + } } diff --git a/ktlint-ruleset-standard/src/test/resources/spec/string-template/format-expected.kt.spec b/ktlint-ruleset-standard/src/test/resources/spec/string-template/format-expected.kt.spec index 9a706d1b8f..bb9884d30d 100644 --- a/ktlint-ruleset-standard/src/test/resources/spec/string-template/format-expected.kt.spec +++ b/ktlint-ruleset-standard/src/test/resources/spec/string-template/format-expected.kt.spec @@ -2,6 +2,7 @@ fun main() { val x = "${String::class}" println("$x.hello") println("$x.hello") + println("$x") println("${x}hello") println("${x.length}.hello") println("$x.hello") diff --git a/ktlint-ruleset-standard/src/test/resources/spec/string-template/format.kt.spec b/ktlint-ruleset-standard/src/test/resources/spec/string-template/format.kt.spec index 867175c316..02f75d55a7 100644 --- a/ktlint-ruleset-standard/src/test/resources/spec/string-template/format.kt.spec +++ b/ktlint-ruleset-standard/src/test/resources/spec/string-template/format.kt.spec @@ -2,6 +2,7 @@ fun main() { val x = "${String::class.toString()}" println("${x}.hello") println("${x.toString()}.hello") + println("${x.toString()}") println("${x}hello") println("${x.length}.hello") println("$x.hello") diff --git a/ktlint-ruleset-standard/src/test/resources/spec/string-template/lint.kt.spec b/ktlint-ruleset-standard/src/test/resources/spec/string-template/lint.kt.spec index 7e6cb5e352..a38f9780af 100644 --- a/ktlint-ruleset-standard/src/test/resources/spec/string-template/lint.kt.spec +++ b/ktlint-ruleset-standard/src/test/resources/spec/string-template/lint.kt.spec @@ -1,5 +1,6 @@ fun main() { println("${String::class.toString()}") + println("${hello.toString()}") println("""${Int::class.toString()}""") println("$s0") println("""$s1""") @@ -62,9 +63,10 @@ class F { // expect // 2:29:Redundant "toString()" call in string template -// 3:28:Redundant "toString()" call in string template -// 6:15:Redundant curly braces +// 3:21:Redundant "toString()" call in string template +// 4:28:Redundant "toString()" call in string template // 7:15:Redundant curly braces -// 28:79:Redundant "toString()" call in string template -// 45:20:Redundant curly braces -// 55:19:Redundant curly braces +// 8:15:Redundant curly braces +// 29:79:Redundant "toString()" call in string template +// 46:20:Redundant curly braces +// 56:19:Redundant curly braces