Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 15 additions & 20 deletions src/fsharp/CheckFormatStrings.fs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ open Microsoft.FSharp.Compiler.Tast
open Microsoft.FSharp.Compiler.Tastops
open Microsoft.FSharp.Compiler.TcGlobals
open Microsoft.FSharp.Compiler.ConstraintSolver
open Microsoft.FSharp.Compiler.NameResolution

type FormatItem = Simple of TType | FuncAndVal

Expand Down Expand Up @@ -47,27 +48,21 @@ let newInfo ()=
addZeros = false
precision = false}

let parseFormatStringInternal (m:range) (g: TcGlobals) (source: string option) fmt bty cty =
let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringCheckContext option) fmt bty cty =
// Offset is used to adjust ranges depending on whether input string is regular, verbatim or triple-quote.
// We construct a new 'fmt' string since the current 'fmt' string doesn't distinguish between "\n" and escaped "\\n".
let (offset, fmt) =
match source with
| Some source ->
let source = source.Replace("\r\n", "\n").Replace("\r", "\n")
let positions =
source.Split('\n')
|> Seq.map (fun s -> String.length s + 1)
|> Seq.scan (+) 0
|> Seq.toArray
let length = source.Length
if m.EndLine < positions.Length then
let startIndex = positions.[m.StartLine-1] + m.StartColumn
let endIndex = positions.[m.EndLine-1] + m.EndColumn - 1
if startIndex < length-3 && source.[startIndex..startIndex+2] = "\"\"\"" then
(3, source.[startIndex+3..endIndex-3])
elif startIndex < length-2 && source.[startIndex..startIndex+1] = "@\"" then
(2, source.[startIndex+2..endIndex-1])
else (1, source.[startIndex+1..endIndex-1])
match context with
| Some context ->
let length = context.NormalizedSource.Length
if m.EndLine < context.LineEndPositions.Length then
let startIndex = context.LineEndPositions.[m.StartLine-1] + m.StartColumn
let endIndex = context.LineEndPositions.[m.EndLine-1] + m.EndColumn - 1
if startIndex < length-3 && context.NormalizedSource.[startIndex..startIndex+2] = "\"\"\"" then
(3, context.NormalizedSource.[startIndex+3..endIndex-3])
elif startIndex < length-2 && context.NormalizedSource.[startIndex..startIndex+1] = "@\"" then
(2, context.NormalizedSource.[startIndex+2..endIndex-1])
else (1, context.NormalizedSource.[startIndex+1..endIndex-1])
else (1, fmt)
| None -> (1, fmt)

Expand Down Expand Up @@ -292,8 +287,8 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (source: string option) f
let results = parseLoop [] (0, 0, m.StartColumn)
results, Seq.toList specifierLocations

let ParseFormatString m g source fmt bty cty dty =
let argtys, specifierLocations = parseFormatStringInternal m g source fmt bty cty
let ParseFormatString m g formatStringCheckContext fmt bty cty dty =
let argtys, specifierLocations = parseFormatStringInternal m g formatStringCheckContext fmt bty cty
let aty = List.foldBack (-->) argtys dty
let ety = mkRefTupledTy g argtys
(aty, ety), specifierLocations
Expand Down
3 changes: 2 additions & 1 deletion src/fsharp/CheckFormatStrings.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@
module internal Microsoft.FSharp.Compiler.CheckFormatStrings

open Microsoft.FSharp.Compiler
open Microsoft.FSharp.Compiler.NameResolution
open Microsoft.FSharp.Compiler.Tast
open Microsoft.FSharp.Compiler.TcGlobals

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

val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> fmt:string -> bty:TType -> cty:TType -> int option
21 changes: 19 additions & 2 deletions src/fsharp/NameResolution.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,10 @@ type OpenDeclaration =
AppliedScope = appliedScope
IsOwnNamespace = isOwnNamespace }

type FormatStringCheckContext =
{ NormalizedSource: string
LineEndPositions: int[] }

/// An abstract type for reporting the results of name resolution and type checking.
type ITypecheckResultsSink =
abstract NotifyEnvWithScope : range * NameResolutionEnv * AccessorDomain -> unit
Expand All @@ -1254,6 +1258,7 @@ type ITypecheckResultsSink =
abstract NotifyFormatSpecifierLocation : range * int -> unit
abstract NotifyOpenDeclaration : OpenDeclaration -> unit
abstract CurrentSource : string option
abstract FormatStringCheckContext : FormatStringCheckContext option

