Skip to content

Commit d5cabee

Browse files
Unicode escapes are ordinary escape sequences
1 parent a324502 commit d5cabee

24 files changed

+339
-48
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: 41 additions & 4 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,37 @@ 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))
95+
val escapedStrs = strs.zipWithIndex.map { case (str @ Literal(c), i) =>
96+
//the first literal is mispositioned at the opening of the quote
97+
//rather than at the start of the string itself.
98+
//https://github.com/lampepfl/dotty/issues/8627
99+
stringPosition = str.sourcePos
100+
(c.value, i) match {
101+
case (s: String, 0) => {
102+
val givenSpan = stringPosition.span
103+
val movedSpan1 = givenSpan.shift(1)
104+
val movedSpan3 = givenSpan.shift(3)
105+
val s1 = stringPosition.source.content.slice(movedSpan1.start, movedSpan1.end)
106+
val s3 = stringPosition.source.content.slice(movedSpan3.start, movedSpan3.end)
107+
offset = if (s1.toList == s.toList) 1 else if (s3.toList == s.toList) 3 else 0
108+
}
109+
case _ => offset = 0
110+
}
111+
val escaped = StringContext.processEscapes(str.const.stringValue)
112+
cpy.Literal(str)(Constant(escaped))
81113
}
82114
Some(escapedStrs, elems)
83115
} catch {
84-
case _: StringContext.InvalidEscapeException => None
116+
case t @ InvalidEscapePosition(p) => {
117+
val loc = stringPosition.span.startPos.shift(p + offset)
118+
val errorPosition = stringPosition.withSpan(loc)
119+
ctx.error(t.getMessage() + "\n", errorPosition)
120+
None
121+
}
85122
}
86123
}
87124
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

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))

project/Build.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ object Build {
8181
* scala-library.
8282
*/
8383
def stdlibVersion(implicit mode: Mode): String = mode match {
84-
case NonBootstrapped => "2.13.1"
85-
case Bootstrapped => "2.13.1"
84+
case NonBootstrapped => "2.13.2-bin-db6fa85"
85+
case Bootstrapped => "2.13.2-bin-db6fa85"
8686
}
8787

8888
val dottyOrganization = "ch.epfl.lamp"
@@ -149,6 +149,7 @@ object Build {
149149

150150
// Settings shared by the build (scoped in ThisBuild). Used in build.sbt
151151
lazy val thisBuildSettings = Def.settings(
152+
resolvers += "scala-nightlies" at "https://scala-ci.typesafe.com/artifactory/scala-integration/",
152153
organization := dottyOrganization,
153154
organizationName := "LAMP/EPFL",
154155
organizationHomepage := Some(url("http://lamp.epfl.ch")),

0 commit comments

Comments
 (0)