@@ -2,10 +2,12 @@ package dotty.tools.dotc.transform.localopt
2
2
3
3
import dotty .tools .dotc .ast .Trees ._
4
4
import dotty .tools .dotc .ast .tpd
5
+ import dotty .tools .dotc .core .Decorators ._
5
6
import dotty .tools .dotc .core .Constants .Constant
6
7
import dotty .tools .dotc .core .Contexts .Context
7
8
import dotty .tools .dotc .core .StdNames ._
8
9
import dotty .tools .dotc .core .Symbols ._
10
+ import dotty .tools .dotc .core .Types .MethodType
9
11
import dotty .tools .dotc .transform .MegaPhase .MiniPhase
10
12
11
13
/**
@@ -21,6 +23,16 @@ class StringInterpolatorOpt extends MiniPhase {
21
23
22
24
override def phaseName : String = " stringInterpolatorOpt"
23
25
26
+ override def checkPostCondition (tree : tpd.Tree )(implicit ctx : Context ): Unit = {
27
+ tree match {
28
+ case tree : RefTree =>
29
+ val sym = tree.symbol
30
+ assert(sym != defn.StringContext_raw && sym != defn.StringContext_s ,
31
+ i " $tree in ${ctx.owner.showLocated} should have been rewritten by phase $phaseName" )
32
+ case _ =>
33
+ }
34
+ }
35
+
24
36
/** Matches a list of constant literals */
25
37
private object Literals {
26
38
def unapply (tree : SeqLiteral )(implicit ctx : Context ): Option [List [Literal ]] = {
@@ -60,7 +72,7 @@ class StringInterpolatorOpt extends MiniPhase {
60
72
def unapply (tree : Apply )(implicit ctx : Context ): Option [(List [Literal ], List [Tree ])] = {
61
73
tree match {
62
74
case SOrRawInterpolator (strs, elems) =>
63
- if (tree.symbol == defn.StringContextRaw ) Some (strs, elems)
75
+ if (tree.symbol == defn.StringContext_raw ) Some (strs, elems)
64
76
else { // tree.symbol == defn.StringContextS
65
77
try {
66
78
val escapedStrs = strs.map { str =>
@@ -80,28 +92,46 @@ class StringInterpolatorOpt extends MiniPhase {
80
92
override def transformApply (tree : Apply )(implicit ctx : Context ): Tree = {
81
93
val sym = tree.symbol
82
94
val isInterpolatedMethod = // Test names first to avoid loading scala.StringContext if not used
83
- (sym.name == nme.raw_ && sym.eq(defn.StringContextRaw )) ||
84
- (sym.name == nme.s && sym.eq(defn.StringContextS ))
85
- if (isInterpolatedMethod) transformInterpolator(tree)
86
- else tree
87
- }
95
+ (sym.name == nme.raw_ && sym.eq(defn.StringContext_raw )) ||
96
+ (sym.name == nme.s && sym.eq(defn.StringContext_s ))
97
+ if (isInterpolatedMethod)
98
+ tree match {
99
+ case StringContextIntrinsic (strs : List [Literal ], elems : List [Tree ]) =>
100
+ val stri = strs.iterator
101
+ val elemi = elems.iterator
102
+ var result : Tree = stri.next
103
+ def concat (tree : Tree ): Unit = {
104
+ result = result.select(defn.String_+ ).appliedTo(tree)
105
+ }
106
+ while (elemi.hasNext) {
107
+ concat(elemi.next)
108
+ val str = stri.next
109
+ if (! str.const.stringValue.isEmpty) concat(str)
110
+ }
111
+ result
112
+ // Starting with Scala 2.13, s and raw are macros in the standard
113
+ // library, so we need to expand them manually.
114
+ // sc.s(args) --> standardInterpolator(processEscapes, args, sc.parts)
115
+ // sc.raw(args) --> standardInterpolator(x => x, args, sc.parts)
116
+ case Apply (intp, args :: Nil ) =>
117
+ val pre = intp match {
118
+ case Select (pre, _) => pre
119
+ case intp : Ident => tpd.desugarIdentPrefix(intp)
120
+ }
121
+ val isRaw = sym eq defn.StringContext_raw
122
+ val stringToString = defn.StringContextModule_processEscapes .info.asInstanceOf [MethodType ]
88
123
89
- private def transformInterpolator (tree : Tree )(implicit ctx : Context ): Tree = {
90
- tree match {
91
- case StringContextIntrinsic (strs : List [Literal ], elems : List [Tree ]) =>
92
- val stri = strs.iterator
93
- val elemi = elems.iterator
94
- var result : Tree = stri.next
95
- def concat (tree : Tree ): Unit = {
96
- result = result.select(defn.String_+ ).appliedTo(tree)
97
- }
98
- while (elemi.hasNext) {
99
- concat(elemi.next)
100
- val str = stri.next
101
- if (! str.const.stringValue.isEmpty) concat(str)
102
- }
103
- result
104
- case _ => tree
105
- }
124
+ val process = tpd.Lambda (stringToString, args =>
125
+ if (isRaw) args.head else ref(defn.StringContextModule_processEscapes ).appliedToArgs(args))
126
+
127
+ evalOnce(pre) { sc =>
128
+ val parts = sc.select(defn.StringContext_parts )
129
+
130
+ ref(defn.StringContextModule_standardInterpolator )
131
+ .appliedToArgs(List (process, args, parts))
132
+ }
133
+ }
134
+ else
135
+ tree
106
136
}
107
137
}
0 commit comments