@@ -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
0 commit comments