@@ -47,7 +47,7 @@ let newInfo () =
4747 addZeros = false
4848 precision = false }
4949
50- let parseFormatStringInternal ( m : range ) ( g : TcGlobals ) ( context : FormatStringCheckContext option ) fmt bty cty =
50+ let parseFormatStringInternal ( m : range ) ( g : TcGlobals ) isInterp ( context : FormatStringCheckContext option ) fmt bty cty =
5151 // Offset is used to adjust ranges depending on whether input string is regular, verbatim or triple-quote.
5252 // We construct a new 'fmt' string since the current 'fmt' string doesn't distinguish between "\n" and escaped "\\n".
5353 let ( offset , fmt ) =
@@ -77,7 +77,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe
7777 if acc |> List.forall ( fun ( p , _ ) -> p = None) then // without positional specifiers
7878 acc |> List.map snd |> List.rev
7979 else
80- failwithf " %s " <| FSComp.SR.forPositionalSpecifiersNotPermitted()
80+ raise ( Failure ( FSComp.SR.forPositionalSpecifiersNotPermitted()) )
8181 argtys
8282 elif System.Char.IsSurrogatePair( fmt, i) then
8383 parseLoop acc ( i+ 2 , relLine, relCol+ 2 )
@@ -88,65 +88,65 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe
8888 let startCol = relCol
8989 let relCol = relCol+ 1
9090 let i = i+ 1
91- if i >= len then failwithf " %s " <| FSComp.SR.forMissingFormatSpecifier()
91+ if i >= len then raise ( Failure ( FSComp.SR.forMissingFormatSpecifier()) )
9292 let info = newInfo()
9393
9494 let rec flags i =
95- if i >= len then failwithf " %s " <| FSComp.SR.forMissingFormatSpecifier()
95+ if i >= len then raise ( Failure ( FSComp.SR.forMissingFormatSpecifier()) )
9696 match fmt.[ i] with
9797 | '-' ->
98- if info.leftJustify then failwithf " %s " <| FSComp.SR.forFlagSetTwice( " -" )
98+ if info.leftJustify then raise ( Failure ( FSComp.SR.forFlagSetTwice( " -" )) )
9999 info.leftJustify <- true
100100 flags( i+ 1 )
101101 | '+' ->
102- if info.numPrefixIfPos <> None then failwithf " %s " <| FSComp.SR.forPrefixFlagSpacePlusSetTwice()
102+ if info.numPrefixIfPos <> None then raise ( Failure ( FSComp.SR.forPrefixFlagSpacePlusSetTwice()) )
103103 info.numPrefixIfPos <- Some '+'
104104 flags( i+ 1 )
105105 | '0' ->
106- if info.addZeros then failwithf " %s " <| FSComp.SR.forFlagSetTwice( " 0" )
106+ if info.addZeros then raise ( Failure ( FSComp.SR.forFlagSetTwice( " 0" )) )
107107 info.addZeros <- true
108108 flags( i+ 1 )
109109 | ' ' ->
110- if info.numPrefixIfPos <> None then failwithf " %s " <| FSComp.SR.forPrefixFlagSpacePlusSetTwice()
110+ if info.numPrefixIfPos <> None then raise ( Failure ( FSComp.SR.forPrefixFlagSpacePlusSetTwice()) )
111111 info.numPrefixIfPos <- Some ' '
112112 flags( i+ 1 )
113- | '#' -> failwithf " %s " <| FSComp.SR.forHashSpecifierIsInvalid()
113+ | '#' -> raise ( Failure ( FSComp.SR.forHashSpecifierIsInvalid() ))
114114 | _ -> i
115115
116116 let rec digitsPrecision i =
117- if i >= len then failwithf " %s " <| FSComp.SR.forBadPrecision()
117+ if i >= len then raise ( Failure ( FSComp.SR.forBadPrecision()) )
118118 match fmt.[ i] with
119119 | c when System.Char.IsDigit c -> digitsPrecision ( i+ 1 )
120120 | _ -> i
121121
122122 let precision i =
123- if i >= len then failwithf " %s " <| FSComp.SR.forBadWidth()
123+ if i >= len then raise ( Failure ( FSComp.SR.forBadWidth()) )
124124 match fmt.[ i] with
125125 | c when System.Char.IsDigit c -> info.precision <- true ; false , digitsPrecision ( i+ 1 )
126126 | '*' -> info.precision <- true ; true ,( i+ 1 )
127- | _ -> failwithf " %s " <| FSComp.SR.forPrecisionMissingAfterDot()
127+ | _ -> raise ( Failure ( FSComp.SR.forPrecisionMissingAfterDot()) )
128128
129129 let optionalDotAndPrecision i =
130- if i >= len then failwithf " %s " <| FSComp.SR.forBadPrecision()
130+ if i >= len then raise ( Failure ( FSComp.SR.forBadPrecision()) )
131131 match fmt.[ i] with
132132 | '.' -> precision ( i+ 1 )
133133 | _ -> false , i
134134
135135 let rec digitsWidthAndPrecision i =
136- if i >= len then failwithf " %s " <| FSComp.SR.forBadPrecision()
136+ if i >= len then raise ( Failure ( FSComp.SR.forBadPrecision()) )
137137 match fmt.[ i] with
138138 | c when System.Char.IsDigit c -> digitsWidthAndPrecision ( i+ 1 )
139139 | _ -> optionalDotAndPrecision i
140140
141141 let widthAndPrecision i =
142- if i >= len then failwithf " %s " <| FSComp.SR.forBadPrecision()
142+ if i >= len then raise ( Failure ( FSComp.SR.forBadPrecision()) )
143143 match fmt.[ i] with
144144 | c when System.Char.IsDigit c -> false , digitsWidthAndPrecision i
145145 | '*' -> true , optionalDotAndPrecision ( i+ 1 )
146146 | _ -> false , optionalDotAndPrecision i
147147
148148 let rec digitsPosition n i =
149- if i >= len then failwithf " %s " <| FSComp.SR.forBadPrecision()
149+ if i >= len then raise ( Failure ( FSComp.SR.forBadPrecision()) )
150150 match fmt.[ i] with
151151 | c when System.Char.IsDigit c -> digitsPosition ( n* 10 + int c - int '0' ) ( i+ 1 )
152152 | '$' -> Some n, i+ 1
@@ -171,22 +171,35 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe
171171 let widthArg ,( precisionArg , i ) = widthAndPrecision i
172172 let relCol = relCol + i - oldI
173173
174- if i >= len then failwithf " %s " <| FSComp.SR.forBadPrecision()
174+ if i >= len then raise ( Failure ( FSComp.SR.forBadPrecision()) )
175175
176176 let acc = if precisionArg then ( Option.map ((+) 1 ) posi, g.int_ ty) :: acc else acc
177177
178178 let acc = if widthArg then ( Option.map ((+) 1 ) posi, g.int_ ty) :: acc else acc
179179
180- let checkNoPrecision c = if info.precision then failwithf " %s " <| FSComp.SR.forFormatDoesntSupportPrecision( c.ToString())
181- let checkNoZeroFlag c = if info.addZeros then failwithf " %s " <| FSComp.SR.forDoesNotSupportZeroFlag( c.ToString())
182- let checkNoNumericPrefix c = if info.numPrefixIfPos <> None then
183- failwithf " %s " <| FSComp.SR.forDoesNotSupportPrefixFlag( c.ToString(), ( Option.get info.numPrefixIfPos) .ToString())
180+ let checkNoPrecision c =
181+ if info.precision then raise ( Failure ( FSComp.SR.forFormatDoesntSupportPrecision( c.ToString())))
182+
183+ let checkNoZeroFlag c =
184+ if info.addZeros then raise ( Failure ( FSComp.SR.forDoesNotSupportZeroFlag( c.ToString())))
185+
186+ let checkNoNumericPrefix c =
187+ match info.numPrefixIfPos with
188+ | Some n -> raise ( Failure ( FSComp.SR.forDoesNotSupportPrefixFlag( c.ToString(), n.ToString())))
189+ | None -> ()
184190
185191 let checkOtherFlags c =
186192 checkNoPrecision c
187193 checkNoZeroFlag c
188194 checkNoNumericPrefix c
189195
196+ let skipInterp i =
197+ // Explicitly typed holes in interpolated strings get '%P' after them as hole place marker
198+ if isInterp then
199+ if i+ 1 < fmt.Length && fmt.[ i] = '%' && fmt.[ i+ 1 ] = 'P' then i + 2
200+ else raise ( Failure ( FSComp.SR.forFormatInvalidForInterpolated()))
201+ else i
202+
190203 let collectSpecifierLocation relLine relCol numStdArgs =
191204 let numArgsForSpecifier =
192205 numStdArgs + ( if widthArg then 1 else 0 ) + ( if precisionArg then 1 else 0 )
@@ -209,94 +222,110 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe
209222 parseLoop acc ( i+ 1 , relLine, relCol+ 1 )
210223
211224 | ( 'd' | 'i' | 'o' | 'u' | 'x' | 'X' ) ->
212- if info.precision then failwithf " %s " <| FSComp.SR.forFormatDoesntSupportPrecision( ch.ToString())
225+ if info.precision then raise ( Failure ( FSComp.SR.forFormatDoesntSupportPrecision( ch.ToString()) ))
213226 collectSpecifierLocation relLine relCol 1
214- parseLoop (( posi, mkFlexibleIntFormatTypar g m) :: acc) ( i+ 1 , relLine, relCol+ 1 )
227+ let i = skipInterp ( i+ 1 )
228+ parseLoop (( posi, mkFlexibleIntFormatTypar g m) :: acc) ( i, relLine, relCol+ 1 )
215229
216230 | ( 'l' | 'L' ) ->
217- if info.precision then failwithf " %s " <| FSComp.SR.forFormatDoesntSupportPrecision( ch.ToString())
231+ if info.precision then raise ( Failure ( FSComp.SR.forFormatDoesntSupportPrecision( ch.ToString()) ))
218232 let relCol = relCol+ 1
219233 let i = i+ 1
220234
221235 // "bad format specifier ... In F# code you can use %d, %x, %o or %u instead ..."
222236 if i >= len then
223- failwithf " %s " <| FSComp.SR.forBadFormatSpecifier()
237+ raise ( Failure ( FSComp.SR.forBadFormatSpecifier()) )
224238 // Always error for %l and %Lx
225- failwithf " %s " <| FSComp.SR.forLIsUnnecessary()
239+ raise ( Failure ( FSComp.SR.forLIsUnnecessary()) )
226240 match fmt.[ i] with
227241 | ( 'd' | 'i' | 'o' | 'u' | 'x' | 'X' ) ->
228242 collectSpecifierLocation relLine relCol 1
229- parseLoop (( posi, mkFlexibleIntFormatTypar g m) :: acc) ( i+ 1 , relLine, relCol+ 1 )
230- | _ -> failwithf " %s " <| FSComp.SR.forBadFormatSpecifier()
243+ let i = skipInterp ( i+ 1 )
244+ parseLoop (( posi, mkFlexibleIntFormatTypar g m) :: acc) ( i, relLine, relCol+ 1 )
245+ | _ -> raise ( Failure ( FSComp.SR.forBadFormatSpecifier()))
231246
232247 | ( 'h' | 'H' ) ->
233- failwithf " %s " <| FSComp.SR.forHIsUnnecessary()
248+ raise ( Failure ( FSComp.SR.forHIsUnnecessary()) )
234249
235250 | 'M' ->
236251 collectSpecifierLocation relLine relCol 1
237- parseLoop (( posi, mkFlexibleDecimalFormatTypar g m) :: acc) ( i+ 1 , relLine, relCol+ 1 )
252+ let i = skipInterp ( i+ 1 )
253+ parseLoop (( posi, mkFlexibleDecimalFormatTypar g m) :: acc) ( i, relLine, relCol+ 1 )
238254
239255 | ( 'f' | 'F' | 'e' | 'E' | 'g' | 'G' ) ->
240256 collectSpecifierLocation relLine relCol 1
241- parseLoop (( posi, mkFlexibleFloatFormatTypar g m) :: acc) ( i+ 1 , relLine, relCol+ 1 )
257+ let i = skipInterp ( i+ 1 )
258+ parseLoop (( posi, mkFlexibleFloatFormatTypar g m) :: acc) ( i, relLine, relCol+ 1 )
242259
243260 | 'b' ->
244261 checkOtherFlags ch
245262 collectSpecifierLocation relLine relCol 1
246- parseLoop (( posi, g.bool_ ty) :: acc) ( i+ 1 , relLine, relCol+ 1 )
263+ let i = skipInterp ( i+ 1 )
264+ parseLoop (( posi, g.bool_ ty) :: acc) ( i, relLine, relCol+ 1 )
247265
248266 | 'c' ->
249267 checkOtherFlags ch
250268 collectSpecifierLocation relLine relCol 1
251- parseLoop (( posi, g.char_ ty) :: acc) ( i+ 1 , relLine, relCol+ 1 )
269+ let i = skipInterp ( i+ 1 )
270+ parseLoop (( posi, g.char_ ty) :: acc) ( i, relLine, relCol+ 1 )
252271
253272 | 's' ->
254273 checkOtherFlags ch
255274 collectSpecifierLocation relLine relCol 1
256- parseLoop (( posi, g.string_ ty) :: acc) ( i+ 1 , relLine, relCol+ 1 )
275+ let i = skipInterp ( i+ 1 )
276+ parseLoop (( posi, g.string_ ty) :: acc) ( i, relLine, relCol+ 1 )
257277
258278 | 'O' ->
259279 checkOtherFlags ch
260280 collectSpecifierLocation relLine relCol 1
281+ let i = skipInterp ( i+ 1 )
282+ parseLoop (( posi, NewInferenceType ()) :: acc) ( i, relLine, relCol+ 1 )
283+
284+ // residue of hole "...{n}..." in interpolated strings
285+ | 'P' when isInterp ->
286+ checkOtherFlags ch
261287 parseLoop (( posi, NewInferenceType ()) :: acc) ( i+ 1 , relLine, relCol+ 1 )
262288
263289 | 'A' ->
264290 match info.numPrefixIfPos with
265291 | None // %A has BindingFlags=Public, %+A has BindingFlags=Public | NonPublic
266292 | Some '+' ->
267293 collectSpecifierLocation relLine relCol 1
268- parseLoop (( posi, NewInferenceType ()) :: acc) ( i+ 1 , relLine, relCol+ 1 )
269- | Some _ -> failwithf " %s " <| FSComp.SR.forDoesNotSupportPrefixFlag( ch.ToString(), ( Option.get info.numPrefixIfPos) .ToString())
294+ let i = skipInterp ( i+ 1 )
295+ parseLoop (( posi, NewInferenceType ()) :: acc) ( i, relLine, relCol+ 1 )
296+ | Some n -> raise ( Failure ( FSComp.SR.forDoesNotSupportPrefixFlag( ch.ToString(), n.ToString())))
270297
271298 | 'a' ->
272299 checkOtherFlags ch
273300 let xty = NewInferenceType ()
274301 let fty = bty - -> ( xty - -> cty)
275302 collectSpecifierLocation relLine relCol 2
276- parseLoop (( Option.map ((+) 1 ) posi, xty) :: ( posi, fty) :: acc) ( i+ 1 , relLine, relCol+ 1 )
303+ let i = skipInterp ( i+ 1 )
304+ parseLoop (( Option.map ((+) 1 ) posi, xty) :: ( posi, fty) :: acc) ( i, relLine, relCol+ 1 )
277305
278306 | 't' ->
279307 checkOtherFlags ch
280308 collectSpecifierLocation relLine relCol 1
281- parseLoop (( posi, bty - -> cty) :: acc) ( i+ 1 , relLine, relCol+ 1 )
309+ let i = skipInterp ( i+ 1 )
310+ parseLoop (( posi, bty - -> cty) :: acc) ( i, relLine, relCol+ 1 )
282311
283- | c -> failwithf " %s " <| FSComp.SR.forBadFormatSpecifierGeneral( String.make 1 c)
312+ | c -> raise ( Failure ( FSComp.SR.forBadFormatSpecifierGeneral( String.make 1 c)))
284313
285314 | '\n' -> parseLoop acc ( i+ 1 , relLine+ 1 , 0 )
286315 | _ -> parseLoop acc ( i+ 1 , relLine, relCol+ 1 )
287316
288317 let results = parseLoop [] ( 0 , 0 , m.StartColumn)
289318 results, Seq.toList specifierLocations
290319
291- let ParseFormatString m g formatStringCheckContext fmt bty cty dty =
292- let argtys , specifierLocations = parseFormatStringInternal m g formatStringCheckContext fmt bty cty
320+ let ParseFormatString m g isInterp formatStringCheckContext fmt bty cty dty =
321+ let argtys , specifierLocations = parseFormatStringInternal m g isInterp formatStringCheckContext fmt bty cty
293322 let aty = List.foldBack (- -> ) argtys dty
294323 let ety = mkRefTupledTy g argtys
295- ( aty, ety), specifierLocations
324+ ( argtys , aty, ety), specifierLocations
296325
297- let TryCountFormatStringArguments m g fmt bty cty =
326+ let TryCountFormatStringArguments m g isInterp fmt bty cty =
298327 try
299- let argtys , _specifierLocations = parseFormatStringInternal m g None fmt bty cty
328+ let argtys , _specifierLocations = parseFormatStringInternal m g isInterp None fmt bty cty
300329 Some argtys.Length
301330 with _ ->
302331 None
0 commit comments