@@ -30,8 +30,9 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
30
30
def argType (argi : Int , types : Type * ): Type =
31
31
require(argi < argc, s " $argi out of range picking from $types" )
32
32
val tpe = argTypes(argi)
33
- types.find(t => argConformsTo(argi, tpe, t))
34
- .orElse(types.find(t => argConvertsTo(argi, tpe, t)))
33
+ types.find(t => t != defn.AnyType && argConformsTo(argi, tpe, t))
34
+ .orElse(types.find(t => t != defn.AnyType && argConvertsTo(argi, tpe, t)))
35
+ .orElse(types.find(t => t == defn.AnyType && argConformsTo(argi, tpe, t)))
35
36
.getOrElse {
36
37
report.argError(s " Found: ${tpe.show}, Required: ${types.map(_.show).mkString(" , " )}" , argi)
37
38
actuals += args(argi)
@@ -73,15 +74,16 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
73
74
@ tailrec
74
75
def loop (remaining : List [String ], n : Int ): Unit =
75
76
remaining match
76
- case part0 :: more =>
77
+ case part0 :: remaining =>
77
78
def badPart (t : Throwable ): String = " " .tap(_ => report.partError(t.getMessage.nn, index = n, offset = 0 ))
78
79
val part = try StringContext .processEscapes(part0) catch badPart
79
80
val matches = formatPattern.findAllMatchIn(part)
80
81
81
82
def insertStringConversion (): Unit =
82
83
amended += " %s" + part
83
- convert += Conversion (formatPattern.findAllMatchIn(" %s" ).next(), n) // improve
84
- argType(n- 1 , defn.AnyType )
84
+ val cv = Conversion (n)
85
+ cv.accepts(argType(n- 1 , defn.AnyType ))
86
+ convert += cv
85
87
def errorLeading (op : Conversion ) = op.errorAt(Spec )(s " conversions must follow a splice; ${Conversion .literalHelp}" )
86
88
def accept (op : Conversion ): Unit =
87
89
if ! op.isLeading then errorLeading(op)
@@ -105,8 +107,8 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
105
107
if n == 0 && cv.hasFlag('<' ) then cv.badFlag('<' , " No last arg" )
106
108
else if ! cv.isLiteral && ! cv.isIndexed then errorLeading(cv)
107
109
108
- loop(more , n + 1 )
109
- case Nil => ()
110
+ loop(remaining , n + 1 )
111
+ case Nil =>
110
112
end loop
111
113
112
114
loop(parts, n = 0 )
@@ -147,9 +149,10 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
147
149
// the conversion char is the head of the op string (but see DateTimeXn)
148
150
val cc : Char =
149
151
kind match
150
- case ErrorXn => if op.isEmpty then '?' else op(0 )
151
- case DateTimeXn => if op.length > 1 then op(1 ) else '?'
152
- case _ => op(0 )
152
+ case ErrorXn => if op.isEmpty then '?' else op(0 )
153
+ case DateTimeXn => if op.length <= 1 then '?' else op(1 )
154
+ case StringXn => if op.isEmpty then 's' else op(0 ) // accommodate the default %s
155
+ case _ => op(0 )
153
156
154
157
def isIndexed : Boolean = index.nonEmpty || hasFlag('<' )
155
158
def isError : Boolean = kind == ErrorXn
@@ -210,10 +213,9 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
210
213
def accepts (arg : Type ): Boolean =
211
214
kind match
212
215
case BooleanXn => arg == defn.BooleanType orElse warningAt(CC )(" Boolean format is null test for non-Boolean" )
213
- case IntegralXn =>
214
- arg == BigIntType || ! cond(cc) {
215
- case 'o' | 'x' | 'X' if hasAnyFlag(" + (" ) => " + (" .filter(hasFlag).foreach(bad => badFlag(bad, s " only use ' $bad' for BigInt conversions to o, x, X " )) ; true
216
- }
216
+ case IntegralXn => arg == BigIntType || ! cond(cc) {
217
+ case 'o' | 'x' | 'X' if hasAnyFlag(" + (" ) => " + (" .filter(hasFlag).foreach(bad => badFlag(bad, s " only use ' $bad' for BigInt conversions to o, x, X " )); true
218
+ }
217
219
case _ => true
218
220
219
221
// what arg type if any does the conversion accept
@@ -268,6 +270,8 @@ class TypedFormatChecker(partsElems: List[Tree], parts: List[String], args: List
268
270
case Some (cc) => new Conversion (m, i, kindOf(cc(0 ))).tap(_.verify)
269
271
case None => new Conversion (m, i, ErrorXn ).tap(_.errorAt(Spec )(s " Missing conversion operator in ' ${m.matched}'; $literalHelp" ))
270
272
end apply
273
+ // construct a default %s conversion
274
+ def apply (i : Int ): Conversion = new Conversion (formatPattern.findAllMatchIn(" %" ).next(), i, StringXn )
271
275
val literalHelp = " use %% for literal %, %n for newline"
272
276
end Conversion
273
277
0 commit comments