Repository: square/kotlinpoet
Issue: %P incorrectly escapes string literals appearing inside templates

Causes https://github.com/square/sqldelight/issues/1090

Formatting the following string with `%P`:
```
SELECT * FROM socialFeedItem WHERE message IS NOT NULL AND userId ${ if (userId == null) "IS" else "=" } ?1 ORDER BY datetime(creation_time) DESC
```
produces:
```
SELECT * FROM socialFeedItem WHERE message IS NOT NULL AND userId ${ if (userId == null) \\"IS\\" else \\"=\\" } ?1 ORDER BY datetime(creation_time) DESC
```
where `IS` and `=` should not be escaped since they appear inside a `${}` block.

Changed files:
src/test/java/com/squareup/kotlinpoet/StringsTest.kt, src/main/java/com/squareup/kotlinpoet/Util.kt

diff --git a/src/main/java/com/squareup/kotlinpoet/Util.kt b/src/main/java/com/squareup/kotlinpoet/Util.kt
index ae8ce252..dd1023dd 100644
--- a/src/main/java/com/squareup/kotlinpoet/Util.kt
+++ b/src/main/java/com/squareup/kotlinpoet/Util.kt
@@ -111,7 +111,9 @@ internal fun stringLiteralWithQuotes(
     return result.toString()
   } else {
     val result = StringBuilder(value.length + 32)
-    result.append('"')
+    // using pre-formatted strings allows us to get away with not escaping symbols that would
+    // normally require escaping, e.g. "foo ${"bar"} baz"
+    if (escapeDollarSign) result.append('"') else result.append("\\"\\"\\"")
     for (i in 0 until value.length) {
       val c = value[i]
       // Trivial case: single quote must not be escaped.
@@ -120,7 +122,7 @@ internal fun stringLiteralWithQuotes(
         continue
       }
       // Trivial case: double quotes must be escaped.
-      if (c == '\\"') {
+      if (c == '\\"' && escapeDollarSign) {
         result.append("\\\\\\"")
         continue
       }
@@ -133,7 +135,7 @@ internal fun stringLiteralWithQuotes(
       result.append(characterLiteralWithoutSingleQuotes(c))
       // Need to append indent after linefeed?
     }
-    result.append('"')
+    if (escapeDollarSign) result.append('"') else result.append("\\"\\"\\"")
     return result.toString()
   }
 }
diff --git a/src/test/java/com/squareup/kotlinpoet/StringsTest.kt b/src/test/java/com/squareup/kotlinpoet/StringsTest.kt
index e9a6ecc7..8a0cdeda 100644
--- a/src/test/java/com/squareup/kotlinpoet/StringsTest.kt
+++ b/src/test/java/com/squareup/kotlinpoet/StringsTest.kt
@@ -46,7 +46,7 @@ class StringsTest {
         .addStatement("return %P", stringWithTemplate)
         .build()
     assertThat(funSpec.toString())
-        .isEqualTo("fun getString() = \\"\\$annoyingUser is annoying.\\"\\n")
+        .isEqualTo("fun getString() = \\"\\"\\"\\$annoyingUser is annoying.\\"\\"\\"\\n")
   }
 
   @Test fun multilineStringTemplate() {
@@ -59,4 +59,14 @@ class StringsTest {
         "|\\$annoyingUser is annoying.\\n" +
         "\\"\\"\\".trimMargin()\\n")
   }
+
+  // https://github.com/square/kotlinpoet/issues/572
+  @Test fun templateStringWithStringLiteralReference() {
+    val string = "SELECT * FROM socialFeedItem WHERE message IS NOT NULL AND userId \\${ if (userId == null) \\"IS\\" else \\"=\\" } ?1 ORDER BY datetime(creation_time) DESC"
+    val funSpec = FunSpec.builder("getString")
+        .addStatement("return %P", string)
+        .build()
+    assertThat(funSpec.toString())
+        .isEqualTo("fun getString() = \\"\\"\\"SELECT * FROM socialFeedItem WHERE message IS NOT NULL AND userId \\${ if (userId == null) \\"IS\\" else \\"=\\" } ?1 ORDER BY datetime(creation_time) DESC\\"\\"\\"\\n")
+  }
 }