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