Skip to content

Commit b968b5d

Browse files
committed
Report the number of arguments associated with a printf format specifier along with its range
This is needed if one wants to accurately map arguments -> specifiers or specifiers -> arguments. A specifier can take 0, 1, 2, or 3 args: - printfn "%%" // 0 args - printfn "%*%" // 1 arg - printfn "%*.*%" // 2 args - printfn "%*.*f" // 3 args
1 parent 1bbcf25 commit b968b5d

File tree

6 files changed

+32
-29
lines changed

6 files changed

+32
-29
lines changed

src/fsharp/CheckFormatStrings.fs

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -193,27 +193,30 @@ let parseFormatStringInternal (m:range) g (source: string option) fmt bty cty =
193193
checkNoZeroFlag c
194194
checkNoNumericPrefix c
195195

196-
let collectSpecifierLocation relLine relCol =
196+
let collectSpecifierLocation relLine relCol numStdArgs =
197+
let numArgsForSpecifier =
198+
numStdArgs + (if widthArg then 1 else 0) + (if precisionArg then 1 else 0)
197199
match relLine with
198200
| 0 ->
199201
specifierLocations.Add(
200-
Range.mkFileIndexRange m.FileIndex
202+
(Range.mkFileIndexRange m.FileIndex
201203
(Range.mkPos m.StartLine (startCol + offset))
202-
(Range.mkPos m.StartLine (relCol + offset)))
204+
(Range.mkPos m.StartLine (relCol + offset))), numArgsForSpecifier)
203205
| _ ->
204206
specifierLocations.Add(
205-
Range.mkFileIndexRange m.FileIndex
207+
(Range.mkFileIndexRange m.FileIndex
206208
(Range.mkPos (m.StartLine + relLine) startCol)
207-
(Range.mkPos (m.StartLine + relLine) relCol))
209+
(Range.mkPos (m.StartLine + relLine) relCol)), numArgsForSpecifier)
208210

209211
let ch = fmt.[i]
210212
match ch with
211-
| '%' ->
213+
| '%' ->
214+
collectSpecifierLocation relLine relCol 0
212215
parseLoop acc (i+1, relLine, relCol+1)
213216

214217
| ('d' | 'i' | 'o' | 'u' | 'x' | 'X') ->
215218
if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString())
216-
collectSpecifierLocation relLine relCol
219+
collectSpecifierLocation relLine relCol 1
217220
parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i+1, relLine, relCol+1)
218221

219222
| ('l' | 'L') ->
@@ -228,59 +231,59 @@ let parseFormatStringInternal (m:range) g (source: string option) fmt bty cty =
228231
failwithf "%s" <| FSComp.SR.forLIsUnnecessary()
229232
match fmt.[i] with
230233
| ('d' | 'i' | 'o' | 'u' | 'x' | 'X') ->
231-
collectSpecifierLocation relLine relCol
234+
collectSpecifierLocation relLine relCol 1
232235
parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i+1, relLine, relCol+1)
233236
| _ -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifier()
234237

235238
| ('h' | 'H') ->
236239
failwithf "%s" <| FSComp.SR.forHIsUnnecessary()
237240

238241
| 'M' ->
239-
collectSpecifierLocation relLine relCol
242+
collectSpecifierLocation relLine relCol 1
240243
parseLoop ((posi, g.decimal_ty) :: acc) (i+1, relLine, relCol+1)
241244

242245
| ('f' | 'F' | 'e' | 'E' | 'g' | 'G') ->
243-
collectSpecifierLocation relLine relCol
246+
collectSpecifierLocation relLine relCol 1
244247
parseLoop ((posi, mkFlexibleFloatFormatTypar g m) :: acc) (i+1, relLine, relCol+1)
245248

246249
| 'b' ->
247250
checkOtherFlags ch
248-
collectSpecifierLocation relLine relCol
251+
collectSpecifierLocation relLine relCol 1
249252
parseLoop ((posi, g.bool_ty) :: acc) (i+1, relLine, relCol+1)
250253

