Skip to content

Commit dd41a2f

Browse files
Unicode escapes are ordinary escape sequences
1 parent 25c0305 commit dd41a2f

25 files changed

+369
-49
lines changed

compiler/src/dotty/tools/dotc/core/Comments.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ object Comments {
199199
// SI-8210 - The warning would be false negative when this symbol is a setter
200200
if (ownComment.indexOf("@inheritdoc") != -1 && ! sym.isSetter)
201201
dottydoc.println(s"${sym.span}: the comment for ${sym} contains @inheritdoc, but no parent comment is available to inherit from.")
202-
ownComment.replaceAllLiterally("@inheritdoc", "<invalid inheritdoc annotation>")
202+
ownComment.replace("@inheritdoc", "<invalid inheritdoc annotation>")
203203
case Some(sc) =>
204204
if (ownComment == "") sc
205205
else expandInheritdoc(sc, merge(sc, ownComment, sym), sym)
@@ -296,7 +296,7 @@ object Comments {
296296
if (childSection.indexOf("@inheritdoc") == -1)
297297
childSection
298298
else
299-
childSection.replaceAllLiterally("@inheritdoc", parentSection)
299+
childSection.replace("@inheritdoc", parentSection)
300300

301301
def getParentSection(section: (Int, Int)): String = {
302302

@@ -392,7 +392,7 @@ object Comments {
392392

393393
// We suppressed expanding \$ throughout the recursion, and now we
394394
// need to replace \$ with $ so it looks as intended.
395-
expandInternal(initialStr, 0).replaceAllLiterally("""\$""", "$")
395+
expandInternal(initialStr, 0).replace("""\$""", "$")
396396
}
397397

398398
def defineVariables(sym: Symbol)(implicit ctx: Context): Unit = {

compiler/src/dotty/tools/dotc/parsing/CharArrayReader.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ abstract class CharArrayReader { self =>
1010
protected def startFrom: Int = 0
1111

1212
/** Switch whether unicode should be decoded */
13-
protected def decodeUni: Boolean = true
13+
protected def decodeUni: Boolean = false
1414

1515
/** An error routine to call on bad unicode escapes \\uxxxx. */
1616
protected def error(msg: String, offset: Int): Unit

compiler/src/dotty/tools/dotc/parsing/JavaScanners.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ object JavaScanners {
1414

1515
class JavaScanner(source: SourceFile, override val startFrom: Offset = 0)(implicit ctx: Context) extends ScannerCommon(source)(ctx) {
1616

17+
override def decodeUni: Boolean = false
18+
1719
def toToken(name: SimpleName): Token = {
1820
val idx = name.start
1921
if (idx >= 0 && idx <= lastKeywordStart) kwArray(idx) else IDENTIFIER

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,26 @@ object Scanners {
11901190
* and advance to next character.
11911191
*/
11921192
protected def getLitChar(): Unit =
1193+
def invalidUnicodeEscape() = {
1194+
error("invalid character in unicode escape sequence", charOffset - 1)
1195+
putChar(ch)
1196+
}
1197+
def putUnicode(): Unit = {
1198+
while ch == 'u' || ch == 'U' do nextChar()
1199+
var i = 0
1200+
var cp = 0
1201+
while (i < 4) {
1202+
val shift = (3 - i) * 4
1203+
val d = digit2int(ch, 16)
1204+
if(d < 0) {
1205+
return invalidUnicodeEscape()
1206+
}
1207+
cp += (d << shift)
1208+
nextChar()
1209+
i += 1
1210+
}
1211+
putChar(cp.asInstanceOf[Char])
1212+
}
11931213
if (ch == '\\') {
11941214
nextChar()
11951215
if ('0' <= ch && ch <= '7') {
@@ -1206,6 +1226,9 @@ object Scanners {
12061226
}
12071227
putChar(oct.toChar)
12081228
}
1229+
else if (ch == 'u' || ch == 'U') {
1230+
putUnicode()
1231+
}
12091232
else {
12101233
ch match {
12111234
case 'b' => putChar('\b')

compiler/src/dotty/tools/dotc/reporting/Message.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ abstract class Message(val errorId: ErrorMessageID) { self =>
6969
// myMsg might be composed of several d"..." invocations -> nested
7070
// nonsensical tags possible
7171
msg
72-
.replaceAllLiterally(nonSensicalStartTag, "")
73-
.replaceAllLiterally(nonSensicalEndTag, "")
72+
.replace(nonSensicalStartTag, "")
73+
.replace(nonSensicalEndTag, "")
7474
else msg
7575

7676
/** The message with potential embedded <nonsensical> tags */

compiler/src/dotty/tools/dotc/transform/localopt/StringInterpolatorOpt.scala

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,20 @@ class StringInterpolatorOpt extends MiniPhase {
6363
}
6464
}
6565

66+
//Extract the position from InvalidUnicodeEscapeException
67+
//which due to bincompat reasons is unaccessible.
68+
//TODO: remove once there is less restrictive bincompat
69+
private object InvalidEscapePosition {
70+
def unapply(t: Throwable): Option[Int] = t match {
71+
case iee: StringContext.InvalidEscapeException => Some(iee.index)
72+
case il: IllegalArgumentException => il.getMessage() match {
73+
case s"""invalid unicode escape at index $index of $_""" => index.toIntOption
74+
case _ => None
75+
}
76+
case _ => None
77+
}
78+
}
79+
6680
/**
6781
* Match trees that resemble s and raw string interpolations. In the case of the s
6882
* interpolator, escapes the string constants. Exposes the string constants as well as
@@ -74,14 +88,23 @@ class StringInterpolatorOpt extends MiniPhase {
7488
case SOrRawInterpolator(strs, elems) =>
7589
if (tree.symbol == defn.StringContext_raw) Some(strs, elems)
7690
else { // tree.symbol == defn.StringContextS
91+
import dotty.tools.dotc.util.SourcePosition
92+
var stringPosition: SourcePosition = null
93+
var offset: Int = 0
7794
try {
78-
val escapedStrs = strs.map { str =>
79-
val escapedValue = StringContext.processEscapes(str.const.stringValue)
80-
cpy.Literal(str)(Constant(escapedValue))
81-
}
95+
val escapedStrs = strs.map(str => {
96+
stringPosition = str.sourcePos
97+
val escaped = StringContext.processEscapes(str.const.stringValue)
98+
cpy.Literal(str)(Constant(escaped))
99+
})
82100
Some(escapedStrs, elems)
83101
} catch {
84-
case _: StringContext.InvalidEscapeException => None
102+
case t @ InvalidEscapePosition(p) => {
103+
val errorSpan = stringPosition.span.startPos.shift(p)
104+
val errorPosition = stringPosition.withSpan(errorSpan)
105+
ctx.error(t.getMessage() + "\n", errorPosition)
106+
None
107+
}
85108
}
86109
}
87110
case _ => None

compiler/src/dotty/tools/io/ClassPath.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ object ClassPath {
109109
else if (pattern endsWith wildSuffix) lsDir(Directory(pattern dropRight 2))
110110
else if (pattern contains '*') {
111111
try {
112-
val regexp = ("^" + pattern.replaceAllLiterally("""\*""", """.*""") + "$").r
112+
val regexp = ("^" + pattern.replace("""\*""", """.*""") + "$").r
113113
lsDir(Directory(pattern).parent, regexp.findFirstIn(_).isDefined)
114114
}
115115
catch { case _: PatternSyntaxException => List(pattern) }

compiler/test/dotty/tools/backend/jvm/DottyBytecodeTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ trait DottyBytecodeTest {
181181
val msg = new StringBuilder
182182
val success = ms1.lazyZip(ms2) forall { (m1, m2) =>
183183
val c1 = f(m1)
184-
val c2 = f(m2).replaceAllLiterally(name2, name1)
184+
val c2 = f(m2).replace(name2, name1)
185185
if (c1 == c2)
186186
msg append (s"[ok] $m1")
187187
else
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package dotty.tools
2+
package dotc
3+
package parsing
4+
5+
import ast.untpd._
6+
import org.junit.Test
7+
8+
class StringInterpolationPositionTest extends ParserTest {
9+
10+
val tq = "\"\"\""
11+
val program = s"""
12+
|class A {
13+
| val expr = 42
14+
| val s0 = s"string1"
15+
| val s1 = s"string1$${expr}string2"
16+
| val s2 = s"string1$${expr}string2$${expr}string3"
17+
| val s0m = s${tq}string1${tq}
18+
| val s1m = s${tq}string1$${expr}string2${tq}
19+
| val s2m = s${tq}string1$${expr}string2$${expr}string3${tq}
20+
|}""".stripMargin
21+
22+
@Test
23+
def interpolationLiteralPosition: Unit = {
24+
val t = parseText(program)
25+
t match {
26+
case PackageDef(_, List(TypeDef(_, Template(_, _, _, statements: List[Tree])))) => {
27+
val interpolations = statements.collect{ case ValDef(_, _, InterpolatedString(_, int)) => int }
28+
val lits = interpolations.flatten.flatMap {
29+
case l @ Literal(_) => List(l)
30+
case Thicket(trees) => trees.collect { case l @ Literal(_) => l }
31+
}
32+
for {
33+
lit <- lits
34+
Literal(c) = lit
35+
str <- List(c.value).collect { case str: String => str}
36+
} {
37+
val fromPos = program.substring(lit.span.start, lit.span.end)
38+
assert(fromPos == str, s"$fromPos == $str")
39+
}
40+
}
41+
}
42+
}
43+
}

compiler/test/dotty/tools/dotc/semanticdb/SemanticdbTests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class SemanticdbTests:
6161
.resolve("semanticdb")
6262
.resolve(relpath)
6363
.resolveSibling(filename + ".semanticdb")
64-
val expectPath = source.resolveSibling(filename.replaceAllLiterally(".scala", ".expect.scala"))
64+
val expectPath = source.resolveSibling(filename.replace(".scala", ".expect.scala"))
6565
val doc = Tools.loadTextDocument(source, relpath, semanticdbPath)
6666
Tools.metac(doc, rootSrc.relativize(source))(using metacSb)
6767
val obtained = trimTrailingWhitespace(SemanticdbTests.printTextDocument(doc))

0 commit comments

Comments
 (0)