Skip to content

Commit 06ef20d

Browse files
committed
Improve name to stringXn, minor refactor for reading
1 parent 2a15075 commit 06ef20d

File tree

1 file changed

+65
-59
lines changed

1 file changed

+65
-59
lines changed

compiler/src/dotty/tools/dotc/transform/localopt/FormatChecker.scala

+65-59
Original file line numberDiff line numberDiff line change
@@ -68,48 +68,51 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
6868
val amended = ListBuffer.empty[String]
6969
val convert = ListBuffer.empty[Conversion]
7070

71+
def checkPart(part: String, n: Int): Unit =
72+
val matches = formatPattern.findAllMatchIn(part)
73+
74+
def insertStringConversion(): Unit =
75+
amended += "%s" + part
76+
val cv = Conversion.stringXn(n)
77+
cv.accepts(argType(n-1, defn.AnyType))
78+
convert += cv
79+
cv.lintToString(argTypes(n-1))
80+
81+
def errorLeading(op: Conversion) = op.errorAt(Spec):
82+
s"conversions must follow a splice; ${Conversion.literalHelp}"
83+
84+
def accept(op: Conversion): Unit =
85+
if !op.isLeading then errorLeading(op)
86+
op.accepts(argType(n-1, op.acceptableVariants*))
87+
amended += part
88+
convert += op
89+
op.lintToString(argTypes(n-1))
90+
91+
// after the first part, a leading specifier is required for the interpolated arg; %s is supplied if needed
92+
if n == 0 then amended += part
93+
else if !matches.hasNext then insertStringConversion()
94+
else
95+
val cv = Conversion(matches.next(), n)
96+
if cv.isLiteral then insertStringConversion()
97+
else if cv.isIndexed then
98+
if cv.index.getOrElse(-1) == n then accept(cv) else insertStringConversion()
99+
else if !cv.isError then accept(cv)
100+
101+
// any remaining conversions in this part must be either literals or indexed
102+
while matches.hasNext do
103+
val cv = Conversion(matches.next(), n)
104+
if n == 0 && cv.hasFlag('<') then cv.badFlag('<', "No last arg")
105+
else if !cv.isLiteral && !cv.isIndexed then errorLeading(cv)
106+
end checkPart
107+
71108
@tailrec
72-
def loop(remaining: List[String], n: Int): Unit =
73-
remaining match
74-
case part0 :: remaining =>
75-
def badPart(t: Throwable): String = "".tap(_ => report.partError(t.getMessage.nn, index = n, offset = 0))
76-
val part = try StringContext.processEscapes(part0) catch badPart
77-
val matches = formatPattern.findAllMatchIn(part)
78-
79-
def insertStringConversion(): Unit =
80-
amended += "%s" + part
81-
val cv = Conversion(n)
82-
cv.accepts(argType(n-1, defn.AnyType))
83-
convert += cv
84-
cv.lintToString(argTypes(n-1))
85-
86-
def errorLeading(op: Conversion) = op.errorAt(Spec)(s"conversions must follow a splice; ${Conversion.literalHelp}")
87-
def accept(op: Conversion): Unit =
88-
if !op.isLeading then errorLeading(op)
89-
op.accepts(argType(n-1, op.acceptableVariants*))
90-
amended += part
91-
convert += op
92-
op.lintToString(argTypes(n-1))
93-
94-
// after the first part, a leading specifier is required for the interpolated arg; %s is supplied if needed
95-
if n == 0 then amended += part
96-
else if !matches.hasNext then insertStringConversion()
97-
else
98-
val cv = Conversion(matches.next(), n)
99-
if cv.isLiteral then insertStringConversion()
100-
else if cv.isIndexed then
101-
if cv.index.getOrElse(-1) == n then accept(cv) else insertStringConversion()
102-
else if !cv.isError then accept(cv)
103-
104-
// any remaining conversions in this part must be either literals or indexed
105-
while matches.hasNext do
106-
val cv = Conversion(matches.next(), n)
107-
if n == 0 && cv.hasFlag('<') then cv.badFlag('<', "No last arg")
108-
else if !cv.isLiteral && !cv.isIndexed then errorLeading(cv)
109-
110-
loop(remaining, n + 1)
111-
case Nil =>
112-
end loop
109+
def loop(remaining: List[String], n: Int): Unit = remaining match
110+
case part0 :: remaining =>
111+
def badPart(t: Throwable): String = "".tap(_ => report.partError(t.getMessage.nn, index = n, offset = 0))
112+
val part = try StringContext.processEscapes(part0) catch badPart
113+
checkPart(part, n)
114+
loop(remaining, n + 1)
115+
case Nil =>
113116

114117
loop(parts, n = 0)
115118
if reported then (Nil, Nil)
@@ -260,27 +263,30 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
260263

261264
object Conversion:
262265
def apply(m: Match, i: Int): Conversion =
263-
def kindOf(cc: Char) = cc match
264-
case 's' | 'S' => StringXn
265-
case 'h' | 'H' => HashXn
266-
case 'b' | 'B' => BooleanXn
267-
case 'c' | 'C' => CharacterXn
268-
case 'd' | 'o' |
269-
'x' | 'X' => IntegralXn
270-
case 'e' | 'E' |
271-
'f' |
272-
'g' | 'G' |
273-
'a' | 'A' => FloatingPointXn
274-
case 't' | 'T' => DateTimeXn
275-
case '%' | 'n' => LiteralXn
276-
case _ => ErrorXn
277-
end kindOf
278266
m.group(CC) match
279-
case Some(cc) => new Conversion(m, i, kindOf(cc(0))).tap(_.verify)
280-
case None => new Conversion(m, i, ErrorXn).tap(_.errorAt(Spec)(s"Missing conversion operator in '${m.matched}'; $literalHelp"))
267+
case Some(cc) =>
268+
val xn = cc(0) match
269+
case 's' | 'S' => StringXn
270+
case 'h' | 'H' => HashXn
271+
case 'b' | 'B' => BooleanXn
272+
case 'c' | 'C' => CharacterXn
273+
case 'd' | 'o' |
274+
'x' | 'X' => IntegralXn
275+
case 'e' | 'E' |
276+
'f' |
277+
'g' | 'G' |
278+
'a' | 'A' => FloatingPointXn
279+
case 't' | 'T' => DateTimeXn
280+
case '%' | 'n' => LiteralXn
281+
case _ => ErrorXn
282+
new Conversion(m, i, xn)
283+
.tap(_.verify)
284+
case None =>
285+
new Conversion(m, i, ErrorXn)
286+
.tap(_.errorAt(Spec)(s"Missing conversion operator in '${m.matched}'; $literalHelp"))
281287
end apply
282288
// construct a default %s conversion
283-
def apply(i: Int): Conversion = new Conversion(formatPattern.findAllMatchIn("%").next(), i, StringXn)
289+
def stringXn(i: Int): Conversion = new Conversion(formatPattern.findAllMatchIn("%").next(), i, StringXn)
284290
val literalHelp = "use %% for literal %, %n for newline"
285291
end Conversion
286292

0 commit comments

Comments
 (0)