251254
| 'c' ->
252255
checkOtherFlags ch
253-
collectSpecifierLocation relLine relCol
256+
collectSpecifierLocation relLine relCol 1
254257
parseLoop ((posi, g.char_ty) :: acc) (i+1, relLine, relCol+1)
255258

256259
| 's' ->
257260
checkOtherFlags ch
258-
collectSpecifierLocation relLine relCol
261+
collectSpecifierLocation relLine relCol 1
259262
parseLoop ((posi, g.string_ty) :: acc) (i+1, relLine, relCol+1)
260263

261264
| 'O' ->
262265
checkOtherFlags ch
263-
collectSpecifierLocation relLine relCol
266+
collectSpecifierLocation relLine relCol 1
264267
parseLoop ((posi, NewInferenceType ()) :: acc) (i+1, relLine, relCol+1)
265268

266269
| 'A' ->
267270
match info.numPrefixIfPos with
268271
| None // %A has BindingFlags=Public, %+A has BindingFlags=Public | NonPublic
269272
| Some '+' ->
270-
collectSpecifierLocation relLine relCol
273+
collectSpecifierLocation relLine relCol 1
271274
parseLoop ((posi, NewInferenceType ()) :: acc) (i+1, relLine, relCol+1)
272275
| Some _ -> failwithf "%s" <| FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), (Option.get info.numPrefixIfPos).ToString())
273276

274277
| 'a' ->
275278
checkOtherFlags ch
276279
let xty = NewInferenceType ()
277280
let fty = bty --> (xty --> cty)
278-
collectSpecifierLocation relLine relCol
281+
collectSpecifierLocation relLine relCol 1
279282
parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i+1, relLine, relCol+1)
280283

281284
| 't' ->
282285
checkOtherFlags ch
283-
collectSpecifierLocation relLine relCol
286+
collectSpecifierLocation relLine relCol 1
284287
parseLoop ((posi, bty --> cty) :: acc) (i+1, relLine, relCol+1)
285288

286289
| c -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c)

src/fsharp/CheckFormatStrings.fsi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ open Microsoft.FSharp.Compiler.Tast
1313
open Microsoft.FSharp.Compiler.TcGlobals
1414
open Microsoft.FSharp.Compiler.AbstractIL.Internal
1515

16-
val ParseFormatString : Range.range -> TcGlobals -> source: string option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType * TType) * Range.range list
16+
val ParseFormatString : Range.range -> TcGlobals -> source: string option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType * TType) * (Range.range * int) list
1717

1818
val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> fmt:string -> bty:TType -> cty:TType -> int option

src/fsharp/NameResolution.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,7 @@ type ITypecheckResultsSink =
11051105
abstract NotifyEnvWithScope : range * NameResolutionEnv * AccessorDomain -> unit
11061106
abstract NotifyExprHasType : pos * TType * Tastops.DisplayEnv * NameResolutionEnv * AccessorDomain * range -> unit
11071107
abstract NotifyNameResolution : pos * Item * Item * ItemOccurence * Tastops.DisplayEnv * NameResolutionEnv * AccessorDomain * range -> unit
1108-
abstract NotifyFormatSpecifierLocation : range -> unit
1108+
abstract NotifyFormatSpecifierLocation : range * int -> unit
11091109
abstract CurrentSource : string option
11101110

11111111
let (|ValRefOfProp|_|) (pi : PropInfo) = pi.ArbitraryValRef
@@ -1301,7 +1301,7 @@ type TcResolutions
13011301

13021302

13031303
/// Represents container for all name resolutions that were met so far when typechecking some particular file
1304-
type TcSymbolUses(g, capturedNameResolutions : ResizeArray<CapturedNameResolution>, formatSpecifierLocations: range[]) =
1304+
type TcSymbolUses(g, capturedNameResolutions : ResizeArray<CapturedNameResolution>, formatSpecifierLocations: (range * int)[]) =
13051305

