diff --git a/spec/06-expressions.md b/spec/06-expressions.md index d857a56219f7..1316da79db38 100644 --- a/spec/06-expressions.md +++ b/spec/06-expressions.md @@ -1,3 +1,4 @@ + --- title: Expressions layout: default diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index ae1f8b95c042..bfbdbd545d28 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -854,7 +854,12 @@ trait Scanners extends ScannersCommon { } else unclosedStringLit() } - private def unclosedStringLit(): Unit = syntaxError("unclosed string literal") + private def unclosedStringLit(seenEscapedQuoteInInterpolation: Boolean = false): Unit = { + val note = + if (seenEscapedQuoteInInterpolation) "; note that `\\\"` no longer closes single-quoted interpolated string literals since 2.13.6, you can use a triple-quoted string instead" + else "" + syntaxError(s"unclosed string literal$note") + } private def replaceUnicodeEscapesInTriple(): Unit = if(strVal != null) { @@ -890,7 +895,8 @@ trait Scanners extends ScannersCommon { } } - @tailrec private def getStringPart(multiLine: Boolean): Unit = { + // for interpolated strings + @tailrec private def getStringPart(multiLine: Boolean, seenEscapedQuote: Boolean = false): Unit = { def finishStringPart() = { setStrVal() token = STRINGPART @@ -904,7 +910,7 @@ trait Scanners extends ScannersCommon { setStrVal() token = STRINGLIT } else - getStringPart(multiLine) + getStringPart(multiLine, seenEscapedQuote) } else { nextChar() setStrVal() @@ -913,17 +919,18 @@ trait Scanners extends ScannersCommon { } else if (ch == '\\' && !multiLine) { putChar(ch) nextRawChar() - if (ch == '"' || ch == '\\') { + val q = ch == '"' + if (q || ch == '\\') { putChar(ch) nextRawChar() } - getStringPart(multiLine) + getStringPart(multiLine, seenEscapedQuote || q) } else if (ch == '$') { nextRawChar() if (ch == '$') { putChar(ch) nextRawChar() - getStringPart(multiLine) + getStringPart(multiLine, seenEscapedQuote) } else if (ch == '{') { finishStringPart() nextRawChar() @@ -953,13 +960,14 @@ trait Scanners extends ScannersCommon { if (isUnclosedLiteral) { if (multiLine) incompleteInputError("unclosed multi-line string literal") - else - unclosedStringLit() + else { + unclosedStringLit(seenEscapedQuote) + } } else { putChar(ch) nextRawChar() - getStringPart(multiLine) + getStringPart(multiLine, seenEscapedQuote) } } } diff --git a/test/files/neg/t6476.check b/test/files/neg/t6476.check index cffe5523813c..bf0c65efc6b8 100644 --- a/test/files/neg/t6476.check +++ b/test/files/neg/t6476.check @@ -1,4 +1,4 @@ -t6476.scala:8: error: unclosed string literal +t6476.scala:8: error: unclosed string literal; note that `\"` no longer closes single-quoted interpolated string literals since 2.13.6, you can use a triple-quoted string instead mimi"\" ^ 1 error