diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 7e5eb3993f9e..bfac7e922389 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -1053,6 +1053,7 @@ object Scanners { getRawStringLit() } + // for interpolated strings @annotation.tailrec private def getStringPart(multiLine: Boolean): Unit = if (ch == '"') if (multiLine) { @@ -1069,6 +1070,14 @@ object Scanners { setStrVal() token = STRINGLIT } + else if (ch == '\\' && !multiLine) { + putChar(ch) + nextRawChar() + if (ch == '"' || ch == '\\') + putChar(ch) + nextRawChar() + getStringPart(multiLine) + } else if (ch == '$') { nextRawChar() if (ch == '$' || ch == '"') { diff --git a/docs/docs/internals/syntax-3.1.md b/docs/docs/internals/syntax-3.1.md index 5a3e7b912161..8a7eea52a09f 100644 --- a/docs/docs/internals/syntax-3.1.md +++ b/docs/docs/internals/syntax-3.1.md @@ -70,8 +70,10 @@ stringElement ::= printableChar \ (‘"’ | ‘\’) | charEscapeSeq multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} processedStringLiteral - ::= alphaid ‘"’ {printableChar \ (‘"’ | ‘$’) | escape} ‘"’ + ::= alphaid ‘"’ {[‘\’] processedStringPart | ‘\\’ | ‘\"’} ‘"’ | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ +processedStringPart + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape escape ::= ‘$$’ | ‘$’ letter { letter | digit } | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index a7d57858e1b8..d58ee183d530 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -69,8 +69,10 @@ stringElement ::= printableChar \ (‘"’ | ‘\’) | charEscapeSeq multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} processedStringLiteral - ::= alphaid ‘"’ {printableChar \ (‘"’ | ‘$’) | escape} ‘"’ + ::= alphaid ‘"’ {[‘\’] processedStringPart | ‘\\’ | ‘\"’} ‘"’ | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ +processedStringPart + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape escape ::= ‘$$’ | ‘$’ letter { letter | digit } | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ diff --git a/docs/docs/reference/syntax.md b/docs/docs/reference/syntax.md index 93f69a7f5234..c3915a1676fc 100644 --- a/docs/docs/reference/syntax.md +++ b/docs/docs/reference/syntax.md @@ -69,8 +69,10 @@ stringElement ::= printableChar \ (‘"’ | ‘\’) | charEscapeSeq multiLineChars ::= {[‘"’] [‘"’] char \ ‘"’} {‘"’} processedStringLiteral - ::= alphaid ‘"’ {printableChar \ (‘"’ | ‘$’) | escape} ‘"’ + ::= alphaid ‘"’ {[‘\’] processedStringPart | ‘\\’ | ‘\"’} ‘"’ | alphaid ‘"""’ {[‘"’] [‘"’] char \ (‘"’ | ‘$’) | escape} {‘"’} ‘"""’ +processedStringPart + ::= printableChar \ (‘"’ | ‘$’ | ‘\’) | escape escape ::= ‘$$’ | ‘$’ letter { letter | digit } | ‘{’ Block [‘;’ whiteSpace stringFormat whiteSpace] ‘}’ diff --git a/tests/neg/t6476.scala b/tests/neg/t6476.scala new file mode 100644 index 000000000000..9d1415f55dd3 --- /dev/null +++ b/tests/neg/t6476.scala @@ -0,0 +1,9 @@ +// only the last one doesn't parse +class C { + s"""\ """ + s"""\\""" + s"""\""" + s"\ " + s"\\" + s"\" // error +} // error (should not be one) diff --git a/tests/neg/t6476b.scala b/tests/neg/t6476b.scala new file mode 100644 index 000000000000..785bb727ba6f --- /dev/null +++ b/tests/neg/t6476b.scala @@ -0,0 +1,8 @@ +class C { + val sa = s"""\""" // error: invalid escape + val sb = s"""\\""" + val sc = s"""\ """ // error: invalid escape + val ra = raw"""\""" + val rb = raw"""\\""" + val rc = raw"""\ """ +} diff --git a/tests/run/t6476.check b/tests/run/t6476.check new file mode 100644 index 000000000000..69bf68978177 --- /dev/null +++ b/tests/run/t6476.check @@ -0,0 +1,13 @@ +"Hello", Alice +"Hello", Alice +\"Hello\", Alice +\"Hello\", Alice +\"Hello\", Alice +\"Hello\", Alice +\TILT\ +\\TILT\\ +\\TILT\\ +\TILT\ +\\TILT\\ +\\TILT\\ +\TILT\ diff --git a/tests/run/t6476.scala b/tests/run/t6476.scala new file mode 100644 index 000000000000..a04645065a2a --- /dev/null +++ b/tests/run/t6476.scala @@ -0,0 +1,23 @@ +object Test { + def main(args: Array[String]): Unit = { + val person = "Alice" + println(s"\"Hello\", $person") + println(s"""\"Hello\", $person""") + + println(f"\"Hello\", $person") + println(f"""\"Hello\", $person""") + + println(raw"\"Hello\", $person") + println(raw"""\"Hello\", $person""") + + println(s"\\TILT\\") + println(f"\\TILT\\") + println(raw"\\TILT\\") + + println(s"""\\TILT\\""") + println(f"""\\TILT\\""") + println(raw"""\\TILT\\""") + + println(raw"""\TILT\""") + } +}