Skip to content

Commit d08a399

Browse files
committed
Merge remote-tracking branch 'upstream/master' into dotty-t8124
2 parents d7732da + 898d62b commit d08a399

File tree

6 files changed

+117
-17
lines changed

6 files changed

+117
-17
lines changed

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,13 @@ object Scanners {
256256
case _ => false
257257
}
258258

259+
/** Are we in a `${ }` block? such that RBRACE exits back into multiline string. */
260+
private def inMultiLineInterpolatedExpression =
261+
currentRegion match {
262+
case InBraces(_, InString(true, _)) => true
263+
case _ => false
264+
}
265+
259266
/** read next token and return last offset
260267
*/
261268
def skipToken(): Offset = {
@@ -742,17 +749,22 @@ object Scanners {
742749
if (token == INTERPOLATIONID) {
743750
nextRawChar()
744751
if (ch == '\"') {
745-
nextRawChar()
746-
if (ch == '\"') {
752+
if (lookaheadChar() == '\"') {
753+
nextRawChar()
754+
//offset += 3 // first part is positioned at the quote
747755
nextRawChar()
748756
stringPart(multiLine = true)
749757
}
750758
else {
759+
nextChar()
751760
token = STRINGLIT
752761
strVal = ""
753762
}
754763
}
755-
else stringPart(multiLine = false)
764+
else {
765+
//offset += 1 // first part is positioned at the quote
766+
stringPart(multiLine = false)
767+
}
756768
}
757769
else {
758770
nextChar()
@@ -806,7 +818,8 @@ object Scanners {
806818
case ')' =>
807819
nextChar(); token = RPAREN
808820
case '}' =>
809-
nextChar(); token = RBRACE
821+
if (inMultiLineInterpolatedExpression) nextRawChar() else nextChar()
822+
token = RBRACE
810823
case '[' =>
811824
nextChar(); token = LBRACKET
812825
case ']' =>
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package dotty.tools
2+
package dotc
3+
package parsing
4+
5+
import ast.untpd._
6+
import core.Constants._
7+
8+
import org.junit.Test
9+
10+
class ParserEdgeTest extends ParserTest {
11+
//
12+
// CR after right brace of interpolated expression was stripped.
13+
//
14+
@Test def `t9944 respect line separator`: Unit = {
15+
// avoid git stripping CR from a test file; also, inlining doesn't demonstrate the behavior.
16+
val CR = "\u000D"
17+
val NL = "\u000A"
18+
val triple = "\"" * 3
19+
val text = s"""class C { def g = "X" ; def f = s${triple}123${CR}${NL}$${g}${CR}${NL}456${triple} }"""
20+
def isStripped(s: String) = s.contains(NL) && !s.contains(CR)
21+
22+
// sanity check
23+
assert(text.linesIterator.size == 3, s"line count ${text.linesIterator.size}")
24+
assert(text.linesIterator.forall(!isStripped(_)))
25+
26+
val t = parseText(text)
27+
//println(t.show)
28+
assert(!t.existsSubTree {
29+
case Literal(const @ Constant(_)) if const.tag == StringTag => isStripped(const.stringValue)
30+
case st => false
31+
})
32+
}
33+
/* was:
34+
package <empty> {
35+
class C {
36+
def g = "X"
37+
def f =
38+
s"123\r\n{
39+
{
40+
g
41+
}
42+
}\n456"
43+
}
44+
}
45+
* now:
46+
package <empty> {
47+
class C {
48+
def g = "X"
49+
def f =
50+
s"123\r\n{
51+
{
52+
g
53+
}
54+
}\r\n456"
55+
}
56+
}
57+
* tests/run/t9944 expressed ordinarily, with CR as indicated:
58+
class C {
59+
def g = 42
60+
def f =
61+
s"""123^M
62+
|${ g }^M
63+
|123""".stripMargin
64+
}
65+
66+
object Test extends App {
67+
println(new C().f.getBytes.toList.map(c => f"$c%02x"))
68+
}
69+
* was:
70+
➜ dotr Test
71+
List(31, 32, 33, 0d, 0a, 34, 32, 0a, 31, 32, 33)
72+
*/
73+
}

compiler/test/dotty/tools/dotc/parsing/ParserTest.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ class ParserTest extends DottyTest {
2323
parsedTrees.clear()
2424
}
2525

26-
def parse(file: PlainFile): Tree = {
27-
//println("***** parsing " + file)
28-
val source = new SourceFile(file, Codec.UTF8)
26+
def parse(file: PlainFile): Tree = parseSource(new SourceFile(file, Codec.UTF8))
27+
28+
private def parseSource(source: SourceFile): Tree = {
29+
//println("***** parsing " + source.file)
2930
val parser = new Parser(source)
3031
val tree = parser.parse()
3132
parsed += 1
@@ -41,4 +42,6 @@ class ParserTest extends DottyTest {
4142
for (d <- dir.dirs)
4243
parseDir(d.path)
4344
}
45+
46+
def parseText(code: String): Tree = parseSource(SourceFile.virtual("<code>", code))
4447
}

library/src/scala/Eql.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ object Eql {
2323
def eqlAny[L, R]: Eql[L, R] = derived
2424

2525
// Instances of `Eql` for common Java types
26-
implicit def eqlNumber : Eql[Number, Number] = derived
27-
implicit def eqlString : Eql[String, String] = derived
26+
given eqlNumber as Eql[Number, Number] = derived
27+
given eqlString as Eql[String, String] = derived
2828

2929
// The next three definitions can go into the companion objects of classes
3030
// Seq, Set, and Proxy. For now they are here in order not to have to touch the
3131
// source code of these classes
32-
implicit def eqlSeq[T, U](implicit eq: Eql[T, U]): Eql[GenSeq[T], GenSeq[U]] = derived
33-
implicit def eqlSet[T, U](implicit eq: Eql[T, U]): Eql[Set[T], Set[U]] = derived
32+
given eqlSeq[T, U](using eq: Eql[T, U]) as Eql[GenSeq[T], GenSeq[U]] = derived
33+
given eqlSet[T, U](using eq: Eql[T, U]) as Eql[Set[T], Set[U]] = derived
3434

3535
// true asymmetry, modeling the (somewhat problematic) nature of equals on Proxies
36-
implicit def eqlProxy : Eql[Proxy, AnyRef] = derived
36+
given eqlProxy as Eql[Proxy, AnyRef] = derived
3737
}

library/src/scala/implicits/Not.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ package scala.implicits
77
* value of type `C` is available. If we do not want to prioritize `i1` and `i2` by
88
* putting them in different traits we can instead define the following:
99
*
10-
* implicit def i1: D(implicit ev: C) = ...
11-
* implicit def i2: D(implicit ev: Not[C]) = ...
10+
* given i1: D(using ev: C) = ...
11+
* given i2: D(using ev: Not[C]) = ...
1212
*
1313
* `Not` is treated specially in implicit search, similar to the way logical negation
1414
* is treated in Prolog: The implicit search for `Not[C]` succeeds if and only if the implicit
@@ -27,7 +27,7 @@ final class Not[+T] private ()
2727
trait LowPriorityNot {
2828

2929
/** A fallback method used to emulate negation in Scala 2 */
30-
implicit def default[T]: Not[T] = Not.value
30+
given default[T] as Not[T] = Not.value
3131
}
3232
object Not extends LowPriorityNot {
3333

@@ -38,8 +38,8 @@ object Not extends LowPriorityNot {
3838
def value: Not[Nothing] = new Not[Nothing]()
3939

4040
/** One of two ambiguous methods used to emulate negation in Scala 2 */
41-
implicit def amb1[T](implicit ev: T): Not[T] = ???
41+
given amb1[T](using ev: T) as Not[T] = ???
4242

4343
/** One of two ambiguous methods used to emulate negation in Scala 2 */
44-
implicit def amb2[T](implicit ev: T): Not[T] = ???
44+
given amb2[T](using ev: T) as Not[T] = ???
4545
}

tests/pos/t7919.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
object X {
3+
val x = s""
4+
val y = true
5+
}
6+
7+
/* was:
8+
4 | val y = true
9+
| ^^^
10+
| end of statement expected but 'val' found
11+
*/

0 commit comments

Comments
 (0)