1- // ALWAYS KEEP THIS FILE IN src-bootstrapped, DO NOT MOVE TO src
2-
3- package dotty .internal
4-
5- import scala .quoted ._
6-
7- object StringContextMacro {
8-
9- /** Implementation of scala.StringContext.f used in Dotty */
10- inline def f (inline sc : StringContext )(inline args : Any * ): String = $ { interpolate(' sc , ' args ) }
1+ package dotty .tools .dotc
2+ package transform .localopt
3+
4+ import dotty .tools .dotc .ast .Trees ._
5+ import dotty .tools .dotc .ast .tpd
6+ import dotty .tools .dotc .core .Decorators ._
7+ import dotty .tools .dotc .core .Constants .Constant
8+ import dotty .tools .dotc .core .Contexts ._
9+ import dotty .tools .dotc .core .StdNames ._
10+ import dotty .tools .dotc .core .NameKinds ._
11+ import dotty .tools .dotc .core .Symbols ._
12+ import dotty .tools .dotc .core .Types ._
13+
14+ // Ported from old dotty.internal.StringContextMacro
15+ // TODO: port Scala 2 logic? (see https://github.com/scala/scala/blob/2.13.x/src/compiler/scala/tools/reflect/FormatInterpolator.scala#L74)
16+ object StringContextChecker {
17+ import tpd ._
1118
1219 /** This trait defines a tool to report errors/warnings that do not depend on Position. */
1320 trait Reporter {
@@ -51,53 +58,52 @@ object StringContextMacro {
5158 def restoreReported () : Unit
5259 }
5360
54- /** Interpolates the arguments to the formatting String given inside a StringContext
55- *
56- * @param strCtxExpr the Expr that holds the StringContext which contains all the chunks of the formatting string
57- * @param args the Expr that holds the sequence of arguments to interpolate to the String in the correct format
58- * @return the Expr containing the formatted and interpolated String or an error/warning if the parameters are not correct
59- */
60- private def interpolate (strCtxExpr : Expr [StringContext ], argsExpr : Expr [Seq [Any ]])(using qctx : QuoteContext ): Expr [String ] = {
61- import qctx .tasty ._
62- val sourceFile = strCtxExpr.unseal.pos.sourceFile
63-
64- val (partsExpr, parts) = strCtxExpr match {
65- case Expr .StringContext (p1 as Consts (p2)) => (p1.toList, p2.toList)
66- case _ => report.throwError(" Expected statically known String Context" , strCtxExpr)
61+ /** Check the format of the parts of the f".." arguments and returns the string parts of the StringContext */
62+ def checkedParts (strContext_f : Tree , args0 : Tree )(using Context ): String = {
63+
64+ val (partsExpr, parts) = strContext_f match {
65+ case TypeApply (Select (Apply (_, (parts : SeqLiteral ) :: Nil ), _), _) =>
66+ (parts.elems, parts.elems.map { case Literal (Constant (str : String )) => str } )
67+ case _ =>
68+ report.error(" Expected statically known String Context" , strContext_f.srcPos)
69+ return " "
6770 }
6871
69- val args = argsExpr match {
70- case Varargs (args) => args
71- case _ => report.throwError(" Expected statically known argument list" , argsExpr)
72+ val args = args0 match {
73+ case args : SeqLiteral => args.elems
74+ case _ =>
75+ report.error(" Expected statically known argument list" , args0.srcPos)
76+ return " "
7277 }
7378
7479 val reporter = new Reporter {
7580 private [this ] var reported = false
7681 private [this ] var oldReported = false
7782 def partError (message : String , index : Int , offset : Int ) : Unit = {
7883 reported = true
79- val positionStart = partsExpr(index).unseal.pos.start + offset
80- error(message, sourceFile, positionStart, positionStart)
84+ val pos = partsExpr(index).sourcePos
85+ val posOffset = pos.withSpan(pos.span.shift(offset))
86+ report.error(message, posOffset)
8187 }
8288 def partWarning (message : String , index : Int , offset : Int ) : Unit = {
8389 reported = true
84- val positionStart = partsExpr(index).unseal.pos.start + offset
85- warning(message, sourceFile, positionStart, positionStart)
90+ val pos = partsExpr(index).sourcePos
91+ val posOffset = pos.withSpan(pos.span.shift(offset))
92+ report.warning(message, posOffset)
8693 }
8794
8895 def argError (message : String , index : Int ) : Unit = {
8996 reported = true
90- error(message, args(index).unseal.pos )
97+ report. error(message, args(index).srcPos )
9198 }
9299
93100 def strCtxError (message : String ) : Unit = {
94101 reported = true
95- val positionStart = strCtxExpr.unseal.pos.start
96- error(message, sourceFile, positionStart, positionStart)
102+ report.error(message, strContext_f.srcPos)
97103 }
98104 def argsError (message : String ) : Unit = {
99105 reported = true
100- error(message, argsExpr.unseal.pos )
106+ report. error(message, args0.srcPos )
101107 }
102108
103109 def hasReported () : Boolean = {
@@ -114,18 +120,11 @@ object StringContextMacro {
114120 }
115121 }
116122
117- interpolate (parts, args, argsExpr , reporter)
123+ checked (parts, args, reporter)
118124 }
119125
120- /** Helper function for the interpolate function above
121- *
122- * @param partsExpr the list of parts enumerated as Expr
123- * @param args the list of arguments enumerated as Expr
124- * @param reporter the reporter to return any error/warning when a problem is encountered
125- * @return the Expr containing the formatted and interpolated String or an error/warning report if the parameters are not correct
126- */
127- def interpolate (parts0 : List [String ], args : Seq [Expr [Any ]], argsExpr : Expr [Seq [Any ]], reporter : Reporter )(using qctx : QuoteContext ) : Expr [String ] = {
128- import qctx .tasty ._
126+ def checked (parts0 : List [String ], args : List [Tree ], reporter : Reporter )(using Context ): String = {
127+
129128
130129 /** Checks if the number of arguments are the same as the number of formatting strings
131130 *
@@ -585,21 +584,21 @@ object StringContextMacro {
585584 * nothing otherwise
586585 */
587586 def checkTypeWithArgs (argument : (Type , Int ), conversionChar : Char , partIndex : Int , flags : List [(Char , Int )]) = {
588- val booleans = List (Type .of[ Boolean ], Type .of[ Null ] )
589- val dates = List (Type .of[ Long ], Type .of[ java.util.Calendar ], Type .of[ java.util.Date ] )
590- val floatingPoints = List (Type .of[ Double ], Type .of[ Float ], Type .of[ java.math.BigDecimal ] )
591- val integral = List (Type .of[ Int ], Type .of[ Long ], Type .of[ Short ], Type .of[ Byte ], Type .of[ java.math.BigInteger ] )
592- val character = List (Type .of[ Char ], Type .of[ Byte ], Type .of[ Short ], Type .of[ Int ] )
587+ val booleans = List (defn. BooleanType , defn. NullType )
588+ val dates = List (defn. LongType , requiredClass( " java.util.Calendar" ).typeRef, requiredClass( " java.util.Date" ).typeRef )
589+ val floatingPoints = List (defn. DoubleType , defn. FloatType , requiredClass( " java.math.BigDecimal" ).typeRef )
590+ val integral = List (defn. IntType , defn. LongType , defn. ShortType , defn. ByteType , requiredClass( " java.math.BigInteger" ).typeRef )
591+ val character = List (defn. CharType , defn. ByteType , defn. ShortType , defn. IntType )
593592
594593 val (argType, argIndex) = argument
595594 conversionChar match {
596595 case 'c' | 'C' => checkSubtype(argType, " Char" , argIndex, character : _* )
597596 case 'd' | 'o' | 'x' | 'X' => {
598597 checkSubtype(argType, " Int" , argIndex, integral : _* )
599598 if (conversionChar != 'd' ) {
600- val notAllowedFlagOnCondition = List (('+' , ! (argType <:< Type .of[ java.math.BigInteger ] ), " only use '+' for BigInt conversions to o, x, X" ),
601- (' ' , ! (argType <:< Type .of[ java.math.BigInteger ] ), " only use ' ' for BigInt conversions to o, x, X" ),
602- ('(' , ! (argType <:< Type .of[ java.math.BigInteger ] ), " only use '(' for BigInt conversions to o, x, X" ),
599+ val notAllowedFlagOnCondition = List (('+' , ! (argType <:< requiredClass( " java.math.BigInteger" ).typeRef ), " only use '+' for BigInt conversions to o, x, X" ),
600+ (' ' , ! (argType <:< requiredClass( " java.math.BigInteger" ).typeRef ), " only use ' ' for BigInt conversions to o, x, X" ),
601+ ('(' , ! (argType <:< requiredClass( " java.math.BigInteger" ).typeRef ), " only use '(' for BigInt conversions to o, x, X" ),
603602 (',' , true , " ',' only allowed for d conversion of integral types" ))
604603 checkFlags(partIndex, flags, notAllowedFlagOnCondition : _* )
605604 }
@@ -608,7 +607,7 @@ object StringContextMacro {
608607 case 't' | 'T' => checkSubtype(argType, " Date" , argIndex, dates : _* )
609608 case 'b' | 'B' => checkSubtype(argType, " Boolean" , argIndex, booleans : _* )
610609 case 'h' | 'H' | 'S' | 's' =>
611- if ( ! (argType <:< Type .of[ java.util.Formattable ]))
610+ if ! (argType <:< requiredClass( " java.util.Formattable" ).typeRef) then
612611 for {flag <- flags ; if (flag._1 == '#' )}
613612 reporter.argError(" type mismatch;\n found : " + argType.widen.show.stripPrefix(" scala.Predef." ).stripPrefix(" java.lang." ).stripPrefix(" scala." ) + " \n required: java.util.Formattable" , argIndex)
614613 case 'n' | '%' =>
@@ -647,7 +646,7 @@ object StringContextMacro {
647646 * @param maxArgumentIndex an Option containing the maximum argument index possible, None if no args are specified
648647 * @return a list with all the elements of the conversion per formatting string
649648 */
650- def checkPart (part : String , start : Int , argument : Option [(Int , Expr [ Any ] )], maxArgumentIndex : Option [Int ]) : List [(Option [(Type , Int )], Char , List [(Char , Int )])] = {
649+ def checkPart (part : String , start : Int , argument : Option [(Int , Tree )], maxArgumentIndex : Option [Int ]) : List [(Option [(Type , Int )], Char , List [(Char , Int )])] = {
651650 reporter.resetReported()
652651 val hasFormattingSubstring = getFormattingSubstring(part, part.size, start)
653652 if (hasFormattingSubstring.nonEmpty) {
@@ -658,7 +657,7 @@ object StringContextMacro {
658657 case Some (argIndex, arg) => {
659658 val (hasArgumentIndex, argumentIndex, flags, hasWidth, width, hasPrecision, precision, hasRelative, relativeIndex, conversion) = getFormatSpecifiers(part, argIndex, argIndex + 1 , false , formattingStart)
660659 if (! reporter.hasReported()){
661- val conversionWithType = checkFormatSpecifiers(argIndex + 1 , hasArgumentIndex, argumentIndex, Some (argIndex + 1 ), start == 0 , maxArgumentIndex, hasRelative, hasWidth, width, hasPrecision, precision, flags, conversion, Some (arg.unseal. tpe), part)
660+ val conversionWithType = checkFormatSpecifiers(argIndex + 1 , hasArgumentIndex, argumentIndex, Some (argIndex + 1 ), start == 0 , maxArgumentIndex, hasRelative, hasWidth, width, hasPrecision, precision, flags, conversion, Some (arg.tpe), part)
662661 nextStart = conversion + 1
663662 conversionWithType :: checkPart(part, nextStart, argument, maxArgumentIndex)
664663 } else checkPart(part, conversion + 1 , argument, maxArgumentIndex)
@@ -710,7 +709,6 @@ object StringContextMacro {
710709 }
711710 }
712711
713- // macro expansion
714- ' {($ {Expr (parts.mkString)}).format($ {argsExpr}: _* )}
712+ parts.mkString
715713 }
716714}
0 commit comments