13061306
member this.GetUsesOfSymbol(item) =
13071307
[| for cnr in capturedNameResolutions do
@@ -1367,8 +1367,8 @@ type TcResultsSinkImpl(g, ?source: string) =
13671367
capturedNameResolutions.Add(CapturedNameResolution(endPos,item,occurenceType,denv,nenv,ad,m))
13681368
capturedMethodGroupResolutions.Add(CapturedNameResolution(endPos,itemMethodGroup,occurenceType,denv,nenv,ad,m))
13691369

1370-
member sink.NotifyFormatSpecifierLocation(m) =
1371-
capturedFormatSpecifierLocations.Add(m)
1370+
member sink.NotifyFormatSpecifierLocation(m, numArgs) =
1371+
capturedFormatSpecifierLocations.Add((m, numArgs))
13721372

13731373
member sink.CurrentSource = source
13741374

src/fsharp/NameResolution.fsi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,15 +233,15 @@ type internal TcSymbolUses =
233233

234234
member GetAllUsesOfSymbols : unit -> (Item * ItemOccurence * DisplayEnv * range)[]
235235

236-
member GetFormatSpecifierLocations : unit -> range[]
236+
member GetFormatSpecifierLocations : unit -> (range * int)[]
237237

238238

239239
/// An abstract type for reporting the results of name resolution and type checking
240240
type ITypecheckResultsSink =
241241
abstract NotifyEnvWithScope : range * NameResolutionEnv * AccessorDomain -> unit
242242
abstract NotifyExprHasType : pos * TType * DisplayEnv * NameResolutionEnv * AccessorDomain * range -> unit
243243
abstract NotifyNameResolution : pos * Item * Item * ItemOccurence * DisplayEnv * NameResolutionEnv * AccessorDomain * range -> unit
244-
abstract NotifyFormatSpecifierLocation : range -> unit
244+
abstract NotifyFormatSpecifierLocation : range * int -> unit
245245
abstract CurrentSource : string option
246246

247247
type internal TcResultsSinkImpl =

src/fsharp/TypeChecker.fs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,7 +1739,7 @@ let MakeAndPublishSimpleVals cenv env m names mergeNamesInOneNameresEnv =
17391739
if not m.IsSynthetic then
17401740
nameResolutions.Add(pos, a, b, occurence, denv, nenv, ad, m)
17411741
member this.NotifyExprHasType(_, _, _, _, _, _) = assert false // no expr typings in MakeSimpleVals
1742-
member this.NotifyFormatSpecifierLocation _ = ()
1742+
member this.NotifyFormatSpecifierLocation(_, _) = ()
17431743
member this.CurrentSource = None }
17441744

17451745
use _h = WithNewTypecheckResultsSink(sink, cenv.tcSink)
@@ -6314,8 +6314,8 @@ and TcConstStringExpr cenv overallTy env m tpenv s =
63146314
match cenv.tcSink.CurrentSink with
63156315
| None -> ()
63166316
| Some sink ->
6317-
for specifierLocation in specifierLocations do
6318-
sink.NotifyFormatSpecifierLocation specifierLocation
6317+
for specifierLocation,numArgs in specifierLocations do
6318+
sink.NotifyFormatSpecifierLocation(specifierLocation, numArgs)
63196319

63206320
UnifyTypes cenv env m aty aty'
63216321
UnifyTypes cenv env m ety ety'

src/fsharp/vs/service.fsi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ type FSharpCheckFileResults =
259259
/// <summary>Get any extra colorization info that is available after the typecheck</summary>
260260
member GetExtraColorizationsAlternate : unit -> (range * FSharpTokenColorKind)[]
261261

262-
/// <summary>Get the locations of format specifiers</summary>
263-
member GetFormatSpecifierLocations : unit -> range[]
262+
/// <summary>Get the locations of and number of arguments associated with format specifiers</summary>
263+
member GetFormatSpecifierLocations : unit -> (range*int)[]
264264

265265
/// Get all textual usages of all symbols throughout the file
266266
member GetAllUsesOfAllSymbolsInFile : unit -> Async<FSharpSymbolUse[]>

0 commit comments

Comments
 (0)