let (|ValRefOfProp|_|) (pi : PropInfo) = pi.ArbitraryValRef
let (|ValRefOfMeth|_|) (mi : MethInfo) = mi.ArbitraryValRef
Expand Down Expand Up @@ -1497,7 +1502,6 @@ type TcSymbolUses(g, capturedNameResolutions : ResizeArray<CapturedNameResolutio

member this.GetFormatSpecifierLocationsAndArity() = formatSpecifierLocations


/// An accumulator for the results being emitted into the tcSink.
type TcResultsSinkImpl(g, ?source: string) =
let capturedEnvs = ResizeArray<_>()
Expand All @@ -1521,6 +1525,18 @@ type TcResultsSinkImpl(g, ?source: string) =
let capturedOpenDeclarations = ResizeArray<OpenDeclaration>()
let allowedRange (m:range) = not m.IsSynthetic

let formatStringCheckContext =
lazy
source |> Option.map (fun source ->
let source = source.Replace("\r\n", "\n").Replace("\r", "\n")
let positions =
source.Split('\n')
|> Seq.map (fun s -> String.length s + 1)
|> Seq.scan (+) 0
|> Seq.toArray
{ NormalizedSource = source
LineEndPositions = positions })

member this.GetResolutions() =
TcResolutions(capturedEnvs, capturedExprTypings, capturedNameResolutions, capturedMethodGroupResolutions)

Expand Down Expand Up @@ -1574,7 +1590,8 @@ type TcResultsSinkImpl(g, ?source: string) =
capturedOpenDeclarations.Add(openDeclaration)

member sink.CurrentSource = source


member sink.FormatStringCheckContext = formatStringCheckContext.Value

/// An abstract type for reporting the results of name resolution and type checking, and which allows
/// temporary suspension and/or redirection of reporting.
Expand Down
10 changes: 10 additions & 0 deletions src/fsharp/NameResolution.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,13 @@ type internal OpenDeclaration =
/// Create a new instance of OpenDeclaration.
static member Create : longId: Ident list * modules: ModuleOrNamespaceRef list * appliedScope: range * isOwnNamespace: bool -> OpenDeclaration

/// Line-end normalized source text and an array of line end positions, used for format string parsing
type FormatStringCheckContext =
{ /// Line-end normalized source text
NormalizedSource: string
/// Array of line end positions
LineEndPositions: int[] }

/// An abstract type for reporting the results of name resolution and type checking
type ITypecheckResultsSink =

Expand All @@ -362,6 +369,9 @@ type ITypecheckResultsSink =
/// Get the current source
abstract CurrentSource : string option

/// Cached line-end normalized source text and an array of line end positions, used for format string parsing
abstract FormatStringCheckContext : FormatStringCheckContext option

/// An implementation of ITypecheckResultsSink to collect information during type checking
type internal TcResultsSinkImpl =

Expand Down
7 changes: 4 additions & 3 deletions src/fsharp/TypeChecker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1840,7 +1840,8 @@ let MakeAndPublishSimpleVals cenv env m names mergeNamesInOneNameresEnv =
member this.NotifyExprHasType(_, _, _, _, _, _) = assert false // no expr typings in MakeSimpleVals
member this.NotifyFormatSpecifierLocation(_, _) = ()
member this.NotifyOpenDeclaration(_) = ()
member this.CurrentSource = None }
member this.CurrentSource = None
member this.FormatStringCheckContext = None }

