summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/reflect/FormatInterpolator.scala51
-rw-r--r--src/library/scala/StringContext.scala11
-rw-r--r--test/files/neg/t8266-invalid-interp.check10
-rw-r--r--test/files/neg/t8266-invalid-interp.scala9
-rw-r--r--test/files/run/t8266-octal-interp.check30
-rw-r--r--test/files/run/t8266-octal-interp.flags1
-rw-r--r--test/files/run/t8266-octal-interp.scala16
7 files changed, 124 insertions, 4 deletions
diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala
index d5e674ebae..0258002850 100644
--- a/src/compiler/scala/tools/reflect/FormatInterpolator.scala
+++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala
@@ -81,7 +81,56 @@ abstract class FormatInterpolator {
case Literal(Constant(x: String)) => x
case _ => throw new IllegalArgumentException("internal error: argument parts must be a list of string literals")
}
- val s = StringContext.treatEscapes(s0)
+ def escapeHatch: PartialFunction[Throwable, String] = {
+ // trailing backslash, octal escape, or other
+ case e: StringContext.InvalidEscapeException =>
+ def errPoint = part.pos withPoint (part.pos.point + e.index)
+ def octalOf(c: Char) = Character.digit(c, 8)
+ def alt = {
+ def altOf(i: Int) = i match {
+ case '\b' => "\\b"
+ case '\t' => "\\t"
+ case '\n' => "\\n"
+ case '\f' => "\\f"
+ case '\r' => "\\r"
+ case '\"' => "\\u0022" // $" in future
+ case '\'' => "'"
+ case '\\' => """\\"""
+ case x => "\\u%04x" format x
+ }
+ val suggest = {
+ val r = "([0-7]{1,3}).*".r
+ (s0 drop e.index + 1) match {
+ case r(n) => altOf { (0 /: n) { case (a, o) => (8 * a) + (o - '0') } }
+ case _ => ""
+ }
+ }
+ val txt =
+ if ("" == suggest) ""
+ else s", use $suggest instead"
+ txt
+ }
+ def badOctal = {
+ def msg(what: String) = s"Octal escape literals are $what$alt."
+ if (settings.future) {
+ c.error(errPoint, msg("unsupported"))
+ s0
+ } else {
+ c.enclosingUnit.deprecationWarning(errPoint, msg("deprecated"))
+ try StringContext.treatEscapes(s0) catch escapeHatch
+ }
+ }
+ if (e.index == s0.length - 1) {
+ c.error(errPoint, """Trailing '\' escapes nothing.""")
+ s0
+ } else if (octalOf(s0(e.index + 1)) >= 0) {
+ badOctal
+ } else {
+ c.error(errPoint, e.getMessage)
+ s0
+ }
+ }
+ val s = try StringContext.processEscapes(s0) catch escapeHatch
val ms = fpat findAllMatchIn s
def errorLeading(op: Conversion) = op.errorAt(Spec, s"conversions must follow a splice; ${Conversion.literalHelp}")
diff --git a/src/library/scala/StringContext.scala b/src/library/scala/StringContext.scala
index 2d79452c5d..cd928a2b61 100644
--- a/src/library/scala/StringContext.scala
+++ b/src/library/scala/StringContext.scala
@@ -172,8 +172,8 @@ object StringContext {
* @param str The offending string
* @param idx The index of the offending backslash character in `str`.
*/
- class InvalidEscapeException(str: String, idx: Int)
- extends IllegalArgumentException("invalid escape character at index "+idx+" in \""+str+"\"")
+ class InvalidEscapeException(str: String, @deprecatedName('idx) val index: Int)
+ extends IllegalArgumentException("invalid escape character at index "+index+" in \""+str+"\"")
/** Expands standard Scala escape sequences in a string.
* Escape sequences are:
@@ -184,7 +184,11 @@ object StringContext {
* @param str A string that may contain escape sequences
* @return The string with all escape sequences expanded.
*/
- def treatEscapes(str: String): String = {
+ def treatEscapes(str: String): String = treatEscapes0(str, strict = false)
+
+ def processEscapes(str: String): String = treatEscapes0(str, strict = true)
+
+ private def treatEscapes0(str: String, strict: Boolean): String = {
lazy val bldr = new java.lang.StringBuilder
val len = str.length
var start = 0
@@ -201,6 +205,7 @@ object StringContext {
idx += 1
if (idx >= len) throw new InvalidEscapeException(str, cur)
if ('0' <= str(idx) && str(idx) <= '7') {
+ if (strict) throw new InvalidEscapeException(str, cur)
val leadch = str(idx)
var oct = leadch - '0'
idx += 1
diff --git a/test/files/neg/t8266-invalid-interp.check b/test/files/neg/t8266-invalid-interp.check
new file mode 100644
index 0000000000..70dd4081b0
--- /dev/null
+++ b/test/files/neg/t8266-invalid-interp.check
@@ -0,0 +1,10 @@
+t8266-invalid-interp.scala:4: error: Trailing '\' escapes nothing.
+ f"a\",
+ ^
+t8266-invalid-interp.scala:5: error: invalid escape character at index 1 in "a\xc"
+ f"a\xc",
+ ^
+t8266-invalid-interp.scala:7: error: invalid escape character at index 1 in "a\vc"
+ f"a\vc"
+ ^
+three errors found
diff --git a/test/files/neg/t8266-invalid-interp.scala b/test/files/neg/t8266-invalid-interp.scala
new file mode 100644
index 0000000000..4b26546880
--- /dev/null
+++ b/test/files/neg/t8266-invalid-interp.scala
@@ -0,0 +1,9 @@
+
+trait X {
+ def f = Seq(
+ f"a\",
+ f"a\xc",
+ // following could suggest \u000b for vertical tab, similar for \a alert
+ f"a\vc"
+ )
+}
diff --git a/test/files/run/t8266-octal-interp.check b/test/files/run/t8266-octal-interp.check
new file mode 100644
index 0000000000..6e9454119b
--- /dev/null
+++ b/test/files/run/t8266-octal-interp.check
@@ -0,0 +1,30 @@
+t8266-octal-interp.scala:4: warning: Octal escape literals are deprecated, use \b instead.
+ f"a\10c",
+ ^
+t8266-octal-interp.scala:5: warning: Octal escape literals are deprecated, use \t instead.
+ f"a\11c",
+ ^
+t8266-octal-interp.scala:6: warning: Octal escape literals are deprecated, use \n instead.
+ f"a\12c",
+ ^
+t8266-octal-interp.scala:7: warning: Octal escape literals are deprecated, use \r instead.
+ f"a\15c",
+ ^
+t8266-octal-interp.scala:8: warning: Octal escape literals are deprecated, use \u0022 instead.
+ f"a\42c",
+ ^
+t8266-octal-interp.scala:9: warning: Octal escape literals are deprecated, use \\ instead.
+ f"a\134c",
+ ^
+t8266-octal-interp.scala:10: warning: Octal escape literals are deprecated, use \u0069 instead.
+ f"a\15151515c"
+ ^
+ac
+a c
+a
+c
+a
+c
+a"c
+a\c
+ai51515c
diff --git a/test/files/run/t8266-octal-interp.flags b/test/files/run/t8266-octal-interp.flags
new file mode 100644
index 0000000000..dcc59ebe32
--- /dev/null
+++ b/test/files/run/t8266-octal-interp.flags
@@ -0,0 +1 @@
+-deprecation
diff --git a/test/files/run/t8266-octal-interp.scala b/test/files/run/t8266-octal-interp.scala
new file mode 100644
index 0000000000..f85ae0367d
--- /dev/null
+++ b/test/files/run/t8266-octal-interp.scala
@@ -0,0 +1,16 @@
+
+trait X {
+ def f = Seq(
+ f"a\10c",
+ f"a\11c",
+ f"a\12c",
+ f"a\15c",
+ f"a\42c",
+ f"a\134c",
+ f"a\15151515c"
+ )
+}
+
+object Test extends App with X {
+ f foreach println
+}