Skip to content

Commit

Permalink
SI-6476 Accept backslash at end of interpolation
Browse files Browse the repository at this point in the history
Everyone wants to say `raw"\hello, world.\"`.

Parser accepts arbitrary escaped quotes optionally followed by
a quote. That is, an escaped quote is deemed to terminate the
interpolated string if it is not followed by another quote on
the same line.
  • Loading branch information
som-snytt committed Jun 26, 2015
1 parent 5d0b194 commit 195d369
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 14 deletions.
21 changes: 20 additions & 1 deletion src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
Expand Up @@ -718,6 +718,14 @@ trait Scanners extends ScannersCommon {
next.lastOffset = charOffset - 1
next.offset = charOffset - 1
}
def isLastQuoteOnLine: Boolean = {
val lookahead = lookaheadReader
var c: Char = 0
do {
c = lookahead.getc()
} while (c != '"' && c != LF && c != SU)
(c != '"')
}
@tailrec def loop(escaped: Boolean): Unit = ch match {
case '\\' if !multiLine =>
putChar(ch)
Expand All @@ -730,7 +738,18 @@ trait Scanners extends ScannersCommon {
token = STRINGLIT
} else
loop(escaped = false)
case '"' | '$' if escaped =>
case '"' if escaped =>
// at \", unescape and finish if this is the last quote on the line
if (!multiLine && isLastQuoteOnLine) {
nextChar()
setStrVal()
token = STRINGLIT
} else {
putChar(ch)
nextRawChar()
loop(escaped = false)
}
case '$' if escaped =>
putChar(ch)
nextRawChar()
loop(escaped = false)
Expand Down
19 changes: 8 additions & 11 deletions test/files/neg/t5510.check
@@ -1,25 +1,22 @@
t5510.scala:2: error: unclosed string literal (\" is an escaped quote, not the end of string)
val sa = s"x\"y
^
t5510.scala:3: error: unclosed string literal (\" is an escaped quote, not the end of string)
t5510.scala:5: error: unclosed string literal (\" is an escaped quote, not the end of string)
val sb = "x\"y
^
t5510.scala:4: error: unclosed string literal
t5510.scala:6: error: unclosed string literal
val s1 = s"xxx
^
t5510.scala:5: error: unclosed string literal
t5510.scala:7: error: unclosed string literal
val s2 = s"xxx $x
^
t5510.scala:6: error: unclosed string literal
t5510.scala:8: error: unclosed string literal
val s3 = s"xxx $$
^
t5510.scala:7: error: unclosed string literal
t5510.scala:9: error: unclosed string literal
val s4 = ""s"
^
t5510.scala:8: error: unclosed multi-line string literal
t5510.scala:10: error: unclosed multi-line string literal
val s5 = ""s""" $s1 $s2 s"
^
t5510.scala:9: error: unclosed multi-line string literal
t5510.scala:11: error: unclosed multi-line string literal
}
^
8 errors found
7 errors found
6 changes: 4 additions & 2 deletions test/files/neg/t5510.scala
@@ -1,5 +1,7 @@
object Test {
val sa = s"x\"y
trait TypeErrors {
val sa = s"x\"y // see t6476.scala
}
trait ParseErrors {
val sb = "x\"y
val s1 = s"xxx
val s2 = s"xxx $x
Expand Down
4 changes: 4 additions & 0 deletions test/files/neg/t6476.check
@@ -0,0 +1,4 @@
t6476.scala:2: error: value y is not a member of String
val sa = s"x\"y
^
one error found
3 changes: 3 additions & 0 deletions test/files/neg/t6476.scala
@@ -0,0 +1,3 @@
trait TypeErrors {
val sa = s"x\"y
}
3 changes: 3 additions & 0 deletions test/junit/scala/StringContextTest.scala
Expand Up @@ -85,6 +85,9 @@ class StringContextTest {
val i = 42
assertEquals("""Forty-two is "42"""", s"Forty-two is \"42\"")
assertEquals("""dir\""", s"dir\\")
assertEquals("""dir\""", raw"dir\")
assertEquals("""1 \ 42""", raw"1 \ $i")
assertEquals("""1 \ 42 \""", raw"1 \ $i \")
assertEquals("""\\""", s"\\\\")
// colon separator, in case we like to escape dollar someday
assertEquals("""\\\:42""", raw"\\\:$i")
Expand Down

0 comments on commit 195d369

Please sign in to comment.