use _h = WithNewTypecheckResultsSink(sink, cenv.tcSink)
MakeSimpleVals cenv env names
Expand Down Expand Up @@ -6785,10 +6786,10 @@ and TcConstStringExpr cenv overallTy env m tpenv s =
let ty' = mkPrintfFormatTy cenv.g aty bty cty dty ety
if (not (isObjTy cenv.g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy ty') then
// Parse the format string to work out the phantom types
let source = match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.CurrentSource
let formatStringCheckContext = match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.FormatStringCheckContext
let normalizedString = (s.Replace("\r\n", "\n").Replace("\r", "\n"))

let (aty', ety'), specifierLocations = (try CheckFormatStrings.ParseFormatString m cenv.g source normalizedString bty cty dty with Failure s -> error (Error(FSComp.SR.tcUnableToParseFormatString(s), m)))
let (aty', ety'), specifierLocations = (try CheckFormatStrings.ParseFormatString m cenv.g formatStringCheckContext normalizedString bty cty dty with Failure s -> error (Error(FSComp.SR.tcUnableToParseFormatString(s), m)))

match cenv.tcSink.CurrentSink with
| None -> ()
Expand Down
6 changes: 3 additions & 3 deletions src/fsharp/xlf/FSComp.txt.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
</trans-unit>
<trans-unit id="followingPatternMatchClauseHasWrongType">
<source>All branches of a pattern match expression must return values of the same type. The first branch returned a value of type '{0}', but this branch returned a value of type '{1}'.</source>
<target state="needs-review-translation">Všechny větve výrazu porovnání vzorů musí mít stejný typ. Očekávalo se, že tento výraz bude mít typ {0}, ale tady je typu {1}.</target>
<target state="translated">Všechny větve výrazu porovnání vzorů musí vracet hodnoty stejného typu. První větev vrátila hodnotu typu {0}, ale tato větev vrátila hodnotu typu {1}.</target>
<note />
</trans-unit>
<trans-unit id="patternMatchGuardIsNotBool">
Expand Down Expand Up @@ -5434,7 +5434,7 @@
</trans-unit>
<trans-unit id="fscBadAssemblyVersion">
<source>The attribute {0} specified version '{1}', but this value is invalid and has been ignored</source>
<target state="translated">Třída {0} definovala verzi {1}, ale tato hodnota není platná a ignoruje se.</target>
<target state="translated">Atribut {0} určoval verzi {1}, ale tato hodnota není platná a ignoruje se.</target>
<note />
</trans-unit>
<trans-unit id="fscTwoResourceManifests">
Expand Down Expand Up @@ -6979,7 +6979,7 @@
</trans-unit>
<trans-unit id="keywordDescriptionMatchBang">
<source>Used in computation expressions to pattern match directly over the result of another computation expression.</source>
<target state="new">Used in computation expressions to pattern match directly over the result of another computation expression.</target>
<target state="translated">Používá se ve výrazech výpočtu k přímému porovnání vzorů s výsledkem jiného výrazu výpočtu.</target>
<note />
</trans-unit>
<trans-unit id="ilreadFileChanged">
Expand Down
6 changes: 3 additions & 3 deletions src/fsharp/xlf/FSComp.txt.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
</trans-unit>
<trans-unit id="followingPatternMatchClauseHasWrongType">
<source>All branches of a pattern match expression must return values of the same type. The first branch returned a value of type '{0}', but this branch returned a value of type '{1}'.</source>
<target state="needs-review-translation">Alle Branches eines Musterabgleichsausdrucks müssen den gleichen Typ aufweisen. Es wurde erwartet, dass dieser Ausdruck den Typ "{0}" aufweist, hier liegt aber der Typ "{1}" vor.</target>
<target state="translated">Alle Branches eines Musterübereinstimmungsausdrucks müssen Werte des gleichen Typs zurückgeben. Der erste Branch hat einen Wert vom Typ "{0}" zurückgegeben, aber dieser Branch gab einen Wert vom Typ "{1}" zurück.</target>
<note />
</trans-unit>
<trans-unit id="patternMatchGuardIsNotBool">
Expand Down Expand Up @@ -5434,7 +5434,7 @@
</trans-unit>
<trans-unit id="fscBadAssemblyVersion">
<source>The attribute {0} specified version '{1}', but this value is invalid and has been ignored</source>
<target state="translated">Ein {0} gab Version '{1}' an, dieser Wert ist jedoch ungültig und wurde ignoriert.</target>
<target state="translated">Das Attribut "{0}" hat Version "{1}" angegeben, dieser Wert ist jedoch ungültig und wurde ignoriert.</target>
<note />
</trans-unit>
<trans-unit id="fscTwoResourceManifests">
Expand Down Expand Up @@ -6979,7 +6979,7 @@
</trans-unit>
<trans-unit id="keywordDescriptionMatchBang">
<source>Used in computation expressions to pattern match directly over the result of another computation expression.</source>
<target state="new">Used in computation expressions to pattern match directly over the result of another computation expression.</target>
<target state="translated">Wird in Berechnungsausdrücken zur Musterübereinstimmung direkt über dem Ergebnis eines anderen Berechnungsausdrucks verwendet.</target>
<note />
</trans-unit>
<trans-unit id="ilreadFileChanged">
Expand Down
6 changes: 3 additions & 3 deletions src/fsharp/xlf/FSComp.txt.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
</trans-unit>
<trans-unit id="followingPatternMatchClauseHasWrongType">
<source>All branches of a pattern match expression must return values of the same type. The first branch returned a value of type '{0}', but this branch returned a value of type '{1}'.</source>
<target state="needs-review-translation">Todas las ramas de una expresión de coincidencia de patrones deben tener el mismo tipo. Se esperaba que esta expresión tuviera el tipo "{0}", pero aquí tiene el tipo "{1}".</target>
<target state="translated">Todas las ramas de una expresión de coincidencia de patrón deben devolver valores del mismo tipo. La primera rama devolvió un valor de tipo "{0}", pero esta rama devolvió un valor de tipo "\{1 \}".</target>
<note />
</trans-unit>
<trans-unit id="patternMatchGuardIsNotBool">
Expand Down Expand Up @@ -5434,7 +5434,7 @@
</trans-unit>
<trans-unit id="fscBadAssemblyVersion">
<source>The attribute {0} specified version '{1}', but this value is invalid and has been ignored</source>
<target state="translated">Un {0} ha especificado la versión "{1}", pero este valor no es válido y se ha omitido</target>
<target state="translated">El atributo {0} ha especificado la versión "{1}", pero este valor no es válido y se ha omitido</target>
<note />
</trans-unit>
<trans-unit id="fscTwoResourceManifests">
Expand Down Expand Up @@ -6979,7 +6979,7 @@
</trans-unit>
<trans-unit id="keywordDescriptionMatchBang">
<source>Used in computation expressions to pattern match directly over the result of another computation expression.</source>
<target state="new">Used in computation expressions to pattern match directly over the result of another computation expression.</target>
<target state="translated">Se utiliza en expresiones de cálculo para hacer coincidir patrones directamente sobre el resultado de otra expresión de cálculo.</target>
<note />
</trans-unit>
<trans-unit id="ilreadFileChanged">
Expand Down
6 changes: 3 additions & 3 deletions src/fsharp/xlf/FSComp.txt.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
</trans-unit>
<trans-unit id="followingPatternMatchClauseHasWrongType">
<source>All branches of a pattern match expression must return values of the same type. The first branch returned a value of type '{0}', but this branch returned a value of type '{1}'.</source>
<target state="needs-review-translation">Toutes les branches d'une expression comportant des critères spéciaux doivent avoir le même type. Cette expression était censée avoir le type '{0}', mais elle a ici le type '{1}'.</target>
<target state="translated">Toutes les branches d'une expression comportant des critères spéciaux doivent retourner des valeurs du même type. La première branche a retourné une valeur de type '{0}', mais cette branche a retourné une valeur de type '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="patternMatchGuardIsNotBool">
Expand Down Expand Up @@ -5434,7 +5434,7 @@
</trans-unit>
<trans-unit id="fscBadAssemblyVersion">
<source>The attribute {0} specified version '{1}', but this value is invalid and has been ignored</source>
<target state="translated">Un {0} a spécifié la version '{1}', mais cette valeur n'est pas valide et a été ignorée</target>
<target state="translated">L'attribut {0} a spécifié la version '{1}', mais cette valeur est non valide et a été ignorée</target>
<note />
</trans-unit>
<trans-unit id="fscTwoResourceManifests">
Expand Down Expand Up @@ -6979,7 +6979,7 @@
</trans-unit>
<trans-unit id="keywordDescriptionMatchBang">
<source>Used in computation expressions to pattern match directly over the result of another computation expression.</source>
<target state="new">Used in computation expressions to pattern match directly over the result of another computation expression.</target>
<target state="translated">Permet dans les expressions de calcul d'appliquer directement des critères spéciaux au résultat d'une autre expression de calcul.</target>
<note />
</trans-unit>
<trans-unit id="ilreadFileChanged">
Expand Down
6 changes: 3 additions & 3 deletions src/fsharp/xlf/FSComp.txt.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
</trans-unit>
<trans-unit id="followingPatternMatchClauseHasWrongType">
<source>All branches of a pattern match expression must return values of the same type. The first branch returned a value of type '{0}', but this branch returned a value of type '{1}'.</source>
<target state="needs-review-translation">Il tipo di tutti i rami di un'espressione di criteri di ricerca deve essere lo stesso. Il tipo previsto di questa espressione è '{0}', ma quello effettivo è '{1}'.</target>
<target state="translated">Tutti i rami di un'espressione di criteri di ricerca devono restituire valori dello stesso tipo. Il primo ramo ha restituito un valore di tipo '{0}', ma questo ramo ha restituito un valore di tipo '{1}'.</target>
<note />
</trans-unit>
<trans-unit id="patternMatchGuardIsNotBool">
Expand Down Expand Up @@ -5434,7 +5434,7 @@
</trans-unit>
<trans-unit id="fscBadAssemblyVersion">
<source>The attribute {0} specified version '{1}', but this value is invalid and has been ignored</source>
<target state="translated">Un oggetto {0} ha specificato la versione '{1}', ma questo valore non è valido ed è stato ignorato</target>
<target state="translated">L'attributo {0} ha specificato la versione '{1}', ma questo valore non è valido ed è stato ignorato</target>
<note />
</trans-unit>
<trans-unit id="fscTwoResourceManifests">
Expand Down Expand Up @@ -6979,7 +6979,7 @@
</trans-unit>
<trans-unit id="keywordDescriptionMatchBang">
<source>Used in computation expressions to pattern match directly over the result of another computation expression.</source>
<target state="new">Used in computation expressions to pattern match directly over the result of another computation expression.</target>
<target state="translated">Usata nelle espressioni di calcolo per includere direttamente nei criteri di ricerca il risultato di un'altra espressione di calcolo.</target>
<note />
</trans-unit>
<trans-unit id="ilreadFileChanged">
Expand Down
Loading