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