Skip to content

Commit

Permalink
improve error messages
Browse files Browse the repository at this point in the history
  • Loading branch information
Martijn Hoekstra committed Aug 9, 2019
1 parent 2426812 commit 711a638
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 39 deletions.
3 changes: 2 additions & 1 deletion build.sbt
Expand Up @@ -94,7 +94,8 @@ val mimaFilterSettings = Seq(
ProblemFilters.exclude[MissingClassProblem]("scala.reflect.runtime.JavaMirrors$JavaMirror$typeTagCache$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.api.TypeTags.TypeTagImpl"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.reflect.api.Universe.TypeTagImpl"),
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.StringContext.processUnicode")
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.StringContext.processUnicode"),
ProblemFilters.exclude[MissingClassProblem]("scala.StringContext$InvalidUnicodeEscapeException")
),
)

Expand Down
59 changes: 37 additions & 22 deletions src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
Expand Up @@ -760,21 +760,28 @@ trait Scanners extends ScannersCommon {

private def unclosedStringLit(): Unit = syntaxError("unclosed string literal")

private def replaceUnicodeEscapes(warn: Boolean): Unit =
private def replaceUnicodeEscapesInTriple(): Unit =
if(strVal != null) {
val replaced = StringContext.processUnicode(strVal)
if(warn && replaced != strVal) {
deprecationWarning("Unicode escapes in triple quoted strings and raw interpolations are deprecated, use the literal character instead" , since="2.13.1")
try {
val replaced = StringContext.processUnicode(strVal)
if(replaced != strVal) {
val diffPosition = replaced.zip(strVal).zipWithIndex.collectFirst{ case ((r, o), i) if r != o => i}.getOrElse(replaced.length - 1)
deprecationWarning(offset + 3 + diffPosition, "Unicode escapes in triple quoted strings are deprecated, use the literal character instead", since="2.13.1")
}
strVal = replaced
} catch {
case ue: StringContext.InvalidUnicodeEscapeException => {
syntaxError(offset + ue.index, ue.getMessage())
}
}
strVal = replaced
}

@tailrec private def getRawStringLit(): Unit = {
if (ch == '\"') {
nextRawChar()
if (isTripleQuote()) {
setStrVal()
if(!currentRun.isScala214) replaceUnicodeEscapes(true)
if(!currentRun.isScala214) replaceUnicodeEscapesInTriple()
token = STRINGLIT
} else
getRawStringLit()
Expand Down Expand Up @@ -901,41 +908,49 @@ trait Scanners extends ScannersCommon {
syntaxError(start, s"octal escape literals are unsupported: use $alt instead")
putChar(oct.toChar)
} else {
ch match {
case 'b' => putChar('\b')
case 't' => putChar('\t')
case 'n' => putChar('\n')
case 'f' => putChar('\f')
case 'r' => putChar('\r')
case '\"' => putChar('\"')
case '\'' => putChar('\'')
case '\\' => putChar('\\')
case 'u' => getUEscape()
case _ => invalidEscape()
if (ch == 'u') {
if (getUEscape()) nextChar()
}
else {
ch match {
case 'b' => putChar('\b')
case 't' => putChar('\t')
case 'n' => putChar('\n')
case 'f' => putChar('\f')
case 'r' => putChar('\r')
case '\"' => putChar('\"')
case '\'' => putChar('\'')
case '\\' => putChar('\\')
case _ => invalidEscape()
}
nextChar()
}
nextChar()
}
} else {
putChar(ch)
nextChar()
}

private def getUEscape(): Unit = {
private def getUEscape(): Boolean = {
while (ch == 'u') nextChar()
var codepoint = 0
var digitsRead = 0
while(digitsRead < 4){
while (digitsRead < 4) {
if (digitsRead > 0) nextChar()
val digit = digit2int(ch, 16)
digitsRead += 1
if (digit >= 0) {
codepoint = codepoint << 4
codepoint += digit
}
else invalidUnicodeEscape(digitsRead)
else {
invalidUnicodeEscape(digitsRead)
return false
}
}
val found = codepoint.asInstanceOf[Char]
putChar(found)
true
}


Expand All @@ -945,7 +960,7 @@ trait Scanners extends ScannersCommon {
}

protected def invalidUnicodeEscape(n: Int): Unit = {
syntaxError(charOffset -n, "invalid unicode escape")
syntaxError(charOffset - n, "invalid unicode escape")
putChar(ch)
}

Expand Down
8 changes: 5 additions & 3 deletions src/compiler/scala/tools/reflect/FastStringInterpolator.scala
Expand Up @@ -36,10 +36,11 @@ trait FastStringInterpolator extends FormatInterpolator {
else if (isRaw) {
val processed = StringContext.processUnicode(stringVal)
if(processed != stringVal){
var diffindex = processed.zip(stringVal).zipWithIndex.collectFirst{
val diffindex = processed.zip(stringVal).zipWithIndex.collectFirst {
case ((p, o), i) if p != o => i
}.getOrElse(processed.length - 1)
c.warning(lit.pos, "Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14")

c.warning(lit.pos.withShift(diffindex), "Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14. Use the literal character instead.")
}
processed
}
Expand All @@ -48,7 +49,8 @@ trait FastStringInterpolator extends FormatInterpolator {
treeCopy.Literal(lit, k).setType(ConstantType(k))
}
catch {
case e: StringContext.InvalidEscapeException => c.abort(parts.head.pos.withShift(e.index), e.getMessage)
case ie: StringContext.InvalidEscapeException => c.abort(parts.head.pos.withShift(ie.index), ie.getMessage)
case iue: StringContext.InvalidUnicodeEscapeException => c.abort(parts.head.pos.withShift(iue.index), iue.getMessage)
}

val argsIndexed = args.toVector
Expand Down
12 changes: 9 additions & 3 deletions src/library/scala/StringContext.scala
Expand Up @@ -324,11 +324,15 @@ object StringContext {
class InvalidEscapeException(str: String, val index: Int) extends IllegalArgumentException(
s"""invalid escape ${
require(index >= 0 && index < str.length)
val ok = s"""[\\b, \\t, \\n, \\f, \\r, \\, \\", \\', \\uxxxx]"""
val ok = s"""[\\b, \\t, \\n, \\f, \\r, \\\\, \\", \\', \\uxxxx]"""
if (index == str.length - 1) "at terminal" else s"'\\${str(index + 1)}' not one of $ok at"
} index $index in "$str". Use \\\\ for literal \\."""
)

protected[scala] class InvalidUnicodeEscapeException(str: String, val escapeStart: Int, val index: Int) extends IllegalArgumentException(
s"""invalid unicode escape at index $index of $str"""
)

private[this] def readUEscape(src: String, startindex: Int): (Char, Int) = {
val len = src.length()
def loop(uindex: Int): (Char, Int) = {
Expand All @@ -340,14 +344,16 @@ object StringContext {
val digitsRead = dindex
(codepoint.asInstanceOf[Char], usRead + digitsRead)
}
else if (dindex + uindex >= len)
throw new InvalidUnicodeEscapeException(src, startindex, dindex + uindex)
else {
val ch = src(dindex + uindex)
val e = ch.asDigit
if(e >= 0 && e <= 15) loopCP(dindex + 1, (codepoint << 4) + e)
else throw new InvalidEscapeException(src, startindex)
else throw new InvalidUnicodeEscapeException(src, startindex, dindex + uindex)
}
}
if(uindex > len) throw new InvalidEscapeException(src, startindex)
if(uindex >= len) throw new InvalidUnicodeEscapeException(src, startindex, uindex - 1)
//allow one or more `u` characters between the
//backslash and the code unit
else if(src(uindex) == 'u') loop(uindex + 1)
Expand Down
25 changes: 25 additions & 0 deletions test/files/neg/t3220-1.check
@@ -0,0 +1,25 @@
t3220-1.scala:2: error: invalid unicode escape
val badsingle = "foo \unope that's wrong"
^
t3220-1.scala:3: error: invalid unicode escape at index 6 of foo \unope that's wrong
val badtriple = """foo \unope that's wrong"""
^
t3220-1.scala:4: error: invalid unicode escape
val caretPos = "foo \u12x3 pos @ x"
^
t3220-1.scala:5: error: invalid unicode escape
val caretPos2 = "foo \uuuuuuu12x3 pos @ x"
^
t3220-1.scala:6: error: invalid unicode escape
val carPosTerm = "foo \u123"
^
t3220-1.scala:7: error: invalid unicode escape
val halfAnEscape = "foo \u12"
^
t3220-1.scala:8: error: invalid unicode escape
val halfAnEscapeChar = '\u45'
^
t3220-1.scala:9: error: invalid unicode escape
val `half An Identifier\u45` = "nope"
^
8 errors found
10 changes: 10 additions & 0 deletions test/files/neg/t3220-1.scala
@@ -0,0 +1,10 @@
object Example {
val badsingle = "foo \unope that's wrong"
val badtriple = """foo \unope that's wrong"""
val caretPos = "foo \u12x3 pos @ x"
val caretPos2 = "foo \uuuuuuu12x3 pos @ x"
val carPosTerm = "foo \u123"
val halfAnEscape = "foo \u12"
val halfAnEscapeChar = '\u45'
val `half An Identifier\u45` = "nope"
}
16 changes: 16 additions & 0 deletions test/files/neg/t3220-2.check
@@ -0,0 +1,16 @@
t3220-2.scala:2: error: invalid unicode escape at index 6 of foo \unope that's wrong
val badInters1 = s"foo \unope that's wrong"
^
t3220-2.scala:3: error: invalid unicode escape at index 8 of foo \u12
val badIntersEnd1 = s"foo \u12"
^
t3220-2.scala:4: error: invalid unicode escape at index 6 of foo \unope that's wrong
val badInterRaw1 = raw"foo \unope that's wrong"
^
t3220-2.scala:6: error: invalid unicode escape at index 6 of foo \unope that's wrong
val badInters3 = s"""foo \unope that's wrong"""
^
t3220-2.scala:7: error: invalid unicode escape at index 6 of foo \unope that's wrong
val badInterRaw3 = raw"""foo \unope that's wrong"""
^
5 errors found
8 changes: 8 additions & 0 deletions test/files/neg/t3220-2.scala
@@ -0,0 +1,8 @@
object Example {
val badInters1 = s"foo \unope that's wrong"
val badIntersEnd1 = s"foo \u12"
val badInterRaw1 = raw"foo \unope that's wrong"
val badInterRawEnd1 = raw"foo \u12"
val badInters3 = s"""foo \unope that's wrong"""
val badInterRaw3 = raw"""foo \unope that's wrong"""
}
20 changes: 10 additions & 10 deletions test/files/run/t3220-213.check
@@ -1,21 +1,21 @@
t3220-213.scala:9: warning: Unicode escapes in triple quoted strings and raw interpolations are deprecated, use the literal character instead
t3220-213.scala:9: warning: Unicode escapes in triple quoted strings are deprecated, use the literal character instead
def inTripleQuoted = """\u000A"""
^
t3220-213.scala:42: warning: Unicode escapes in triple quoted strings and raw interpolations are deprecated, use the literal character instead
^
t3220-213.scala:42: warning: Unicode escapes in triple quoted strings are deprecated, use the literal character instead
"tab unicode escape in triple quoted string" -> """tab\u0009tab""",
^
t3220-213.scala:10: warning: Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14
^
t3220-213.scala:10: warning: Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14. Use the literal character instead.
def inInterpolation = raw"\u000A"
^
t3220-213.scala:11: warning: Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14
t3220-213.scala:11: warning: Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14. Use the literal character instead.
def inTripleQuotedInterpolation = raw"""\u000A"""
^
t3220-213.scala:43: warning: Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14
t3220-213.scala:43: warning: Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14. Use the literal character instead.
"tab unicode escape in single quoted raw interpolator" -> raw"tab\u0009tab",
^
t3220-213.scala:44: warning: Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14
^
t3220-213.scala:44: warning: Unicode escapes in raw interpolations are deprecated as of scala 2.13.1, and will be removed in scala 2.14. Use the literal character instead.
"tab unicode escape in triple quoted raw interpolator" -> raw"""tab\u0009tab"""
^
^
supported
literals that result in tab tab:
literal tab in single quoted string
Expand Down

0 comments on commit 711a638

Please sign in to comment.