From 1ba6b74fa4daf7d88590072239a94060a0b7a215 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 9 Apr 2020 19:19:45 +0100 Subject: [PATCH 01/87] string interploation implementation --- .gitignore | 2 + src/fsharp/CheckFormatStrings.fs | 117 +++--- src/fsharp/CheckFormatStrings.fsi | 5 +- src/fsharp/CompileOps.fs | 4 + src/fsharp/ErrorLogger.fs | 7 +- src/fsharp/FSComp.txt | 5 + src/fsharp/FSStrings.resx | 12 + src/fsharp/FSharp.Core/printf.fs | 171 ++++++--- src/fsharp/FSharp.Core/printf.fsi | 8 + src/fsharp/LanguageFeatures.fs | 3 + src/fsharp/LanguageFeatures.fsi | 1 + src/fsharp/LexFilter.fs | 22 +- src/fsharp/ParseHelpers.fs | 16 +- src/fsharp/PostInferenceChecks.fs | 2 +- src/fsharp/SyntaxTree.fs | 8 +- src/fsharp/SyntaxTreeOps.fs | 3 + src/fsharp/TcGlobals.fs | 6 + src/fsharp/TypeChecker.fs | 72 +++- src/fsharp/TypedTreeOps.fs | 3 + src/fsharp/TypedTreeOps.fsi | 2 + src/fsharp/lex.fsl | 353 +++++++++++------- src/fsharp/lexhelp.fs | 56 ++- src/fsharp/lexhelp.fsi | 15 +- src/fsharp/pars.fsy | 35 +- src/fsharp/service/ServiceLexing.fs | 121 ++++-- src/fsharp/service/ServiceParseTreeWalk.fs | 75 +++- src/fsharp/service/ServiceUntypedParse.fs | 3 + .../SurfaceArea.coreclr.fs | 1 + .../SurfaceArea.net40.fs | 1 + tests/fsharp/FSharpSuite.Tests.fsproj | 1 + tests/service/TokenizerTests.fs | 36 +- 31 files changed, 827 insertions(+), 339 deletions(-) diff --git a/.gitignore b/.gitignore index 7f3870c3d05..997f346844c 100644 --- a/.gitignore +++ b/.gitignore @@ -130,3 +130,5 @@ msbuild.binlog /fcs/FSharp.Compiler.Service.netstandard/*.fsi /.ionide/ **/.DS_Store +Session.vim +coc-settings.json diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index 4b09c88665e..fb473ced98a 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -47,7 +47,7 @@ let newInfo () = addZeros = false precision = false} -let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringCheckContext option) fmt bty cty = +let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (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) = @@ -77,7 +77,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe if acc |> List.forall (fun (p, _) -> p = None) then // without positional specifiers acc |> List.map snd |> List.rev else - failwithf "%s" <| FSComp.SR.forPositionalSpecifiersNotPermitted() + raise (Failure (FSComp.SR.forPositionalSpecifiersNotPermitted())) argtys elif System.Char.IsSurrogatePair(fmt,i) then parseLoop acc (i+2, relLine, relCol+2) @@ -88,65 +88,65 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe let startCol = relCol let relCol = relCol+1 let i = i+1 - if i >= len then failwithf "%s" <| FSComp.SR.forMissingFormatSpecifier() + if i >= len then raise (Failure (FSComp.SR.forMissingFormatSpecifier())) let info = newInfo() let rec flags i = - if i >= len then failwithf "%s" <| FSComp.SR.forMissingFormatSpecifier() + if i >= len then raise (Failure (FSComp.SR.forMissingFormatSpecifier())) match fmt.[i] with | '-' -> - if info.leftJustify then failwithf "%s" <| FSComp.SR.forFlagSetTwice("-") + if info.leftJustify then raise (Failure (FSComp.SR.forFlagSetTwice("-"))) info.leftJustify <- true flags(i+1) | '+' -> - if info.numPrefixIfPos <> None then failwithf "%s" <| FSComp.SR.forPrefixFlagSpacePlusSetTwice() + if info.numPrefixIfPos <> None then raise (Failure (FSComp.SR.forPrefixFlagSpacePlusSetTwice())) info.numPrefixIfPos <- Some '+' flags(i+1) | '0' -> - if info.addZeros then failwithf "%s" <| FSComp.SR.forFlagSetTwice("0") + if info.addZeros then raise (Failure (FSComp.SR.forFlagSetTwice("0"))) info.addZeros <- true flags(i+1) | ' ' -> - if info.numPrefixIfPos <> None then failwithf "%s" <| FSComp.SR.forPrefixFlagSpacePlusSetTwice() + if info.numPrefixIfPos <> None then raise (Failure (FSComp.SR.forPrefixFlagSpacePlusSetTwice())) info.numPrefixIfPos <- Some ' ' flags(i+1) - | '#' -> failwithf "%s" <| FSComp.SR.forHashSpecifierIsInvalid() + | '#' -> raise (Failure (FSComp.SR.forHashSpecifierIsInvalid() )) | _ -> i let rec digitsPrecision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) match fmt.[i] with | c when System.Char.IsDigit c -> digitsPrecision (i+1) | _ -> i let precision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadWidth() + if i >= len then raise (Failure (FSComp.SR.forBadWidth())) match fmt.[i] with | c when System.Char.IsDigit c -> info.precision <- true; false,digitsPrecision (i+1) | '*' -> info.precision <- true; true,(i+1) - | _ -> failwithf "%s" <| FSComp.SR.forPrecisionMissingAfterDot() + | _ -> raise (Failure (FSComp.SR.forPrecisionMissingAfterDot())) let optionalDotAndPrecision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) match fmt.[i] with | '.' -> precision (i+1) | _ -> false,i let rec digitsWidthAndPrecision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) match fmt.[i] with | c when System.Char.IsDigit c -> digitsWidthAndPrecision (i+1) | _ -> optionalDotAndPrecision i let widthAndPrecision i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) match fmt.[i] with | c when System.Char.IsDigit c -> false,digitsWidthAndPrecision i | '*' -> true,optionalDotAndPrecision (i+1) | _ -> false,optionalDotAndPrecision i let rec digitsPosition n i = - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) match fmt.[i] with | c when System.Char.IsDigit c -> digitsPosition (n*10 + int c - int '0') (i+1) | '$' -> Some n, i+1 @@ -171,22 +171,35 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe let widthArg,(precisionArg,i) = widthAndPrecision i let relCol = relCol + i - oldI - if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() + if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) let acc = if precisionArg then (Option.map ((+)1) posi, g.int_ty) :: acc else acc let acc = if widthArg then (Option.map ((+)1) posi, g.int_ty) :: acc else acc - let checkNoPrecision c = if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(c.ToString()) - let checkNoZeroFlag c = if info.addZeros then failwithf "%s" <| FSComp.SR.forDoesNotSupportZeroFlag(c.ToString()) - let checkNoNumericPrefix c = if info.numPrefixIfPos <> None then - failwithf "%s" <| FSComp.SR.forDoesNotSupportPrefixFlag(c.ToString(), (Option.get info.numPrefixIfPos).ToString()) + let checkNoPrecision c = + if info.precision then raise (Failure (FSComp.SR.forFormatDoesntSupportPrecision(c.ToString()))) + + let checkNoZeroFlag c = + if info.addZeros then raise (Failure (FSComp.SR.forDoesNotSupportZeroFlag(c.ToString()))) + + let checkNoNumericPrefix c = + match info.numPrefixIfPos with + | Some n -> raise (Failure (FSComp.SR.forDoesNotSupportPrefixFlag(c.ToString(), n.ToString()))) + | None -> () let checkOtherFlags c = checkNoPrecision c checkNoZeroFlag c checkNoNumericPrefix c + let skipInterp i = + // Explicitly typed holes in interpolated strings get '%P' after them as hole place marker + if isInterp then + if i+1 < fmt.Length && fmt.[i] = '%' && fmt.[i+1] = 'P' then i + 2 + else raise (Failure (FSComp.SR.forFormatInvalidForInterpolated())) + else i + let collectSpecifierLocation relLine relCol numStdArgs = let numArgsForSpecifier = numStdArgs + (if widthArg then 1 else 0) + (if precisionArg then 1 else 0) @@ -209,55 +222,68 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe parseLoop acc (i+1, relLine, relCol+1) | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> - if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()) + if info.precision then raise (Failure (FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()))) collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i+1, relLine, relCol+1) + let i = skipInterp (i+1) + parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, relLine, relCol+1) | ('l' | 'L') -> - if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()) + if info.precision then raise (Failure (FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()))) let relCol = relCol+1 let i = i+1 // "bad format specifier ... In F# code you can use %d, %x, %o or %u instead ..." if i >= len then - failwithf "%s" <| FSComp.SR.forBadFormatSpecifier() + raise (Failure (FSComp.SR.forBadFormatSpecifier())) // Always error for %l and %Lx - failwithf "%s" <| FSComp.SR.forLIsUnnecessary() + raise (Failure (FSComp.SR.forLIsUnnecessary())) match fmt.[i] with | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i+1, relLine, relCol+1) - | _ -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifier() + let i = skipInterp (i+1) + parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, relLine, relCol+1) + | _ -> raise (Failure (FSComp.SR.forBadFormatSpecifier())) | ('h' | 'H') -> - failwithf "%s" <| FSComp.SR.forHIsUnnecessary() + raise (Failure (FSComp.SR.forHIsUnnecessary())) | 'M' -> collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, mkFlexibleDecimalFormatTypar g m) :: acc) (i+1, relLine, relCol+1) + let i = skipInterp (i+1) + parseLoop ((posi, mkFlexibleDecimalFormatTypar g m) :: acc) (i, relLine, relCol+1) | ('f' | 'F' | 'e' | 'E' | 'g' | 'G') -> collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, mkFlexibleFloatFormatTypar g m) :: acc) (i+1, relLine, relCol+1) + let i = skipInterp (i+1) + parseLoop ((posi, mkFlexibleFloatFormatTypar g m) :: acc) (i, relLine, relCol+1) | 'b' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, g.bool_ty) :: acc) (i+1, relLine, relCol+1) + let i = skipInterp (i+1) + parseLoop ((posi, g.bool_ty) :: acc) (i, relLine, relCol+1) | 'c' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, g.char_ty) :: acc) (i+1, relLine, relCol+1) + let i = skipInterp (i+1) + parseLoop ((posi, g.char_ty) :: acc) (i, relLine, relCol+1) | 's' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, g.string_ty) :: acc) (i+1, relLine, relCol+1) + let i = skipInterp (i+1) + parseLoop ((posi, g.string_ty) :: acc) (i, relLine, relCol+1) | 'O' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 + let i = skipInterp (i+1) + parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) + + // residue of hole "...{n}..." in interpolated strings + | 'P' when isInterp -> + checkOtherFlags ch parseLoop ((posi, NewInferenceType ()) :: acc) (i+1, relLine, relCol+1) | 'A' -> @@ -265,22 +291,25 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe | None // %A has BindingFlags=Public, %+A has BindingFlags=Public | NonPublic | Some '+' -> collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, NewInferenceType ()) :: acc) (i+1, relLine, relCol+1) - | Some _ -> failwithf "%s" <| FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), (Option.get info.numPrefixIfPos).ToString()) + let i = skipInterp (i+1) + parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) + | Some n -> raise (Failure (FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), n.ToString()))) | 'a' -> checkOtherFlags ch let xty = NewInferenceType () let fty = bty --> (xty --> cty) collectSpecifierLocation relLine relCol 2 - parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i+1, relLine, relCol+1) + let i = skipInterp (i+1) + parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i, relLine, relCol+1) | 't' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - parseLoop ((posi, bty --> cty) :: acc) (i+1, relLine, relCol+1) + let i = skipInterp (i+1) + parseLoop ((posi, bty --> cty) :: acc) (i, relLine, relCol+1) - | c -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c) + | c -> raise (Failure (FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c))) | '\n' -> parseLoop acc (i+1, relLine+1, 0) | _ -> parseLoop acc (i+1, relLine, relCol+1) @@ -288,15 +317,15 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) (context: FormatStringChe let results = parseLoop [] (0, 0, m.StartColumn) results, Seq.toList specifierLocations -let ParseFormatString m g formatStringCheckContext fmt bty cty dty = - let argtys, specifierLocations = parseFormatStringInternal m g formatStringCheckContext fmt bty cty +let ParseFormatString m g isInterp formatStringCheckContext fmt bty cty dty = + let argtys, specifierLocations = parseFormatStringInternal m g isInterp formatStringCheckContext fmt bty cty let aty = List.foldBack (-->) argtys dty let ety = mkRefTupledTy g argtys - (aty, ety), specifierLocations + (argtys, aty, ety), specifierLocations -let TryCountFormatStringArguments m g fmt bty cty = +let TryCountFormatStringArguments m g isInterp fmt bty cty = try - let argtys, _specifierLocations = parseFormatStringInternal m g None fmt bty cty + let argtys, _specifierLocations = parseFormatStringInternal m g isInterp None fmt bty cty Some argtys.Length with _ -> None diff --git a/src/fsharp/CheckFormatStrings.fsi b/src/fsharp/CheckFormatStrings.fsi index 91cce79b7d4..4053813e291 100644 --- a/src/fsharp/CheckFormatStrings.fsi +++ b/src/fsharp/CheckFormatStrings.fsi @@ -9,9 +9,10 @@ module internal FSharp.Compiler.CheckFormatStrings open FSharp.Compiler open FSharp.Compiler.NameResolution +open FSharp.Compiler.Range open FSharp.Compiler.TypedTree open FSharp.Compiler.TcGlobals -val ParseFormatString : Range.range -> TcGlobals -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType * TType) * (Range.range * int) list +val ParseFormatString : m: range -> g: TcGlobals -> isInterp: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType list * TType * TType) * (range * int) list -val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> fmt:string -> bty:TType -> cty:TType -> int option +val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> isInterp: bool -> fmt:string -> bty:TType -> cty:TType -> int option diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 161b6731950..29c99d9258f 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -1165,6 +1165,10 @@ let OutputPhasedErrorR (os: StringBuilder) (err: PhasedDiagnostic) (canSuggestNa | Parser.TOKEN_EOF -> getErrorString("Parser.TOKEN.EOF") | Parser.TOKEN_CONST -> getErrorString("Parser.TOKEN.CONST") | Parser.TOKEN_FIXED -> getErrorString("Parser.TOKEN.FIXED") + | Parser.TOKEN_INTERP_STRING_BEGIN_END -> getErrorString("Parser.TOKEN.INTERP.STRING.BEGIN.END") + | Parser.TOKEN_INTERP_STRING_BEGIN_PART -> getErrorString("Parser.TOKEN.INTERP.STRING.BEGIN.PART") + | Parser.TOKEN_INTERP_STRING_PART -> getErrorString("Parser.TOKEN.INTERP.STRING.PART") + | Parser.TOKEN_INTERP_STRING_END -> getErrorString("Parser.TOKEN.INTERP.STRING.END") | unknown -> Debug.Assert(false, "unknown token tag") let result = sprintf "%+A" unknown diff --git a/src/fsharp/ErrorLogger.fs b/src/fsharp/ErrorLogger.fs index 3e345ea4abf..85caca57d78 100755 --- a/src/fsharp/ErrorLogger.fs +++ b/src/fsharp/ErrorLogger.fs @@ -688,4 +688,9 @@ let internal tryLanguageFeatureError langVersion langFeature m = tryLanguageFeatureErrorAux langVersion langFeature m error let internal tryLanguageFeatureErrorRecover langVersion langFeature m = - tryLanguageFeatureErrorAux langVersion langFeature m errorR \ No newline at end of file + tryLanguageFeatureErrorAux langVersion langFeature m errorR + +let internal languageFeatureNotSupportedInLibraryError (langVersion: LanguageVersion) (langFeature: LanguageFeature) (m: range) = + let featureStr = langVersion.GetFeatureString langFeature + let suggestedVersionStr = langVersion.GetFeatureVersionString langFeature + error (Error(FSComp.SR.chkFeatureNotSupportedInLibrary(featureStr, suggestedVersionStr), m)) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index bbcd42bf9f7..6b9d4975bbf 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1489,6 +1489,8 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3350,chkFeatureNotLanguageSupported,"Feature '%s' is not available in F# %s. Please use language version %s or greater." 3351,chkFeatureNotRuntimeSupported,"Feature '%s' is not supported by target runtime." 3352,typrelInterfaceMemberNoMostSpecificImplementation,"Interface member '%s' does not have a most specific implementation." +3353,chkFeatureNotSupportedInLibrary,"Feature '%s' requires the F# library for language version %s or greater." +3360,lexByteStringMayNotBeInterpolated,"a byte string may not be interpolated" useSdkRefs,"Use reference assemblies for .NET framework references when available (Enabled by default)." fSharpBannerVersion,"%s for F# %s" optsLangVersion,"Display the allowed values for language version, specify language version such as 'latest' or 'preview'" @@ -1509,3 +1511,6 @@ featureFixedIndexSlice3d4d,"fixed-index slice 3d/4d" featureAndBang,"applicative computation expressions" featureNullableOptionalInterop,"nullable optional interop" featureDefaultInterfaceMemberConsumption,"default interface member consumption" +featureStringInterpolation,"string interpolation" +3361,tcInterpolationMixedWithPercent,"Mismatch in interpolated string. Interpolated strings may not use '%%' formats unless each is given an expression, e.g. '%%d{{1+1}}'" +forFormatInvalidForInterpolated,"Invalid interpolated string. interpolated strings may not use '%%' formats unless each is given an expression, e.g. '%%d{{1+1}}'" diff --git a/src/fsharp/FSStrings.resx b/src/fsharp/FSStrings.resx index e755b590b49..1b0f5b38379 100644 --- a/src/fsharp/FSStrings.resx +++ b/src/fsharp/FSStrings.resx @@ -441,6 +441,18 @@ keyword 'fixed' + + interpolated string + + + interpolated string (first part) + + + interpolated string (part) + + + interpolated string (final part) + keyword 'constraint' diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index d751ba1317f..0e008aa4fad 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -41,13 +41,12 @@ module internal PrintfImpl = open System.Text open System.Collections.Generic + open System.Collections.Concurrent open System.Reflection open Microsoft.FSharp.Core open Microsoft.FSharp.Core.Operators open Microsoft.FSharp.Collections open LanguagePrimitives.IntrinsicOperators - - open System.IO [] type FormatFlags = @@ -130,6 +129,9 @@ module internal PrintfImpl = let parseTypeChar (s: string) i = s.[i], (i + 1) + + let skipInterpHole isInterp (s:string) i = + if isInterp && i+1 < s.Length && s.[i] = '%' && s.[i+1] = 'P' then i+2 else i let findNextFormatSpecifier (s: string) i = let rec go i (buf: Text.StringBuilder) = @@ -143,6 +145,7 @@ module internal PrintfImpl = let w, i2 = parseWidth s i1 let p, i3 = parsePrecision s i2 let typeChar, i4 = parseTypeChar s i3 + // shortcut for the simpliest case // if typeChar is not % or it has star as width\precision - resort to long path if typeChar = '%' && not (w = StarValue || p = StarValue) then @@ -170,33 +173,43 @@ module internal PrintfImpl = static member inline Write (env: PrintfEnv<_, _, _>, a, b) = env.Write a env.Write b + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c) = Utils.Write(env, a, b) env.Write c + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d) = Utils.Write(env, a, b) Utils.Write(env, c, d) + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e) = Utils.Write(env, a, b, c) Utils.Write(env, d, e) + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f) = Utils.Write(env, a, b, c, d) Utils.Write(env, e, f) + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g) = Utils.Write(env, a, b, c, d, e) Utils.Write(env, f, g) + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h) = Utils.Write(env, a, b, c, d, e, f) Utils.Write(env, g, h) + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h, i) = Utils.Write(env, a, b, c, d, e, f, g) Utils.Write(env, h, i) + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h, i, j) = Utils.Write(env, a, b, c, d, e, f, g, h) Utils.Write(env, i, j) + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h, i, j, k) = Utils.Write(env, a, b, c, d, e, f, g, h, i) Utils.Write(env, j, k) + static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h, i, j, k, l, m) = Utils.Write(env, a, b, c, d, e, f, g, h, i, j, k) Utils.Write(env, l, m) @@ -980,6 +993,7 @@ module internal PrintfImpl = System.Diagnostics.Debug.Assert((padChar = ' ')) fun (fmt: string) (w: int) v -> rightJustifyWithSpaceAsPadChar (f fmt v) true (isPositive v) w prefixForPositives + module Float = let inline noJustification f (prefixForPositives: string) = fun (fmt: string) v -> @@ -1054,16 +1068,20 @@ module internal PrintfImpl = else raise (ArgumentException()) type ObjectPrinter = + static member ObjectToString<'T>(spec: FormatSpecifier) = basicWithPadding spec (fun (v: 'T) -> match box v with null -> "" | x -> x.ToString()) + /// Convert an interpoland to a string + static member InterpolandToString<'T>(spec: FormatSpecifier) = + basicWithPadding spec (fun (v: 'T) -> match box v with null -> "" | x -> x.ToString()) + static member GenericToStringCore(v: 'T, opts: Microsoft.FSharp.Text.StructuredPrintfImpl.FormatOptions, bindingFlags) = - // printfn %0A is considered to mean 'print width zero' - match box v with - | null -> - Microsoft.FSharp.Text.StructuredPrintfImpl.Display.anyToStringForPrintf opts bindingFlags (v, typeof<'T>) - | _ -> - Microsoft.FSharp.Text.StructuredPrintfImpl.Display.anyToStringForPrintf opts bindingFlags (v, v.GetType()) + let vty = + match box v with + | null -> typeof<'T> + | _ -> v.GetType() + Microsoft.FSharp.Text.StructuredPrintfImpl.Display.anyToStringForPrintf opts bindingFlags (v, vty) static member GenericToString<'T>(spec: FormatSpecifier) = let bindingFlags = @@ -1079,6 +1097,7 @@ module internal PrintfImpl = else o if spec.IsPrecisionSpecified then { o with PrintSize = spec.Precision} else o + match spec.IsStarWidth, spec.IsStarPrecision with | true, true -> box (fun (v: 'T) (width: int) (prec: int) -> @@ -1086,21 +1105,21 @@ module internal PrintfImpl = let opts = if not useZeroWidth then { opts with PrintWidth = width} else opts ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) ) + | true, false -> box (fun (v: 'T) (width: int) -> let opts = if not useZeroWidth then { opts with PrintWidth = width} else opts - ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) - ) + ObjectPrinter.GenericToStringCore(v, opts, bindingFlags)) + | false, true -> box (fun (v: 'T) (prec: int) -> let opts = { opts with PrintSize = prec } - ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) - ) + ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) ) + | false, false -> box (fun (v: 'T) -> - ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) - ) - + ObjectPrinter.GenericToStringCore(v, opts, bindingFlags)) + let basicNumberToString (ty: Type) (spec: FormatSpecifier) = System.Diagnostics.Debug.Assert(not spec.IsPrecisionSpecified, "not spec.IsPrecisionSpecified") @@ -1139,6 +1158,10 @@ module internal PrintfImpl = let private NonPublicStatics = BindingFlags.NonPublic ||| BindingFlags.Static + let mi_GenericToString = typeof.GetMethod("GenericToString", NonPublicStatics) + let mi_ObjectToString = typeof.GetMethod("ObjectToString", NonPublicStatics) + let mi_InterpolandToString = typeof.GetMethod("InterpolandToString", NonPublicStatics) + let private getValueConverter (ty: Type) (spec: FormatSpecifier) : obj = match spec.TypeChar with | 'b' -> @@ -1160,12 +1183,13 @@ module internal PrintfImpl = | 'g' | 'G' -> basicFloatToString ty spec | 'A' -> - let mi = typeof.GetMethod("GenericToString", NonPublicStatics) - let mi = mi.MakeGenericMethod ty + let mi = mi_GenericToString.MakeGenericMethod ty mi.Invoke(null, [| box spec |]) | 'O' -> - let mi = typeof.GetMethod("ObjectToString", NonPublicStatics) - let mi = mi.MakeGenericMethod ty + let mi = mi_ObjectToString.MakeGenericMethod ty + mi.Invoke(null, [| box spec |]) + | 'P' -> + let mi = mi_InterpolandToString.MakeGenericMethod ty mi.Invoke(null, [| box spec |]) | _ -> raise (ArgumentException(SR.GetString(SR.printfBadFormatSpecifier))) @@ -1242,6 +1266,15 @@ module internal PrintfImpl = System.Diagnostics.Debug.Assert(args.Count = types.Count, "args.Count = types.Count") args.Count = 0 + /// Type of element that is stored in cache + /// Pair: factory for the printer + number of text blocks that printer will produce (used to preallocate buffers) + [] + type CachedItem<'T, 'State, 'Residue, 'Result> = + { format: string + factory: PrintfFactory<'State, 'Residue, 'Result, 'T> + blockCount: int + isInterp: bool } + /// Parses format string and creates result printer function. /// First it recursively consumes format string up to the end, then during unwinding builds printer using PrintfBuilderStack as storage for arguments. /// idea of implementation is very simple: every step can either push argument to the stack (if current block of 5 format specifiers is not yet filled) @@ -1399,10 +1432,9 @@ module internal PrintfImpl = else buildPlainFinal(plainArgs, plainTypes) - let rec parseFromFormatSpecifier (prefix: string) (s: string) (funcTy: Type) i: int = + let rec parseFromFormatSpecifier isInterp (prefix: string) (s: string) (funcTy: Type) i: int = - if i >= s.Length then 0 - else + if i >= s.Length then 0 else System.Diagnostics.Debug.Assert(s.[i] = '%', "s.[i] = '%'") count <- count + 1 @@ -1411,6 +1443,9 @@ module internal PrintfImpl = let width, i = FormatString.parseWidth s i let precision, i = FormatString.parsePrecision s i let typeChar, i = FormatString.parseTypeChar s i + // Skip %P insertion points added after %d in interpolated strings + let i = FormatString.skipInterpHole isInterp s i + let spec = { TypeChar = typeChar; Precision = precision; Flags = flags; Width = width} let next, suffix = FormatString.findNextFormatSpecifier s i @@ -1431,7 +1466,7 @@ module internal PrintfImpl = let retTy = argTys.[argTys.Length - 1] - let numberOfArgs = parseFromFormatSpecifier suffix s retTy next + let numberOfArgs = parseFromFormatSpecifier isInterp suffix s retTy next if spec.TypeChar = 'a' || spec.TypeChar = 't' || spec.IsStarWidth || spec.IsStarPrecision then if numberOfArgs = ContinuationOnStack then @@ -1491,7 +1526,7 @@ module internal PrintfImpl = else numberOfArgs + 1 - let parseFormatString (s: string) (funcTy: System.Type) : obj = + let parseFormatString isInterp (s: string) (funcTy: System.Type) : obj = optimizedArgCount <- 0 let prefixPos, prefix = FormatString.findNextFormatSpecifier s 0 if prefixPos = s.Length then @@ -1501,41 +1536,55 @@ module internal PrintfImpl = env.Finish() ) else - let n = parseFromFormatSpecifier prefix s funcTy prefixPos + let n = parseFromFormatSpecifier isInterp prefix s funcTy prefixPos if n = ContinuationOnStack || n = 0 then builderStack.PopValueUnsafe() else buildPlain n prefix - member __.Build<'T>(s: string) : PrintfFactory<'S, 'Re, 'Res, 'T> * int = - parseFormatString s typeof<'T> :?> _, (2 * count + 1) - optimizedArgCount // second component is used in SprintfEnv as value for internal buffer - - /// Type of element that is stored in cache - /// Pair: factory for the printer + number of text blocks that printer will produce (used to preallocate buffers) - type CachedItem<'T, 'State, 'Residue, 'Result> = PrintfFactory<'State, 'Residue, 'Result, 'T> * int + member __.Build<'T>(s: string, isInterp: bool) = + { format = s + factory = (parseFormatString isInterp s typeof<'T> :?> PrintfFactory<'S, 'Re, 'Res, 'T>) + blockCount = (2 * count + 1) - optimizedArgCount // second component is used in SprintfEnv as value for internal buffer + isInterp = isInterp } /// 2-level cache. - /// 1st-level stores last value that was consumed by the current thread in thread-static field thus providing shortcuts for scenarios when - /// printf is called in tight loop - /// 2nd level is global dictionary that maps format string to the corresponding PrintfFactory + /// + /// We can use the same caches for both interpolated and non-interpolated strings + /// since interpolated strings contain %P and don't overlap with non-interpolation strings, and if an interpolated + /// string doesn't contain %P then the processing of the format strings is semantically identical. type Cache<'T, 'State, 'Residue, 'Result>() = - static let generate fmt = PrintfBuilder<'State, 'Residue, 'Result>().Build<'T>(fmt) - static let mutable map = System.Collections.Concurrent.ConcurrentDictionary>() - static let getOrAddFunc = Func<_, _>(generate) - static let get (key: string) = map.GetOrAdd(key, getOrAddFunc) - - [] - [] - static val mutable private last: string * CachedItem<'T, 'State, 'Residue, 'Result> + + /// 1st level cache (type-indexed). Stores last value that was consumed by the current + /// thread in thread-static field thus providing shortcuts for scenarios when printf is + /// called in tight loop. + [] + static val mutable private mostRecent: CachedItem<'T, 'State, 'Residue, 'Result> - static member Get(key: Format<'T, 'State, 'Residue, 'Result>) = - if not (Cache<'T, 'State, 'Residue, 'Result>.last === null) - && key.Value.Equals (fst Cache<'T, 'State, 'Residue, 'Result>.last) then - snd Cache<'T, 'State, 'Residue, 'Result>.last + // 2nd level cache (type-indexed). Dictionary that maps format string to the corresponding cache entry + static let mutable dict : ConcurrentDictionary> = null + + static member Get(fmt: Format<'T, 'State, 'Residue, 'Result>, isInterp) = + let cacheEntry = Cache<'T, 'State, 'Residue, 'Result>.mostRecent + let key = fmt.Value + if not (cacheEntry === null) && key.Equals cacheEntry.format then + cacheEntry else - let v = get key.Value - Cache<'T, 'State, 'Residue, 'Result>.last <- (key.Value, v) + // Initialize the 2nd level cache if necessary. Note there's a race condition but it doesn't + // matter if we initialize these values twice (and lose one entry) + if isNull dict then + dict <- ConcurrentDictionary<_,_>() + + let v = + match dict.TryGetValue(key) with + | true, res -> res + | _ -> + let entry = PrintfBuilder<'State, 'Residue, 'Result>().Build<'T>(key, isInterp) + // Note there's a race condition but it doesn't matter if lose one entry + dict.TryAdd(key, entry) |> ignore + entry + Cache<'T, 'State, 'Residue, 'Result>.mostRecent <- v v type StringPrintfEnv<'Result>(k, n) = @@ -1573,10 +1622,10 @@ module internal PrintfImpl = override __.Write(s: string) = tw.Write s override __.WriteT(()) = () - let inline doPrintf fmt f = - let formatter, n = Cache<_, _, _, _>.Get fmt - let env() = f n - formatter env + let inline doPrintf fmt isInterp f = + let formatter = Cache<_, _, _, _>.Get (fmt, isInterp) + let env() = f formatter.blockCount + formatter.factory env [] module Printf = @@ -1595,34 +1644,40 @@ module Printf = [] let ksprintf continuation (format: StringFormat<'T, 'Result>) : 'T = - doPrintf format (fun n -> + doPrintf format false (fun n -> if n <= 2 then SmallStringPrintfEnv continuation :> PrintfEnv<_, _, _> else StringPrintfEnv(continuation, n) :> PrintfEnv<_, _, _> ) - [] - let sprintf (format: StringFormat<'T>) = - doPrintf format (fun n -> + let inline sprintfAux isInterp (format: StringFormat<'T>) = + doPrintf format isInterp (fun n -> if n <= 2 then SmallStringPrintfEnv id :> PrintfEnv<_, _, _> else StringPrintfEnv(id, n) :> PrintfEnv<_, _, _> ) + [] + let sprintf (format: StringFormat<'T>) = sprintfAux false format + + [] + [] + let isprintf (format: StringFormat<'T>) = sprintfAux true format + [] let kprintf continuation format = ksprintf continuation format [] let kbprintf continuation (builder: StringBuilder) format = - doPrintf format (fun _ -> + doPrintf format false (fun _ -> StringBuilderPrintfEnv(continuation, builder) :> PrintfEnv<_, _, _> ) [] let kfprintf continuation textWriter format = - doPrintf format (fun _ -> + doPrintf format false (fun _ -> TextWriterPrintfEnv(continuation, textWriter) :> PrintfEnv<_, _, _> ) diff --git a/src/fsharp/FSharp.Core/printf.fsi b/src/fsharp/FSharp.Core/printf.fsi index 10e17ec68d8..1d406efcea9 100644 --- a/src/fsharp/FSharp.Core/printf.fsi +++ b/src/fsharp/FSharp.Core/printf.fsi @@ -214,6 +214,14 @@ module Printf = [] val sprintf : format:StringFormat<'T> -> 'T + /// Interpolated print to a string via an internal string buffer and return + /// the result as a string. Helper printers must return strings. + /// The input formatter. + /// The formatted string. + [] + [] + val isprintf: format:StringFormat<'T> -> 'T + /// bprintf, but call the given 'final' function to generate the result. /// See kprintf. /// The function called after formatting to generate the format result. diff --git a/src/fsharp/LanguageFeatures.fs b/src/fsharp/LanguageFeatures.fs index 05572cfb9e9..7b340029981 100644 --- a/src/fsharp/LanguageFeatures.fs +++ b/src/fsharp/LanguageFeatures.fs @@ -31,6 +31,7 @@ type LanguageFeature = | AndBang | NullableOptionalInterop | DefaultInterfaceMemberConsumption + | StringInterpolation /// LanguageVersion management type LanguageVersion (specifiedVersionAsString) = @@ -67,6 +68,7 @@ type LanguageVersion (specifiedVersionAsString) = LanguageFeature.AndBang, previewVersion LanguageFeature.NullableOptionalInterop, previewVersion LanguageFeature.DefaultInterfaceMemberConsumption, previewVersion + LanguageFeature.StringInterpolation, previewVersion ] let specified = @@ -135,6 +137,7 @@ type LanguageVersion (specifiedVersionAsString) = | LanguageFeature.AndBang -> FSComp.SR.featureAndBang() | LanguageFeature.NullableOptionalInterop -> FSComp.SR.featureNullableOptionalInterop() | LanguageFeature.DefaultInterfaceMemberConsumption -> FSComp.SR.featureDefaultInterfaceMemberConsumption() + | LanguageFeature.StringInterpolation -> FSComp.SR.featureStringInterpolation() /// Get a version string associated with the given feature. member _.GetFeatureVersionString feature = diff --git a/src/fsharp/LanguageFeatures.fsi b/src/fsharp/LanguageFeatures.fsi index fd367388bdd..0110454f67e 100644 --- a/src/fsharp/LanguageFeatures.fsi +++ b/src/fsharp/LanguageFeatures.fsi @@ -19,6 +19,7 @@ type LanguageFeature = | AndBang | NullableOptionalInterop | DefaultInterfaceMemberConsumption + | StringInterpolation /// LanguageVersion management type LanguageVersion = diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index b270910224b..1588a25cbe0 100644 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -385,6 +385,10 @@ let parenTokensBalance t1 t2 = | (CLASS, END) | (SIG, END) | (STRUCT, END) + | (INTERP_STRING_BEGIN_PART _, INTERP_STRING_END _) + | (INTERP_STRING_BEGIN_PART _, INTERP_STRING_PART _) + | (INTERP_STRING_PART _, INTERP_STRING_PART _) + | (INTERP_STRING_PART _, INTERP_STRING_END _) | (LBRACK_BAR, BAR_RBRACK) | (LESS true, GREATER true) | (BEGIN, END) -> true @@ -1229,6 +1233,8 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, | BAR_RBRACK | WITH | FINALLY + | INTERP_STRING_PART _ + | INTERP_STRING_END _ | RQUOTE _ -> not (tokenBalancesHeadContext token stack) && // Only close the context if some context is going to match at some point in the stack. @@ -1362,12 +1368,17 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, hwTokenFetch useBlockRule // Balancing rule. Encountering a ')' or '}' balances with a '(' or '{', even if not offside - | ((END | RPAREN | RBRACE | BAR_RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ | GREATER true) as t2), (CtxtParen (t1, _) :: _) + | ((END | RPAREN | RBRACE | BAR_RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ | GREATER true | INTERP_STRING_END _ | INTERP_STRING_PART _) as t2), (CtxtParen (t1, _) :: _) when parenTokensBalance t1 t2 -> if debug then dprintf "RPAREN/RBRACE/BAR_RBRACE/RBRACK/BAR_RBRACK/RQUOTE/END at %a terminates CtxtParen()\n" outputPos tokenStartPos popCtxt() - // Queue a dummy token at this position to check if any closing rules apply - delayToken(pool.UseLocation(tokenTup, ODUMMY token)) + match t2 with + | INTERP_STRING_PART _ -> + pushCtxt tokenTup (CtxtParen (token, tokenStartPos)) + pushCtxtSeqBlock(false, NoAddBlockEnd) + | _ -> + // Queue a dummy token at this position to check if any closing rules apply + delayToken(pool.UseLocation(tokenTup, ODUMMY token)) returnToken tokenLexbufState token // Balancing rule. Encountering a 'end' can balance with a 'with' but only when not offside @@ -1852,6 +1863,7 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, else returnToken tokenLexbufState token + // 'with id = ' ~~~> CtxtSeqBlock // 'with M.id = ' ~~~> CtxtSeqBlock // 'with id1 = 1 @@ -1889,7 +1901,9 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, returnToken tokenLexbufState token // '(' tokens are balanced with ')' tokens and also introduce a CtxtSeqBlock - | (BEGIN | LPAREN | SIG | LBRACE | LBRACE_BAR | LBRACK | LBRACK_BAR | LQUOTE _ | LESS true), _ -> + // $".... { ... } ... { ....} " pushes a block context at first { + // $".... { ... } ... { ....} " pushes a block context at second { + | (BEGIN | LPAREN | SIG | LBRACE | LBRACE_BAR | LBRACK | LBRACK_BAR | LQUOTE _ | LESS true | INTERP_STRING_BEGIN_PART _), _ -> if debug then dprintf "LPAREN etc., pushes CtxtParen, pushing CtxtSeqBlock, tokenStartPos = %a\n" outputPos tokenStartPos pushCtxt tokenTup (CtxtParen (token, tokenStartPos)) pushCtxtSeqBlock(false, NoAddBlockEnd) diff --git a/src/fsharp/ParseHelpers.fs b/src/fsharp/ParseHelpers.fs index 0024246643b..5b3f3c59447 100644 --- a/src/fsharp/ParseHelpers.fs +++ b/src/fsharp/ParseHelpers.fs @@ -147,6 +147,16 @@ let rec LexerIfdefEval (lookup: string -> bool) = function // Parsing: continuations for whitespace tokens //------------------------------------------------------------------------ +[] +type LexerStringKind = + { IsByteString: bool + IsInterpolated: bool + IsInterpolatedFirst: bool } + static member String = { IsByteString = false; IsInterpolated = false; IsInterpolatedFirst=false } + static member ByteString = { IsByteString = true; IsInterpolated = false; IsInterpolatedFirst=false } + static member InterpolatedStringFirst = { IsByteString = false; IsInterpolated = true; IsInterpolatedFirst=true } + static member InterpolatedStringPart = { IsByteString = false; IsInterpolated = true; IsInterpolatedFirst=false } + /// The parser defines a number of tokens for whitespace and /// comments eliminated by the lexer. These carry a specification of /// a continuation for the lexer for continued processing after we've dealt with @@ -156,9 +166,9 @@ let rec LexerIfdefEval (lookup: string -> bool) = function type LexerWhitespaceContinuation = | Token of ifdef: LexerIfdefStackEntries | IfDefSkip of ifdef: LexerIfdefStackEntries * int * range: range - | String of ifdef: LexerIfdefStackEntries * range: range - | VerbatimString of ifdef: LexerIfdefStackEntries * range: range - | TripleQuoteString of ifdef: LexerIfdefStackEntries * range: range + | String of ifdef: LexerIfdefStackEntries * kind: LexerStringKind * range: range + | VerbatimString of ifdef: LexerIfdefStackEntries * kind: LexerStringKind * range: range + | TripleQuoteString of ifdef: LexerIfdefStackEntries * kind: LexerStringKind * range: range | Comment of ifdef: LexerIfdefStackEntries * int * range: range | SingleLineComment of ifdef: LexerIfdefStackEntries * int * range: range | StringInComment of ifdef: LexerIfdefStackEntries * int * range: range diff --git a/src/fsharp/PostInferenceChecks.fs b/src/fsharp/PostInferenceChecks.fs index 711bae6cbbc..60ca09c6781 100644 --- a/src/fsharp/PostInferenceChecks.fs +++ b/src/fsharp/PostInferenceChecks.fs @@ -799,7 +799,7 @@ and CheckForOverAppliedExceptionRaisingPrimitive (cenv: cenv) expr = | OptionalCoerce(Expr.Val (failwithfFunc, _, funcRange)) when valRefEq g failwithfFunc g.failwithf_vref -> match argsl with | Expr.App (Expr.Val (newFormat, _, _), _, [_; typB; typC; _; _], [Expr.Const (Const.String formatString, formatRange, _)], _) :: xs when valRefEq g newFormat g.new_format_vref -> - match CheckFormatStrings.TryCountFormatStringArguments formatRange g formatString typB typC with + match CheckFormatStrings.TryCountFormatStringArguments formatRange g false formatString typB typC with | Some n -> let expected = n + 1 let actual = List.length xs + 1 diff --git a/src/fsharp/SyntaxTree.fs b/src/fsharp/SyntaxTree.fs index a071f1c526b..d21611c24a4 100644 --- a/src/fsharp/SyntaxTree.fs +++ b/src/fsharp/SyntaxTree.fs @@ -1003,6 +1003,11 @@ type SynExpr = expr: SynExpr * range: range + /// F# syntax: interpolated string, e.g. "abc%{123}" + | InterpolatedString of + contents: Choice list * + range: range + /// Gets the syntax range of this constuct member e.Range = match e with @@ -1069,7 +1074,8 @@ type SynExpr = | SynExpr.LetOrUseBang (range=m) | SynExpr.MatchBang (range=m) | SynExpr.DoBang (range=m) - | SynExpr.Fixed (range=m) -> m + | SynExpr.Fixed (range=m) + | SynExpr.InterpolatedString (range=m) -> m | SynExpr.Ident id -> id.idRange /// Get the Range ignoring any (parse error) extra trailing dots diff --git a/src/fsharp/SyntaxTreeOps.fs b/src/fsharp/SyntaxTreeOps.fs index 43d473a137c..9e94afc0047 100644 --- a/src/fsharp/SyntaxTreeOps.fs +++ b/src/fsharp/SyntaxTreeOps.fs @@ -723,4 +723,7 @@ let rec synExprContainsError inpExpr = | SynExpr.LetOrUseBang (rhs=e1;body=e2;andBangs=es) -> walkExpr e1 || walkExprs [ for (_,_,_,_,e,_) in es do yield e ] || walkExpr e2 + | SynExpr.InterpolatedString (parts, _m) -> + walkExprs (parts |> List.choose (function Choice1Of2 _ -> None | Choice2Of2 x -> Some x)) + walkExpr inpExpr diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index 68ef24cb764..9eeef3abc01 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -428,6 +428,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let fslib_MFQueryRunExtensionsLowPriority_nleref = mkNestedNonLocalEntityRef fslib_MFQueryRunExtensions_nleref "LowPriority" let fslib_MFQueryRunExtensionsHighPriority_nleref = mkNestedNonLocalEntityRef fslib_MFQueryRunExtensions_nleref "HighPriority" + let fslib_MFPrintfModule_nleref = mkNestedNonLocalEntityRef fslib_MFCore_nleref "PrintfModule" let fslib_MFSeqModule_nleref = mkNestedNonLocalEntityRef fslib_MFCollections_nleref "SeqModule" let fslib_MFListModule_nleref = mkNestedNonLocalEntityRef fslib_MFCollections_nleref "ListModule" let fslib_MFArrayModule_nleref = mkNestedNonLocalEntityRef fslib_MFCollections_nleref "ArrayModule" @@ -492,6 +493,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d fslib_MFQueryRunExtensionsLowPriority_nleref fslib_MFQueryRunExtensionsHighPriority_nleref + fslib_MFPrintfModule_nleref fslib_MFSeqModule_nleref fslib_MFListModule_nleref fslib_MFArrayModule_nleref @@ -703,6 +705,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_seq_empty_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "empty" , None , Some "Empty" , [vara], ([], mkSeqTy varaTy)) let v_new_format_info = makeIntrinsicValRef(fslib_MFCore_nleref, ".ctor" , Some "PrintfFormat`5", None , [vara;varb;varc;vard;vare], ([[v_string_ty]], mkPrintfFormatTy varaTy varbTy varcTy vardTy vareTy)) let v_sprintf_info = makeIntrinsicValRef(fslib_MFExtraTopLevelOperators_nleref, "sprintf" , None , Some "PrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) + let v_isprintf_info = makeIntrinsicValRef(fslib_MFPrintfModule_nleref, "isprintf" , None , Some "InterpolatedPrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) let v_lazy_force_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Force" , Some "Lazy`1" , None , [vara], ([[mkLazyTy varaTy]; []], varaTy)) let v_lazy_create_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Create" , Some "Lazy`1" , None , [vara], ([[v_unit_ty --> varaTy]], mkLazyTy varaTy)) @@ -1365,6 +1368,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val seq_empty_vref = ValRefForIntrinsic v_seq_empty_info member val new_format_vref = ValRefForIntrinsic v_new_format_info member val sprintf_vref = ValRefForIntrinsic v_sprintf_info + member val isprintf_vref = ValRefForIntrinsic v_isprintf_info member val unbox_vref = ValRefForIntrinsic v_unbox_info member val unbox_fast_vref = ValRefForIntrinsic v_unbox_fast_info member val istype_vref = ValRefForIntrinsic v_istype_info @@ -1390,6 +1394,8 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.seq_map_info = v_seq_map_info member __.seq_singleton_info = v_seq_singleton_info member __.seq_empty_info = v_seq_empty_info + member __.sprintf_info = v_sprintf_info + member __.isprintf_info = v_isprintf_info member __.new_format_info = v_new_format_info member __.unbox_info = v_unbox_info member __.get_generic_comparer_info = v_get_generic_comparer_info diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 2cb72a3047b..ae91c455012 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -5900,6 +5900,18 @@ and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) = CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) TcConstStringExpr cenv overallTy env m tpenv s + | SynExpr.InterpolatedString (parts, m) -> + tryLanguageFeatureError cenv.g.langVersion LanguageFeature.StringInterpolation m + + // CHeck the library support is available in the referenced FSharp.Core + if cenv.g.isprintf_vref.TryDeref.IsNone then + languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m + + CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) + let exprFills = parts |> List.choose(function Choice1Of2 _ -> None | Choice2Of2 e -> Some e) + let stringText = parts |> List.map(function Choice1Of2 s -> s | Choice2Of2 _ -> "%P") |> String.concat "" + TcConstStringFormatExpr cenv overallTy env m tpenv (Some exprFills) stringText + | SynExpr.Const (synConst, m) -> CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) TcConstExpr cenv overallTy env m tpenv synConst @@ -7081,20 +7093,38 @@ and TcObjectExpr cenv overallTy env tpenv (synObjTy, argopt, binds, extraImpls, and TcConstStringExpr cenv overallTy env m tpenv s = if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.string_ty) then - mkString cenv.g m s, tpenv + mkString cenv.g m s, tpenv else - let aty = NewInferenceType () - let bty = NewInferenceType () - let cty = NewInferenceType () - let dty = NewInferenceType () - let ety = NewInferenceType () - 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 + TcConstStringFormatExpr cenv overallTy env m tpenv None s + +and TcConstStringFormatExpr cenv overallTy env m tpenv (optFills: SynExpr list option) s = + let g = cenv.g + let isInterp = optFills.IsSome + let aty = NewInferenceType () + let bty = if isInterp then g.unit_ty else NewInferenceType () + let cty = if isInterp then g.string_ty else NewInferenceType () + let dty = if isInterp then g.string_ty else NewInferenceType () + let ety = NewInferenceType () + let formatTy = mkPrintfFormatTy g aty bty cty dty ety + + let ok = + match optFills with + | Some _ -> + // If this is an interpolated string then the result must be a string + UnifyTypes cenv env m overallTy g.string_ty + true + | None -> + // This might qualify as a format string - check via a type directed rule + not (isObjTy g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy formatTy + + if ok then // Parse the format string to work out the phantom types 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 formatStringCheckContext normalizedString bty cty dty with Failure s -> error (Error(FSComp.SR.tcUnableToParseFormatString s, m))) + let (argtys, atyRequired, etyRequired), specifierLocations = + try CheckFormatStrings.ParseFormatString m g optFills.IsSome formatStringCheckContext normalizedString bty cty dty + with Failure s -> error (Error(FSComp.SR.tcUnableToParseFormatString s, m)) match cenv.tcSink.CurrentSink with | None -> () @@ -7102,12 +7132,23 @@ and TcConstStringExpr cenv overallTy env m tpenv s = for specifierLocation, numArgs in specifierLocations do sink.NotifyFormatSpecifierLocation(specifierLocation, numArgs) - UnifyTypes cenv env m aty aty' - UnifyTypes cenv env m ety ety' - mkCallNewFormat cenv.g m aty bty cty dty ety (mkString cenv.g m s), tpenv - else - UnifyTypes cenv env m overallTy cenv.g.string_ty - mkString cenv.g m s, tpenv + UnifyTypes cenv env m aty atyRequired + UnifyTypes cenv env m ety etyRequired + let fmtExpr = mkCallNewFormat g m aty bty cty dty ety (mkString g m s) + match optFills with + | None -> fmtExpr, tpenv + | Some synFillExprs -> + // Check the expressions filling the holes + if argtys.Length <> synFillExprs.Length then + error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) + let flexes = argtys |> List.map (fun _ -> false) + let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs + // Make the call to isprintf + mkCall_isprintf g m aty fmtExpr fillExprs, tpenv + + else + UnifyTypes cenv env m overallTy g.string_ty + mkString g m s, tpenv //------------------------------------------------------------------------- // TcConstExpr @@ -9694,6 +9735,7 @@ and TcItemThen cenv overallTy env tpenv (item, mItem, rest, afterResolution) del | SynExpr.AddressOf (_, synExpr, _, _) | SynExpr.Quote (_, _, synExpr, _, _) -> isSimpleArgument synExpr + | SynExpr.InterpolatedString _ | SynExpr.Null _ | SynExpr.Ident _ | SynExpr.Const _ diff --git a/src/fsharp/TypedTreeOps.fs b/src/fsharp/TypedTreeOps.fs index 513727bf1c2..978f151ae6c 100644 --- a/src/fsharp/TypedTreeOps.fs +++ b/src/fsharp/TypedTreeOps.fs @@ -6972,6 +6972,9 @@ let mkCallSeqSingleton g m ty1 arg1 = let mkCallSeqEmpty g m ty1 = mkApps g (typedExprForIntrinsic g m g.seq_empty_info, [[ty1]], [ ], m) +let mkCall_isprintf (g: TcGlobals) m aty fmt es = + mkApps g (typedExprForIntrinsic g m g.isprintf_info, [[aty]], fmt::es , m) + let mkCallDeserializeQuotationFSharp20Plus g m e1 e2 e3 e4 = let args = [ e1; e2; e3; e4 ] mkApps g (typedExprForIntrinsic g m g.deserialize_quoted_FSharp_20_plus_info, [], [ mkRefTupledNoTypes g m args ], m) diff --git a/src/fsharp/TypedTreeOps.fsi b/src/fsharp/TypedTreeOps.fsi index dce873e1a8c..98c4e7eeaee 100755 --- a/src/fsharp/TypedTreeOps.fsi +++ b/src/fsharp/TypedTreeOps.fsi @@ -1969,6 +1969,8 @@ val mkCallSeqSingleton : TcGlobals -> range -> TType -> Expr -> Expr val mkCallSeqEmpty : TcGlobals -> range -> TType -> Expr +val mkCall_isprintf: g: TcGlobals -> m: range -> funcTy: TType -> fmtExpr: Expr -> fillExprs: Expr list -> Expr + val mkILAsmCeq : TcGlobals -> range -> Expr -> Expr -> Expr val mkILAsmClt : TcGlobals -> range -> Expr -> Expr -> Expr diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index 527f6ebd084..83f7209d9f1 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -115,19 +115,36 @@ let startString args (lexbuf: UnicodeLexing.Lexbuf) = let buf = ByteBuffer.Create 100 let m = lexbuf.LexemeRange let startp = lexbuf.StartPos - let fin = (fun _m2 b s -> - // Adjust the start-of-token mark back to the true start of the token - lexbuf.StartPos <- startp - if b then - if Lexhelp.stringBufferIsBytes buf then - BYTEARRAY (Lexhelp.stringBufferAsBytes buf) - else ( - fail args lexbuf (FSComp.SR.lexByteArrayCannotEncode()) () - BYTEARRAY (Lexhelp.stringBufferAsBytes buf) - ) - else - STRING (Lexhelp.stringBufferAsString s)) + let fin = + LexerStringFinisher (fun buf kind isPart -> + // Adjust the start-of-token mark back to the true start of the token + lexbuf.StartPos <- startp + if kind.IsByteString then + if kind.IsInterpolated then + fail args lexbuf (FSComp.SR.lexByteStringMayNotBeInterpolated()) () + BYTEARRAY (Lexhelp.stringBufferAsBytes buf) + elif Lexhelp.stringBufferIsBytes buf then + BYTEARRAY (Lexhelp.stringBufferAsBytes buf) + else + fail args lexbuf (FSComp.SR.lexByteArrayCannotEncode()) () + BYTEARRAY (Lexhelp.stringBufferAsBytes buf) + elif kind.IsInterpolated then + let s = Lexhelp.stringBufferAsString buf + if kind.IsInterpolatedFirst then + if isPart then + INTERP_STRING_BEGIN_PART s + else + INTERP_STRING_BEGIN_END s + else + if isPart then + INTERP_STRING_PART s + else + INTERP_STRING_END s + else + let s = Lexhelp.stringBufferAsString buf + STRING s) buf,fin,m + // Utility functions for processing XML documentation @@ -522,22 +539,31 @@ rule token args skip = parse | "(*IF-CAML*)" | "(*IF-OCAML*)" { let m = lexbuf.LexemeRange - if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack,m))) else mlOnly m args skip lexbuf } + if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } | '"' - { let buf,fin,m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string (buf,fin,m,args) skip lexbuf } + { let buf, fin, m = startString args lexbuf + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringKind.String, m))) else string (buf, fin, m, LexerStringKind.String, args) skip lexbuf } - | '"' '"' '"' + | '$' '"' { let buf,fin,m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack,m))) else tripleQuoteString (buf,fin,m,args) skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringKind.InterpolatedStringFirst, m))) else string (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } + + | '"' '"' '"' + { let buf, fin, m = startString args lexbuf + if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, LexerStringKind.String, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } - | '$' '"' - { fail args lexbuf (FSComp.SR.lexTokenReserved()) (WHITESPACE (LexCont.Token args.ifdefStack)) } + | '$' '"' '"' '"' + { let buf, fin, m = startString args lexbuf + if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, LexerStringKind.InterpolatedStringFirst, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | '@' '"' - { let buf,fin,m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack,m))) else verbatimString (buf,fin,m,args) skip lexbuf } + { let buf, fin, m = startString args lexbuf + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, LexerStringKind.String, m))) else verbatimString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } + + | ("$@" | "@$") '"' + { let buf, fin, m = startString args lexbuf + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, LexerStringKind.InterpolatedStringFirst, m))) else verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | truewhite+ { if skip then token args skip lexbuf @@ -703,11 +729,26 @@ rule token args skip = parse | ">]" { GREATER_RBRACK } - | "{" { LBRACE } + | "{" { + if args.interpolatedStringNesting > 0 then + args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + LBRACE } | "|" { BAR } - | "}" { RBRACE } + | "}" + { + // We encounter a '}' in the expression token stream. First check if we're in an interpolated string expression + if args.interpolatedStringNesting = 1 then + let buf, fin, m = startString args lexbuf + args.interpolatedStringNesting <- args.interpolatedStringNesting - 1 + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringKind.InterpolatedStringPart, m))) else string (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf + elif args.interpolatedStringNesting > 1 then + args.interpolatedStringNesting <- args.interpolatedStringNesting - 1 + RBRACE + else + RBRACE + } | "$" { DOLLAR } @@ -817,7 +858,7 @@ and ifdefSkip n m args skip = parse // If #if is the first thing on the line then increase depth, otherwise skip, because it is invalid (e.g. "(**) #if ...") if (m.StartColumn <> 0) then - if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack,n,m))) else ifdefSkip n m args skip lexbuf + if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, n, m))) else ifdefSkip n m args skip lexbuf else let tok = INACTIVECODE(LexCont.EndLine(LexerEndlineContinuation.Skip(args.ifdefStack,n+1,m))) if not skip then tok else endline (LexerEndlineContinuation.Skip(args.ifdefStack,n+1,m)) args skip lexbuf } @@ -828,7 +869,7 @@ and ifdefSkip n m args skip = parse // If #else is the first thing on the line then process it, otherwise ignore, because it is invalid (e.g. "(**) #else ...") if (m.StartColumn <> 0) then - if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack,n,m))) else ifdefSkip n m args skip lexbuf + if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, n, m))) else ifdefSkip n m args skip lexbuf elif n = 0 then match args.ifdefStack with | []-> LEX_FAILURE (FSComp.SR.lexHashElseNoMatchingIf()) @@ -838,7 +879,7 @@ and ifdefSkip n m args skip = parse args.ifdefStack <- (IfDefElse,m) :: rest if not skip then (HASH_ELSE(m,lexed,LexCont.EndLine(LexerEndlineContinuation.Token(args.ifdefStack)))) else endline (LexerEndlineContinuation.Token(args.ifdefStack)) args skip lexbuf else - if not skip then (INACTIVECODE(LexCont.EndLine(LexerEndlineContinuation.Skip(args.ifdefStack,n,m)))) else endline (LexerEndlineContinuation.Skip(args.ifdefStack,n,m)) args skip lexbuf } + if not skip then (INACTIVECODE(LexCont.EndLine(LexerEndlineContinuation.Skip(args.ifdefStack, n, m)))) else endline (LexerEndlineContinuation.Skip(args.ifdefStack, n, m)) args skip lexbuf } | anywhite* "#endif" anywhite* ("//" [^'\n''\r']*)? { let lexed = lexeme lexbuf @@ -846,7 +887,7 @@ and ifdefSkip n m args skip = parse // If #endif is the first thing on the line then process it, otherwise ignore, because it is invalid (e.g. "(**) #endif ...") if (m.StartColumn <> 0) then - if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack,n,m))) else ifdefSkip n m args skip lexbuf + if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, n, m))) else ifdefSkip n m args skip lexbuf elif n = 0 then match args.ifdefStack with | [] -> LEX_FAILURE (FSComp.SR.lexHashEndingNoMatchingIf()) @@ -869,10 +910,10 @@ and ifdefSkip n m args skip = parse | _ { // This tries to be nice and get tokens as 'words' because VS uses this when selecting stuff - if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack,n,m))) else ifdefSkip n m args skip lexbuf } + if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, n, m))) else ifdefSkip n m args skip lexbuf } | eof - { EOF (LexCont.IfDefSkip(args.ifdefStack,n,m)) } + { EOF (LexCont.IfDefSkip(args.ifdefStack, n, m)) } // Called after lexing #if IDENT/#else/#endif - this checks whether there is nothing except end of line // or end of file and then calls the lexing function specified by 'cont' - either token or ifdefSkip @@ -898,35 +939,35 @@ and endline cont args skip = parse and string sargs skip = parse | '\\' newline anywhite* - { let (_buf,_fin,m,args) = sargs + { let (_buf, _fin, m, kind, args) = sargs newline lexbuf - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | escape_char - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addByteChar buf (escape (lexeme lexbuf).[1]) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | trigraph - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs let s = lexeme lexbuf addByteChar buf (trigraph s.[1] s.[2] s.[3]) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | hexGraphShort - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeChar buf (int (hexGraphShort (lexemeTrimLeft lexbuf 2))) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | unicodeGraphShort - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeChar buf (int (unicodeGraphShort (lexemeTrimLeft lexbuf 2))) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | unicodeGraphLong - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs let hexChars = lexemeTrimLeft lexbuf 2 - let result () = if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf + let result () = if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf match unicodeGraphLong hexChars with | Invalid -> fail args lexbuf (FSComp.SR.lexInvalidUnicodeLiteral hexChars) (result ()) @@ -939,213 +980,237 @@ and string sargs skip = parse result () } | '"' - { let (buf,fin,_m,_args) = sargs - let m2 = lexbuf.LexemeRange - callStringFinisher fin buf m2 false } + { let (buf, fin, _m, kind, _args) = sargs + fin.Finish buf kind false } | '"''B' - { let (buf,fin,_m,_args) = sargs - let m2 = lexbuf.LexemeRange - callStringFinisher fin buf m2 true } + { let (buf, fin, _m, kind, _args) = sargs + fin.Finish buf { kind with IsByteString = true } false } + + | '{' + { let (buf, fin, m, kind, args) = sargs + if kind.IsInterpolated then + args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + fin.Finish buf kind true + else + addUnicodeString buf (lexeme lexbuf) + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf + } | newline - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | ident - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | integer | xinteger - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | anywhite + - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } | eof - { let (_buf,_fin,m,args) = sargs - EOF (LexCont.String(args.ifdefStack,m)) } + { let (_buf, _fin, m, kind, args) = sargs + EOF (LexCont.String(args.ifdefStack, kind, m)) } | surrogateChar surrogateChar // surrogate code points always come in pairs | _ - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack,m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } and verbatimString sargs skip = parse | '"' '"' - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addByteChar buf '\"' - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack,m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } | '"' - { let (buf,fin,_m,_args) = sargs - let m2 = lexbuf.LexemeRange - callStringFinisher fin buf m2 false } + { let (buf, fin, _m, kind, _args) = sargs + fin.Finish buf kind false } | '"''B' - { let (buf,fin,_m,_args) = sargs - let m2 = lexbuf.LexemeRange - callStringFinisher fin buf m2 true } + { let (buf, fin, _m, kind, _args) = sargs + fin.Finish buf { kind with IsByteString = true } false } | newline - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack,m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } + + | '{' + { let (buf, fin, m, kind, args) = sargs + if kind.IsInterpolated then + fin.Finish buf kind true + else + addUnicodeString buf (lexeme lexbuf) + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf + } | ident - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack,m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } | integer | xinteger - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack,m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } | anywhite + - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack,m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } | eof - { let (_buf,_fin,m,args) = sargs - EOF (LexCont.VerbatimString(args.ifdefStack,m)) } + { let (_buf, _fin, m, kind, args) = sargs + EOF (LexCont.VerbatimString(args.ifdefStack, kind, m)) } + | surrogateChar surrogateChar // surrogate code points always come in pairs | _ - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack,m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } and tripleQuoteString sargs skip = parse | '"' '"' '"' - { let (buf,fin,_m,_args) = sargs - let m2 = lexbuf.LexemeRange - callStringFinisher fin buf m2 false } + { let (buf, fin, _m, kind, _args) = sargs + fin.Finish buf kind false } | newline - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack,m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } // The rest is to break into pieces to allow double-click-on-word and other such things | ident - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack,m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } | integer | xinteger - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack,m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } | anywhite + - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack,m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } + + | '{' + { let (buf, fin, m, kind, args) = sargs + if kind.IsInterpolated then + fin.Finish buf kind true + else + addUnicodeString buf (lexeme lexbuf) + if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf + } | eof - { let (_buf,_fin,m,args) = sargs - EOF (LexCont.TripleQuoteString(args.ifdefStack,m)) } + { let (_buf, _fin, m, kind, args) = sargs + EOF (LexCont.TripleQuoteString(args.ifdefStack, kind, m)) } | surrogateChar surrogateChar // surrogate code points always come in pairs | _ - { let (buf,_fin,m,args) = sargs + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack,m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } // Parsing single-line comment - we need to split it into words for Visual Studio IDE and singleLineComment cargs skip = parse | newline - { let buff,_n,_m,args = cargs + { let buff,_n, _m, args = cargs trySaveXmlDoc lexbuf buff newline lexbuf // Saves the documentation (if we're collecting any) into a buffer-local variable. if not skip then (LINE_COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } | eof - { let _, _n,_m,args = cargs + { let _, _n, _m, args = cargs // NOTE: it is legal to end a file with this comment, so we'll return EOF as a token EOF (LexCont.Token args.ifdefStack) } | [^ ' ' '\n' '\r' ]+ | anywhite+ - { let buff,n,m,args = cargs + { let buff, n, m, args = cargs // Append the current token to the XML documentation if we're collecting it tryAppendXmlDoc buff (lexeme lexbuf) - if not skip then (LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack,n,m))) else singleLineComment (buff,n,m,args) skip lexbuf } + if not skip then (LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack, n, m))) else singleLineComment (buff,n, m, args) skip lexbuf } | surrogateChar surrogateChar - | _ { let _, _n,_m,args = cargs - if not skip then (LINE_COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + | _ { let _, _n, _m, args = cargs + if not skip then (LINE_COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } and comment cargs skip = parse | char - { let n,m,args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n,m))) else comment (n,m,args) skip lexbuf } + { let n, m, args = cargs + if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } | '"' - { let n,m,args = cargs - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack,n,m))) else stringInComment n m args skip lexbuf } + { let n, m, args = cargs + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } | '"' '"' '"' - { let n,m,args = cargs - if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack,n,m))) else tripleQuoteStringInComment n m args skip lexbuf } + { let n, m, args = cargs + if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } | '@' '"' - { let n,m,args = cargs - if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack,n,m))) else verbatimStringInComment n m args skip lexbuf } + { let n, m, args = cargs + if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } | "(*)" - { let n,m,args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n,m))) else comment cargs skip lexbuf } + { let n, m, args = cargs + if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment cargs skip lexbuf } | '(' '*' - { let n,m,args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n+1,m))) else comment (n+1,m,args) skip lexbuf } + { let n, m, args = cargs + if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n+1, m))) else comment (n+1,m,args) skip lexbuf } | newline - { let n,m,args = cargs + { let n, m, args = cargs newline lexbuf - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n,m))) else comment cargs skip lexbuf } + if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment cargs skip lexbuf } | "*)" { - let n,m,args = cargs + let n, m, args = cargs if n > 1 then if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n-1,m))) else comment (n-1,m,args) skip lexbuf else if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } | anywhite+ | [^ '\'' '(' '*' '\n' '\r' '"' ')' '@' ' ' '\t' ]+ - { let n,m,args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n,m))) else comment cargs skip lexbuf } + { let n, m, args = cargs + if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment cargs skip lexbuf } | eof - { let n,m,args = cargs - EOF (LexCont.Comment(args.ifdefStack,n,m)) } + { let n, m, args = cargs + EOF (LexCont.Comment(args.ifdefStack, n, m)) } | surrogateChar surrogateChar - | _ { let n,m,args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n,m))) else comment (n,m,args) skip lexbuf } + | _ { let n, m, args = cargs + if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } and stringInComment n m args skip = parse // Follow string lexing, skipping tokens until it finishes | '\\' newline anywhite* { newline lexbuf - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack,n,m))) else stringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } | escape_char | trigraph @@ -1156,80 +1221,80 @@ and stringInComment n m args skip = parse | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack,n,m))) else stringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } | '"' - { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n,m))) else comment (n,m,args) skip lexbuf } + { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } | newline { newline lexbuf - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack,n,m))) else stringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } | eof - { EOF (LexCont.StringInComment(args.ifdefStack,n,m)) } + { EOF (LexCont.StringInComment(args.ifdefStack, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack,n,m))) else stringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } and verbatimStringInComment n m args skip = parse // Follow verbatimString lexing, in short, skip double-quotes and other chars until we hit a single quote | '"' '"' - { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack,n,m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } | '"' - { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n,m))) else comment (n,m,args) skip lexbuf } + { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } | ident | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack,n,m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } | newline { newline lexbuf - if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack,n,m))) else verbatimStringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } | eof - { EOF (LexCont.VerbatimStringInComment(args.ifdefStack,n,m)) } + { EOF (LexCont.VerbatimStringInComment(args.ifdefStack, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack,n,m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } and tripleQuoteStringInComment n m args skip = parse // Follow tripleQuoteString lexing | '"' '"' '"' - { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n,m))) else comment (n,m,args) skip lexbuf } + { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } | ident | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack,n,m))) else tripleQuoteStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } | newline { newline lexbuf - if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack,n,m))) else tripleQuoteStringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } | eof - { EOF (LexCont.TripleQuoteStringInComment(args.ifdefStack,n,m)) } + { EOF (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack,n,m))) else tripleQuoteStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } and mlOnly m args skip = parse | "\"" { let buf = ByteBuffer.Create 100 let m2 = lexbuf.LexemeRange - let _ = string (buf,defaultStringFinisher,m2,args) skip lexbuf - if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack,m))) else mlOnly m args skip lexbuf } + let _ = string (buf, LexerStringFinisher.Default, m2, LexerStringKind.String, args) skip lexbuf + if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } | newline - { newline lexbuf; if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack,m))) else mlOnly m args skip lexbuf } + { newline lexbuf; if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } | "(*ENDIF-CAML*)" { if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } @@ -1238,11 +1303,11 @@ and mlOnly m args skip = parse { if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } | [^ '(' '"' '\n' '\r' ]+ - { if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack,m))) else mlOnly m args skip lexbuf } + { if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } | eof - { EOF (LexCont.MLOnly(args.ifdefStack,m)) } + { EOF (LexCont.MLOnly(args.ifdefStack, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack,m))) else mlOnly m args skip lexbuf } + { if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } diff --git a/src/fsharp/lexhelp.fs b/src/fsharp/lexhelp.fs index 707d7f0b499..c71d27c7240 100644 --- a/src/fsharp/lexhelp.fs +++ b/src/fsharp/lexhelp.fs @@ -56,6 +56,7 @@ type lexargs = lightSyntaxStatus : LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool + mutable interpolatedStringNesting: int pathMap: PathMap } /// possible results of lexing a long Unicode escape sequence in a string literal, e.g. "\U0001F47D", @@ -72,6 +73,7 @@ let mkLexargs (_filename, defines, lightSyntaxStatus, resourceManager, ifdefStac resourceManager=resourceManager errorLogger=errorLogger applyLineDirectives=true + interpolatedStringNesting = 0 pathMap=pathMap } /// Register the lexbuf and call the given function @@ -95,20 +97,8 @@ let usingLexbufForParsing (lexbuf:UnicodeLexing.Lexbuf, filename) f = // Functions to manipulate lexer transient state //----------------------------------------------------------------------- -let defaultStringFinisher = (fun _endm _b s -> STRING (Encoding.Unicode.GetString(s, 0, s.Length))) - -let callStringFinisher fin (buf: ByteBuffer) endm b = fin endm b (buf.Close()) - -let addUnicodeString (buf: ByteBuffer) (x:string) = buf.EmitBytes (Encoding.Unicode.GetBytes x) - -let addIntChar (buf: ByteBuffer) c = - buf.EmitIntAsByte (c % 256) - buf.EmitIntAsByte (c / 256) - -let addUnicodeChar buf c = addIntChar buf (int c) -let addByteChar buf (c:char) = addIntChar buf (int32 c % 256) - -let stringBufferAsString (buf: byte[]) = +let stringBufferAsString (buf: ByteBuffer) = + let buf = buf.Close() if buf.Length % 2 <> 0 then failwith "Expected even number of bytes" let chars : char[] = Array.zeroCreate (buf.Length/2) for i = 0 to (buf.Length/2) - 1 do @@ -127,6 +117,44 @@ let stringBufferAsBytes (buf: ByteBuffer) = let bytes = buf.Close() Array.init (bytes.Length / 2) (fun i -> bytes.[i*2]) +type LexerStringFinisher = + | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> bool -> token) + + member fin.Finish (buf: ByteBuffer) kind isPart = + let (LexerStringFinisher f) = fin + f buf kind isPart + + static member Default = + LexerStringFinisher (fun buf kind isPart -> + if kind.IsInterpolated then + let s = stringBufferAsString buf + if kind.IsInterpolatedFirst then + if isPart then + INTERP_STRING_BEGIN_PART s + else + INTERP_STRING_BEGIN_END s + else + if isPart then + INTERP_STRING_PART s + else + INTERP_STRING_END s + elif kind.IsByteString then + BYTEARRAY (stringBufferAsBytes buf) + else + STRING (stringBufferAsString buf) + ) + +let addUnicodeString (buf: ByteBuffer) (x:string) = + buf.EmitBytes (Encoding.Unicode.GetBytes x) + +let addIntChar (buf: ByteBuffer) c = + buf.EmitIntAsByte (c % 256) + buf.EmitIntAsByte (c / 256) + +let addUnicodeChar buf c = addIntChar buf (int c) + +let addByteChar buf (c:char) = addIntChar buf (int32 c % 256) + /// Sanity check that high bytes are zeros. Further check each low byte <= 127 let stringBufferIsBytes (buf: ByteBuffer) = let bytes = buf.Close() diff --git a/src/fsharp/lexhelp.fsi b/src/fsharp/lexhelp.fsi index 2bed4e89c60..22e5c2fffce 100644 --- a/src/fsharp/lexhelp.fsi +++ b/src/fsharp/lexhelp.fsi @@ -8,6 +8,7 @@ open Internal.Utilities.Text open FSharp.Compiler open FSharp.Compiler.AbstractIL.Internal open FSharp.Compiler.ErrorLogger +open FSharp.Compiler.Parser open FSharp.Compiler.ParseHelpers open FSharp.Compiler.Range @@ -32,6 +33,7 @@ type lexargs = lightSyntaxStatus: LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool + mutable interpolatedStringNesting: int pathMap: PathMap } type LongUnicodeLexResult = @@ -47,9 +49,12 @@ val reusingLexbufForParsing: UnicodeLexing.Lexbuf -> (unit -> 'a) -> 'a val usingLexbufForParsing: UnicodeLexing.Lexbuf * string -> (UnicodeLexing.Lexbuf -> 'a) -> 'a -val defaultStringFinisher: 'a -> 'b -> byte[] -> Parser.token +type LexerStringFinisher = + | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> bool -> token) + + member Finish: buf: ByteBuffer -> kind: LexerStringKind -> isInterpolatedStringPart: bool -> token -val callStringFinisher: ('a -> 'b -> byte[] -> 'c) -> ByteBuffer -> 'a -> 'b -> 'c + static member Default: LexerStringFinisher val addUnicodeString: ByteBuffer -> string -> unit @@ -57,7 +62,7 @@ val addUnicodeChar: ByteBuffer -> int -> unit val addByteChar: ByteBuffer -> char -> unit -val stringBufferAsString: byte[] -> string +val stringBufferAsString: ByteBuffer -> string val stringBufferAsBytes: ByteBuffer -> byte[] @@ -85,9 +90,9 @@ exception IndentationProblem of string * Range.range module Keywords = - val KeywordOrIdentifierToken: lexargs -> UnicodeLexing.Lexbuf -> string -> Parser.token + val KeywordOrIdentifierToken: lexargs -> UnicodeLexing.Lexbuf -> string -> token - val IdentifierToken: lexargs -> UnicodeLexing.Lexbuf -> string -> Parser.token + val IdentifierToken: lexargs -> UnicodeLexing.Lexbuf -> string -> token val DoesIdentifierNeedQuotation: string -> bool diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 343f737a9ac..e950010f548 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -94,9 +94,9 @@ let raiseParseErrorAt m s = let checkEndOfFileError t = match t with | LexCont.IfDefSkip(_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInHashIf()) - | LexCont.String (_, m) -> reportParseErrorAt m (FSComp.SR.parsEofInString()) - | LexCont.TripleQuoteString (_, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteString()) - | LexCont.VerbatimString (_, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimString()) + | LexCont.String (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInString()) + | LexCont.TripleQuoteString (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteString()) + | LexCont.VerbatimString (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimString()) | LexCont.Comment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) | LexCont.SingleLineComment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) | LexCont.StringInComment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInStringInComment()) @@ -157,6 +157,10 @@ let rangeOfLongIdent(lid:LongIdent) = %token BYTEARRAY %token STRING +%token INTERP_STRING_BEGIN_END +%token INTERP_STRING_BEGIN_PART +%token INTERP_STRING_PART +%token INTERP_STRING_END %token KEYWORD_STRING // Like __SOURCE_DIRECTORY__ %token IDENT %token INFIX_STAR_STAR_OP @@ -382,6 +386,7 @@ let rangeOfLongIdent(lid:LongIdent) = */ %nonassoc prec_atompat_pathop %nonassoc INT8 UINT8 INT16 UINT16 INT32 UINT32 INT64 UINT64 NATIVEINT UNATIVEINT IEEE32 IEEE64 CHAR KEYWORD_STRING STRING BYTEARRAY BIGNUM DECIMAL +%nonassoc INTERP_STRING_BEGIN INTERP_STRING_PART INTERP_STRING_END %nonassoc LPAREN LBRACE LBRACK_BAR %nonassoc TRUE FALSE UNDERSCORE NULL @@ -4143,7 +4148,7 @@ rangeDeclExpr: if $1 <> "^" then reportParseErrorAt (rhs parseState 1) (FSComp.SR.parsInvalidPrefixOperator()) $2, true } -/* the start et of atomicExprAfterType must not overlap with the valid postfix tokens of the type syntax, e.g. new List(...) */ +/* the start of atomicExprAfterType must not overlap with the valid postfix tokens of the type syntax, e.g. new List(...) */ atomicExprAfterType: | constant { SynExpr.Const ($1, $1.Range (lhs parseState)) } @@ -4157,6 +4162,9 @@ atomicExprAfterType: | braceBarExpr { $1 } + | interpolatedString + { SynExpr.InterpolatedString($1, rhs parseState 1) } + | NULL { SynExpr.Null (lhs parseState) } @@ -5419,11 +5427,28 @@ colonOrEquals: | COLON { mlCompatWarning (FSComp.SR.parsSyntaxModuleSigEndDeprecated()) (lhs parseState); } | EQUALS { } -/* A literal string or a string fromm a keyword like __SOURCE_FILE__ */ +/* A literal string or a string from a keyword like __SOURCE_FILE__ */ stringOrKeywordString: | STRING { $1 } | KEYWORD_STRING { $1 } +interpolatedStringParts: + | INTERP_STRING_END + { [ Choice1Of2 $1 ] } + + | INTERP_STRING_PART declExpr interpolatedStringParts + { Choice1Of2 $1 :: Choice2Of2 $2 :: $3 } + +/* INTERP_STRING_BEGIN_END */ +/* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_END */ +/* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_PART int32 INTERP_STRING_END */ +interpolatedString: + | INTERP_STRING_BEGIN_PART declExpr interpolatedStringParts + { Choice1Of2 $1 :: Choice2Of2 $2 :: $3 } + + | INTERP_STRING_BEGIN_END + { [ Choice1Of2 $1 ] } + opt_HIGH_PRECEDENCE_APP: | HIGH_PRECEDENCE_BRACK_APP { } | HIGH_PRECEDENCE_PAREN_APP { } diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index 3318358c1e3..7c4e1ac7550 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -310,6 +310,10 @@ module internal TokenClassifications = | KEYWORD_STRING _ -> (FSharpTokenColorKind.Keyword, FSharpTokenCharKind.Keyword, FSharpTokenTriggerClass.None) + | INTERP_STRING_BEGIN_END _ + | INTERP_STRING_BEGIN_PART _ + | INTERP_STRING_PART _ + | INTERP_STRING_END _ | BYTEARRAY _ | STRING _ | CHAR _ -> (FSharpTokenColorKind.String, FSharpTokenCharKind.String, FSharpTokenTriggerClass.None) @@ -363,7 +367,10 @@ module internal LexerStateEncoding = | LINE_COMMENT s | STRING_TEXT s | EOF s -> s - | BYTEARRAY _ | STRING _ -> LexCont.Token(prevLexcont.LexerIfdefStack) + + | BYTEARRAY _ + | STRING _ -> LexCont.Token(prevLexcont.LexerIfdefStack) + | _ -> prevLexcont // Note that this will discard all lexcont state, including the ifdefStack. @@ -374,23 +381,27 @@ module internal LexerStateEncoding = let hardwhiteNumBits = 1 let ifdefstackCountNumBits = 8 let ifdefstackNumBits = 24 // 0 means if, 1 means else + let stringKindBits = 3 let _ = assert (lexstateNumBits + ncommentsNumBits + hardwhiteNumBits + ifdefstackCountNumBits - + ifdefstackNumBits <= 64) + + ifdefstackNumBits + + stringKindBits <= 64) let lexstateStart = 0 let ncommentsStart = lexstateNumBits let hardwhitePosStart = lexstateNumBits+ncommentsNumBits let ifdefstackCountStart = lexstateNumBits+ncommentsNumBits+hardwhiteNumBits let ifdefstackStart = lexstateNumBits+ncommentsNumBits+hardwhiteNumBits+ifdefstackCountNumBits + let stringKindStart = lexstateNumBits+ncommentsNumBits+hardwhiteNumBits+ifdefstackCountNumBits+ifdefstackNumBits let lexstateMask = Bits.mask64 lexstateStart lexstateNumBits let ncommentsMask = Bits.mask64 ncommentsStart ncommentsNumBits let hardwhitePosMask = Bits.mask64 hardwhitePosStart hardwhiteNumBits let ifdefstackCountMask = Bits.mask64 ifdefstackCountStart ifdefstackCountNumBits let ifdefstackMask = Bits.mask64 ifdefstackStart ifdefstackNumBits + let stringKindMask = Bits.mask64 stringKindStart stringKindBits let bitOfBool b = if b then 1 else 0 let boolOfBit n = (n = 1L) @@ -401,7 +412,7 @@ module internal LexerStateEncoding = let inline lexStateOfColorState (state: FSharpTokenizerColorState) = (int64 state <<< lexstateStart) &&& lexstateMask - let encodeLexCont (colorState: FSharpTokenizerColorState) ncomments (b: pos) ifdefStack light = + let encodeLexCont (colorState: FSharpTokenizerColorState, numComments, b: pos, ifdefStack, light, stringKind: LexerStringKind) = let mutable ifdefStackCount = 0 let mutable ifdefStackBits = 0 for ifOrElse in ifdefStack do @@ -411,12 +422,19 @@ module internal LexerStateEncoding = ifdefStackBits <- (ifdefStackBits ||| (1 <<< ifdefStackCount)) ifdefStackCount <- ifdefStackCount + 1 + let stringKindValue = + (if stringKind.IsByteString then 0b100 else 0) ||| + (if stringKind.IsInterpolated then 0b010 else 0) ||| + (if stringKind.IsInterpolatedFirst then 0b001 else 0) + let bits = lexStateOfColorState colorState - ||| ((ncomments <<< ncommentsStart) &&& ncommentsMask) + ||| ((numComments <<< ncommentsStart) &&& ncommentsMask) ||| ((int64 (bitOfBool light) <<< hardwhitePosStart) &&& hardwhitePosMask) ||| ((int64 ifdefStackCount <<< ifdefstackCountStart) &&& ifdefstackCountMask) ||| ((int64 ifdefStackBits <<< ifdefstackStart) &&& ifdefstackMask) + ||| ((int64 stringKindValue <<< stringKindStart) &&& stringKindMask) + { PosBits = b.Encoding OtherBits = bits } @@ -424,6 +442,11 @@ module internal LexerStateEncoding = let decodeLexCont (state: FSharpTokenizerLexState) = let mutable ifDefs = [] let bits = state.OtherBits + + let colorState = colorStateOfLexState state + let ncomments = int32 ((bits &&& ncommentsMask) >>> ncommentsStart) + let pos = pos.Decode state.PosBits + let ifdefStackCount = int32 ((bits &&& ifdefstackCountMask) >>> ifdefstackCountStart) if ifdefStackCount>0 then let ifdefStack = int32 ((bits &&& ifdefstackMask) >>> ifdefstackStart) @@ -432,46 +455,50 @@ module internal LexerStateEncoding = let mask = 1 <<< bit let ifDef = (if ifdefStack &&& mask = 0 then IfDefIf else IfDefElse) ifDefs <- (ifDef, range0) :: ifDefs - colorStateOfLexState state, - int32 ((bits &&& ncommentsMask) >>> ncommentsStart), - pos.Decode state.PosBits, - ifDefs, - boolOfBit ((bits &&& hardwhitePosMask) >>> hardwhitePosStart) + + let stringKindValue = int32 ((bits &&& stringKindMask) >>> stringKindStart) + let stringKind : LexerStringKind = + { IsByteString = ((stringKindValue &&& 0b100) = 0b100) + IsInterpolated = ((stringKindValue &&& 0b010) = 0b010) + IsInterpolatedFirst = ((stringKindValue &&& 0b001) = 0b001) } + + let hardwhite = boolOfBit ((bits &&& hardwhitePosMask) >>> hardwhitePosStart) + + (colorState, ncomments, pos, ifDefs, hardwhite, stringKind) let encodeLexInt lightSyntaxStatus (lexcont: LexerWhitespaceContinuation) = - let tag, n1, p1, ifd = + let tag, n1, p1, ifd, stringKind = match lexcont with - | LexCont.Token ifd -> FSharpTokenizerColorState.Token, 0L, pos0, ifd - | LexCont.IfDefSkip (ifd, n, m) -> FSharpTokenizerColorState.IfDefSkip, int64 n, m.Start, ifd - | LexCont.EndLine(LexerEndlineContinuation.Skip(ifd, n, m)) -> FSharpTokenizerColorState.EndLineThenSkip, int64 n, m.Start, ifd - | LexCont.EndLine(LexerEndlineContinuation.Token ifd) -> FSharpTokenizerColorState.EndLineThenToken, 0L, pos0, ifd - | LexCont.String (ifd, m) -> FSharpTokenizerColorState.String, 0L, m.Start, ifd - | LexCont.Comment (ifd, n, m) -> FSharpTokenizerColorState.Comment, int64 n, m.Start, ifd - | LexCont.SingleLineComment (ifd, n, m) -> FSharpTokenizerColorState.SingleLineComment, int64 n, m.Start, ifd - | LexCont.StringInComment (ifd, n, m) -> FSharpTokenizerColorState.StringInComment, int64 n, m.Start, ifd - | LexCont.VerbatimStringInComment (ifd, n, m) -> FSharpTokenizerColorState.VerbatimStringInComment, int64 n, m.Start, ifd - | LexCont.TripleQuoteStringInComment (ifd, n, m) -> FSharpTokenizerColorState.TripleQuoteStringInComment, int64 n, m.Start, ifd - | LexCont.MLOnly (ifd, m) -> FSharpTokenizerColorState.CamlOnly, 0L, m.Start, ifd - | LexCont.VerbatimString (ifd, m) -> FSharpTokenizerColorState.VerbatimString, 0L, m.Start, ifd - | LexCont.TripleQuoteString (ifd, m) -> FSharpTokenizerColorState.TripleQuoteString, 0L, m.Start, ifd - encodeLexCont tag n1 p1 ifd lightSyntaxStatus - + | LexCont.Token ifd -> FSharpTokenizerColorState.Token, 0L, pos0, ifd, LexerStringKind.String + | LexCont.IfDefSkip (ifd, n, m) -> FSharpTokenizerColorState.IfDefSkip, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.EndLine(LexerEndlineContinuation.Skip(ifd, n, m)) -> FSharpTokenizerColorState.EndLineThenSkip, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.EndLine(LexerEndlineContinuation.Token ifd) -> FSharpTokenizerColorState.EndLineThenToken, 0L, pos0, ifd, LexerStringKind.String + | LexCont.String (ifd, kind, m) -> FSharpTokenizerColorState.String, 0L, m.Start, ifd, kind + | LexCont.Comment (ifd, n, m) -> FSharpTokenizerColorState.Comment, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.SingleLineComment (ifd, n, m) -> FSharpTokenizerColorState.SingleLineComment, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.StringInComment (ifd, n, m) -> FSharpTokenizerColorState.StringInComment, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.VerbatimStringInComment (ifd, n, m) -> FSharpTokenizerColorState.VerbatimStringInComment, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.TripleQuoteStringInComment (ifd, n, m) -> FSharpTokenizerColorState.TripleQuoteStringInComment, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.MLOnly (ifd, m) -> FSharpTokenizerColorState.CamlOnly, 0L, m.Start, ifd, LexerStringKind.String + | LexCont.VerbatimString (ifd, kind, m) -> FSharpTokenizerColorState.VerbatimString, 0L, m.Start, ifd, kind + | LexCont.TripleQuoteString (ifd, kind, m) -> FSharpTokenizerColorState.TripleQuoteString, 0L, m.Start, ifd, kind + encodeLexCont (tag, n1, p1, ifd, lightSyntaxStatus, stringKind) let decodeLexInt (state: FSharpTokenizerLexState) = - let tag, n1, p1, ifd, lightSyntaxStatusInitial = decodeLexCont state + let tag, n1, p1, ifd, lightSyntaxStatusInitial, stringKind = decodeLexCont state let lexcont = match tag with | FSharpTokenizerColorState.Token -> LexCont.Token ifd | FSharpTokenizerColorState.IfDefSkip -> LexCont.IfDefSkip (ifd, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.String -> LexCont.String (ifd, mkRange "file" p1 p1) + | FSharpTokenizerColorState.String -> LexCont.String (ifd, stringKind, mkRange "file" p1 p1) | FSharpTokenizerColorState.Comment -> LexCont.Comment (ifd, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.SingleLineComment -> LexCont.SingleLineComment (ifd, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.StringInComment -> LexCont.StringInComment (ifd, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.VerbatimStringInComment -> LexCont.VerbatimStringInComment (ifd, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.TripleQuoteStringInComment -> LexCont.TripleQuoteStringInComment (ifd, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.CamlOnly -> LexCont.MLOnly (ifd, mkRange "file" p1 p1) - | FSharpTokenizerColorState.VerbatimString -> LexCont.VerbatimString (ifd, mkRange "file" p1 p1) - | FSharpTokenizerColorState.TripleQuoteString -> LexCont.TripleQuoteString (ifd, mkRange "file" p1 p1) + | FSharpTokenizerColorState.VerbatimString -> LexCont.VerbatimString (ifd, stringKind, mkRange "file" p1 p1) + | FSharpTokenizerColorState.TripleQuoteString -> LexCont.TripleQuoteString (ifd, stringKind, mkRange "file" p1 p1) | FSharpTokenizerColorState.EndLineThenSkip -> LexCont.EndLine(LexerEndlineContinuation.Skip(ifd, n1, mkRange "file" p1 p1)) | FSharpTokenizerColorState.EndLineThenToken -> LexCont.EndLine(LexerEndlineContinuation.Token ifd) | _ -> LexCont.Token [] @@ -488,7 +515,7 @@ module internal LexerStateEncoding = | LexCont.Token ifd -> Lexer.token (argsWithIfDefs ifd) skip lexbuf | LexCont.IfDefSkip (ifd, n, m) -> Lexer.ifdefSkip n m (argsWithIfDefs ifd) skip lexbuf // Q: What's this magic 100 number for? Q: it's just an initial buffer size. - | LexCont.String (ifd, m) -> Lexer.string (ByteBuffer.Create 100, defaultStringFinisher, m, (argsWithIfDefs ifd)) skip lexbuf + | LexCont.String (ifd, kind, m) -> Lexer.string (ByteBuffer.Create 100, LexerStringFinisher.Default, m, kind, argsWithIfDefs ifd) skip lexbuf | LexCont.Comment (ifd, n, m) -> Lexer.comment (n, m, (argsWithIfDefs ifd)) skip lexbuf // The first argument is 'None' because we don't need XML comments when called from VS | LexCont.SingleLineComment (ifd, n, m) -> Lexer.singleLineComment (None, n, m, (argsWithIfDefs ifd)) skip lexbuf @@ -496,8 +523,8 @@ module internal LexerStateEncoding = | LexCont.VerbatimStringInComment (ifd, n, m) -> Lexer.verbatimStringInComment n m (argsWithIfDefs ifd) skip lexbuf | LexCont.TripleQuoteStringInComment (ifd, n, m) -> Lexer.tripleQuoteStringInComment n m (argsWithIfDefs ifd) skip lexbuf | LexCont.MLOnly (ifd, m) -> Lexer.mlOnly m (argsWithIfDefs ifd) skip lexbuf - | LexCont.VerbatimString (ifd, m) -> Lexer.verbatimString (ByteBuffer.Create 100, defaultStringFinisher, m, (argsWithIfDefs ifd)) skip lexbuf - | LexCont.TripleQuoteString (ifd, m) -> Lexer.tripleQuoteString (ByteBuffer.Create 100, defaultStringFinisher, m, (argsWithIfDefs ifd)) skip lexbuf + | LexCont.VerbatimString (ifd, kind, m) -> Lexer.verbatimString (ByteBuffer.Create 100, LexerStringFinisher.Default, m, kind, argsWithIfDefs ifd) skip lexbuf + | LexCont.TripleQuoteString (ifd, kind, m) -> Lexer.tripleQuoteString (ByteBuffer.Create 100, LexerStringFinisher.Default, m, kind, argsWithIfDefs ifd) skip lexbuf //---------------------------------------------------------------------------- // Colorization @@ -588,8 +615,6 @@ type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, // ---------------------------------------------------------------------------------- - - do match filename with | None -> lexbuf.EndPos <- Internal.Utilities.Text.Lexing.Position.Empty | Some value -> resetLexbufPos value lexbuf @@ -696,13 +721,33 @@ type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, | _ -> // Get the information about the token let (colorClass, charClass, triggerClass) = TokenClassifications.tokenInfo token + let lexcontFinal = // If we're using token from cache, we don't move forward with lexing if isCached then lexcontInitial else LexerStateEncoding.computeNextLexState token lexcontInitial - let tokenTag = tagOfToken token + + let tokenTag = + // Tokenization just reports repeated STRING_TEXT then STRING for each part of an interpolated string + match token with + | INTERP_STRING_BEGIN_END _ + | INTERP_STRING_BEGIN_PART _ + | INTERP_STRING_PART _ + | INTERP_STRING_END _ -> FSharpTokenTag.STRING + | _ -> tagOfToken token + + let tokenName = + // Tokenization just reports repeated STRING_TEXT then STRING for each part of an interpolated string + match token with + | INTERP_STRING_BEGIN_END _ + | INTERP_STRING_BEGIN_PART _ + | INTERP_STRING_PART _ + | INTERP_STRING_END _ -> token_to_string (STRING "") + | _ -> token_to_string token + let fullMatchedLength = lexbuf.EndPos.AbsoluteOffset - lexbuf.StartPos.AbsoluteOffset + let tokenData = - { TokenName = token_to_string token + { TokenName = tokenName LeftColumn=leftc RightColumn=rightc ColorClass=colorClass @@ -1211,6 +1256,10 @@ module Lexer = | INFIX_STAR_STAR_OP _ -> FSharpSyntaxTokenKind.InfixStarStarOperator | IDENT _ -> FSharpSyntaxTokenKind.Identifier | KEYWORD_STRING _ -> FSharpSyntaxTokenKind.KeywordString + | INTERP_STRING_BEGIN_END _ + | INTERP_STRING_BEGIN_PART _ + | INTERP_STRING_PART _ + | INTERP_STRING_END _ | STRING _ -> FSharpSyntaxTokenKind.String | BYTEARRAY _ -> FSharpSyntaxTokenKind.ByteArray | _ -> FSharpSyntaxTokenKind.None diff --git a/src/fsharp/service/ServiceParseTreeWalk.fs b/src/fsharp/service/ServiceParseTreeWalk.fs index 7bf60285087..fd068ecd984 100755 --- a/src/fsharp/service/ServiceParseTreeWalk.fs +++ b/src/fsharp/service/ServiceParseTreeWalk.fs @@ -200,25 +200,36 @@ module public AstTraversal = let path = TraverseStep.Expr e :: path let traverseSynExpr = traverseSynExpr path match e with + | SynExpr.Paren (synExpr, _, _, _parenRange) -> traverseSynExpr synExpr + | SynExpr.Quote (_synExpr, _, synExpr2, _, _range) -> [//dive synExpr synExpr.Range traverseSynExpr // TODO, what is this? dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.Const (_synConst, _range) -> None - | SynExpr.Typed (synExpr, synType, _range) -> [ traverseSynExpr synExpr; traverseSynType synType ] |> List.tryPick id + + | SynExpr.InterpolatedString (es, _) -> + [ for e in es do match e with Choice1Of2 _ -> () | Choice2Of2 e -> yield dive e e.Range traverseSynExpr ] + |> pick expr + + | SynExpr.Typed (synExpr, synType, _range) -> + [ traverseSynExpr synExpr; traverseSynType synType ] |> List.tryPick id + | SynExpr.Tuple (_, synExprList, _, _range) - | SynExpr.ArrayOrList (_, synExprList, _range) -> synExprList |> List.map (fun x -> dive x x.Range traverseSynExpr) |> pick expr + | SynExpr.ArrayOrList (_, synExprList, _range) -> + synExprList |> List.map (fun x -> dive x x.Range traverseSynExpr) |> pick expr | SynExpr.AnonRecd (_isStruct, copyOpt, synExprList, _range) -> [ match copyOpt with - | Some(expr, (withRange, _)) -> - yield dive expr expr.Range traverseSynExpr + | Some(origExpr, (withRange, _)) -> + yield dive origExpr origExpr.Range traverseSynExpr yield dive () withRange (fun () -> if posGeq pos withRange.End then // special case: caret is after WITH // { x with $ } - visitor.VisitRecordField (path, Some expr, None) + visitor.VisitRecordField (path, Some origExpr, None) else None ) @@ -226,6 +237,7 @@ module public AstTraversal = for (_,x) in synExprList do yield dive x x.Range traverseSynExpr ] |> pick expr + | SynExpr.Record (inheritOpt,copyOpt,fields, _range) -> [ let diveIntoSeparator offsideColumn scPosOpt copyOpt = @@ -312,6 +324,7 @@ module public AstTraversal = | _ -> () ] |> pick expr + | SynExpr.New (_, _synType, synExpr, _range) -> traverseSynExpr synExpr | SynExpr.ObjExpr (ty,baseCallOpt,binds,ifaces,_range1,_range2) -> let result = @@ -335,21 +348,26 @@ module public AstTraversal = for b in binds do yield dive b b.RangeOfBindingAndRhs (traverseSynBinding path) ] |> pick expr + | SynExpr.While (_sequencePointInfoForWhileLoop, synExpr, synExpr2, _range) -> [dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.For (_sequencePointInfoForForLoop, _ident, synExpr, _, synExpr2, synExpr3, _range) -> [dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr dive synExpr3 synExpr3.Range traverseSynExpr] |> pick expr + | SynExpr.ForEach (_sequencePointInfoForForLoop, _seqExprOnly, _isFromSource, synPat, synExpr, synExpr2, _range) -> [dive synPat synPat.Range traversePat dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.ArrayOrListOfSeqExpr (_, synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.CompExpr (_, _, synExpr, _range) -> // now parser treats this syntactic expression as computation expression // { identifier } @@ -367,6 +385,7 @@ module public AstTraversal = if ok.IsSome then ok else traverseSynExpr synExpr + | SynExpr.Lambda (_, _, synSimplePats, synExpr, _range) -> match synSimplePats with | SynSimplePats.SimplePats(pats,_) -> @@ -374,17 +393,23 @@ module public AstTraversal = | Some x -> Some x | None -> traverseSynExpr synExpr | _ -> traverseSynExpr synExpr + | SynExpr.MatchLambda (_isExnMatch,_argm,synMatchClauseList,_spBind,_wholem) -> synMatchClauseList |> List.map (fun x -> dive x x.Range (traverseSynMatchClause path)) |> pick expr + | SynExpr.Match (_sequencePointInfoForBinding, synExpr, synMatchClauseList, _range) -> [yield dive synExpr synExpr.Range traverseSynExpr yield! synMatchClauseList |> List.map (fun x -> dive x x.RangeOfGuardAndRhs (traverseSynMatchClause path))] |> pick expr + | SynExpr.Do (synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.Assert (synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.Fixed (synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.App (_exprAtomicFlag, isInfix, synExpr, synExpr2, _range) -> if isInfix then [dive synExpr2 synExpr2.Range traverseSynExpr @@ -394,7 +419,9 @@ module public AstTraversal = [dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.TypeApp (synExpr, _, _synTypeList, _commas, _, _, _range) -> traverseSynExpr synExpr + | SynExpr.LetOrUse (_, _, synBindingList, synExpr, range) -> match visitor.VisitLetOrUse(path, traverseSynBinding path, synBindingList, range) with | Some x -> Some x @@ -402,20 +429,26 @@ module public AstTraversal = [yield! synBindingList |> List.map (fun x -> dive x x.RangeOfBindingAndRhs (traverseSynBinding path)) yield dive synExpr synExpr.Range traverseSynExpr] |> pick expr + | SynExpr.TryWith (synExpr, _range, synMatchClauseList, _range2, _range3, _sequencePointInfoForTry, _sequencePointInfoForWith) -> [yield dive synExpr synExpr.Range traverseSynExpr yield! synMatchClauseList |> List.map (fun x -> dive x x.Range (traverseSynMatchClause path))] |> pick expr + | SynExpr.TryFinally (synExpr, synExpr2, _range, _sequencePointInfoForTry, _sequencePointInfoForFinally) -> [dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.Lazy (synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.SequentialOrImplicitYield (_sequencePointInfoForSequential, synExpr, synExpr2, _, _range) + | SynExpr.Sequential (_sequencePointInfoForSequential, _, synExpr, synExpr2, _range) -> [dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.IfThenElse (synExpr, synExpr2, synExprOpt, _sequencePointInfoForBinding, _isRecovery, _range, _range2) -> [yield dive synExpr synExpr.Range traverseSynExpr yield dive synExpr2 synExpr2.Range traverseSynExpr @@ -423,21 +456,29 @@ module public AstTraversal = | None -> () | Some(x) -> yield dive x x.Range traverseSynExpr] |> pick expr + | SynExpr.Ident (_ident) -> None + | SynExpr.LongIdent (_, _longIdent, _altNameRefCell, _range) -> None + | SynExpr.LongIdentSet (_longIdent, synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.DotGet (synExpr, _dotm, _longIdent, _range) -> traverseSynExpr synExpr + | SynExpr.Set (synExpr, synExpr2, _) + | SynExpr.DotSet (synExpr, _, synExpr2, _) -> [dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.DotIndexedGet (synExpr, synExprList, _range, _range2) -> [yield dive synExpr synExpr.Range traverseSynExpr for synExpr in synExprList do for x in synExpr.Exprs do yield dive x x.Range traverseSynExpr] |> pick expr + | SynExpr.DotIndexedSet (synExpr, synExprList, synExpr2, _, _range, _range2) -> [yield dive synExpr synExpr.Range traverseSynExpr for synExpr in synExprList do @@ -445,33 +486,48 @@ module public AstTraversal = yield dive x x.Range traverseSynExpr yield dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.JoinIn (synExpr1, _range, synExpr2, _range2) -> [dive synExpr1 synExpr1.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.NamedIndexedPropertySet (_longIdent, synExpr, synExpr2, _range) -> [dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr] |> pick expr + | SynExpr.DotNamedIndexedPropertySet (synExpr, _longIdent, synExpr2, synExpr3, _range) -> [dive synExpr synExpr.Range traverseSynExpr dive synExpr2 synExpr2.Range traverseSynExpr dive synExpr3 synExpr3.Range traverseSynExpr] |> pick expr + | SynExpr.TypeTest (synExpr, synType, _range) + | SynExpr.Upcast (synExpr, synType, _range) + | SynExpr.Downcast (synExpr, synType, _range) -> [dive synExpr synExpr.Range traverseSynExpr dive synType synType.Range traverseSynType] |> pick expr + | SynExpr.InferredUpcast (synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.InferredDowncast (synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.Null (_range) -> None + | SynExpr.AddressOf (_, synExpr, _range, _range2) -> traverseSynExpr synExpr + | SynExpr.TraitCall (_synTyparList, _synMemberSig, synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.ImplicitZero (_range) -> None + | SynExpr.YieldOrReturn (_, synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.YieldOrReturnFrom (_, synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.LetOrUseBang(_sequencePointInfoForBinding, _, _, synPat, synExpr, andBangSynExprs, synExpr2, _range) -> [ yield dive synPat synPat.Range traversePat @@ -483,17 +539,26 @@ module public AstTraversal = yield dive synExpr2 synExpr2.Range traverseSynExpr ] |> pick expr + | SynExpr.MatchBang (_sequencePointInfoForBinding, synExpr, synMatchClauseList, _range) -> [yield dive synExpr synExpr.Range traverseSynExpr yield! synMatchClauseList |> List.map (fun x -> dive x x.RangeOfGuardAndRhs (traverseSynMatchClause path))] |> pick expr + | SynExpr.DoBang (synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.LibraryOnlyILAssembly _ -> None + | SynExpr.LibraryOnlyStaticOptimization _ -> None + | SynExpr.LibraryOnlyUnionCaseFieldGet _ -> None + | SynExpr.LibraryOnlyUnionCaseFieldSet _ -> None + | SynExpr.ArbitraryAfterError (_debugStr, _range) -> None + | SynExpr.FromParseError (synExpr, _range) -> traverseSynExpr synExpr + | SynExpr.DiscardAfterMissingQualificationAfterDot (synExpr, _range) -> traverseSynExpr synExpr visitor.VisitExpr(path, traverseSynExpr path, defaultTraverse, expr) diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 684f3a04e06..2667a10e8ac 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -209,6 +209,9 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option | SynExpr.Paren (e, _, _, _) -> yield! walkExpr false e + | SynExpr.InterpolatedString (es, _) -> + yield! walkExprs [ for e in es do match e with Choice1Of2 _ -> () | Choice2Of2 e -> yield e ] + | SynExpr.YieldOrReturn (_, e, _) | SynExpr.YieldOrReturnFrom (_, e, _) | SynExpr.DoBang (e, _) -> diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index e496f677f6c..2815d8594cc 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -2246,6 +2246,7 @@ Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringBuilder[T](System.Text. Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThenFail[T,TResult](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[System.String,TResult], Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) +Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriterThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,TResult], System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index e2bc4a25a85..cb77401ddfc 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -2246,6 +2246,7 @@ Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringBuilder[T](System.Text. Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThenFail[T,TResult](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[System.String,TResult], Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) +Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriterThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,TResult], System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/fsharp/FSharpSuite.Tests.fsproj b/tests/fsharp/FSharpSuite.Tests.fsproj index 347dacb464c..570357f8002 100644 --- a/tests/fsharp/FSharpSuite.Tests.fsproj +++ b/tests/fsharp/FSharpSuite.Tests.fsproj @@ -70,6 +70,7 @@ + diff --git a/tests/service/TokenizerTests.fs b/tests/service/TokenizerTests.fs index 0827ff35027..c8b024a67ca 100644 --- a/tests/service/TokenizerTests.fs +++ b/tests/service/TokenizerTests.fs @@ -54,5 +54,39 @@ let ``Tokenizer test 1``() = ("STRING_TEXT", "\""); ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); ("STRING", "\""); ("WHITESPACE", " ")])] - Assert.AreEqual(actual, expected) + if actual <> expected then + printfn "actual = %A" actual + printfn "expected = %A" expected + Assert.Fail(sprintf "actual and expected did not match,actual =\n%A\nexpected=\n%A\n" actual expected) + +[] +let ``Tokenizer test 2``() = + let tokenizedLines = + tokenizeLines + [| "// Tests tokenizing string interpolation" + "let hello = $\"Hello world {1+1} = {2}\" " |] + + let actual = + [ for lineNo, lineToks in tokenizedLines do + yield lineNo, [ for str, info in lineToks do yield info.TokenName, str ] ] + let expected = + [(0, + [("LINE_COMMENT", "//"); ("LINE_COMMENT", " "); ("LINE_COMMENT", "Tests"); + ("LINE_COMMENT", " "); ("LINE_COMMENT", "tokenizing"); ("LINE_COMMENT", " "); + ("LINE_COMMENT", "string"); ("LINE_COMMENT", " "); + ("LINE_COMMENT", "interpolation")]); + (1, + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello"); ("WHITESPACE", " "); + ("EQUALS", "="); ("WHITESPACE", " "); ("STRING_TEXT", "$\""); + ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); + ("STRING_TEXT", " "); ("STRING", "{"); ("STRING_TEXT", "1"); + ("STRING_TEXT", "+"); ("STRING_TEXT", "1"); ("STRING_TEXT", "}"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("STRING", "{"); ("STRING_TEXT", "2"); ("STRING_TEXT", "}"); ("STRING", "\""); + ("STRING_TEXT", " ")])] + + if actual <> expected then + printfn "actual = %A" actual + printfn "expected = %A" expected + Assert.Fail(sprintf "actual and expected did not match,actual =\n%A\nexpected=\n%A\n" actual expected) From 23d38cca53113a8895acf3defd8b3ae20540041d Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 15 Apr 2020 11:54:25 +0100 Subject: [PATCH 02/87] string interploation tests --- .../Compiler/Language/StringInterpolation.fs | 247 ++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 tests/fsharp/Compiler/Language/StringInterpolation.fs diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs new file mode 100644 index 00000000000..653c0802fc2 --- /dev/null +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests + +open NUnit.Framework +open FSharp.Compiler.SourceCodeServices +open FSharp.Compiler.UnitTests.Utilities + +[] +module StringInterpolationTests = + + [] + let ``Basic string interpolation`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "vcewweh1" $"this is 2" "this is 2" + +check "vcewweh2" $"this is {1} + 1 = 2" "this is 1 + 1 = 2" + +check "vcewweh3" $"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" + +check "vcewweh4" $"this is {1} + {1+1}" "this is 1 + 2" + +check "vcewweh5" $"this is {1}" "this is 1" + """ + + + [] + let ``String interpolation using atomic expression forms`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let x = 12 +let s = "sixsix" +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "vcewweh4" $"this is %d{1} + {1+1+3} = 6" "this is 1 + 5 = 6" + +check "vcewweh5" $"this is 0x%08x{x} + {1+1} = 14" "this is 0x0000000c + 2 = 14" + +// Check dot notation +check "vcewweh6" $"this is {s.Length} + {1+1} = 8" "this is 6 + 2 = 8" + +// Check null expression +check "vcewweh8" $"abc{null}def" "abcdef" + + """ + + + [] + let ``String interpolation using nested control flow expressions`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +let x = 12 +let s = "sixsix" + +// Check let expression +check "vcewweh7" $"abc {let x = 3 in x + x} def" "abc 6 def" + +// Check if expression (parenthesized) +check "vcewweh9" $"abc{(if true then 3 else 4)}def" "abc3def" + +// Check if-then-else expression (un-parenthesized) +check "vcewweh10" $"abc{if true then 3 else 4}def" "abc3def" + +// Check two if-then-else expression (un-parenthesized) +check "vcewweh11" $"abc{if true then 3 else 4}def{if false then 3 else 4}xyz" "abc3def4xyz" + +// Check two if-then-else expression (un-parenthesized, first split) +check "vcewweh12" $"abc{if true then 3 + else 4}def{if false then 3 else 4}xyz" "abc3def4xyz" + +// Check two if-then-else expression (un-parenthesized, second split) +check "vcewweh13" $"abc{if true then 3 else 4}def{if false then 3 + else 4}xyz" "abc3def4xyz" + +// Check two if-then-else expression (un-parenthesized, both split) +check "vcewweh14" $"abc{if true then 3 + else 4}def{if false then 3 + else 4}xyz" "abc3def4xyz" + +// Check if-then expression (un-parenthesized) +check "vcewweh15" $"abc{if true then ()}def" "abcdef" + +// Check two if-then expression (un-parenthesized) +check "vcewweh16" $"abc{if true then ()}def{if true then ()}xyz" "abcdefxyz" + +// Check multi-line let with parentheses +check "fahweelvlo" + $"abc {(let x = 3 + x + x)} def" + "abc 6 def" + +// Check multi-line let without parentheses +check "fahweelvlo3" + $"abc {let x = 3 + x + x} def" + "abc 6 def" + +// Check multi-line let without parentheses (two) +check "fahweelvlo4" + $"abc {let x = 3 + x + x} def {let x = 3 + x + x} xyz" + "abc 6 def 6 xyz" + +// Check while expression (un-parenthesized) +check "vcewweh17" $"abc{while false do ()}def" "abcdef" + + """ + + + + [] + let ``String interpolation using nested string`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +// check nested string +check "vcewweh22m" $"x = {"1"} " "x = 1 " + """ + + + + [] + let ``String interpolation using record data`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +type R = { X : int } +type R2 = { X : int ; Y: int } + +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +// Check record expression (parenthesized) +check "vcewweh18" $"abc{({contents=1}.contents)}def" "abc1def" + +// Check record expression (un-parenthesized) +check "vcewweh19" $"abc{{contents=1}.contents}def" "abc1def" + +// Check record expression (un-parenthesized) +check "vcewweh20" $"abc{{X=1}}def" "abc{ X = 1 }def" + +// Check record expression (un-parenthesized, multi-line) +check "vcewweh21" $"abc{{X=1; Y=2}}def" "abc{ X = 1\n Y = 2 }def" + + """ + + + [] + let ``String interpolation using printf formats`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +let x = 12 +let s = "sixsix" + +// check %A +check "vcewweh22" $"x = %A{1}" "x = 1" + +// check %d +check "vcewweh22b" $"x = %d{1}" "x = 1" + +// check %x +check "vcewweh22c" $"x = %x{1}" "x = 1" + +// check %o (octal) +check "vcewweh22d" $"x = %o{15}" "x = 17" + +// check %b +check "vcewweh22e" $"x = %b{true}" "x = true" + +// check %s +check "vcewweh22f" $"x = %s{s}" "x = sixsix" + +// check %A of string +check "vcewweh22g" $"x = %A{s}" "x = \"sixsix\"" + +// check nested string with %s +check "vcewweh22l" $"x = %s{"1"}" "x = 1" + +check "vcewweh20" $"x = %A{1}" "x = 1" + + """ + + + [] + let ``String interpolation using list and array data`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +// check unannotated of list +check "vcewweh22i" $"x = {[0..100]} " "x = [0; 1; 2; ... ] " + +let xs = [0..100] +// check unannotated of list +check "vcewweh22i" $"x = {xs} " "x = [0; 1; 2; ... ] " + +// check %A of list +check "vcewweh22h" $"x = %0A{[0..100]} " "x = [0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97; 98; 99; ...] " + +// check unannotated of array +check "vcewweh22j" $"x = {[|0..100|]} " "x = System.Int32[] " + +let arr = [|0..100|] +// check unannotated of array +check "vcewweh22j" $"x = {arr} " "x = System.Int32[] " + +// check %0A of array +check "vcewweh22k" $"x = %0A{[|0..100|]} " "x = [|0; 1; 2; 3; 4; 5; 6; 7; 8; 9; 10; 11; 12; 13; 14; 15; 16; 17; 18; 19; 20; 21; 22; 23; 24; 25; 26; 27; 28; 29; 30; 31; 32; 33; 34; 35; 36; 37; 38; 39; 40; 41; 42; 43; 44; 45; 46; 47; 48; 49; 50; 51; 52; 53; 54; 55; 56; 57; 58; 59; 60; 61; 62; 63; 64; 65; 66; 67; 68; 69; 70; 71; 72; 73; 74; 75; 76; 77; 78; 79; 80; 81; 82; 83; 84; 85; 86; 87; 88; 89; 90; 91; 92; 93; 94; 95; 96; 97; 98; 99; ...|] " + + """ + + + [] + let ``String interpolation using anonymous records`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +// Check anonymous record expression (parenthesized) +check "vcewweh23" $"abc{({| A=1 |})}def" "abc{ A = 1 }def" + + """ + + + [] + let ``Basic string interpolation (no preview)`` () = + CompilerAssert.TypeCheckWithErrorsAndOptions [| |] + """ +let x = $"one" + """ + [|(FSharpErrorSeverity.Error, 3350, (2, 9, 2, 15), + "Feature 'string interpolation' is not available in F# 4.7. Please use language version 'preview' or greater.")|] From cf658de04969c10794bc49df3c07546fd00ad9a4 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 15 Apr 2020 17:24:19 +0100 Subject: [PATCH 03/87] escape {{ }}, test verbatim and triple quote, implement .NET specifiers --- src/fsharp/CheckFormatStrings.fs | 65 ++++--- src/fsharp/FSComp.txt | 7 +- src/fsharp/FSharp.Core/printf.fs | 51 +++++- src/fsharp/ParseHelpers.fs | 18 +- src/fsharp/SyntaxTree.fs | 4 +- src/fsharp/SyntaxTreeOps.fs | 2 +- src/fsharp/TypeChecker.fs | 31 +++- src/fsharp/lex.fsl | 164 +++++++++++------- src/fsharp/lexhelp.fs | 8 +- src/fsharp/lexhelp.fsi | 5 + src/fsharp/pars.fsy | 28 +-- src/fsharp/service/ServiceLexing.fs | 46 ++--- src/fsharp/service/ServiceParseTreeWalk.fs | 8 +- src/fsharp/service/ServiceUntypedParse.fs | 4 +- .../Compiler/Language/StringInterpolation.fs | 109 ++++++++++++ 15 files changed, 401 insertions(+), 149 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index fb473ced98a..b46836edf87 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -47,7 +47,7 @@ let newInfo () = addZeros = false precision = false} -let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (context: FormatStringCheckContext option) fmt bty cty = +let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolation (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) = @@ -193,13 +193,31 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (context: Format checkNoZeroFlag c checkNoNumericPrefix c - let skipInterp i = - // Explicitly typed holes in interpolated strings get '%P' after them as hole place marker - if isInterp then - if i+1 < fmt.Length && fmt.[i] = '%' && fmt.[i+1] = 'P' then i + 2 - else raise (Failure (FSComp.SR.forFormatInvalidForInterpolated())) + // Explicitly typed holes in interpolated strings "....%d{x}..." get additional '%P()' as a hole place marker + let skipPossibleInterpolationHole i = + if isInterpolation then + if i+1 < len && fmt.[i] = '%' && fmt.[i+1] = 'P' then + let i = i + 2 + if i+1 < len && fmt.[i] = '(' && fmt.[i+1] = ')' then + i + 2 + else + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated2())) + else + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated())) else i + // Implicitly typed holes in interpolated strings are translated to '... %P(...)...' in the + // type checker. They should always have '(...)' after for format string. + let requireAndSkipInterpolationHoleFormat i = + if i < len && fmt.[i] = '(' then + let i2 = fmt.IndexOf(")", i+1) + if i2 = -1 then + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated3())) + else + i2+1 + else + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated3())) + let collectSpecifierLocation relLine relCol numStdArgs = let numArgsForSpecifier = numStdArgs + (if widthArg then 1 else 0) + (if precisionArg then 1 else 0) @@ -224,7 +242,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (context: Format | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> if info.precision then raise (Failure (FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()))) collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, relLine, relCol+1) | ('l' | 'L') -> @@ -240,7 +258,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (context: Format match fmt.[i] with | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, relLine, relCol+1) | _ -> raise (Failure (FSComp.SR.forBadFormatSpecifier())) @@ -249,41 +267,42 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (context: Format | 'M' -> collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, mkFlexibleDecimalFormatTypar g m) :: acc) (i, relLine, relCol+1) | ('f' | 'F' | 'e' | 'E' | 'g' | 'G') -> collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, mkFlexibleFloatFormatTypar g m) :: acc) (i, relLine, relCol+1) | 'b' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, g.bool_ty) :: acc) (i, relLine, relCol+1) | 'c' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, g.char_ty) :: acc) (i, relLine, relCol+1) | 's' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, g.string_ty) :: acc) (i, relLine, relCol+1) | 'O' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) - // residue of hole "...{n}..." in interpolated strings - | 'P' when isInterp -> + // residue of hole "...{n}..." in interpolated strings become %P(...) + | 'P' when isInterpolation -> checkOtherFlags ch + let i = requireAndSkipInterpolationHoleFormat (i+1) parseLoop ((posi, NewInferenceType ()) :: acc) (i+1, relLine, relCol+1) | 'A' -> @@ -291,7 +310,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (context: Format | None // %A has BindingFlags=Public, %+A has BindingFlags=Public | NonPublic | Some '+' -> collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) | Some n -> raise (Failure (FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), n.ToString()))) @@ -300,13 +319,13 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (context: Format let xty = NewInferenceType () let fty = bty --> (xty --> cty) collectSpecifierLocation relLine relCol 2 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i, relLine, relCol+1) | 't' -> checkOtherFlags ch collectSpecifierLocation relLine relCol 1 - let i = skipInterp (i+1) + let i = skipPossibleInterpolationHole (i+1) parseLoop ((posi, bty --> cty) :: acc) (i, relLine, relCol+1) | c -> raise (Failure (FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c))) @@ -317,15 +336,15 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterp (context: Format let results = parseLoop [] (0, 0, m.StartColumn) results, Seq.toList specifierLocations -let ParseFormatString m g isInterp formatStringCheckContext fmt bty cty dty = - let argtys, specifierLocations = parseFormatStringInternal m g isInterp formatStringCheckContext fmt bty cty +let ParseFormatString m g isInterpolation formatStringCheckContext fmt bty cty dty = + let argtys, specifierLocations = parseFormatStringInternal m g isInterpolation formatStringCheckContext fmt bty cty let aty = List.foldBack (-->) argtys dty let ety = mkRefTupledTy g argtys (argtys, aty, ety), specifierLocations -let TryCountFormatStringArguments m g isInterp fmt bty cty = +let TryCountFormatStringArguments m g isInterpolation fmt bty cty = try - let argtys, _specifierLocations = parseFormatStringInternal m g isInterp None fmt bty cty + let argtys, _specifierLocations = parseFormatStringInternal m g isInterpolation None fmt bty cty Some argtys.Length with _ -> None diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 6b9d4975bbf..b59f6223c73 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1512,5 +1512,8 @@ featureAndBang,"applicative computation expressions" featureNullableOptionalInterop,"nullable optional interop" featureDefaultInterfaceMemberConsumption,"default interface member consumption" featureStringInterpolation,"string interpolation" -3361,tcInterpolationMixedWithPercent,"Mismatch in interpolated string. Interpolated strings may not use '%%' formats unless each is given an expression, e.g. '%%d{{1+1}}'" -forFormatInvalidForInterpolated,"Invalid interpolated string. interpolated strings may not use '%%' formats unless each is given an expression, e.g. '%%d{{1+1}}'" +3361,tcInterpolationMixedWithPercent,"Mismatch in interpolated string. Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'" +forFormatInvalidForInterpolated,"Invalid interpolated string. Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'." +forFormatInvalidForInterpolated2,"Invalid interpolated string. .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%%' format specifiers." +forFormatInvalidForInterpolated3,"Invalid interpolated string. The '%%P' specifier may not be used explicitly." +3362,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 0e008aa4fad..791565f652c 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -77,6 +77,7 @@ module internal PrintfImpl = Precision: int Width: int Flags: FormatFlags + InteropHoleDotNetFormat: string option } member this.IsStarPrecision = this.Precision = StarValue member this.IsPrecisionSpecified = this.Precision <> NotSpecifiedValue @@ -130,8 +131,27 @@ module internal PrintfImpl = let parseTypeChar (s: string) i = s.[i], (i + 1) - let skipInterpHole isInterp (s:string) i = - if isInterp && i+1 < s.Length && s.[i] = '%' && s.[i+1] = 'P' then i+2 else i + let parseInteropHoleDotNetFormat typeChar (s: string) i = + if typeChar = 'P' then + if i < s.Length && s.[i] = '(' then + let i2 = s.IndexOf(")", i) + if i2 = -1 then + None, i + else + Some s.[i+1..i2-1], i2+1 + else + None, i + else + None, i + + // Skip %P() added for hole in "...%d{x}..." + let skipInterpolationHole isInterp (s:string) i = + if isInterp && i+3 < s.Length && + s.[i] = '%' && + s.[i+1] = 'P' && + s.[i+2] = '(' && + s.[i+3] = ')' then i+4 + else i let findNextFormatSpecifier (s: string) i = let rec go i (buf: Text.StringBuilder) = @@ -1051,7 +1071,9 @@ module internal PrintfImpl = Padding.withPaddingFormatted spec getFormat defaultFormat (Float.noJustification f prefix) (Float.leftJustify isGFormat f prefix padChar) (Float.rightJustify f prefix padChar) let inline identity v = v + let inline toString v = (^T : (member ToString: IFormatProvider -> string)(v, invariantCulture)) + let inline toFormattedString fmt = fun (v: ^T) -> (^T: (member ToString: string * IFormatProvider -> string)(v, fmt, invariantCulture)) let inline numberToString c spec alt unsignedConv = @@ -1070,11 +1092,24 @@ module internal PrintfImpl = type ObjectPrinter = static member ObjectToString<'T>(spec: FormatSpecifier) = - basicWithPadding spec (fun (v: 'T) -> match box v with null -> "" | x -> x.ToString()) + basicWithPadding spec (fun (v: 'T) -> + match box v with + | null -> "" + | x -> x.ToString()) /// Convert an interpoland to a string static member InterpolandToString<'T>(spec: FormatSpecifier) = - basicWithPadding spec (fun (v: 'T) -> match box v with null -> "" | x -> x.ToString()) + let fmt = + match spec.InteropHoleDotNetFormat with + | None -> null + | Some fmt -> "{0:" + fmt + "}" + basicWithPadding spec (fun (v: 'T) -> + match box v with + | null -> "" + | x -> + match fmt with + | null -> x.ToString() + | fmt -> String.Format(fmt, x)) static member GenericToStringCore(v: 'T, opts: Microsoft.FSharp.Text.StructuredPrintfImpl.FormatOptions, bindingFlags) = let vty = @@ -1443,10 +1478,12 @@ module internal PrintfImpl = let width, i = FormatString.parseWidth s i let precision, i = FormatString.parsePrecision s i let typeChar, i = FormatString.parseTypeChar s i - // Skip %P insertion points added after %d in interpolated strings - let i = FormatString.skipInterpHole isInterp s i + let interpHoleDotnetFormat, i = FormatString.parseInteropHoleDotNetFormat typeChar s i + + // Skip %P insertion points added after %d{...} etc. in interpolated strings + let i = FormatString.skipInterpolationHole isInterp s i - let spec = { TypeChar = typeChar; Precision = precision; Flags = flags; Width = width} + let spec = { TypeChar = typeChar; Precision = precision; Flags = flags; Width = width; InteropHoleDotNetFormat = interpHoleDotnetFormat } let next, suffix = FormatString.findNextFormatSpecifier s i diff --git a/src/fsharp/ParseHelpers.fs b/src/fsharp/ParseHelpers.fs index 5b3f3c59447..969ce6fdc9d 100644 --- a/src/fsharp/ParseHelpers.fs +++ b/src/fsharp/ParseHelpers.fs @@ -147,6 +147,12 @@ let rec LexerIfdefEval (lookup: string -> bool) = function // Parsing: continuations for whitespace tokens //------------------------------------------------------------------------ +[] +type LexerStringStyle = + | Verbatim + | TripleQuote + | SingleQuote + [] type LexerStringKind = { IsByteString: bool @@ -166,14 +172,10 @@ type LexerStringKind = type LexerWhitespaceContinuation = | Token of ifdef: LexerIfdefStackEntries | IfDefSkip of ifdef: LexerIfdefStackEntries * int * range: range - | String of ifdef: LexerIfdefStackEntries * kind: LexerStringKind * range: range - | VerbatimString of ifdef: LexerIfdefStackEntries * kind: LexerStringKind * range: range - | TripleQuoteString of ifdef: LexerIfdefStackEntries * kind: LexerStringKind * range: range + | String of ifdef: LexerIfdefStackEntries * style: LexerStringStyle * kind: LexerStringKind * range: range | Comment of ifdef: LexerIfdefStackEntries * int * range: range | SingleLineComment of ifdef: LexerIfdefStackEntries * int * range: range - | StringInComment of ifdef: LexerIfdefStackEntries * int * range: range - | VerbatimStringInComment of ifdef: LexerIfdefStackEntries * int * range: range - | TripleQuoteStringInComment of ifdef: LexerIfdefStackEntries * int * range: range + | StringInComment of ifdef: LexerIfdefStackEntries * style: LexerStringStyle * int * range: range | MLOnly of ifdef: LexerIfdefStackEntries * range: range | EndLine of LexerEndlineContinuation @@ -182,13 +184,9 @@ type LexerWhitespaceContinuation = | LexCont.Token (ifdef=ifd) | LexCont.IfDefSkip (ifdef=ifd) | LexCont.String (ifdef=ifd) - | LexCont.VerbatimString (ifdef=ifd) | LexCont.Comment (ifdef=ifd) | LexCont.SingleLineComment (ifdef=ifd) - | LexCont.TripleQuoteString (ifdef=ifd) | LexCont.StringInComment (ifdef=ifd) - | LexCont.VerbatimStringInComment (ifdef=ifd) - | LexCont.TripleQuoteStringInComment (ifdef=ifd) | LexCont.MLOnly (ifdef=ifd) -> ifd | LexCont.EndLine endl -> endl.LexerIfdefStack diff --git a/src/fsharp/SyntaxTree.fs b/src/fsharp/SyntaxTree.fs index d21611c24a4..68ecf2224ee 100644 --- a/src/fsharp/SyntaxTree.fs +++ b/src/fsharp/SyntaxTree.fs @@ -1003,9 +1003,9 @@ type SynExpr = expr: SynExpr * range: range - /// F# syntax: interpolated string, e.g. "abc%{123}" + /// F# syntax: interpolated string, e.g. "abc{x}" or "abc{x,3}" or "abc{x:N4}" | InterpolatedString of - contents: Choice list * + contents: Choice list * range: range /// Gets the syntax range of this constuct diff --git a/src/fsharp/SyntaxTreeOps.fs b/src/fsharp/SyntaxTreeOps.fs index 9e94afc0047..5fc694cc793 100644 --- a/src/fsharp/SyntaxTreeOps.fs +++ b/src/fsharp/SyntaxTreeOps.fs @@ -724,6 +724,6 @@ let rec synExprContainsError inpExpr = walkExpr e1 || walkExprs [ for (_,_,_,_,e,_) in es do yield e ] || walkExpr e2 | SynExpr.InterpolatedString (parts, _m) -> - walkExprs (parts |> List.choose (function Choice1Of2 _ -> None | Choice2Of2 x -> Some x)) + walkExprs (parts |> List.choose (function Choice1Of2 _ -> None | Choice2Of2 (x, _) -> Some x)) walkExpr inpExpr diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index ae91c455012..16469f83d06 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -5908,9 +5908,34 @@ and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) = languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) - let exprFills = parts |> List.choose(function Choice1Of2 _ -> None | Choice2Of2 e -> Some e) - let stringText = parts |> List.map(function Choice1Of2 s -> s | Choice2Of2 _ -> "%P") |> String.concat "" - TcConstStringFormatExpr cenv overallTy env m tpenv (Some exprFills) stringText + + let fillExprs = + parts + |> List.choose (function + | Choice1Of2 _ -> None + | Choice2Of2 (fillExpr, _) -> + match fillExpr with + // Detect "x" part of "...{x,3}..." + | SynExpr.Tuple (false, [e; SynExpr.Const (SynConst.Int32 _align, _)], _, _) -> Some e + | e -> Some e) + + let stringText = + parts + |> List.map (function + | Choice1Of2 s -> s + | Choice2Of2 (fillExpr, format) -> + let alignText = + match fillExpr with + // Validate and detect ",3" part of "...{x,3}..." + | SynExpr.Tuple (false, args, _, _) -> + match args with + | [_; SynExpr.Const (SynConst.Int32 align, _)] -> string align + | _ -> errorR(Error(FSComp.SR.tcInvalidAlignmentInInterpolatedString(), m)); "" + | _ -> "" + let formatText = match format with None -> "()" | Some n -> "(" + n.idText + ")" + "%" + alignText + "P" + formatText ) + |> String.concat "" + TcConstStringFormatExpr cenv overallTy env m tpenv (Some fillExprs) stringText | SynExpr.Const (synConst, m) -> CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index 83f7209d9f1..f336cae47ca 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -543,27 +543,27 @@ rule token args skip = parse | '"' { let buf, fin, m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringKind.String, m))) else string (buf, fin, m, LexerStringKind.String, args) skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, LexerStringKind.String, m))) else singleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } + + | '$' '"' '"' '"' + { let buf, fin, m = startString args lexbuf + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, LexerStringKind.InterpolatedStringFirst, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | '$' '"' { let buf,fin,m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringKind.InterpolatedStringFirst, m))) else string (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, LexerStringKind.InterpolatedStringFirst, m))) else singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | '"' '"' '"' { let buf, fin, m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, LexerStringKind.String, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } - - | '$' '"' '"' '"' - { let buf, fin, m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, LexerStringKind.InterpolatedStringFirst, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, LexerStringKind.String, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } | '@' '"' { let buf, fin, m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, LexerStringKind.String, m))) else verbatimString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, LexerStringKind.String, m))) else verbatimString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } | ("$@" | "@$") '"' { let buf, fin, m = startString args lexbuf - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, LexerStringKind.InterpolatedStringFirst, m))) else verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, LexerStringKind.InterpolatedStringFirst, m))) else verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | truewhite+ { if skip then token args skip lexbuf @@ -729,20 +729,31 @@ rule token args skip = parse | ">]" { GREATER_RBRACK } - | "{" { - if args.interpolatedStringNesting > 0 then + | "{" + { + if args.interpolatedStringNesting > 0 then args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 - LBRACE } + LBRACE + } | "|" { BAR } | "}" { // We encounter a '}' in the expression token stream. First check if we're in an interpolated string expression - if args.interpolatedStringNesting = 1 then + // and continue the string if necessary + if args.interpolatedStringNesting = 1 && args.interpolatedStringStyle.IsSome then let buf, fin, m = startString args lexbuf - args.interpolatedStringNesting <- args.interpolatedStringNesting - 1 - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringKind.InterpolatedStringPart, m))) else string (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf + args.interpolatedStringNesting <- 0 + let style = args.interpolatedStringStyle.Value + if not skip then + (STRING_TEXT (LexCont.String(args.ifdefStack, style, LexerStringKind.InterpolatedStringPart, m))) + else + match style with + | LexerStringStyle.Verbatim -> verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf + | LexerStringStyle.SingleQuote -> singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf + | LexerStringStyle.TripleQuote -> tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf + elif args.interpolatedStringNesting > 1 then args.interpolatedStringNesting <- args.interpolatedStringNesting - 1 RBRACE @@ -937,37 +948,37 @@ and endline cont args skip = parse { let tok = fail args lexbuf (FSComp.SR.pplexExpectedSingleLineComment()) (WHITESPACE (LexCont.Token args.ifdefStack)) if not skip then tok else token args skip lexbuf } -and string sargs skip = parse +and singleQuoteString sargs skip = parse | '\\' newline anywhite* { let (_buf, _fin, m, kind, args) = sargs newline lexbuf - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | escape_char { let (buf, _fin, m, kind, args) = sargs addByteChar buf (escape (lexeme lexbuf).[1]) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | trigraph { let (buf, _fin, m, kind, args) = sargs let s = lexeme lexbuf addByteChar buf (trigraph s.[1] s.[2] s.[3]) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | hexGraphShort { let (buf, _fin, m, kind, args) = sargs addUnicodeChar buf (int (hexGraphShort (lexemeTrimLeft lexbuf 2))) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | unicodeGraphShort { let (buf, _fin, m, kind, args) = sargs addUnicodeChar buf (int (unicodeGraphShort (lexemeTrimLeft lexbuf 2))) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | unicodeGraphLong { let (buf, _fin, m, kind, args) = sargs let hexChars = lexemeTrimLeft lexbuf 2 - let result () = if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf + let result () = if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf match unicodeGraphLong hexChars with | Invalid -> fail args lexbuf (FSComp.SR.lexInvalidUnicodeLiteral hexChars) (result ()) @@ -987,54 +998,61 @@ and string sargs skip = parse { let (buf, fin, _m, kind, _args) = sargs fin.Finish buf { kind with IsByteString = true } false } - | '{' + | ("{{" | "}}") + { let (buf, _fin, m, kind, args) = sargs + let s = lexeme lexbuf + addUnicodeString buf (if kind.IsInterpolated then s.[0..0] else s) + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + + | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + args.interpolatedStringStyle <- Some LexerStringStyle.SingleQuote fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | newline { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | ident { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | integer | xinteger { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | anywhite + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } | eof { let (_buf, _fin, m, kind, args) = sargs - EOF (LexCont.String(args.ifdefStack, kind, m)) } + EOF (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m)) } | surrogateChar surrogateChar // surrogate code points always come in pairs | _ { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, kind, m))) else string sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } and verbatimString sargs skip = parse | '"' '"' { let (buf, _fin, m, kind, args) = sargs addByteChar buf '\"' - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } | '"' { let (buf, fin, _m, kind, _args) = sargs @@ -1048,42 +1066,50 @@ and verbatimString sargs skip = parse { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } + + | ("{{" | "}}") + { let (buf, _fin, m, kind, args) = sargs + let s = lexeme lexbuf + addUnicodeString buf (if kind.IsInterpolated then s.[0..0] else s) + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } - | '{' + | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then + args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + args.interpolatedStringStyle <- Some LexerStringStyle.Verbatim fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } | ident { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } | integer | xinteger { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } | anywhite + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } | eof { let (_buf, _fin, m, kind, args) = sargs - EOF (LexCont.VerbatimString(args.ifdefStack, kind, m)) } + EOF (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m)) } | surrogateChar surrogateChar // surrogate code points always come in pairs | _ { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.VerbatimString(args.ifdefStack, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } and tripleQuoteString sargs skip = parse | '"' '"' '"' @@ -1094,43 +1120,51 @@ and tripleQuoteString sargs skip = parse { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } // The rest is to break into pieces to allow double-click-on-word and other such things | ident { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } | integer | xinteger { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } | anywhite + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } + + | ("{{" | "}}") + { let (buf, _fin, m, kind, args) = sargs + let s = lexeme lexbuf + addUnicodeString buf (if kind.IsInterpolated then s.[0..0] else s) + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } - | '{' + | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then + args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + args.interpolatedStringStyle <- Some LexerStringStyle.TripleQuote fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } | eof { let (_buf, _fin, m, kind, args) = sargs - EOF (LexCont.TripleQuoteString(args.ifdefStack, kind, m)) } + EOF (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m)) } | surrogateChar surrogateChar // surrogate code points always come in pairs | _ { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.TripleQuoteString(args.ifdefStack, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } // Parsing single-line comment - we need to split it into words for Visual Studio IDE and singleLineComment cargs skip = parse @@ -1165,15 +1199,15 @@ and comment cargs skip = parse | '"' { let n, m, args = cargs - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } | '"' '"' '"' { let n, m, args = cargs - if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } | '@' '"' { let n, m, args = cargs - if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } | "(*)" { let n, m, args = cargs @@ -1210,7 +1244,7 @@ and stringInComment n m args skip = parse // Follow string lexing, skipping tokens until it finishes | '\\' newline anywhite* { newline lexbuf - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } | escape_char | trigraph @@ -1221,7 +1255,7 @@ and stringInComment n m args skip = parse | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } | '"' @@ -1229,19 +1263,19 @@ and stringInComment n m args skip = parse | newline { newline lexbuf - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } | eof - { EOF (LexCont.StringInComment(args.ifdefStack, n, m)) } + { EOF (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, n, m))) else stringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } and verbatimStringInComment n m args skip = parse // Follow verbatimString lexing, in short, skip double-quotes and other chars until we hit a single quote | '"' '"' - { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } | '"' { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } @@ -1250,18 +1284,18 @@ and verbatimStringInComment n m args skip = parse | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } | newline { newline lexbuf - if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } | eof - { EOF (LexCont.VerbatimStringInComment(args.ifdefStack, n, m)) } + { EOF (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.VerbatimStringInComment(args.ifdefStack, n, m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } and tripleQuoteStringInComment n m args skip = parse // Follow tripleQuoteString lexing @@ -1272,25 +1306,25 @@ and tripleQuoteStringInComment n m args skip = parse | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } | newline { newline lexbuf - if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } + if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } | eof - { EOF (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m)) } + { EOF (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.TripleQuoteStringInComment(args.ifdefStack, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } + { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } and mlOnly m args skip = parse | "\"" { let buf = ByteBuffer.Create 100 let m2 = lexbuf.LexemeRange - let _ = string (buf, LexerStringFinisher.Default, m2, LexerStringKind.String, args) skip lexbuf + let _ = singleQuoteString (buf, LexerStringFinisher.Default, m2, LexerStringKind.String, args) skip lexbuf if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } | newline diff --git a/src/fsharp/lexhelp.fs b/src/fsharp/lexhelp.fs index c71d27c7240..6951dd8e40b 100644 --- a/src/fsharp/lexhelp.fs +++ b/src/fsharp/lexhelp.fs @@ -57,6 +57,7 @@ type lexargs = errorLogger: ErrorLogger applyLineDirectives: bool mutable interpolatedStringNesting: int + mutable interpolatedStringStyle: LexerStringStyle option pathMap: PathMap } /// possible results of lexing a long Unicode escape sequence in a string literal, e.g. "\U0001F47D", @@ -67,14 +68,17 @@ type LongUnicodeLexResult = | Invalid let mkLexargs (_filename, defines, lightSyntaxStatus, resourceManager, ifdefStack, errorLogger, pathMap:PathMap) = - { defines = defines + { + defines = defines ifdefStack= ifdefStack lightSyntaxStatus=lightSyntaxStatus resourceManager=resourceManager errorLogger=errorLogger applyLineDirectives=true interpolatedStringNesting = 0 - pathMap=pathMap } + pathMap=pathMap + interpolatedStringStyle = None + } /// Register the lexbuf and call the given function let reusingLexbufForParsing lexbuf f = diff --git a/src/fsharp/lexhelp.fsi b/src/fsharp/lexhelp.fsi index 22e5c2fffce..fda5e57cdad 100644 --- a/src/fsharp/lexhelp.fsi +++ b/src/fsharp/lexhelp.fsi @@ -33,7 +33,12 @@ type lexargs = lightSyntaxStatus: LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool + + /// The degree of nesting of '{..}' in an interpolation fill mutable interpolatedStringNesting: int + + /// The style of the string to continue after the matching '}' is found + mutable interpolatedStringStyle: LexerStringStyle option pathMap: PathMap } type LongUnicodeLexResult = diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index e950010f548..70c4e163f03 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -94,15 +94,15 @@ let raiseParseErrorAt m s = let checkEndOfFileError t = match t with | LexCont.IfDefSkip(_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInHashIf()) - | LexCont.String (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInString()) - | LexCont.TripleQuoteString (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteString()) - | LexCont.VerbatimString (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimString()) + | LexCont.String (_, LexerStringStyle.SingleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInString()) + | LexCont.String (_, LexerStringStyle.TripleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteString()) + | LexCont.String (_, LexerStringStyle.Verbatim, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimString()) | LexCont.Comment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) | LexCont.SingleLineComment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) - | LexCont.StringInComment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInStringInComment()) - | LexCont.VerbatimStringInComment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimStringInComment()) - | LexCont.TripleQuoteStringInComment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteStringInComment()) - | LexCont.MLOnly (_, m) -> reportParseErrorAt m (FSComp.SR.parsEofInIfOcaml()) + | LexCont.StringInComment (_, LexerStringStyle.SingleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInStringInComment()) + | LexCont.StringInComment (_, LexerStringStyle.Verbatim, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimStringInComment()) + | LexCont.StringInComment (_, LexerStringStyle.TripleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteStringInComment()) + | LexCont.MLOnly (_, m) -> reportParseErrorAt m (FSComp.SR.parsEofInIfOcaml()) | LexCont.EndLine(LexerEndlineContinuation.Skip(_, _, m)) -> reportParseErrorAt m (FSComp.SR.parsEofInDirective()) | LexCont.EndLine(LexerEndlineContinuation.Token(stack)) | LexCont.Token(stack) -> @@ -451,7 +451,8 @@ let rangeOfLongIdent(lid:LongIdent) = %right COLON_EQUALS %nonassoc pat_tuple expr_tuple %left COMMA -%nonassoc slice_expr /* matrix.[e COMMA e] has higher precedence than "e COMMA e" */ +%nonassoc slice_expr /* matrix.[e COMMA e] has higher precedence than "e COMMA e" */ +%nonassoc interpolation_fill /* "...{3,N4}..." .NET style fill has higher precedence than "e COMMA e" */ %nonassoc DOT_DOT /* for matrix.[1..2, 3..4] the ".." has higher precedence than expression "2 COMMA 3" */ %nonassoc slice_comma /* for matrix.[1..2, 3..4] the ", " has higher precedence than ".." */ %nonassoc paren_pat_colon @@ -5432,18 +5433,25 @@ stringOrKeywordString: | STRING { $1 } | KEYWORD_STRING { $1 } +interpolatedStringHole: + | declExpr + { ($1, None) } + + | declExpr COLON ident %prec interpolation_fill + { ($1, Some $3) } + interpolatedStringParts: | INTERP_STRING_END { [ Choice1Of2 $1 ] } - | INTERP_STRING_PART declExpr interpolatedStringParts + | INTERP_STRING_PART interpolatedStringHole interpolatedStringParts { Choice1Of2 $1 :: Choice2Of2 $2 :: $3 } /* INTERP_STRING_BEGIN_END */ /* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_END */ /* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_PART int32 INTERP_STRING_END */ interpolatedString: - | INTERP_STRING_BEGIN_PART declExpr interpolatedStringParts + | INTERP_STRING_BEGIN_PART interpolatedStringHole interpolatedStringParts { Choice1Of2 $1 :: Choice2Of2 $2 :: $3 } | INTERP_STRING_BEGIN_END diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index 7c4e1ac7550..25af14af053 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -473,15 +473,15 @@ module internal LexerStateEncoding = | LexCont.IfDefSkip (ifd, n, m) -> FSharpTokenizerColorState.IfDefSkip, int64 n, m.Start, ifd, LexerStringKind.String | LexCont.EndLine(LexerEndlineContinuation.Skip(ifd, n, m)) -> FSharpTokenizerColorState.EndLineThenSkip, int64 n, m.Start, ifd, LexerStringKind.String | LexCont.EndLine(LexerEndlineContinuation.Token ifd) -> FSharpTokenizerColorState.EndLineThenToken, 0L, pos0, ifd, LexerStringKind.String - | LexCont.String (ifd, kind, m) -> FSharpTokenizerColorState.String, 0L, m.Start, ifd, kind + | LexCont.String (ifd, LexerStringStyle.SingleQuote, kind, m) -> FSharpTokenizerColorState.String, 0L, m.Start, ifd, kind + | LexCont.String (ifd, LexerStringStyle.Verbatim, kind, m) -> FSharpTokenizerColorState.VerbatimString, 0L, m.Start, ifd, kind + | LexCont.String (ifd, LexerStringStyle.TripleQuote, kind, m) -> FSharpTokenizerColorState.TripleQuoteString, 0L, m.Start, ifd, kind | LexCont.Comment (ifd, n, m) -> FSharpTokenizerColorState.Comment, int64 n, m.Start, ifd, LexerStringKind.String | LexCont.SingleLineComment (ifd, n, m) -> FSharpTokenizerColorState.SingleLineComment, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.StringInComment (ifd, n, m) -> FSharpTokenizerColorState.StringInComment, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.VerbatimStringInComment (ifd, n, m) -> FSharpTokenizerColorState.VerbatimStringInComment, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.TripleQuoteStringInComment (ifd, n, m) -> FSharpTokenizerColorState.TripleQuoteStringInComment, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.StringInComment (ifd, LexerStringStyle.SingleQuote, n, m) -> FSharpTokenizerColorState.StringInComment, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.StringInComment (ifd, LexerStringStyle.Verbatim, n, m) -> FSharpTokenizerColorState.VerbatimStringInComment, int64 n, m.Start, ifd, LexerStringKind.String + | LexCont.StringInComment (ifd, LexerStringStyle.TripleQuote, n, m) -> FSharpTokenizerColorState.TripleQuoteStringInComment, int64 n, m.Start, ifd, LexerStringKind.String | LexCont.MLOnly (ifd, m) -> FSharpTokenizerColorState.CamlOnly, 0L, m.Start, ifd, LexerStringKind.String - | LexCont.VerbatimString (ifd, kind, m) -> FSharpTokenizerColorState.VerbatimString, 0L, m.Start, ifd, kind - | LexCont.TripleQuoteString (ifd, kind, m) -> FSharpTokenizerColorState.TripleQuoteString, 0L, m.Start, ifd, kind encodeLexCont (tag, n1, p1, ifd, lightSyntaxStatus, stringKind) let decodeLexInt (state: FSharpTokenizerLexState) = @@ -490,15 +490,15 @@ module internal LexerStateEncoding = match tag with | FSharpTokenizerColorState.Token -> LexCont.Token ifd | FSharpTokenizerColorState.IfDefSkip -> LexCont.IfDefSkip (ifd, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.String -> LexCont.String (ifd, stringKind, mkRange "file" p1 p1) + | FSharpTokenizerColorState.String -> LexCont.String (ifd, LexerStringStyle.SingleQuote, stringKind, mkRange "file" p1 p1) | FSharpTokenizerColorState.Comment -> LexCont.Comment (ifd, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.SingleLineComment -> LexCont.SingleLineComment (ifd, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.StringInComment -> LexCont.StringInComment (ifd, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.VerbatimStringInComment -> LexCont.VerbatimStringInComment (ifd, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.TripleQuoteStringInComment -> LexCont.TripleQuoteStringInComment (ifd, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.StringInComment -> LexCont.StringInComment (ifd, LexerStringStyle.SingleQuote, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.VerbatimStringInComment -> LexCont.StringInComment (ifd, LexerStringStyle.Verbatim, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.TripleQuoteStringInComment -> LexCont.StringInComment (ifd, LexerStringStyle.TripleQuote, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.CamlOnly -> LexCont.MLOnly (ifd, mkRange "file" p1 p1) - | FSharpTokenizerColorState.VerbatimString -> LexCont.VerbatimString (ifd, stringKind, mkRange "file" p1 p1) - | FSharpTokenizerColorState.TripleQuoteString -> LexCont.TripleQuoteString (ifd, stringKind, mkRange "file" p1 p1) + | FSharpTokenizerColorState.VerbatimString -> LexCont.String (ifd, LexerStringStyle.Verbatim, stringKind, mkRange "file" p1 p1) + | FSharpTokenizerColorState.TripleQuoteString -> LexCont.String (ifd, LexerStringStyle.TripleQuote, stringKind, mkRange "file" p1 p1) | FSharpTokenizerColorState.EndLineThenSkip -> LexCont.EndLine(LexerEndlineContinuation.Skip(ifd, n1, mkRange "file" p1 p1)) | FSharpTokenizerColorState.EndLineThenToken -> LexCont.EndLine(LexerEndlineContinuation.Token ifd) | _ -> LexCont.Token [] @@ -515,16 +515,22 @@ module internal LexerStateEncoding = | LexCont.Token ifd -> Lexer.token (argsWithIfDefs ifd) skip lexbuf | LexCont.IfDefSkip (ifd, n, m) -> Lexer.ifdefSkip n m (argsWithIfDefs ifd) skip lexbuf // Q: What's this magic 100 number for? Q: it's just an initial buffer size. - | LexCont.String (ifd, kind, m) -> Lexer.string (ByteBuffer.Create 100, LexerStringFinisher.Default, m, kind, argsWithIfDefs ifd) skip lexbuf - | LexCont.Comment (ifd, n, m) -> Lexer.comment (n, m, (argsWithIfDefs ifd)) skip lexbuf + | LexCont.String (ifd, style, kind, m) -> + let buf = ByteBuffer.Create 100 + let args = (buf, LexerStringFinisher.Default, m, kind, argsWithIfDefs ifd) + match style with + | LexerStringStyle.SingleQuote -> Lexer.singleQuoteString args skip lexbuf + | LexerStringStyle.Verbatim -> Lexer.verbatimString args skip lexbuf + | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteString args skip lexbuf + | LexCont.Comment (ifd, n, m) -> Lexer.comment (n, m, argsWithIfDefs ifd) skip lexbuf // The first argument is 'None' because we don't need XML comments when called from VS - | LexCont.SingleLineComment (ifd, n, m) -> Lexer.singleLineComment (None, n, m, (argsWithIfDefs ifd)) skip lexbuf - | LexCont.StringInComment (ifd, n, m) -> Lexer.stringInComment n m (argsWithIfDefs ifd) skip lexbuf - | LexCont.VerbatimStringInComment (ifd, n, m) -> Lexer.verbatimStringInComment n m (argsWithIfDefs ifd) skip lexbuf - | LexCont.TripleQuoteStringInComment (ifd, n, m) -> Lexer.tripleQuoteStringInComment n m (argsWithIfDefs ifd) skip lexbuf + | LexCont.SingleLineComment (ifd, n, m) -> Lexer.singleLineComment (None, n, m, argsWithIfDefs ifd) skip lexbuf + | LexCont.StringInComment (ifd, style, n, m) -> + match style with + | LexerStringStyle.SingleQuote -> Lexer.stringInComment n m (argsWithIfDefs ifd) skip lexbuf + | LexerStringStyle.Verbatim -> Lexer.verbatimStringInComment n m (argsWithIfDefs ifd) skip lexbuf + | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteStringInComment n m (argsWithIfDefs ifd) skip lexbuf | LexCont.MLOnly (ifd, m) -> Lexer.mlOnly m (argsWithIfDefs ifd) skip lexbuf - | LexCont.VerbatimString (ifd, kind, m) -> Lexer.verbatimString (ByteBuffer.Create 100, LexerStringFinisher.Default, m, kind, argsWithIfDefs ifd) skip lexbuf - | LexCont.TripleQuoteString (ifd, kind, m) -> Lexer.tripleQuoteString (ByteBuffer.Create 100, LexerStringFinisher.Default, m, kind, argsWithIfDefs ifd) skip lexbuf //---------------------------------------------------------------------------- // Colorization diff --git a/src/fsharp/service/ServiceParseTreeWalk.fs b/src/fsharp/service/ServiceParseTreeWalk.fs index fd068ecd984..72d35700dd0 100755 --- a/src/fsharp/service/ServiceParseTreeWalk.fs +++ b/src/fsharp/service/ServiceParseTreeWalk.fs @@ -210,8 +210,12 @@ module public AstTraversal = | SynExpr.Const (_synConst, _range) -> None - | SynExpr.InterpolatedString (es, _) -> - [ for e in es do match e with Choice1Of2 _ -> () | Choice2Of2 e -> yield dive e e.Range traverseSynExpr ] + | SynExpr.InterpolatedString (parts, _) -> + [ for part in parts do + match part with + | Choice1Of2 _ -> () + | Choice2Of2 (fillExpr, _) -> + yield dive fillExpr fillExpr.Range traverseSynExpr ] |> pick expr | SynExpr.Typed (synExpr, synType, _range) -> diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index 2667a10e8ac..52559b1d944 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -209,8 +209,8 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option | SynExpr.Paren (e, _, _, _) -> yield! walkExpr false e - | SynExpr.InterpolatedString (es, _) -> - yield! walkExprs [ for e in es do match e with Choice1Of2 _ -> () | Choice2Of2 e -> yield e ] + | SynExpr.InterpolatedString (parts, _) -> + yield! walkExprs [ for part in parts do match part with Choice1Of2 _ -> () | Choice2Of2 (fillExpr, _) -> yield fillExpr ] | SynExpr.YieldOrReturn (_, e, _) | SynExpr.YieldOrReturnFrom (_, e, _) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 653c0802fc2..629386e4425 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -25,8 +25,68 @@ check "vcewweh3" $"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" check "vcewweh4" $"this is {1} + {1+1}" "this is 1 + 2" check "vcewweh5" $"this is {1}" "this is 1" + """ + [] + let ``Basic string interpolation verbatim strings`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "xvcewweh1" @$"this is 2" "this is 2" + +check "xvcewweh2" @$"this is {1} + 1 = 2" "this is 1 + 1 = 2" + +check "xvcewweh3" @$"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" + +check "xvcewweh4" @$"this is {1} + {1+1}" "this is 1 + 2" + +check "xvcewweh5" @$"this is {1}" "this is 1" + +check "xvcewweh6" @$"this i\s {1}" "this i\s 1" + +check "xvcewweh1b" $@"this is 2" "this is 2" + +check "xvcewweh2b" $@"this is {1} + 1 = 2" "this is 1 + 1 = 2" + +check "xvcewweh3b" $@"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" + +check "xvcewweh4b" $@"this is {1} + {1+1}" "this is 1 + 2" + +check "xvcewweh5b" $@"this is {1}" "this is 1" + +check "xvcewweh6b" $@"this i\s {1}" "this i\s 1" + + """ + + [] + let ``Basic string interpolation triple quote strings`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + " +let check msg a b = + if a = b then printfn \"%s succeeded\" msg else failwithf \"%s failed, expected %A, got %A\" msg b a + +check \"xvcewweh1\" $\"\"\"this is 2\"\"\" \"this is 2\" + +check \"xvcewweh2\" $\"\"\"this is {1} + 1 = 2\"\"\" \"this is 1 + 1 = 2\" + +check \"xvcewweh3\" $\"\"\"this is {1} + {1+1} = 3\"\"\" \"this is 1 + 2 = 3\" + +check \"xvcewweh4\" $\"\"\"this is {1} + {1+1}\"\"\" \"this is 1 + 2\" + +check \"xvcewweh5\" $\"\"\"this is {1}\"\"\" \"this is 1\" + +check \"xvcewweh6\" $\"\"\"this i\s {1}\"\"\" \"this i\s 1\" + +// multiline +check \"xvcewweh6\" + $\"\"\"this +is {1+1}\"\"\" + \"\"\"this +is 2\"\"\" + " [] let ``String interpolation using atomic expression forms`` () = @@ -128,6 +188,55 @@ let check msg a b = check "vcewweh22m" $"x = {"1"} " "x = 1 " """ + [] + let ``String interpolation using .NET Formats`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "vcewweh221q" $"abc {1}" "abc 1" +check "vcewweh222w" $"abc {1:N3}" "abc 1.000" +check "vcewweh223e" $"abc {1,10}" "abc 1" +check "vcewweh223r" $"abc {1,-10}" "abc 1 " +check "vcewweh224t" $"abc {1,10:N3}" "abc 1.000" +check "vcewweh224y" $"abc {1,-10:N3}" "abc 1.000 " +check "vcewweh225u" $"abc %d{1}" "abc 1" +check "vcewweh225u" $"abc %5d{1}" "abc 1" +check "vcewweh225u" $"abc %-5d{1}" "abc 1 " + """ + + [] + let ``String interpolation using escaped braces`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "vcewweh226i" $"{{" "{" +check "vcewweh226o" $"{{{{" "{{" +check "vcewweh226p" $"{{{1}}}" "{1}" +check "vcewweh227a" $"}}" "}" +check "vcewweh227s" $"}}}}" "}}" +check "vcewweh228d" "{{" "{{" +check "vcewweh229f" "}}" "}}" + """ + + [] + let ``String interpolation using verbatim strings`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "vcewweh226i" $"{{" "{" +check "vcewweh226o" $"{{{{" "{{" +check "vcewweh226p" $"{{{1}}}" "{1}" +check "vcewweh227a" $"}}" "}" +check "vcewweh227s" $"}}}}" "}}" +check "vcewweh228d" "{{" "{{" +check "vcewweh229f" "}}" "}}" + """ [] From 6a9128b5e0008556871f4ddb0aeae9eece62b6eb Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 15 Apr 2020 17:59:09 +0100 Subject: [PATCH 04/87] fix tests --- .../Compiler/Language/StringInterpolation.fs | 61 +++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 629386e4425..0807e32da16 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -207,6 +207,57 @@ check "vcewweh225u" $"abc %-5d{1}" "abc 1 " """ [] + let ``String interpolation of null`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "vcewweh221q1" $"{null}" "" +check "vcewweh221q2" $"%s{null}" "" +check "vcewweh221q3" $"abc %s{null}" "abc " +check "vcewweh221q4" $"abc %s{null} def" "abc def" + """ + + [] + let ``String interpolation of basic types`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "vcewweh221q11" $"{1y}" "1" +check "vcewweh221q12" $"{1uy}" "1" +check "vcewweh221q13" $"{1s}" "1" +check "vcewweh221q14" $"{1us}" "1" +check "vcewweh221q15" $"{1u}" "1" +check "vcewweh221q16" $"{1}" "1" +check "vcewweh221q17" $"{-1}" "-1" +check "vcewweh221q18" $"{1L}" "1" +check "vcewweh221q19" $"{1UL}" "1" +check "vcewweh221q1q" $"{1n}" "1" +check "vcewweh221q1w" $"{1un}" "1" +check "vcewweh221q1e" $"{1.0}" "1" +check "vcewweh221q1r" $"{1.01}" "1.01" +check "vcewweh221q1t" $"{-1.01}" "-1.01" +check "vcewweh221q1y" $"{1I}" "1" +check "vcewweh221q1i" $"{1M}" "1" + +check "vcewweh221q1o" $"%d{1y}" "1" +check "vcewweh221q1p" $"%d{1uy}" "1" +check "vcewweh221q1a" $"%d{1s}" "1" +check "vcewweh221q1s" $"%d{1us}" "1" +check "vcewweh221q1d" $"%d{1u}" "1" +check "vcewweh221q1f" $"%d{1}" "1" +check "vcewweh221q1g" $"%d{-1}" "-1" +check "vcewweh221q1h" $"%d{1L}" "1" +check "vcewweh221q1j" $"%d{1UL}" "1" +check "vcewweh221q1k" $"%d{1n}" "1" +check "vcewweh221q1l" $"%d{1un}" "1" + +check "vcewweh221q1" $"%f{1.0}" "1.000000" + """ + [] let ``String interpolation using escaped braces`` () = CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] """ @@ -252,14 +303,14 @@ let check msg a b = // Check record expression (parenthesized) check "vcewweh18" $"abc{({contents=1}.contents)}def" "abc1def" -// Check record expression (un-parenthesized) -check "vcewweh19" $"abc{{contents=1}.contents}def" "abc1def" +// Check {{ }} expression (un-parenthesized) +check "vcewweh19" $"abc{{contents=1}.contents}def" "abc{contents=1}.contents}def" // Check record expression (un-parenthesized) -check "vcewweh20" $"abc{{X=1}}def" "abc{ X = 1 }def" +check "vcewweh20" $"abc{{X=1}}def" "abc{X=1}def" -// Check record expression (un-parenthesized, multi-line) -check "vcewweh21" $"abc{{X=1; Y=2}}def" "abc{ X = 1\n Y = 2 }def" +// Check {{ }} expression (un-parenthesized, multi-line) +check "vcewweh21" $"abc{{X=1; Y=2}}def" "abc{X=1; Y=2}def" """ From 047fda67d99c7bcae4686db81252e4e100db8140 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Thu, 16 Apr 2020 18:00:24 +0800 Subject: [PATCH 05/87] string interpolation tests: internal representation corner cases --- .../Compiler/Language/StringInterpolation.fs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 0807e32da16..7eeb9d03931 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -26,6 +26,10 @@ check "vcewweh4" $"this is {1} + {1+1}" "this is 1 + 2" check "vcewweh5" $"this is {1}" "this is 1" +check "vcewweh6" $"this is {1} {2} {3} {4} {5} {6} {7}" "this is 1 2 3 4 5 6 7" + +check "vcewweh7" $"this is {7} {6} {5} {4} {3} {2} {1}" "this is 7 6 5 4 3 2 1" + """ [] @@ -405,3 +409,26 @@ let x = $"one" """ [|(FSharpErrorSeverity.Error, 3350, (2, 9, 2, 15), "Feature 'string interpolation' is not available in F# 4.7. Please use language version 'preview' or greater.")|] + + [] + let ``String interpolation internal representation`` () = + CompilerAssert.TypeCheckWithErrorsAndOptions [| |] + """ +let x = $"this is %P()" + """ + [|(FSharpErrorSeverity.Error, 3361, (2, 9, 2, 24), + "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'")|] + + CompilerAssert.TypeCheckWithErrorsAndOptions [| |] + """ +let x = $"this is %P" + """ + [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 24), + "Unable to parse format string 'Invalid interpolated string. The '%P' specifier may not be used explicitly.")|] + + CompilerAssert.TypeCheckWithErrorsAndOptions [| |] + """ +let x = $"%P(){"gotcha"}" + """ + [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 26), + "Unable to parse format string 'Invalid interpolated string. The '%P' specifier may not be used explicitly.")|] From b31c14f9eaa0951abf68929f0df6f538afd73893 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 17 Apr 2020 01:25:45 +0800 Subject: [PATCH 06/87] string-interp tests should have --langversion:preview --- tests/fsharp/Compiler/Language/StringInterpolation.fs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 7eeb9d03931..4f549962a34 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -412,21 +412,21 @@ let x = $"one" [] let ``String interpolation internal representation`` () = - CompilerAssert.TypeCheckWithErrorsAndOptions [| |] + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] """ let x = $"this is %P()" """ [|(FSharpErrorSeverity.Error, 3361, (2, 9, 2, 24), "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'")|] - CompilerAssert.TypeCheckWithErrorsAndOptions [| |] + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] """ let x = $"this is %P" """ [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 24), "Unable to parse format string 'Invalid interpolated string. The '%P' specifier may not be used explicitly.")|] - CompilerAssert.TypeCheckWithErrorsAndOptions [| |] + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] """ let x = $"%P(){"gotcha"}" """ From 484e46c156e330fd69a5cdfaa37a6ffc55da587b Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 17 Apr 2020 01:46:43 +0800 Subject: [PATCH 07/87] string interop tests: sprintf --- .../Compiler/Language/StringInterpolation.fs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 4f549962a34..09e6137c485 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -432,3 +432,17 @@ let x = $"%P(){"gotcha"}" """ [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 26), "Unable to parse format string 'Invalid interpolated string. The '%P' specifier may not be used explicitly.")|] + + + [] + let ``String interpolation with *printf`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "pvcewweh1" (sprintf $"foo") "foo" + +check "pvcewweh2" (sprintf $"{"foo"}") "foo" + """ + From d475013400d6579d96779881ce52216131cb8d96 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 17 Apr 2020 02:23:50 +0800 Subject: [PATCH 08/87] string interp tests: format specifier negative cases --- .../Compiler/Language/StringInterpolation.fs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 09e6137c485..3d4c3edf5c6 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -111,6 +111,9 @@ check "vcewweh6" $"this is {s.Length} + {1+1} = 8" "this is 6 + 2 = 8" // Check null expression check "vcewweh8" $"abc{null}def" "abcdef" +// Check mod operator +check "vcewweh8" $"abc{4%3}def" "abc1def" + """ @@ -446,3 +449,45 @@ check "pvcewweh1" (sprintf $"foo") "foo" check "pvcewweh2" (sprintf $"{"foo"}") "foo" """ + [] + let ``String interpolation error: format specifiers`` () = + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + """ +// mixed F#/.NET format specifiers +let x = $"%5d{1:N3}" + """ + [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 21), + "Unable to parse format string 'Invalid interpolated string. .NET-style format specifiers such as '{x,3}' or '{x:N5}' may not be mixed with '%' format specifiers.'")|] + + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + """ +// inner error that looks like format specifiers +let x = $"{%5d{1:N3}}" + """ + [|(FSharpErrorSeverity.Error, 1156, (2, 13, 2, 14), + "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger).")|] + + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + """ +// bad F# format specifier +let x = $"5%6" + """ + [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 14), + "Unable to parse format string 'Bad precision in format specifier'")|] + + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + """ +// incompatible type +let x = $"%d{"str"}" + """ + [|(FSharpErrorSeverity.Error, 1, (2, 14, 2, 18), + "The type 'string' is not compatible with any of the types byte,int16,int32,int64,sbyte,uint16,uint32,uint64,nativeint,unativeint, arising from the use of a printf-style format string")|] + + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + """ +// dangling F#-style specifier +let x = $"100% sure it fails" + """ + [|(FSharpErrorSeverity.Error, 1, (2, 9, 2, 29), + "Unable to parse format string ''s' does not support prefix ' ' flag'")|] + From e1f416fa4651923d0cb53cd64ac31d7bb00134c3 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 17 Apr 2020 02:45:59 +0800 Subject: [PATCH 09/87] string interp tests: format specifier negative cases, .NET-style padding --- .../Compiler/Language/StringInterpolation.fs | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 3d4c3edf5c6..866ce9d5a6b 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -451,6 +451,15 @@ check "pvcewweh2" (sprintf $"{"foo"}") "foo" [] let ``String interpolation error: format specifiers`` () = + + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + """ +// no expression +let x = $"%A" + """ + [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 13), + "Unable to parse format string 'Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'")|] + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] """ // mixed F#/.NET format specifiers @@ -488,6 +497,42 @@ let x = $"%d{"str"}" // dangling F#-style specifier let x = $"100% sure it fails" """ - [|(FSharpErrorSeverity.Error, 1, (2, 9, 2, 29), + [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 29), "Unable to parse format string ''s' does not support prefix ' ' flag'")|] + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + """ +// fake expression (delimiters escaped) +let x = $"%A{{1}}" + """ + [|(FSharpErrorSeverity.Error, 741, (2, 9, 2, 18), + "Unable to parse format string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'")|] + + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + """ +// empty expression +let x = $"%A{}" + """ + [|(FSharpErrorSeverity.Error, 10, (2, 14, 2, 15), + "Unexpected interpolated string (final part) in interaction")|] + + [] + let ``String interpolation: .NET-style padding`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "padvcewweh0" $"{1,5}" " 1" + +check "padvcewweh1" $"{"hello",7}" " 1" + +check "padvcewweh2" $"%A{let a:int=1 in a,5}" "(1, 5)" + +check "padvcewweh3" $"{(let a:int=1 in a),5}" " 1" + +check "padvcewweh4" $"{(if true then 2 else 3),4}" " 2" + +check "padvcewweh5" $"{((if true then 2 else 3),4),8}" " (2, 4)" + + """ From b17c3edc82ab5152f422136d5f99c293df04849c Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 19 Apr 2020 03:17:40 +0800 Subject: [PATCH 10/87] fix nested interp strings --- src/fsharp/lex.fsl | 36 +++++++++++++++++++++++++++--------- src/fsharp/lexhelp.fs | 6 ++++-- src/fsharp/lexhelp.fsi | 4 +++- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index f336cae47ca..21a059bfcdb 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -731,8 +731,8 @@ rule token args skip = parse | "{" { - if args.interpolatedStringNesting > 0 then - args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + if args.interpolatedStringNestingCurrent > 0 then + args.interpolatedStringNestingCurrent <- args.interpolatedStringNestingCurrent + 1 LBRACE } @@ -742,9 +742,15 @@ rule token args skip = parse { // We encounter a '}' in the expression token stream. First check if we're in an interpolated string expression // and continue the string if necessary - if args.interpolatedStringNesting = 1 && args.interpolatedStringStyle.IsSome then + if args.interpolatedStringNestingCurrent = 1 && args.interpolatedStringStyle.IsSome then + match args.interpolatedStringNestingStack with + | head :: rest -> + args.interpolatedStringNestingCurrent <- head + args.interpolatedStringNestingStack <- rest + | [] -> + // back to initial state + args.interpolatedStringNestingCurrent <- 0 let buf, fin, m = startString args lexbuf - args.interpolatedStringNesting <- 0 let style = args.interpolatedStringStyle.Value if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, style, LexerStringKind.InterpolatedStringPart, m))) @@ -754,8 +760,8 @@ rule token args skip = parse | LexerStringStyle.SingleQuote -> singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.TripleQuote -> tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf - elif args.interpolatedStringNesting > 1 then - args.interpolatedStringNesting <- args.interpolatedStringNesting - 1 + elif args.interpolatedStringNestingCurrent > 1 then + args.interpolatedStringNestingCurrent <- args.interpolatedStringNestingCurrent - 1 RBRACE else RBRACE @@ -1007,7 +1013,11 @@ and singleQuoteString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + match args.interpolatedStringNestingCurrent, args.interpolatedStringNestingStack with + // initial state, depth = 0 + | 0, _ -> () + | x, y -> args.interpolatedStringNestingStack <- x :: y + args.interpolatedStringNestingCurrent <- 1 args.interpolatedStringStyle <- Some LexerStringStyle.SingleQuote fin.Finish buf kind true else @@ -1077,7 +1087,11 @@ and verbatimString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + match args.interpolatedStringNestingCurrent, args.interpolatedStringNestingStack with + // initial state, depth = 0 + | 0, _ -> () + | x, y -> args.interpolatedStringNestingStack <- x :: y + args.interpolatedStringNestingCurrent <- 1 args.interpolatedStringStyle <- Some LexerStringStyle.Verbatim fin.Finish buf kind true else @@ -1148,7 +1162,11 @@ and tripleQuoteString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- args.interpolatedStringNesting + 1 + match args.interpolatedStringNestingCurrent, args.interpolatedStringNestingStack with + // initial state, depth = 0 + | 0, _ -> () + | x, y -> args.interpolatedStringNestingStack <- x :: y + args.interpolatedStringNestingCurrent <- 1 args.interpolatedStringStyle <- Some LexerStringStyle.TripleQuote fin.Finish buf kind true else diff --git a/src/fsharp/lexhelp.fs b/src/fsharp/lexhelp.fs index 6951dd8e40b..1f231562145 100644 --- a/src/fsharp/lexhelp.fs +++ b/src/fsharp/lexhelp.fs @@ -56,7 +56,8 @@ type lexargs = lightSyntaxStatus : LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool - mutable interpolatedStringNesting: int + mutable interpolatedStringNestingCurrent: int + mutable interpolatedStringNestingStack: int list mutable interpolatedStringStyle: LexerStringStyle option pathMap: PathMap } @@ -75,7 +76,8 @@ let mkLexargs (_filename, defines, lightSyntaxStatus, resourceManager, ifdefStac resourceManager=resourceManager errorLogger=errorLogger applyLineDirectives=true - interpolatedStringNesting = 0 + interpolatedStringNestingCurrent = 0 + interpolatedStringNestingStack = [] pathMap=pathMap interpolatedStringStyle = None } diff --git a/src/fsharp/lexhelp.fsi b/src/fsharp/lexhelp.fsi index fda5e57cdad..417ec6b077e 100644 --- a/src/fsharp/lexhelp.fsi +++ b/src/fsharp/lexhelp.fsi @@ -35,7 +35,9 @@ type lexargs = applyLineDirectives: bool /// The degree of nesting of '{..}' in an interpolation fill - mutable interpolatedStringNesting: int + mutable interpolatedStringNestingCurrent: int + /// Nesting counters of outer interpolating strings + mutable interpolatedStringNestingStack: int list /// The style of the string to continue after the matching '}' is found mutable interpolatedStringStyle: LexerStringStyle option From e5d3a2ced4013a7d1653139d57a1bed48c864f6d Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 19 Apr 2020 13:21:09 +0800 Subject: [PATCH 11/87] style cleanup --- src/fsharp/lex.fsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index 21a059bfcdb..fc0254cefa8 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -1090,7 +1090,8 @@ and verbatimString sargs skip = parse match args.interpolatedStringNestingCurrent, args.interpolatedStringNestingStack with // initial state, depth = 0 | 0, _ -> () - | x, y -> args.interpolatedStringNestingStack <- x :: y + | x, y -> + args.interpolatedStringNestingStack <- x :: y args.interpolatedStringNestingCurrent <- 1 args.interpolatedStringStyle <- Some LexerStringStyle.Verbatim fin.Finish buf kind true From 1072e519a97e240aaa6723d8b1e67d358cc4dc79 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 19 Apr 2020 14:01:45 +0800 Subject: [PATCH 12/87] lex: unify string interp stack and counter --- src/fsharp/lex.fsl | 44 +++++++++++++----------------------------- src/fsharp/lexhelp.fs | 6 ++---- src/fsharp/lexhelp.fsi | 7 +++---- 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index fc0254cefa8..e695eec8c3f 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -731,8 +731,10 @@ rule token args skip = parse | "{" { - if args.interpolatedStringNestingCurrent > 0 then - args.interpolatedStringNestingCurrent <- args.interpolatedStringNestingCurrent + 1 + match args.interpolatedStringNesting with + | [] -> () + | head :: rest -> + args.interpolatedStringNesting <- head + 1 :: rest LBRACE } @@ -742,16 +744,10 @@ rule token args skip = parse { // We encounter a '}' in the expression token stream. First check if we're in an interpolated string expression // and continue the string if necessary - if args.interpolatedStringNestingCurrent = 1 && args.interpolatedStringStyle.IsSome then - match args.interpolatedStringNestingStack with - | head :: rest -> - args.interpolatedStringNestingCurrent <- head - args.interpolatedStringNestingStack <- rest - | [] -> - // back to initial state - args.interpolatedStringNestingCurrent <- 0 + match args.interpolatedStringNesting, args.interpolatedStringStyle with + | 1 :: rest, Some style -> + args.interpolatedStringNesting <- rest let buf, fin, m = startString args lexbuf - let style = args.interpolatedStringStyle.Value if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, style, LexerStringKind.InterpolatedStringPart, m))) else @@ -759,11 +755,10 @@ rule token args skip = parse | LexerStringStyle.Verbatim -> verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.SingleQuote -> singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.TripleQuote -> tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf - - elif args.interpolatedStringNestingCurrent > 1 then - args.interpolatedStringNestingCurrent <- args.interpolatedStringNestingCurrent - 1 + | head :: rest, _ -> + args.interpolatedStringNesting <- head - 1 :: rest RBRACE - else + | _ -> RBRACE } @@ -1013,11 +1008,7 @@ and singleQuoteString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - match args.interpolatedStringNestingCurrent, args.interpolatedStringNestingStack with - // initial state, depth = 0 - | 0, _ -> () - | x, y -> args.interpolatedStringNestingStack <- x :: y - args.interpolatedStringNestingCurrent <- 1 + args.interpolatedStringNesting <- 1 :: args.interpolatedStringNesting args.interpolatedStringStyle <- Some LexerStringStyle.SingleQuote fin.Finish buf kind true else @@ -1087,12 +1078,7 @@ and verbatimString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - match args.interpolatedStringNestingCurrent, args.interpolatedStringNestingStack with - // initial state, depth = 0 - | 0, _ -> () - | x, y -> - args.interpolatedStringNestingStack <- x :: y - args.interpolatedStringNestingCurrent <- 1 + args.interpolatedStringNesting <- 1 :: args.interpolatedStringNesting args.interpolatedStringStyle <- Some LexerStringStyle.Verbatim fin.Finish buf kind true else @@ -1163,11 +1149,7 @@ and tripleQuoteString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - match args.interpolatedStringNestingCurrent, args.interpolatedStringNestingStack with - // initial state, depth = 0 - | 0, _ -> () - | x, y -> args.interpolatedStringNestingStack <- x :: y - args.interpolatedStringNestingCurrent <- 1 + args.interpolatedStringNesting <- 1 :: args.interpolatedStringNesting args.interpolatedStringStyle <- Some LexerStringStyle.TripleQuote fin.Finish buf kind true else diff --git a/src/fsharp/lexhelp.fs b/src/fsharp/lexhelp.fs index 1f231562145..43283301003 100644 --- a/src/fsharp/lexhelp.fs +++ b/src/fsharp/lexhelp.fs @@ -56,8 +56,7 @@ type lexargs = lightSyntaxStatus : LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool - mutable interpolatedStringNestingCurrent: int - mutable interpolatedStringNestingStack: int list + mutable interpolatedStringNesting: int list mutable interpolatedStringStyle: LexerStringStyle option pathMap: PathMap } @@ -76,8 +75,7 @@ let mkLexargs (_filename, defines, lightSyntaxStatus, resourceManager, ifdefStac resourceManager=resourceManager errorLogger=errorLogger applyLineDirectives=true - interpolatedStringNestingCurrent = 0 - interpolatedStringNestingStack = [] + interpolatedStringNesting = [] pathMap=pathMap interpolatedStringStyle = None } diff --git a/src/fsharp/lexhelp.fsi b/src/fsharp/lexhelp.fsi index 417ec6b077e..a468c9943ce 100644 --- a/src/fsharp/lexhelp.fsi +++ b/src/fsharp/lexhelp.fsi @@ -34,10 +34,9 @@ type lexargs = errorLogger: ErrorLogger applyLineDirectives: bool - /// The degree of nesting of '{..}' in an interpolation fill - mutable interpolatedStringNestingCurrent: int - /// Nesting counters of outer interpolating strings - mutable interpolatedStringNestingStack: int list + /// The degree of nesting of '{..}' in an interpolation fill. + /// Nesting counters of outer interpolating strings are pushed on stack. + mutable interpolatedStringNesting: int list /// The style of the string to continue after the matching '}' is found mutable interpolatedStringStyle: LexerStringStyle option From 3c19e2240212fa4748ae8e022be40e7050cae420 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 19 Apr 2020 16:54:05 +0800 Subject: [PATCH 13/87] string-interp: add test cases --- .../Compiler/Language/StringInterpolation.fs | 74 ++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 0807e32da16..0695bf4cc9e 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -26,6 +26,10 @@ check "vcewweh4" $"this is {1} + {1+1}" "this is 1 + 2" check "vcewweh5" $"this is {1}" "this is 1" +check "vcewweh6" $"123{456}789{012}345" "12345678912345" + +check "vcewweh7" $"123{456}789{"012"}345" "123456789012345" + """ [] @@ -185,8 +189,74 @@ let check msg a b = if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a // check nested string -check "vcewweh22m" $"x = {"1"} " "x = 1 " - """ +check "vcewweh22m1" $"x = {"1"} " "x = 1 " + +check "vcewweh22m2" $"x = {$""} " "x = " + +check "vcewweh22m3" $"x = {$"1 {"2" + $"{3}" + "2"} 1"} " "x = 1 232 1 " + +check "vcewweh22m4" $"x = {$"1 {"2" + $"{3}" + "2"} 11 {2} 1"} " "x = 1 232 11 2 1 " + +check "vcewweh22m5" $"x = {$"1 {"2" + $"{3}" + "2"} 11 { $"{{%A{seq{yield 2; yield 2;} }}}" } 1"} " "x = 1 232 11 {seq [2; 2]} 1 " + +do + let genreSpecified = true + let getGenre() = "comedy" + check "vcewweh22m6" $"/api/movie/{if not genreSpecified then "" else $"q?genre={getGenre()}"}" "/api/movie/q?genre=comedy" + +""" + + [] + let ``Triple quote string interpolation using nested string`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + " +let check msg a b = + if a = b then printfn \"%s succeeded\" msg else failwithf \"%s failed, expected %A, got %A\" msg b a +do + let itvar=\"i\" + let iterfrom=\"0\" + let iterto=\"100\" + let block= $\"\"\"printf(\"%%d\", {itvar}); + do({itvar});\"\"\" + check \"vcewweh22m7\" $\"\"\" +for({itvar}={iterfrom};{itvar}<{iterto};++{itvar}) {{ + {block} +}}\"\"\" \"\"\" +for(i=0;i<100;++i) { + printf(\"%d\", i); + do(i); +}\"\"\" +" + + [] + let ``Mixed quote string interpolation using nested string`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + " +let check msg a b = + if a = b then printfn \"%s succeeded\" msg else failwithf \"%s failed, expected %A, got %A\" msg b a + +check \"vcewweh22n1\" + $\"\"\" + PROCEDURE SEARCH; + BEGIN + END;\"\"\" + \"\"\" + PROCEDURE SEARCH; + BEGIN + END;\"\"\" + +check \"vcewweh22n2\" + $\"\"\" + PROCEDURE SEARCH; + BEGIN + WRITELN({ $\"{21+21}\" }); + END;\"\"\" + \"\"\" + PROCEDURE SEARCH; + BEGIN + WRITELN(42); + END;\"\"\" +" [] let ``String interpolation using .NET Formats`` () = From ddaf4b2cdc29622382948cf100e2c4a22162c8d1 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 19 Apr 2020 17:12:25 +0800 Subject: [PATCH 14/87] fix mixed quote nested string interpolation --- src/fsharp/lex.fsl | 21 +++++++++------------ src/fsharp/lexhelp.fs | 4 +--- src/fsharp/lexhelp.fsi | 10 +++------- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index e695eec8c3f..b405d167801 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -733,8 +733,8 @@ rule token args skip = parse { match args.interpolatedStringNesting with | [] -> () - | head :: rest -> - args.interpolatedStringNesting <- head + 1 :: rest + | (counter, style) :: rest -> + args.interpolatedStringNesting <- (counter + 1, style) :: rest LBRACE } @@ -744,8 +744,8 @@ rule token args skip = parse { // We encounter a '}' in the expression token stream. First check if we're in an interpolated string expression // and continue the string if necessary - match args.interpolatedStringNesting, args.interpolatedStringStyle with - | 1 :: rest, Some style -> + match args.interpolatedStringNesting with + | (1, style) :: rest -> args.interpolatedStringNesting <- rest let buf, fin, m = startString args lexbuf if not skip then @@ -755,8 +755,8 @@ rule token args skip = parse | LexerStringStyle.Verbatim -> verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.SingleQuote -> singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.TripleQuote -> tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf - | head :: rest, _ -> - args.interpolatedStringNesting <- head - 1 :: rest + | (counter, style) :: rest -> + args.interpolatedStringNesting <- (counter - 1, style) :: rest RBRACE | _ -> RBRACE @@ -1008,8 +1008,7 @@ and singleQuoteString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- 1 :: args.interpolatedStringNesting - args.interpolatedStringStyle <- Some LexerStringStyle.SingleQuote + args.interpolatedStringNesting <- (1, LexerStringStyle.SingleQuote) :: args.interpolatedStringNesting fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) @@ -1078,8 +1077,7 @@ and verbatimString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- 1 :: args.interpolatedStringNesting - args.interpolatedStringStyle <- Some LexerStringStyle.Verbatim + args.interpolatedStringNesting <- (1, LexerStringStyle.Verbatim) :: args.interpolatedStringNesting fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) @@ -1149,8 +1147,7 @@ and tripleQuoteString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- 1 :: args.interpolatedStringNesting - args.interpolatedStringStyle <- Some LexerStringStyle.TripleQuote + args.interpolatedStringNesting <- (1, LexerStringStyle.TripleQuote) :: args.interpolatedStringNesting fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) diff --git a/src/fsharp/lexhelp.fs b/src/fsharp/lexhelp.fs index 43283301003..155edc91654 100644 --- a/src/fsharp/lexhelp.fs +++ b/src/fsharp/lexhelp.fs @@ -56,8 +56,7 @@ type lexargs = lightSyntaxStatus : LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool - mutable interpolatedStringNesting: int list - mutable interpolatedStringStyle: LexerStringStyle option + mutable interpolatedStringNesting: (int* LexerStringStyle) list pathMap: PathMap } /// possible results of lexing a long Unicode escape sequence in a string literal, e.g. "\U0001F47D", @@ -77,7 +76,6 @@ let mkLexargs (_filename, defines, lightSyntaxStatus, resourceManager, ifdefStac applyLineDirectives=true interpolatedStringNesting = [] pathMap=pathMap - interpolatedStringStyle = None } /// Register the lexbuf and call the given function diff --git a/src/fsharp/lexhelp.fsi b/src/fsharp/lexhelp.fsi index a468c9943ce..388deeac57b 100644 --- a/src/fsharp/lexhelp.fsi +++ b/src/fsharp/lexhelp.fsi @@ -33,13 +33,9 @@ type lexargs = lightSyntaxStatus: LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool - - /// The degree of nesting of '{..}' in an interpolation fill. - /// Nesting counters of outer interpolating strings are pushed on stack. - mutable interpolatedStringNesting: int list - - /// The style of the string to continue after the matching '}' is found - mutable interpolatedStringStyle: LexerStringStyle option + /// The degree of nesting of '{..}' and the style of the string to continue afterwards, in an interpolation fill. + /// Nesting counters and styles of outer interpolating strings are pushed on this stack. + mutable interpolatedStringNesting: (int* LexerStringStyle) list pathMap: PathMap } type LongUnicodeLexResult = From 7677c994bba27a911166019ecc58be294aef47eb Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sun, 19 Apr 2020 19:17:00 +0800 Subject: [PATCH 15/87] string-interp: add test case for multiple interpolation points with different indentation --- src/fsharp/LexFilter.fs | 2 +- tests/fsharp/Compiler/Language/StringInterpolation.fs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index 1588a25cbe0..0d5009f313b 100644 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -86,7 +86,7 @@ type Context = | CtxtDo _ -> "do" | CtxtInterfaceHead _ -> "interface-decl" | CtxtTypeDefns _ -> "type" - | CtxtParen _ -> "paren" + | CtxtParen(_, p) -> sprintf "paren(%s)" (stringOfPos p) | CtxtMemberHead _ -> "member-head" | CtxtMemberBody _ -> "body" | CtxtSeqBlock (b, p, _addBlockEnd) -> sprintf "seqblock(%s, %s)" (match b with FirstInSeqBlock -> "first" | NotFirstInSeqBlock -> "subsequent") (stringOfPos p) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 0695bf4cc9e..f3ca2c88611 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -30,6 +30,9 @@ check "vcewweh6" $"123{456}789{012}345" "12345678912345" check "vcewweh7" $"123{456}789{"012"}345" "123456789012345" +check "vcewweh8" $"{1} +{2}" "1 +2" """ [] From 7a4d70708d102ec8a60f645e1a25ce34c66a1903 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 20 Apr 2020 00:50:59 +0800 Subject: [PATCH 16/87] lexfilter: push new CtxtParen at endPos for INTERP_STRING_PART and INTERP_STRING_BEGIN_PART --- src/fsharp/LexFilter.fs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index 0d5009f313b..111a13587ad 100644 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -1320,7 +1320,7 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, | _ when tokenForcesHeadContextClosure token offsideStack -> let ctxt = offsideStack.Head - if debug then dprintf "IN/ELSE/ELIF/DONE/RPAREN/RBRACE/END at %a terminates context at position %a\n" outputPos tokenStartPos outputPos ctxt.StartPos + if debug then dprintf "IN/ELSE/ELIF/DONE/RPAREN/RBRACE/END/INTERP at %a terminates context at position %a\n" outputPos tokenStartPos outputPos ctxt.StartPos popCtxt() match endTokenForACtxt ctxt with | Some tok -> @@ -1373,8 +1373,11 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, if debug then dprintf "RPAREN/RBRACE/BAR_RBRACE/RBRACK/BAR_RBRACK/RQUOTE/END at %a terminates CtxtParen()\n" outputPos tokenStartPos popCtxt() match t2 with + // $".... { ... } ... { ....} " pushes a block context at second { + // ~~~~~~~~ + // ^---------INTERP_STRING_PART | INTERP_STRING_PART _ -> - pushCtxt tokenTup (CtxtParen (token, tokenStartPos)) + pushCtxt tokenTup (CtxtParen (token, tokenTup.LexbufState.EndPos)) pushCtxtSeqBlock(false, NoAddBlockEnd) | _ -> // Queue a dummy token at this position to check if any closing rules apply @@ -1902,10 +1905,14 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, // '(' tokens are balanced with ')' tokens and also introduce a CtxtSeqBlock // $".... { ... } ... { ....} " pushes a block context at first { - // $".... { ... } ... { ....} " pushes a block context at second { + // ~~~~~~~~ + // ^---------INTERP_STRING_BEGIN_PART | (BEGIN | LPAREN | SIG | LBRACE | LBRACE_BAR | LBRACK | LBRACK_BAR | LQUOTE _ | LESS true | INTERP_STRING_BEGIN_PART _), _ -> if debug then dprintf "LPAREN etc., pushes CtxtParen, pushing CtxtSeqBlock, tokenStartPos = %a\n" outputPos tokenStartPos - pushCtxt tokenTup (CtxtParen (token, tokenStartPos)) + let pos = match token with + | INTERP_STRING_BEGIN_PART _ -> tokenTup.LexbufState.EndPos + | _ -> tokenStartPos + pushCtxt tokenTup (CtxtParen (token, pos)) pushCtxtSeqBlock(false, NoAddBlockEnd) returnToken tokenLexbufState token From 6f6a6b32cab120e89454bda1af89a3c6a6e2620d Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 20 Apr 2020 01:31:30 +0800 Subject: [PATCH 17/87] lexfilter: do not check undentation limit for string interpolation tokens. --- src/fsharp/LexFilter.fs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index 111a13587ad..a3704a18b5e 100644 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -849,7 +849,9 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, match newCtxt with // Don't bother to check pushes of Vanilla blocks since we've // always already pushed a SeqBlock at this position. - | CtxtVanilla _ -> () + | CtxtVanilla _ + // String interpolation inner expressions are not limited (e.g. multiline strings) + | CtxtParen((INTERP_STRING_BEGIN_PART _ | INTERP_STRING_PART _),_) -> () | _ -> let p1 = undentationLimit true offsideStack let c2 = newCtxt.StartCol From 2b242dbe1d308f7487e62085bba494f7efa218da Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 21 Apr 2020 16:26:02 +0100 Subject: [PATCH 18/87] FormattableString prototype --- src/fsharp/FSharp.Core/printf.fs | 945 ++++++++++++++++-------------- src/fsharp/FSharp.Core/printf.fsi | 50 +- 2 files changed, 537 insertions(+), 458 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 791565f652c..ba53a40eeb6 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -145,8 +145,8 @@ module internal PrintfImpl = None, i // Skip %P() added for hole in "...%d{x}..." - let skipInterpolationHole isInterp (s:string) i = - if isInterp && i+3 < s.Length && + let skipInterpolationHole isInterpolatedString (s:string) i = + if isInterpolatedString && i+3 < s.Length && s.[i] = '%' && s.[i+1] = 'P' && s.[i+2] = '(' && @@ -182,11 +182,18 @@ module internal PrintfImpl = /// Abstracts generated printer from the details of particular environment: how to write text, how to produce results etc... [] - type PrintfEnv<'State, 'Residue, 'Result> = - val State: 'State - new(s: 'State) = { State = s } + type PrintfEnv<'State, 'Residue, 'Result>(state: 'State) = + member _.State = state + abstract Finish: unit -> 'Result + abstract Write: string -> unit + + /// Write a captured interpolation value + abstract CaptureInterpoland: FormatSpecifier * obj -> unit + + /// Write the result of a '%t' format. If this is a string it is written. If it is a 'unit' value + /// the side effect has already happened abstract WriteT: 'Residue -> unit type Utils = @@ -247,446 +254,379 @@ module internal PrintfImpl = /// - functions that can transform collected values to strings /// basic shape of the signature of specialization /// + + + ... + - type Specializations<'State, 'Residue, 'Result> private ()= + type Specializations<'State, 'Residue, 'Result> = - static member Final1<'A> - ( - s0, conv1, s1 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) -> - let env = env() - Utils.Write(env, s0, (conv1 a), s1) + /// + + + static member FinalInterpoland1<'A>(s0, spec1, s1) = + Console.WriteLine("FinalInterpoland1 (build part 0)") // TODO remove me + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + Console.WriteLine("FinalInterpoland1 (build part1)") // TODO remove me + (fun (arg1: 'A) -> + let env = prev() + Console.WriteLine("FinalInterpoland1 (execute): arg1 = {0}", arg1) // TODO remove me + env.Write(s0) + env.CaptureInterpoland(spec1, box arg1) + env.Write(s1) + env.Finish() + ) + ) + /// + + + static member Final1<'A>(s0, conv1, s1) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) -> + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1) env.Finish() ) ) - static member FinalFastEnd1<'A> - ( - s0, conv1 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) -> - let env = env() - Utils.Write(env, s0, (conv1 a)) + /// + + static member FinalNoSuffix1<'A>(s0, conv1) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) -> + let env = prev() + Utils.Write(env, s0, conv1 arg1) env.Finish() ) ) - static member FinalFastStart1<'A> - ( - conv1, s1 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) -> - let env = env() - Utils.Write(env, (conv1 a), s1) + /// + + static member FinalNoPrefix1<'A>(conv1, s1) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) -> + let env = prev() + Utils.Write(env, conv1 arg1, s1) env.Finish() ) ) - static member FinalFast1<'A> - ( - conv1 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) -> - let env = env() - env.Write (conv1 a) + static member FinalNoPrefixOrSuffix1<'A>(conv1) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) -> + let env = prev() + env.Write (conv1 arg1) env.Finish() ) ) - static member Final2<'A, 'B> - ( - s0, conv1, s1, conv2, s2 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member Final2<'A, 'B>(s0, conv1, s1, conv2, s2) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (a: 'A) (b: 'B) -> - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2) + let env = prev() + Utils.Write(env, s0, conv1 a, s1, conv2 b, s2) env.Finish() ) ) - static member FinalFastEnd2<'A, 'B> - ( - s0, conv1, s1, conv2 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) -> - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b)) + static member FinalNoSuffix2<'A, 'B>(s0, conv1, s1, conv2) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) -> + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2) env.Finish() ) ) - static member FinalFastStart2<'A, 'B> - ( - conv1, s1, conv2, s2 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) -> - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2) + static member FinalNoPrefix2<'A, 'B>(conv1, s1, conv2, s2) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) -> + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2) env.Finish() ) ) - static member FinalFast2<'A, 'B> - ( - conv1, s1, conv2 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member FinalNoPrefixOrSuffix2<'A, 'B>(conv1, s1, conv2) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (a: 'A) (b: 'B) -> - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b)) + let env = prev() + Utils.Write(env, conv1 a, s1, conv2 b) env.Finish() ) ) - static member Final3<'A, 'B, 'C> - ( - s0, conv1, s1, conv2, s2, conv3, s3 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) -> - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3) + static member Final3<'A, 'B, 'C>(s0, conv1, s1, conv2, s2, conv3, s3) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3) env.Finish() ) ) - static member FinalFastEnd3<'A, 'B, 'C> - ( - s0, conv1, s1, conv2, s2, conv3 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) -> - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c)) + static member FinalNoSuffix3<'A, 'B, 'C>(s0, conv1, s1, conv2, s2, conv3) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3) env.Finish() ) ) - static member FinalFastStart3<'A, 'B, 'C> - ( - conv1, s1, conv2, s2, conv3, s3 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) -> - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3) + static member FinalNoPrefix3<'A, 'B, 'C>(conv1, s1, conv2, s2, conv3, s3) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3) env.Finish() ) ) - static member FinalFast3<'A, 'B, 'C> - ( - conv1, s1, conv2, s2, conv3 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) -> - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c)) + static member FinalNoPrefixOrSuffix3<'A, 'B, 'C>(conv1, s1, conv2, s2, conv3) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3) env.Finish() ) ) - static member Final4<'A, 'B, 'C, 'D> - ( - s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D)-> - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d), s4) + static member Final4<'A, 'B, 'C, 'D>(s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4) env.Finish() ) ) - static member FinalFastEnd4<'A, 'B, 'C, 'D> - ( - s0, conv1, s1, conv2, s2, conv3, s3, conv4 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D)-> - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d)) + static member FinalNoSuffix4<'A, 'B, 'C, 'D>(s0, conv1, s1, conv2, s2, conv3, s3, conv4) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4) env.Finish() ) ) - static member FinalFastStart4<'A, 'B, 'C, 'D> - ( - conv1, s1, conv2, s2, conv3, s3, conv4, s4 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D)-> - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d), s4) + static member FinalNoPrefix4<'A, 'B, 'C, 'D>(conv1, s1, conv2, s2, conv3, s3, conv4, s4) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4) env.Finish() ) ) - static member FinalFast4<'A, 'B, 'C, 'D> - ( - conv1, s1, conv2, s2, conv3, s3, conv4 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D)-> - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d)) + static member FinalNoPrefixOrSuffix4<'A, 'B, 'C, 'D>(conv1, s1, conv2, s2, conv3, s3, conv4) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4) env.Finish() ) ) - static member Final5<'A, 'B, 'C, 'D, 'E> - ( - s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, s5 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D) (e: 'E)-> - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d), s4, (conv5 e), s5) + static member Final5<'A, 'B, 'C, 'D, 'E>(s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, s5) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5, s5) env.Finish() ) ) - static member FinalFastEnd5<'A, 'B, 'C, 'D, 'E> - ( - s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D) (e: 'E)-> - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d), s4, (conv5 e)) + static member FinalNoSuffix5<'A, 'B, 'C, 'D, 'E>(s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5) env.Finish() ) ) - static member FinalFastStart5<'A, 'B, 'C, 'D, 'E> - ( - conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, s5 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D) (e: 'E)-> - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d), s4, (conv5 e), s5) + static member FinalNoPrefix5<'A, 'B, 'C, 'D, 'E>(conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, s5) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5, s5) env.Finish() ) ) - static member FinalFast5<'A, 'B, 'C, 'D, 'E> - ( - conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5 - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D) (e: 'E)-> - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d), s4, (conv5 e)) + static member FinalNoPrefixOrSuffix5<'A, 'B, 'C, 'D, 'E>(conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5) env.Finish() ) ) - static member Chained1<'A, 'Tail> - ( - s0, conv1, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) -> + static member Chained1<'A, 'Tail>(s0, conv1, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) -> let env() = - let env = env() - Utils.Write(env, s0, (conv1 a)) + let env = prev() + Utils.Write(env, s0, conv1 arg1) env next env : 'Tail ) ) - static member ChainedFastStart1<'A, 'Tail> - ( - conv1, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) -> + static member ChainedInterpoland1<'A, 'Tail>(s0, spec1, next) = + Console.WriteLine("ChainedInterpoland1 (build part 0)") // TODO remove me + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + Console.WriteLine("ChainedInterpoland1 (build part 1)") // TODO remove me + (fun (arg1: 'A) -> + Console.WriteLine("ChainedInterpoland1 (arg capture), arg1 = {0}", arg1) // TODO remove me let env() = - let env = env() - env.Write(conv1 a) + let env = prev() + Console.WriteLine("ChainedInterpoland1 (execute), arg1 = {0}", arg1) // TODO remove me + env.Write(s0) + env.CaptureInterpoland(spec1, arg1) env next env : 'Tail ) ) - static member Chained2<'A, 'B, 'Tail> - ( - s0, conv1, s1, conv2, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) -> - let env() = - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b)) + static member ChainedNoPrefix1<'A, 'Tail>(conv1, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) -> + let curr() = + let env = prev() + env.Write(conv1 arg1) env - next env : 'Tail + next curr : 'Tail ) ) - static member ChainedFastStart2<'A, 'B, 'Tail> - ( - conv1, s1, conv2, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) -> - let env() = - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b)) + static member Chained2<'A, 'B, 'Tail>(s0, conv1, s1, conv2, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) -> + let curr() = + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2) env - next env : 'Tail + next curr : 'Tail ) ) - static member Chained3<'A, 'B, 'C, 'Tail> - ( - s0, conv1, s1, conv2, s2, conv3, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) -> - let env() = - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c)) + static member ChainedNoPrefix2<'A, 'B, 'Tail>(conv1, s1, conv2, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) -> + let curr() = + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2) env - next env : 'Tail + next curr : 'Tail ) ) - static member ChainedFastStart3<'A, 'B, 'C, 'Tail> - ( - conv1, s1, conv2, s2, conv3, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) -> - let env() = - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c)) + static member Chained3<'A, 'B, 'C, 'Tail> (s0, conv1, s1, conv2, s2, conv3, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> + let curr() = + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3) env - next env : 'Tail + next curr : 'Tail ) ) - static member Chained4<'A, 'B, 'C, 'D, 'Tail> - ( - s0, conv1, s1, conv2, s2, conv3, s3, conv4, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D)-> - let env() = - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d)) + static member ChainedNoPrefix3<'A, 'B, 'C, 'Tail> (conv1, s1, conv2, s2, conv3, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> + let curr() = + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3) env - next env : 'Tail + next curr : 'Tail ) ) - static member ChainedFastStart4<'A, 'B, 'C, 'D, 'Tail> - ( - conv1, s1, conv2, s2, conv3, s3, conv4, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D)-> - let env() = - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d)) + static member Chained4<'A, 'B, 'C, 'D, 'Tail> (s0, conv1, s1, conv2, s2, conv3, s3, conv4, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> + let curr() = + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4) env - next env : 'Tail + next curr : 'Tail ) ) - static member Chained5<'A, 'B, 'C, 'D, 'E, 'Tail> - ( - s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D) (e: 'E)-> - let env() = - let env = env() - Utils.Write(env, s0, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d), s4, (conv5 e)) + static member ChainedNoPrefix4<'A, 'B, 'C, 'D, 'Tail> (conv1, s1, conv2, s2, conv3, s3, conv4, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> + let curr() = + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4) env - next env : 'Tail + next curr : 'Tail ) ) - static member ChainedFastStart5<'A, 'B, 'C, 'D, 'E, 'Tail> - ( - conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, - next - ) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) (c: 'C) (d: 'D) (e: 'E)-> + static member Chained5<'A, 'B, 'C, 'D, 'E, 'Tail> (s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> + let curr() = + let env = prev() + Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5) + env + next curr : 'Tail + ) + ) + + static member ChainedNoPrefix5<'A, 'B, 'C, 'D, 'E, 'Tail> (conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, next) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> let env() = - let env = env() - Utils.Write(env, (conv1 a), s1, (conv2 b), s2, (conv3 c), s3, (conv4 d), s4, (conv5 e)) + let env = prev() + Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5) env next env : 'Tail ) ) static member TFinal(s1: string, s2: string) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'Residue) -> - let env = env() + let env = prev() env.Write s1 env.WriteT(f env.State) env.Write s2 env.Finish() ) ) - static member TChained<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result,'Tail>) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member TChained<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'Residue) -> - let env() = - let env = env() + let curr() = + let env = prev() env.Write s1 env.WriteT(f env.State) env - next env: 'Tail + next curr: 'Tail ) ) static member LittleAFinal<'A>(s1: string, s2: string) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'A ->'Residue) (a: 'A) -> - let env = env() + let env = prev() env.Write s1 env.WriteT(f env.State a) env.Write s2 env.Finish() ) ) - static member LittleAChained<'A, 'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result,'Tail>) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member LittleAChained<'A, 'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'A ->'Residue) (a: 'A) -> - let env() = - let env = env() + let curr() = + let env = prev() env.Write s1 env.WriteT(f env.State a) env - next env: 'Tail + next curr: 'Tail ) ) static member StarFinal1<'A>(s1: string, conv, s2: string) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (star1: int) (a: 'A) -> - let env = env() + let env = prev() env.Write s1 env.Write (conv a star1: string) env.Write s2 @@ -695,9 +635,9 @@ module internal PrintfImpl = ) static member PercentStarFinal1(s1: string, s2: string) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (_star1 : int) -> - let env = env() + let env = prev() env.Write s1 env.Write("%") env.Write s2 @@ -706,11 +646,11 @@ module internal PrintfImpl = ) static member StarFinal2<'A>(s1: string, conv, s2: string) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (star1: int) (star2: int) (a: 'A) -> - let env = env() + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (star1: int) (star2: int) (arg1: 'A) -> + let env = prev() env.Write s1 - env.Write (conv a star1 star2: string) + env.Write (conv arg1 star1 star2: string) env.Write s2 env.Finish() ) @@ -718,9 +658,9 @@ module internal PrintfImpl = /// Handles case when '%*.*%' is used at the end of string static member PercentStarFinal2(s1: string, s2: string) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (_star1 : int) (_star2 : int) -> - let env = env() + let env = prev() env.Write s1 env.Write("%") env.Write s2 @@ -728,45 +668,45 @@ module internal PrintfImpl = ) ) - static member StarChained1<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'State, 'Residue, 'Result,'Tail>) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (star1: int) (a: 'A) -> - let env() = - let env = env() + static member StarChained1<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (star1: int) (arg1: 'A) -> + let curr() = + let env = prev() env.Write s1 - env.Write(conv a star1 : string) + env.Write(conv arg1 star1 : string) env - next env : 'Tail + next curr : 'Tail ) ) /// Handles case when '%*%' is used in the middle of the string so it needs to be chained to another printing block - static member PercentStarChained1<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result,'Tail>) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member PercentStarChained1<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (_star1 : int) -> - let env() = - let env = env() + let curr() = + let env = prev() env.Write s1 env.Write("%") env - next env: 'Tail + next curr: 'Tail ) ) - static member StarChained2<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'State, 'Residue, 'Result,'Tail>) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (star1: int) (star2: int) (a: 'A) -> - let env() = - let env = env() + static member StarChained2<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + (fun (star1: int) (star2: int) (arg1: 'A) -> + let curr() = + let env = prev() env.Write s1 - env.Write(conv a star1 star2 : string) + env.Write(conv arg1 star1 star2 : string) env - next env : 'Tail + next curr : 'Tail ) ) /// Handles case when '%*.*%' is used in the middle of the string so it needs to be chained to another printing block - static member PercentStarChained2<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result,'Tail>) = + static member PercentStarChained2<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (_star1 : int) (_star2 : int) -> let env() = @@ -790,7 +730,18 @@ module internal PrintfImpl = [] let DefaultPrecision = 6 + /// A wrapper struct used to slightly strengthen the types of "ValueConverter" objects produced during composition of + /// the dynamic implementation. These are always functions but sometimes they take one argument, sometimes two. + [] + type ValueConverter private (f: obj) = + member x.FuncObj = f + + static member inline Make (f: 'T1 -> string) = ValueConverter(box f) + static member inline Make (f: 'T1 -> 'T2 -> string) = ValueConverter(box f) + static member inline Make (f: 'T1 -> 'T2 -> 'T3 -> string) = ValueConverter(box f) + let getFormatForFloat (ch: char) (prec: int) = ch.ToString() + prec.ToString() + let normalizePrecision prec = min (max prec 0) 99 /// Contains helpers to convert printer functions to functions that prints value with respect to specified justification @@ -801,32 +752,33 @@ module internal PrintfImpl = /// - withPadding - adapts first category /// - withPaddingFormatted - adapts second category module Padding = + /// pad here is function that converts T to string with respect of justification /// basic - function that converts T to string without applying justification rules /// adaptPaddedFormatted returns boxed function that has various number of arguments depending on if width\precision flags has '*' value - let inline adaptPaddedFormatted (spec: FormatSpecifier) getFormat (basic: string -> 'T -> string) (pad: string -> int -> 'T -> string) = + let inline adaptPaddedFormatted (spec: FormatSpecifier) getFormat (basic: string -> 'T -> string) (pad: string -> int -> 'T -> string) : ValueConverter = if spec.IsStarWidth then if spec.IsStarPrecision then // width=*, prec=* - box(fun v width prec -> + ValueConverter.Make (fun v width prec -> let fmt = getFormat (normalizePrecision prec) pad fmt width v) else // width=*, prec=? let prec = if spec.IsPrecisionSpecified then normalizePrecision spec.Precision else DefaultPrecision let fmt = getFormat prec - box(fun v width -> + ValueConverter.Make (fun v width -> pad fmt width v) elif spec.IsStarPrecision then if spec.IsWidthSpecified then // width=val, prec=* - box(fun v prec -> + ValueConverter.Make (fun v prec -> let fmt = getFormat prec pad fmt spec.Width v) else // width=X, prec=* - box(fun v prec -> + ValueConverter.Make (fun v prec -> let fmt = getFormat prec basic fmt v) else @@ -834,43 +786,43 @@ module internal PrintfImpl = let fmt = getFormat prec if spec.IsWidthSpecified then // width=val, prec=* - box(fun v -> + ValueConverter.Make (fun v -> pad fmt spec.Width v) else // width=X, prec=* - box(fun v -> + ValueConverter.Make (fun v -> basic fmt v) /// pad here is function that converts T to string with respect of justification /// basic - function that converts T to string without applying justification rules /// adaptPadded returns boxed function that has various number of arguments depending on if width flags has '*' value - let inline adaptPadded (spec: FormatSpecifier) (basic: 'T -> string) (pad: int -> 'T -> string) = + let inline adaptPadded (spec: FormatSpecifier) (basic: 'T -> string) (pad: int -> 'T -> string) : ValueConverter = if spec.IsStarWidth then - // width=*, prec=? - box(fun v width -> - pad width v) + // width=*, prec=? + ValueConverter.Make (fun v width -> + pad width v) else if spec.IsWidthSpecified then // width=val, prec=* - box(fun v -> + ValueConverter.Make (fun v -> pad spec.Width v) else // width=X, prec=* - box(fun v -> + ValueConverter.Make (fun v -> basic v) - let inline withPaddingFormatted (spec: FormatSpecifier) getFormat (defaultFormat: string) (f: string -> 'T -> string) left right = + let inline withPaddingFormatted (spec: FormatSpecifier) getFormat (defaultFormat: string) (f: string -> 'T -> string) left right : ValueConverter = if not (spec.IsWidthSpecified || spec.IsPrecisionSpecified) then - box (f defaultFormat) + ValueConverter.Make (f defaultFormat) else if isLeftJustify spec.Flags then adaptPaddedFormatted spec getFormat f left else adaptPaddedFormatted spec getFormat f right - let inline withPadding (spec: FormatSpecifier) (f: 'T -> string) left right = + let inline withPadding (spec: FormatSpecifier) (f: 'T -> string) left right : ValueConverter = if not spec.IsWidthSpecified then - box f + ValueConverter.Make f else if isLeftJustify spec.Flags then adaptPadded spec f left @@ -1060,7 +1012,7 @@ module internal PrintfImpl = let isGFormat = isGFormat spec Padding.withPadding spec (GenericNumber.noJustification f prefix isUnsigned) (GenericNumber.leftJustify isGFormat f prefix padChar isUnsigned) (GenericNumber.rightJustify f prefix padChar isUnsigned) - let inline decimalWithPadding (spec: FormatSpecifier) getFormat defaultFormat f = + let inline decimalWithPadding (spec: FormatSpecifier) getFormat defaultFormat f : ValueConverter = let padChar, prefix = getPadAndPrefix true spec let isGFormat = isGFormat spec Padding.withPaddingFormatted spec getFormat defaultFormat (GenericNumber.noJustificationWithFormat f prefix) (GenericNumber.leftJustifyWithFormat isGFormat f prefix padChar) (GenericNumber.rightJustifyWithFormat f prefix padChar) @@ -1091,14 +1043,14 @@ module internal PrintfImpl = type ObjectPrinter = - static member ObjectToString<'T>(spec: FormatSpecifier) = + static member ObjectToString<'T>(spec: FormatSpecifier) : ValueConverter = basicWithPadding spec (fun (v: 'T) -> match box v with | null -> "" | x -> x.ToString()) /// Convert an interpoland to a string - static member InterpolandToString<'T>(spec: FormatSpecifier) = + static member InterpolandToString<'T>(spec: FormatSpecifier) : ValueConverter = let fmt = match spec.InteropHoleDotNetFormat with | None -> null @@ -1118,7 +1070,7 @@ module internal PrintfImpl = | _ -> v.GetType() Microsoft.FSharp.Text.StructuredPrintfImpl.Display.anyToStringForPrintf opts bindingFlags (v, vty) - static member GenericToString<'T>(spec: FormatSpecifier) = + static member GenericToString<'T>(spec: FormatSpecifier) : ValueConverter = let bindingFlags = if isPlusForPositives spec.Flags then BindingFlags.Public ||| BindingFlags.NonPublic else BindingFlags.Public @@ -1135,24 +1087,24 @@ module internal PrintfImpl = match spec.IsStarWidth, spec.IsStarPrecision with | true, true -> - box (fun (v: 'T) (width: int) (prec: int) -> + ValueConverter.Make (fun (v: 'T) (width: int) (prec: int) -> let opts = { opts with PrintSize = prec } let opts = if not useZeroWidth then { opts with PrintWidth = width} else opts ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) ) | true, false -> - box (fun (v: 'T) (width: int) -> + ValueConverter.Make (fun (v: 'T) (width: int) -> let opts = if not useZeroWidth then { opts with PrintWidth = width} else opts ObjectPrinter.GenericToStringCore(v, opts, bindingFlags)) | false, true -> - box (fun (v: 'T) (prec: int) -> + ValueConverter.Make (fun (v: 'T) (prec: int) -> let opts = { opts with PrintSize = prec } ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) ) | false, false -> - box (fun (v: 'T) -> + ValueConverter.Make (fun (v: 'T) -> ObjectPrinter.GenericToStringCore(v, opts, bindingFlags)) let basicNumberToString (ty: Type) (spec: FormatSpecifier) = @@ -1197,7 +1149,7 @@ module internal PrintfImpl = let mi_ObjectToString = typeof.GetMethod("ObjectToString", NonPublicStatics) let mi_InterpolandToString = typeof.GetMethod("InterpolandToString", NonPublicStatics) - let private getValueConverter (ty: Type) (spec: FormatSpecifier) : obj = + let private getValueConverter (ty: Type) (spec: FormatSpecifier) : ValueConverter = match spec.TypeChar with | 'b' -> System.Diagnostics.Debug.Assert(ty === typeof, "ty === typeof") @@ -1219,13 +1171,13 @@ module internal PrintfImpl = basicFloatToString ty spec | 'A' -> let mi = mi_GenericToString.MakeGenericMethod ty - mi.Invoke(null, [| box spec |]) + mi.Invoke(null, [| box spec |]) |> unbox | 'O' -> let mi = mi_ObjectToString.MakeGenericMethod ty - mi.Invoke(null, [| box spec |]) + mi.Invoke(null, [| box spec |]) |> unbox | 'P' -> let mi = mi_InterpolandToString.MakeGenericMethod ty - mi.Invoke(null, [| box spec |]) + mi.Invoke(null, [| box spec |]) |> unbox | _ -> raise (ArgumentException(SR.GetString(SR.printfBadFormatSpecifier))) @@ -1246,8 +1198,9 @@ module internal PrintfImpl = go ty 0 type private PrintfBuilderStack() = - let args = Stack 10 - let types = Stack 5 + // Note this 'obj' is an untagged union of type "string | value converter function | continuation function" + let args = Stack 10 + let types = Stack 5 let stackToArray size start count (s: Stack<_>) = let arr = Array.zeroCreate size @@ -1264,7 +1217,7 @@ module internal PrintfImpl = let typesArray = stackToArray typesArraySize typesArrayStartPos typesArrayTotalCount types argsArray, typesArray - member __.PopContinuationWithType() = + member __.PopContinuation() = System.Diagnostics.Debug.Assert(args.Count = 1, "args.Count = 1") System.Diagnostics.Debug.Assert(types.Count = 1, "types.Count = 1") @@ -1275,7 +1228,14 @@ module internal PrintfImpl = member __.PopValueUnsafe() = args.Pop() - member this.PushContinuationWithType (cont: obj, contTy: Type) = + member __.PushString(value: string) = + args.Push (box value) + + member __.PushValueConverter(value: ValueConverter, ty) = + args.Push value.FuncObj + types.Push ty + + member this.PushContinuation (cont: obj, contTy: Type) = System.Diagnostics.Debug.Assert(this.IsEmpty, "this.IsEmpty") System.Diagnostics.Debug.Assert( ( @@ -1285,21 +1245,15 @@ module internal PrintfImpl = "incorrect type" ) - this.PushArgumentWithType(cont, contTy) - - member __.PushArgument(value: obj) = - args.Push value - - member __.PushArgumentWithType(value: obj, ty) = - args.Push value - types.Push ty + args.Push cont + types.Push contTy member __.HasContinuationOnStack expectedNumberOfArguments = - types.Count = expectedNumberOfArguments + 1 + (types.Count = expectedNumberOfArguments + 1) member __.IsEmpty = System.Diagnostics.Debug.Assert(args.Count = types.Count, "args.Count = types.Count") - args.Count = 0 + (args.Count = 0) /// Type of element that is stored in cache /// Pair: factory for the printer + number of text blocks that printer will produce (used to preallocate buffers) @@ -1307,14 +1261,13 @@ module internal PrintfImpl = type CachedItem<'T, 'State, 'Residue, 'Result> = { format: string factory: PrintfFactory<'State, 'Residue, 'Result, 'T> - blockCount: int - isInterp: bool } + blockCount: int } /// Parses format string and creates result printer function. /// First it recursively consumes format string up to the end, then during unwinding builds printer using PrintfBuilderStack as storage for arguments. /// idea of implementation is very simple: every step can either push argument to the stack (if current block of 5 format specifiers is not yet filled) // or grab the content of stack, build intermediate printer and push it back to stack (so it can later be consumed by as argument) - type private PrintfBuilder<'S, 'Re, 'Res>() = + type private PrintfBuilder<'S, 'Re, 'Res>(isInterpolatedString, isFormattableString) = let mutable count = 0 let mutable optimizedArgCount = 0 @@ -1325,7 +1278,18 @@ module internal PrintfImpl = #endif let buildSpecialChained(spec: FormatSpecifier, argTys: Type[], prefix: string, tail: obj, retTy) = - if spec.TypeChar = 'a' then + if isFormattableString then + let mi = typeof>.GetMethod("ChainedInterpoland1", NonPublicStatics) +#if DEBUG + verifyMethodInfoWasTaken mi +#endif + let argTy = argTys.[0] + Console.WriteLine("buildSpecialChained: argTy = {0}", argTy) // TODO remove me + let mi = mi.MakeGenericMethod ([| argTy; retTy |]) + let args = [| box prefix; box spec; |] + mi.Invoke(null, args) + + elif spec.TypeChar = 'a' then let mi = typeof>.GetMethod("LittleAChained", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi @@ -1359,13 +1323,25 @@ module internal PrintfImpl = else let argTy = argTys.[argTys.Length - 2] let conv = getValueConverter argTy spec - [| argTy; retTy |], [| box prefix; box conv; tail |] + [| argTy; retTy |], [| box prefix; conv.FuncObj; tail |] let mi = mi.MakeGenericMethod argTypes mi.Invoke(null, args) let buildSpecialFinal(spec: FormatSpecifier, argTys: Type[], prefix: string, suffix: string) = - if spec.TypeChar = 'a' then + if isFormattableString then + // Every hole in a formattable string captures the interpoland + let mi = typeof>.GetMethod("FinalInterpoland1", NonPublicStatics) +#if DEBUG + verifyMethodInfoWasTaken mi +#endif + let argTy = argTys.[0] + Console.WriteLine("buildSpecialFinal: argTy = {0}", argTy) // TODO remove me + let mi = mi.MakeGenericMethod [| argTy |] + let args = [| box prefix; box spec; box suffix |] + mi.Invoke(null, args) + + elif spec.TypeChar = 'a' then let mi = typeof>.GetMethod("LittleAFinal", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi @@ -1399,29 +1375,37 @@ module internal PrintfImpl = let argTy = argTys.[argTys.Length - 2] let mi = mi.MakeGenericMethod argTy let conv = getValueConverter argTy spec - mi, [| box prefix; box conv; box suffix |] + mi, [| box prefix; conv.FuncObj; box suffix |] mi.Invoke(null, args) + let argIsEmptyString (arg: obj) = + match arg with + | :? string as s -> String.IsNullOrEmpty(s) + | _ -> false + let buildPlainFinal(args: obj[], argTypes: Type[]) = let argsCount = args.Length let methodName,args = - if argsCount > 0 && args.[0].ToString() = "" then - if argsCount > 1 && args.[argsCount - 1].ToString() = "" then + // check if the prefix is empty + if argsCount > 0 && argIsEmptyString args.[0] then + // check if both prefix and suffix are empty + if argsCount > 1 && argIsEmptyString args.[argsCount - 1] then let args = Array.sub args 1 (argsCount - 2) optimizedArgCount <- optimizedArgCount + 2 - "FinalFast", args + "FinalNoPrefixOrSuffix", args else optimizedArgCount <- optimizedArgCount + 1 - "FinalFastStart", args |> Array.skip 1 - elif argsCount > 0 && args.[argsCount - 1].ToString() = "" then + "FinalNoPrefix", args |> Array.skip 1 + // check if suffix is empty + elif argsCount > 0 && argIsEmptyString args.[argsCount - 1] then let args = Array.sub args 0 (argsCount - 1) optimizedArgCount <- optimizedArgCount + 1 - "FinalFastEnd", args + "FinalNoSuffix", args else "Final",args - let mi = typeof>.GetMethod(methodName + argTypes.Length.ToString(), NonPublicStatics) + let mi = typeof>.GetMethod(methodName + string argTypes.Length, NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1431,13 +1415,14 @@ module internal PrintfImpl = let buildPlainChained(args: obj[], argTypes: Type[]) = let argsCount = args.Length let methodName,args = - if argsCount > 0 && args.[0].ToString() = "" then + // check if the prefix is empty + if argsCount > 0 && argIsEmptyString args.[0] then optimizedArgCount <- optimizedArgCount + 1 - "ChainedFastStart", args |> Array.skip 1 + "ChainedNoPrefix", args |> Array.skip 1 else "Chained", args - let mi = typeof>.GetMethod(methodName + (argTypes.Length - 1).ToString(), NonPublicStatics) + let mi = typeof>.GetMethod(methodName + string (argTypes.Length - 1), NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1454,12 +1439,12 @@ module internal PrintfImpl = let extra = if hasCont then 1 else 0 let plainArgs, plainTypes = - builderStack.GetArgumentAndTypesAsArrays(n + 1, 1, n, (numberOfArgs + extra), 0, numberOfArgs) + builderStack.GetArgumentAndTypesAsArrays(n + 1, 1, n, numberOfArgs + extra, 0, numberOfArgs) plainArgs.[0] <- box prefix if hasCont then - let cont, contTy = builderStack.PopContinuationWithType() + let cont, contTy = builderStack.PopContinuation() plainArgs.[plainArgs.Length - 1] <- cont plainTypes.[plainTypes.Length - 1] <- contTy @@ -1467,7 +1452,7 @@ module internal PrintfImpl = else buildPlainFinal(plainArgs, plainTypes) - let rec parseFromFormatSpecifier isInterp (prefix: string) (s: string) (funcTy: Type) i: int = + let rec parseFromFormatSpecifier (prefix: string) (s: string) (funcTy: Type) i: int = if i >= s.Length then 0 else @@ -1481,9 +1466,14 @@ module internal PrintfImpl = let interpHoleDotnetFormat, i = FormatString.parseInteropHoleDotNetFormat typeChar s i // Skip %P insertion points added after %d{...} etc. in interpolated strings - let i = FormatString.skipInterpolationHole isInterp s i - - let spec = { TypeChar = typeChar; Precision = precision; Flags = flags; Width = width; InteropHoleDotNetFormat = interpHoleDotnetFormat } + let i = FormatString.skipInterpolationHole isInterpolatedString s i + + let spec = + { TypeChar = typeChar + Precision = precision + Flags = flags + Width = width + InteropHoleDotNetFormat = interpHoleDotnetFormat } let next, suffix = FormatString.findNextFormatSpecifier s i @@ -1503,14 +1493,15 @@ module internal PrintfImpl = let retTy = argTys.[argTys.Length - 1] - let numberOfArgs = parseFromFormatSpecifier isInterp suffix s retTy next + let numberOfArgs = parseFromFormatSpecifier suffix s retTy next - if spec.TypeChar = 'a' || spec.TypeChar = 't' || spec.IsStarWidth || spec.IsStarPrecision then + if isFormattableString || spec.TypeChar = 'a' || spec.TypeChar = 't' || spec.IsStarWidth || spec.IsStarPrecision then + // Every hole in a formattable string captures the interpoland if numberOfArgs = ContinuationOnStack then - let cont, contTy = builderStack.PopContinuationWithType() + let cont, contTy = builderStack.PopContinuation() let currentCont = buildSpecialChained(spec, argTys, prefix, cont, contTy) - builderStack.PushContinuationWithType(currentCont, funcTy) + builderStack.PushContinuation(currentCont, funcTy) ContinuationOnStack else @@ -1518,7 +1509,7 @@ module internal PrintfImpl = System.Diagnostics.Debug.Assert(builderStack.IsEmpty, "builderStack.IsEmpty") let currentCont = buildSpecialFinal(spec, argTys, prefix, suffix) - builderStack.PushContinuationWithType(currentCont, funcTy) + builderStack.PushContinuation(currentCont, funcTy) ContinuationOnStack else let hasCont = builderStack.HasContinuationOnStack numberOfArgs @@ -1535,7 +1526,7 @@ module internal PrintfImpl = let next = if hasCont then - let nextCont, nextContTy = builderStack.PopContinuationWithType() + let nextCont, nextContTy = builderStack.PopContinuation() plainArgs.[plainArgs.Length - 1] <- nextCont plainTypes.[plainTypes.Length - 1] <- nextContTy buildPlainChained(plainArgs, plainTypes) @@ -1543,27 +1534,29 @@ module internal PrintfImpl = buildPlainFinal(plainArgs, plainTypes) let next = buildSpecialChained(spec, argTys, prefix, next, retTy) - builderStack.PushContinuationWithType(next, funcTy) + builderStack.PushContinuation(next, funcTy) ContinuationOnStack else if numberOfArgs = ContinuationOnStack then let idx = argTys.Length - 2 - builderStack.PushArgument suffix - builderStack.PushArgumentWithType((getValueConverter argTys.[idx] spec), argTys.[idx]) + builderStack.PushString suffix + let conv = getValueConverter argTys.[idx] spec + builderStack.PushValueConverter(conv, argTys.[idx]) 1 else - builderStack.PushArgument suffix - builderStack.PushArgumentWithType((getValueConverter argTys.[0] spec), argTys.[0]) + builderStack.PushString suffix + let conv = getValueConverter argTys.[0] spec + builderStack.PushValueConverter(conv, argTys.[0]) if numberOfArgs = MaxArgumentsInSpecialization - 1 then let cont = buildPlain (numberOfArgs + 1) prefix - builderStack.PushContinuationWithType(cont, funcTy) + builderStack.PushContinuation(cont, funcTy) ContinuationOnStack else numberOfArgs + 1 - let parseFormatString isInterp (s: string) (funcTy: System.Type) : obj = + let parseFormatString (s: string) (funcTy: System.Type) : obj = optimizedArgCount <- 0 let prefixPos, prefix = FormatString.findNextFormatSpecifier s 0 if prefixPos = s.Length then @@ -1573,18 +1566,18 @@ module internal PrintfImpl = env.Finish() ) else - let n = parseFromFormatSpecifier isInterp prefix s funcTy prefixPos + let n = parseFromFormatSpecifier prefix s funcTy prefixPos if n = ContinuationOnStack || n = 0 then builderStack.PopValueUnsafe() else buildPlain n prefix - member __.Build<'T>(s: string, isInterp: bool) = + member __.Build<'T>(s: string) = { format = s - factory = (parseFormatString isInterp s typeof<'T> :?> PrintfFactory<'S, 'Re, 'Res, 'T>) + factory = (parseFormatString s typeof<'T> :?> PrintfFactory<'S, 'Re, 'Res, 'T>) blockCount = (2 * count + 1) - optimizedArgCount // second component is used in SprintfEnv as value for internal buffer - isInterp = isInterp } + } /// 2-level cache. /// @@ -1602,7 +1595,7 @@ module internal PrintfImpl = // 2nd level cache (type-indexed). Dictionary that maps format string to the corresponding cache entry static let mutable dict : ConcurrentDictionary> = null - static member Get(fmt: Format<'T, 'State, 'Residue, 'Result>, isInterp) = + static member Get(fmt: Format<'T, 'State, 'Residue, 'Result>, isInterpolatedString, isFormattableString) = let cacheEntry = Cache<'T, 'State, 'Residue, 'Result>.mostRecent let key = fmt.Value if not (cacheEntry === null) && key.Equals cacheEntry.format then @@ -1617,52 +1610,115 @@ module internal PrintfImpl = match dict.TryGetValue(key) with | true, res -> res | _ -> - let entry = PrintfBuilder<'State, 'Residue, 'Result>().Build<'T>(key, isInterp) + let entry = PrintfBuilder<'State, 'Residue, 'Result>(isInterpolatedString, isFormattableString).Build<'T>(key) // Note there's a race condition but it doesn't matter if lose one entry dict.TryAdd(key, entry) |> ignore entry Cache<'T, 'State, 'Residue, 'Result>.mostRecent <- v v - type StringPrintfEnv<'Result>(k, n) = + type LargeStringPrintfEnv<'Result>(k, n) = inherit PrintfEnv(()) - let buf: string[] = Array.zeroCreate n let mutable ptr = 0 + override __.Finish() : 'Result = k (String.Concat buf) + override __.Write(s: string) = buf.[ptr] <- s ptr <- ptr + 1 + override __.WriteT s = buf.[ptr] <- s ptr <- ptr + 1 - type SmallStringPrintfEnv<'Result>(k) = - inherit PrintfEnv(()) - + override __.CaptureInterpoland(_spec, _s) = failwith "no interpolands expected" + + type SmallStringPrintfEnv() = + inherit PrintfEnv(()) let mutable c = null - override __.Finish() : 'Result = k c + override __.Finish() : string = c override __.Write(s: string) = if isNull c then c <- s else c <- c + s override __.WriteT s = if isNull c then c <- s else c <- c + s + override __.CaptureInterpoland(_spec, _s) = failwith "no interpolands expected" + + let StringPrintfEnv n = + if n <= 2 then + SmallStringPrintfEnv() :> PrintfEnv<_,_,_> + else + LargeStringPrintfEnv(id, n) :> PrintfEnv<_,_,_> + +//#if NETSTANDARD2_0 + let FormattableStringPrintfEnv(n) = + let buf: Choice[] = Array.zeroCreate n + let mutable ptr = 0 + + { new PrintfEnv(()) with + + override __.Finish() : FormattableString = + let fmtb = StringBuilder() + + // i ends up holding the count of interpolation args + let mutable i = 0 + + for s in buf do + match s with + | Choice1Of2 s -> + fmtb.Append s |> ignore + | Choice2Of2 (spec, _v) -> + fmtb.Append "{" |> ignore + fmtb.Append (string i) |> ignore + match spec.InteropHoleDotNetFormat with + | None -> () + | Some txt -> + fmtb.Append ":" |> ignore + fmtb.Append txt |> ignore + fmtb.Append "}" |> ignore + i <- i + 1 + + let args = Array.zeroCreate i + let mutable j = 0 + for s in buf do + match s with + | Choice1Of2 _ -> () + | Choice2Of2 (_spec, s) -> + args.[j] <- s + j <- j + 1 + + let fmt = fmtb.ToString() + Console.WriteLine("FormattableStringPrintfEnv - fmt = {0}", fmt) + System.Runtime.CompilerServices.FormattableStringFactory.Create(fmt, args) + + override __.Write(s: string) = + Console.WriteLine("FormattableStringPrintfEnv - Write({0})", s) + buf.[ptr] <- Choice1Of2 s + ptr <- ptr + 1 + + override __.WriteT s = failwith "no %t formats in FormattableString" + + override __.CaptureInterpoland (fmt, s) = + Console.WriteLine("FormattableStringPrintfEnv - CaptureInterpoland({0})", s) + buf.[ptr] <- Choice2Of2 (fmt, s) + ptr <- ptr + 1 + } +//#endif + + let StringBuilderPrintfEnv<'Result>(k, buf) = + { new PrintfEnv(buf) with + override __.Finish() : 'Result = k () + override __.Write(s: string) = ignore(buf.Append s) + override __.WriteT(()) = () + override __.CaptureInterpoland(_spec, _s) = failwith "no interpolands expected" } + + let TextWriterPrintfEnv<'Result>(k, tw: IO.TextWriter) = + { new PrintfEnv(tw) with + override __.Finish() : 'Result = k() + override __.Write(s: string) = tw.Write s + override __.WriteT(()) = () + override __.CaptureInterpoland(_spec, _s) = failwith "no interpolands expected" } - type StringBuilderPrintfEnv<'Result>(k, buf) = - inherit PrintfEnv(buf) - override __.Finish() : 'Result = k () - override __.Write(s: string) = ignore(buf.Append s) - override __.WriteT(()) = () - - type TextWriterPrintfEnv<'Result>(k, tw: IO.TextWriter) = - inherit PrintfEnv(tw) - override __.Finish() : 'Result = k() - override __.Write(s: string) = tw.Write s - override __.WriteT(()) = () - - let inline doPrintf fmt isInterp f = - let formatter = Cache<_, _, _, _>.Get (fmt, isInterp) - let env() = f formatter.blockCount - formatter.factory env [] module Printf = @@ -1676,47 +1732,54 @@ module Printf = type StringFormat<'T,'Result> = Format<'T, unit, string, 'Result> type TextWriterFormat<'T,'Result> = Format<'T, TextWriter, unit, 'Result> type BuilderFormat<'T> = BuilderFormat<'T,unit> +//#if NETSTANDARD2_0 + type FormattableStringFormat<'T> = StringFormat<'T,FormattableString> +//#endif type StringFormat<'T> = StringFormat<'T,string> type TextWriterFormat<'T> = TextWriterFormat<'T,unit> [] let ksprintf continuation (format: StringFormat<'T, 'Result>) : 'T = - doPrintf format false (fun n -> - if n <= 2 then - SmallStringPrintfEnv continuation :> PrintfEnv<_, _, _> - else - StringPrintfEnv(continuation, n) :> PrintfEnv<_, _, _> - ) - - let inline sprintfAux isInterp (format: StringFormat<'T>) = - doPrintf format isInterp (fun n -> - if n <= 2 then - SmallStringPrintfEnv id :> PrintfEnv<_, _, _> - else - StringPrintfEnv(id, n) :> PrintfEnv<_, _, _> - ) + let cacheItem = Cache.Get (format, false, false) + let initial() = LargeStringPrintfEnv (continuation, cacheItem.blockCount) :> PrintfEnv<_,_,_> + cacheItem.factory initial [] - let sprintf (format: StringFormat<'T>) = sprintfAux false format + let sprintf (format: StringFormat<'T>) = + let cacheItem = Cache.Get (format, false, false) + let initial() = StringPrintfEnv cacheItem.blockCount + cacheItem.factory initial [] [] - let isprintf (format: StringFormat<'T>) = sprintfAux true format + let isprintf (format: StringFormat<'T>) = + let cacheItem = Cache.Get (format, true, false) + let initial() = StringPrintfEnv cacheItem.blockCount + cacheItem.factory initial + +//#if NETSTANDARD2_0 + [] + [] + let ifsprintf (format: FormattableStringFormat<'T>) = + let cacheItem = Cache.Get (format, true, true) + let initial() = FormattableStringPrintfEnv cacheItem.blockCount + cacheItem.factory initial +//#endif [] let kprintf continuation format = ksprintf continuation format [] let kbprintf continuation (builder: StringBuilder) format = - doPrintf format false (fun _ -> - StringBuilderPrintfEnv(continuation, builder) :> PrintfEnv<_, _, _> - ) + let cacheItem = Cache.Get (format, false, false) + let initial() = StringBuilderPrintfEnv(continuation, builder) + cacheItem.factory initial [] let kfprintf continuation textWriter format = - doPrintf format false (fun _ -> - TextWriterPrintfEnv(continuation, textWriter) :> PrintfEnv<_, _, _> - ) + let cacheItem = Cache.Get (format, false, false) + let initial() = TextWriterPrintfEnv(continuation, textWriter) + cacheItem.factory initial [] let bprintf builder format = kbprintf ignore builder format diff --git a/src/fsharp/FSharp.Core/printf.fsi b/src/fsharp/FSharp.Core/printf.fsi index 1d406efcea9..4d1664ce34a 100644 --- a/src/fsharp/FSharp.Core/printf.fsi +++ b/src/fsharp/FSharp.Core/printf.fsi @@ -140,11 +140,11 @@ module Printf = /// Represents a statically-analyzed format associated with writing to a System.Text.StringBuilder. The first type parameter indicates the /// arguments of the format operation and the last the overall return type. - type BuilderFormat<'T,'Result> = Format<'T, StringBuilder, unit, 'Result> + type BuilderFormat<'T,'Result> = Format<'T, StringBuilder, unit, 'Result> /// Represents a statically-analyzed format when formatting builds a string. The first type parameter indicates the /// arguments of the format operation and the last the overall return type. - type StringFormat<'T,'Result> = Format<'T, unit, string, 'Result> + type StringFormat<'T,'Result> = Format<'T, unit, string, 'Result> /// Represents a statically-analyzed format associated with writing to a System.IO.TextWriter. The first type parameter indicates the /// arguments of the format operation and the last the overall return type. @@ -152,67 +152,73 @@ module Printf = /// Represents a statically-analyzed format associated with writing to a System.Text.StringBuilder. The type parameter indicates the /// arguments and return type of the format operation. - type BuilderFormat<'T> = BuilderFormat<'T,unit> + type BuilderFormat<'T> = BuilderFormat<'T, unit> +//#if NETSTANDARD2_0 /// Represents a statically-analyzed format when formatting builds a string. The type parameter indicates the /// arguments and return type of the format operation. - type StringFormat<'T> = StringFormat<'T,string> + type FormattableStringFormat<'T> = StringFormat<'T,FormattableString> +//#endif + + /// Represents a statically-analyzed format when formatting builds a string. The type parameter indicates the + /// arguments and return type of the format operation. + type StringFormat<'T> = StringFormat<'T,string> /// Represents a statically-analyzed format associated with writing to a System.IO.TextWriter. The type parameter indicates the /// arguments and return type of the format operation. - type TextWriterFormat<'T> = TextWriterFormat<'T,unit> + type TextWriterFormat<'T> = TextWriterFormat<'T,unit> /// Print to a System.Text.StringBuilder /// The StringBuilder to print to. /// The input formatter. /// The return type and arguments of the formatter. [] - val bprintf : builder:StringBuilder -> format:BuilderFormat<'T> -> 'T + val bprintf: builder:StringBuilder -> format:BuilderFormat<'T> -> 'T /// Print to a text writer. /// The TextWriter to print to. /// The input formatter. /// The return type and arguments of the formatter. [] - val fprintf : textWriter:TextWriter -> format:TextWriterFormat<'T> -> 'T + val fprintf: textWriter:TextWriter -> format:TextWriterFormat<'T> -> 'T /// Print to a text writer, adding a newline /// The TextWriter to print to. /// The input formatter. /// The return type and arguments of the formatter. [] - val fprintfn : textWriter:TextWriter -> format:TextWriterFormat<'T> -> 'T + val fprintfn: textWriter:TextWriter -> format:TextWriterFormat<'T> -> 'T /// Formatted printing to stderr /// The input formatter. /// The return type and arguments of the formatter. [] - val eprintf : format:TextWriterFormat<'T> -> 'T + val eprintf: format:TextWriterFormat<'T> -> 'T /// Formatted printing to stderr, adding a newline /// The input formatter. /// The return type and arguments of the formatter. [] - val eprintfn : format:TextWriterFormat<'T> -> 'T + val eprintfn: format:TextWriterFormat<'T> -> 'T /// Formatted printing to stdout /// The input formatter. /// The return type and arguments of the formatter. [] - val printf : format:TextWriterFormat<'T> -> 'T + val printf: format:TextWriterFormat<'T> -> 'T /// Formatted printing to stdout, adding a newline. /// The input formatter. /// The return type and arguments of the formatter. [] - val printfn : format:TextWriterFormat<'T> -> 'T + val printfn: format:TextWriterFormat<'T> -> 'T /// Print to a string via an internal string buffer and return /// the result as a string. Helper printers must return strings. /// The input formatter. /// The formatted string. [] - val sprintf : format:StringFormat<'T> -> 'T + val sprintf: format:StringFormat<'T> -> 'T /// Interpolated print to a string via an internal string buffer and return /// the result as a string. Helper printers must return strings. @@ -222,6 +228,16 @@ module Printf = [] val isprintf: format:StringFormat<'T> -> 'T +//#if NETSTANDARD2_0 + /// Interpolated print to a string via an internal string buffer and return + /// the result as a string. Helper printers must return strings. + /// The input formatter. + /// The formatted string. + [] + [] + val ifsprintf: format:FormattableStringFormat<'T> -> 'T +//#endif + /// bprintf, but call the given 'final' function to generate the result. /// See kprintf. /// The function called after formatting to generate the format result. @@ -229,7 +245,7 @@ module Printf = /// The input formatter. /// The arguments of the formatter. [] - val kbprintf : continuation:(unit -> 'Result) -> builder:StringBuilder -> format:BuilderFormat<'T,'Result> -> 'T + val kbprintf: continuation:(unit -> 'Result) -> builder:StringBuilder -> format:BuilderFormat<'T,'Result> -> 'T /// fprintf, but call the given 'final' function to generate the result. /// See kprintf. @@ -238,7 +254,7 @@ module Printf = /// The input formatter. /// The arguments of the formatter. [] - val kfprintf : continuation:(unit -> 'Result) -> textWriter:TextWriter -> format:TextWriterFormat<'T,'Result> -> 'T + val kfprintf: continuation:(unit -> 'Result) -> textWriter:TextWriter -> format:TextWriterFormat<'T,'Result> -> 'T /// printf, but call the given 'final' function to generate the result. /// For example, these let the printing force a flush after all output has @@ -247,7 +263,7 @@ module Printf = /// The input formatter. /// The arguments of the formatter. [] - val kprintf : continuation:(string -> 'Result) -> format:StringFormat<'T,'Result> -> 'T + val kprintf: continuation:(string -> 'Result) -> format:StringFormat<'T,'Result> -> 'T /// sprintf, but call the given 'final' function to generate the result. /// See kprintf. @@ -255,7 +271,7 @@ module Printf = /// The input formatter. /// The arguments of the formatter. [] - val ksprintf : continuation:(string -> 'Result) -> format:StringFormat<'T,'Result> -> 'T + val ksprintf: continuation:(string -> 'Result) -> format:StringFormat<'T,'Result> -> 'T /// Print to a string buffer and raise an exception with the given /// result. Helper printers must return strings. From 69ae73306f1346f990703ead44ad8f4e4329d304 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 22 Apr 2020 16:20:29 +0100 Subject: [PATCH 19/87] add FormattableString support --- src/fsharp/CheckFormatStrings.fs | 16 +- src/fsharp/CheckFormatStrings.fsi | 4 +- src/fsharp/FSComp.txt | 1 + src/fsharp/FSharp.Core/printf.fs | 324 ++++++++++-------- src/fsharp/FSharp.Core/printf.fsi | 8 +- src/fsharp/TcGlobals.fs | 17 +- src/fsharp/TypeChecker.fs | 98 ++++-- src/fsharp/TypedTreeOps.fs | 3 + src/fsharp/TypedTreeOps.fsi | 4 + .../Compiler/Language/StringInterpolation.fs | 22 ++ 10 files changed, 304 insertions(+), 193 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index b46836edf87..aa39faa23c6 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -47,7 +47,7 @@ let newInfo () = addZeros = false precision = false} -let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolation (context: FormatStringCheckContext option) fmt bty cty = +let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormattableString (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) = @@ -195,10 +195,12 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolation (context: // Explicitly typed holes in interpolated strings "....%d{x}..." get additional '%P()' as a hole place marker let skipPossibleInterpolationHole i = - if isInterpolation then + if isInterpolated then if i+1 < len && fmt.[i] = '%' && fmt.[i+1] = 'P' then let i = i + 2 if i+1 < len && fmt.[i] = '(' && fmt.[i+1] = ')' then + if isFormattableString then + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated4())) i + 2 else raise (Failure (FSComp.SR.forFormatInvalidForInterpolated2())) @@ -300,7 +302,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolation (context: parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) // residue of hole "...{n}..." in interpolated strings become %P(...) - | 'P' when isInterpolation -> + | 'P' when isInterpolated -> checkOtherFlags ch let i = requireAndSkipInterpolationHoleFormat (i+1) parseLoop ((posi, NewInferenceType ()) :: acc) (i+1, relLine, relCol+1) @@ -336,15 +338,15 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolation (context: let results = parseLoop [] (0, 0, m.StartColumn) results, Seq.toList specifierLocations -let ParseFormatString m g isInterpolation formatStringCheckContext fmt bty cty dty = - let argtys, specifierLocations = parseFormatStringInternal m g isInterpolation formatStringCheckContext fmt bty cty +let ParseFormatString m g isInterpolated isFormattableString formatStringCheckContext fmt bty cty dty = + let argtys, specifierLocations = parseFormatStringInternal m g isInterpolated isFormattableString formatStringCheckContext fmt bty cty let aty = List.foldBack (-->) argtys dty let ety = mkRefTupledTy g argtys (argtys, aty, ety), specifierLocations -let TryCountFormatStringArguments m g isInterpolation fmt bty cty = +let TryCountFormatStringArguments m g isInterpolated fmt bty cty = try - let argtys, _specifierLocations = parseFormatStringInternal m g isInterpolation None fmt bty cty + let argtys, _specifierLocations = parseFormatStringInternal m g isInterpolated false None fmt bty cty Some argtys.Length with _ -> None diff --git a/src/fsharp/CheckFormatStrings.fsi b/src/fsharp/CheckFormatStrings.fsi index 4053813e291..ec338db1513 100644 --- a/src/fsharp/CheckFormatStrings.fsi +++ b/src/fsharp/CheckFormatStrings.fsi @@ -13,6 +13,6 @@ open FSharp.Compiler.Range open FSharp.Compiler.TypedTree open FSharp.Compiler.TcGlobals -val ParseFormatString : m: range -> g: TcGlobals -> isInterp: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType list * TType * TType) * (range * int) list +val ParseFormatString : m: range -> g: TcGlobals -> isInterpolated: bool -> isFormattableString: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType list * TType * TType) * (range * int) list -val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> isInterp: bool -> fmt:string -> bty:TType -> cty:TType -> int option +val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> isInterpolated: bool -> fmt:string -> bty:TType -> cty:TType -> int option diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index e9aaca502a3..35ac5b484fa 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1519,4 +1519,5 @@ featureStringInterpolation,"string interpolation" forFormatInvalidForInterpolated,"Invalid interpolated string. Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'." forFormatInvalidForInterpolated2,"Invalid interpolated string. .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%%' format specifiers." forFormatInvalidForInterpolated3,"Invalid interpolated string. The '%%P' specifier may not be used explicitly." +forFormatInvalidForInterpolated4,"Invalid interpolated string. Interpolated strings used as the .NET FormattableStrings may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." 3362,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index ba53a40eeb6..9a0e426bdf8 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -190,7 +190,7 @@ module internal PrintfImpl = abstract Write: string -> unit /// Write a captured interpolation value - abstract CaptureInterpoland: FormatSpecifier * obj -> unit + abstract CaptureInterpoland: obj -> unit /// Write the result of a '%t' format. If this is a string it is written. If it is a 'unit' value /// the side effect has already happened @@ -241,10 +241,14 @@ module internal PrintfImpl = Utils.Write(env, a, b, c, d, e, f, g, h, i, j, k) Utils.Write(env, l, m) - /// Type of results produced by specialization - /// This is function that accepts thunk to create PrintfEnv on demand and returns concrete instance of Printer (curried function) - /// After all arguments is collected, specialization obtains concrete PrintfEnv from the thunk and use it to output collected data. - type PrintfFactory<'State, 'Residue, 'Result, 'Printer> = (unit -> PrintfEnv<'State, 'Residue, 'Result>) -> 'Printer + /// Type of results produced by specialization. + /// + /// This is a function that accepts a thunk to create PrintfEnv on demand (at the very last + /// appliction of an argument) and returns a concrete instance of an appriate curried printer. + /// + /// After all arguments are collected, specialization obtains concrete PrintfEnv from the thunk + /// and uses it to output collected data. + type PrintfFactory<'Printer, 'State, 'Residue, 'Result> = (unit -> PrintfEnv<'State, 'Residue, 'Result>) -> 'Printer [] let MaxArgumentsInSpecialization = 5 @@ -257,15 +261,15 @@ module internal PrintfImpl = type Specializations<'State, 'Residue, 'Result> = /// + + - static member FinalInterpoland1<'A>(s0, spec1, s1) = - Console.WriteLine("FinalInterpoland1 (build part 0)") // TODO remove me + static member FinalInterpoland1<'A>(s0, s1) = + //Console.WriteLine("FinalInterpoland1 (build part 0)") // TODO remove me (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - Console.WriteLine("FinalInterpoland1 (build part1)") // TODO remove me + //Console.WriteLine("FinalInterpoland1 (build part1)") // TODO remove me (fun (arg1: 'A) -> let env = prev() - Console.WriteLine("FinalInterpoland1 (execute): arg1 = {0}", arg1) // TODO remove me + //Console.WriteLine("FinalInterpoland1 (execute): arg1 = {0}", arg1) // TODO remove me env.Write(s0) - env.CaptureInterpoland(spec1, box arg1) + env.CaptureInterpoland(box arg1) env.Write(s1) env.Finish() ) @@ -464,17 +468,17 @@ module internal PrintfImpl = ) ) - static member ChainedInterpoland1<'A, 'Tail>(s0, spec1, next) = - Console.WriteLine("ChainedInterpoland1 (build part 0)") // TODO remove me + static member ChainedInterpoland1<'A, 'Tail>(s0, next) = + //Console.WriteLine("ChainedInterpoland1 (build part 0)") // TODO remove me (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - Console.WriteLine("ChainedInterpoland1 (build part 1)") // TODO remove me + //Console.WriteLine("ChainedInterpoland1 (build part 1)") // TODO remove me (fun (arg1: 'A) -> - Console.WriteLine("ChainedInterpoland1 (arg capture), arg1 = {0}", arg1) // TODO remove me + //Console.WriteLine("ChainedInterpoland1 (arg capture), arg1 = {0}", arg1) // TODO remove me let env() = let env = prev() - Console.WriteLine("ChainedInterpoland1 (execute), arg1 = {0}", arg1) // TODO remove me + //Console.WriteLine("ChainedInterpoland1 (execute), arg1 = {0}", arg1) // TODO remove me env.Write(s0) - env.CaptureInterpoland(spec1, arg1) + env.CaptureInterpoland(arg1) env next env : 'Tail ) @@ -589,7 +593,7 @@ module internal PrintfImpl = env.Finish() ) ) - static member TChained<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + static member TChained<'Tail>(s1: string, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'Residue) -> let curr() = @@ -611,7 +615,7 @@ module internal PrintfImpl = env.Finish() ) ) - static member LittleAChained<'A, 'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + static member LittleAChained<'A, 'Tail>(s1: string, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'A ->'Residue) (a: 'A) -> let curr() = @@ -668,7 +672,7 @@ module internal PrintfImpl = ) ) - static member StarChained1<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + static member StarChained1<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (star1: int) (arg1: 'A) -> let curr() = @@ -681,7 +685,7 @@ module internal PrintfImpl = ) /// Handles case when '%*%' is used in the middle of the string so it needs to be chained to another printing block - static member PercentStarChained1<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + static member PercentStarChained1<'Tail>(s1: string, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (_star1 : int) -> let curr() = @@ -693,7 +697,7 @@ module internal PrintfImpl = ) ) - static member StarChained2<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + static member StarChained2<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (star1: int) (star2: int) (arg1: 'A) -> let curr() = @@ -706,7 +710,7 @@ module internal PrintfImpl = ) /// Handles case when '%*.*%' is used in the middle of the string so it needs to be chained to another printing block - static member PercentStarChained2<'Tail>(s1: string, next: PrintfFactory<'State, 'Residue, 'Result, 'Tail>) = + static member PercentStarChained2<'Tail>(s1: string, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (_star1 : int) (_star2 : int) -> let env() = @@ -830,7 +834,9 @@ module internal PrintfImpl = adaptPadded spec f right let inline isNumber (x: ^T) = - not (^T: (static member IsPositiveInfinity: 'T -> bool) x) && not (^T: (static member IsNegativeInfinity: 'T -> bool) x) && not (^T: (static member IsNaN: 'T -> bool) x) + not (^T: (static member IsPositiveInfinity: 'T -> bool) x) && + not (^T: (static member IsNegativeInfinity: 'T -> bool) x) && + not (^T: (static member IsNaN: 'T -> bool) x) let inline isInteger n = n % LanguagePrimitives.GenericOne = LanguagePrimitives.GenericZero @@ -1255,42 +1261,79 @@ module internal PrintfImpl = System.Diagnostics.Debug.Assert(args.Count = types.Count, "args.Count = types.Count") (args.Count = 0) - /// Type of element that is stored in cache + /// Type of element that is stored in cache. This is the residue of the parse of the format string. + /// /// Pair: factory for the printer + number of text blocks that printer will produce (used to preallocate buffers) [] - type CachedItem<'T, 'State, 'Residue, 'Result> = - { format: string - factory: PrintfFactory<'State, 'Residue, 'Result, 'T> - blockCount: int } + type CachedItem<'Printer, 'State, 'Residue, 'Result> = + { + /// The format string, used to help identify the cache entry (the cache index types are taken + /// into account as well). + FormatString: string - /// Parses format string and creates result printer function. + /// The factory function used to generate the result or the resulting function. it is passed an + /// environment-creating function. + FunctionFactory: PrintfFactory<'Printer, 'State, 'Residue, 'Result> + + /// The format for the FormattableString, if we're building one of those + FormattableStringFormat: string + + /// Ther number of holes in the FormattableString, if we're building one of those + FormattableStringHoleCount: int + + /// The maximum number of slots needed in the environment including string fragments and output strings from position holders + BlockCount: int + } + + /// Parses format string and creates result printer factory function. + /// /// First it recursively consumes format string up to the end, then during unwinding builds printer using PrintfBuilderStack as storage for arguments. - /// idea of implementation is very simple: every step can either push argument to the stack (if current block of 5 format specifiers is not yet filled) + /// + /// The idea of implementation is very simple: every step can either push argument to the stack (if current block of 5 format specifiers is not yet filled) // or grab the content of stack, build intermediate printer and push it back to stack (so it can later be consumed by as argument) - type private PrintfBuilder<'S, 'Re, 'Res>(isInterpolatedString, isFormattableString) = + type private FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt: string, isInterpolatedString, isFormattableString) = let mutable count = 0 let mutable optimizedArgCount = 0 + + // If we're building a formattable string, we build the resulting format string during the first pass + let ffmtb = if isFormattableString then StringBuilder() else null + let mutable ffmtCount = 0 + #if DEBUG let verifyMethodInfoWasTaken (mi: System.Reflection.MemberInfo) = if isNull mi then ignore (System.Diagnostics.Debugger.Launch()) #endif + let addFormattableFormatStringPlaceholder (spec: FormatSpecifier) = + ffmtb.Append "{" |> ignore + ffmtb.Append (string ffmtCount) |> ignore + match spec.InteropHoleDotNetFormat with + | None -> () + | Some txt -> + ffmtb.Append ":" |> ignore + ffmtb.Append txt |> ignore + ffmtb.Append "}" |> ignore + ffmtCount <- ffmtCount + 1 + let buildSpecialChained(spec: FormatSpecifier, argTys: Type[], prefix: string, tail: obj, retTy) = if isFormattableString then - let mi = typeof>.GetMethod("ChainedInterpoland1", NonPublicStatics) + let mi = typeof>.GetMethod("ChainedInterpoland1", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif + ffmtb.Append prefix |> ignore + addFormattableFormatStringPlaceholder spec + let argTy = argTys.[0] - Console.WriteLine("buildSpecialChained: argTy = {0}", argTy) // TODO remove me + //Console.WriteLine("buildSpecialChained: argTy = {0}", argTy) // TODO remove me let mi = mi.MakeGenericMethod ([| argTy; retTy |]) - let args = [| box prefix; box spec; |] + let args = [| box prefix |] mi.Invoke(null, args) elif spec.TypeChar = 'a' then - let mi = typeof>.GetMethod("LittleAChained", NonPublicStatics) + let mi = typeof>.GetMethod("LittleAChained", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1299,7 +1342,7 @@ module internal PrintfImpl = let args = [| box prefix; tail |] mi.Invoke(null, args) elif spec.TypeChar = 't' then - let mi = typeof>.GetMethod("TChained", NonPublicStatics) + let mi = typeof>.GetMethod("TChained", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1313,7 +1356,7 @@ module internal PrintfImpl = let n = if spec.IsStarWidth = spec.IsStarPrecision then 2 else 1 let prefix = if spec.TypeChar = '%' then "PercentStarChained" else "StarChained" let name = prefix + (string n) - typeof>.GetMethod(name, NonPublicStatics) + typeof>.GetMethod(name, NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1331,18 +1374,22 @@ module internal PrintfImpl = let buildSpecialFinal(spec: FormatSpecifier, argTys: Type[], prefix: string, suffix: string) = if isFormattableString then // Every hole in a formattable string captures the interpoland - let mi = typeof>.GetMethod("FinalInterpoland1", NonPublicStatics) + let mi = typeof>.GetMethod("FinalInterpoland1", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif + ffmtb.Append prefix |> ignore + addFormattableFormatStringPlaceholder spec + ffmtb.Append suffix |> ignore + let argTy = argTys.[0] - Console.WriteLine("buildSpecialFinal: argTy = {0}", argTy) // TODO remove me + //Console.WriteLine("buildSpecialFinal: argTy = {0}", argTy) // TODO remove me let mi = mi.MakeGenericMethod [| argTy |] - let args = [| box prefix; box spec; box suffix |] + let args = [| box prefix; box suffix |] mi.Invoke(null, args) elif spec.TypeChar = 'a' then - let mi = typeof>.GetMethod("LittleAFinal", NonPublicStatics) + let mi = typeof>.GetMethod("LittleAFinal", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1350,7 +1397,7 @@ module internal PrintfImpl = let args = [| box prefix; box suffix |] mi.Invoke(null, args) elif spec.TypeChar = 't' then - let mi = typeof>.GetMethod("TFinal", NonPublicStatics) + let mi = typeof>.GetMethod("TFinal", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1363,7 +1410,7 @@ module internal PrintfImpl = let n = if spec.IsStarWidth = spec.IsStarPrecision then 2 else 1 let prefix = if spec.TypeChar = '%' then "PercentStarFinal" else "StarFinal" let name = prefix + (string n) - typeof>.GetMethod(name, NonPublicStatics) + typeof>.GetMethod(name, NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1405,7 +1452,7 @@ module internal PrintfImpl = else "Final",args - let mi = typeof>.GetMethod(methodName + string argTypes.Length, NonPublicStatics) + let mi = typeof>.GetMethod(methodName + string argTypes.Length, NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1422,7 +1469,7 @@ module internal PrintfImpl = else "Chained", args - let mi = typeof>.GetMethod(methodName + string (argTypes.Length - 1), NonPublicStatics) + let mi = typeof>.GetMethod(methodName + string (argTypes.Length - 1), NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi #endif @@ -1556,49 +1603,63 @@ module internal PrintfImpl = else numberOfArgs + 1 - let parseFormatString (s: string) (funcTy: System.Type) : obj = - optimizedArgCount <- 0 - let prefixPos, prefix = FormatString.findNextFormatSpecifier s 0 - if prefixPos = s.Length then - box (fun (env: unit -> PrintfEnv<'S, 'Re, 'Res>) -> + let funcTy = typeof<'Printer> + + let factoryObj = + + // Find the format specifier + let prefixPos, prefix = FormatString.findNextFormatSpecifier fmt 0 + + if prefixPos = fmt.Length then + if isFormattableString then + ffmtb.Append prefix |> ignore + + // If there are not format specifiers then take a simple path + box (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> let env = env() env.Write prefix - env.Finish() - ) + env.Finish()) else - let n = parseFromFormatSpecifier prefix s funcTy prefixPos + let n = parseFromFormatSpecifier prefix fmt funcTy prefixPos if n = ContinuationOnStack || n = 0 then builderStack.PopValueUnsafe() else buildPlain n prefix - member __.Build<'T>(s: string) = - { format = s - factory = (parseFormatString s typeof<'T> :?> PrintfFactory<'S, 'Re, 'Res, 'T>) - blockCount = (2 * count + 1) - optimizedArgCount // second component is used in SprintfEnv as value for internal buffer + //do System.Console.WriteLine("factoryObj.GetType() = {0}", factoryObj.GetType()) + let result = + { + FormatString = fmt + FormattableStringFormat = (if isFormattableString then ffmtb.ToString() else null) + FormattableStringHoleCount = ffmtCount + FunctionFactory = factoryObj :?> PrintfFactory<'Printer, 'State, 'Residue, 'Result> + // second component is used in SprintfEnv as value for internal buffer + BlockCount = (2 * count + 1) - optimizedArgCount } + member _.Result = result + /// 2-level cache. /// /// We can use the same caches for both interpolated and non-interpolated strings /// since interpolated strings contain %P and don't overlap with non-interpolation strings, and if an interpolated /// string doesn't contain %P then the processing of the format strings is semantically identical. - type Cache<'T, 'State, 'Residue, 'Result>() = + type Cache<'Printer, 'State, 'Residue, 'Result>() = /// 1st level cache (type-indexed). Stores last value that was consumed by the current /// thread in thread-static field thus providing shortcuts for scenarios when printf is /// called in tight loop. [] - static val mutable private mostRecent: CachedItem<'T, 'State, 'Residue, 'Result> + static val mutable private mostRecent: CachedItem<'Printer, 'State, 'Residue, 'Result> // 2nd level cache (type-indexed). Dictionary that maps format string to the corresponding cache entry - static let mutable dict : ConcurrentDictionary> = null + static let mutable dict : ConcurrentDictionary> = null - static member Get(fmt: Format<'T, 'State, 'Residue, 'Result>, isInterpolatedString, isFormattableString) = - let cacheEntry = Cache<'T, 'State, 'Residue, 'Result>.mostRecent - let key = fmt.Value - if not (cacheEntry === null) && key.Equals cacheEntry.format then + static member Get(format: Format<'Printer, 'State, 'Residue, 'Result>, isInterpolatedString, isFormattableString) = + let cacheEntry = Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent + let fmt = format.Value + if not (cacheEntry === null) && fmt.Equals cacheEntry.FormatString then cacheEntry else // Initialize the 2nd level cache if necessary. Note there's a race condition but it doesn't @@ -1607,14 +1668,15 @@ module internal PrintfImpl = dict <- ConcurrentDictionary<_,_>() let v = - match dict.TryGetValue(key) with + match dict.TryGetValue(fmt) with | true, res -> res | _ -> - let entry = PrintfBuilder<'State, 'Residue, 'Result>(isInterpolatedString, isFormattableString).Build<'T>(key) + let parser = FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt, isInterpolatedString, isFormattableString) + let result = parser.Result // Note there's a race condition but it doesn't matter if lose one entry - dict.TryAdd(key, entry) |> ignore - entry - Cache<'T, 'State, 'Residue, 'Result>.mostRecent <- v + dict.TryAdd(fmt, result) |> ignore + result + Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent <- v v type LargeStringPrintfEnv<'Result>(k, n) = @@ -1633,7 +1695,7 @@ module internal PrintfImpl = buf.[ptr] <- s ptr <- ptr + 1 - override __.CaptureInterpoland(_spec, _s) = failwith "no interpolands expected" + override __.CaptureInterpoland(_s) = failwith "no interpolands expected" type SmallStringPrintfEnv() = inherit PrintfEnv(()) @@ -1642,7 +1704,7 @@ module internal PrintfImpl = override __.Finish() : string = c override __.Write(s: string) = if isNull c then c <- s else c <- c + s override __.WriteT s = if isNull c then c <- s else c <- c + s - override __.CaptureInterpoland(_spec, _s) = failwith "no interpolands expected" + override __.CaptureInterpoland(_s) = failwith "no interpolands expected" let StringPrintfEnv n = if n <= 2 then @@ -1650,75 +1712,41 @@ module internal PrintfImpl = else LargeStringPrintfEnv(id, n) :> PrintfEnv<_,_,_> -//#if NETSTANDARD2_0 - let FormattableStringPrintfEnv(n) = - let buf: Choice[] = Array.zeroCreate n +#if NETSTANDARD + let FormattableStringPrintfEnv(ffmt: string, n) = + let args: obj[] = Array.zeroCreate n let mutable ptr = 0 { new PrintfEnv(()) with override __.Finish() : FormattableString = - let fmtb = StringBuilder() - - // i ends up holding the count of interpolation args - let mutable i = 0 - - for s in buf do - match s with - | Choice1Of2 s -> - fmtb.Append s |> ignore - | Choice2Of2 (spec, _v) -> - fmtb.Append "{" |> ignore - fmtb.Append (string i) |> ignore - match spec.InteropHoleDotNetFormat with - | None -> () - | Some txt -> - fmtb.Append ":" |> ignore - fmtb.Append txt |> ignore - fmtb.Append "}" |> ignore - i <- i + 1 - - let args = Array.zeroCreate i - let mutable j = 0 - for s in buf do - match s with - | Choice1Of2 _ -> () - | Choice2Of2 (_spec, s) -> - args.[j] <- s - j <- j + 1 - - let fmt = fmtb.ToString() - Console.WriteLine("FormattableStringPrintfEnv - fmt = {0}", fmt) - System.Runtime.CompilerServices.FormattableStringFactory.Create(fmt, args) - - override __.Write(s: string) = - Console.WriteLine("FormattableStringPrintfEnv - Write({0})", s) - buf.[ptr] <- Choice1Of2 s - ptr <- ptr + 1 + //Console.WriteLine("FormattableStringPrintfEnv - fmt = {0}", ffmt) + System.Runtime.CompilerServices.FormattableStringFactory.Create(ffmt, args) + + override __.Write(s: string) = () override __.WriteT s = failwith "no %t formats in FormattableString" - override __.CaptureInterpoland (fmt, s) = - Console.WriteLine("FormattableStringPrintfEnv - CaptureInterpoland({0})", s) - buf.[ptr] <- Choice2Of2 (fmt, s) + override __.CaptureInterpoland (value) = + //Console.WriteLine("FormattableStringPrintfEnv - CaptureInterpoland({0})", value) + args.[ptr] <- value ptr <- ptr + 1 } -//#endif +#endif let StringBuilderPrintfEnv<'Result>(k, buf) = { new PrintfEnv(buf) with override __.Finish() : 'Result = k () override __.Write(s: string) = ignore(buf.Append s) override __.WriteT(()) = () - override __.CaptureInterpoland(_spec, _s) = failwith "no interpolands expected" } + override __.CaptureInterpoland(_s) = failwith "no interpolands expected" } let TextWriterPrintfEnv<'Result>(k, tw: IO.TextWriter) = { new PrintfEnv(tw) with override __.Finish() : 'Result = k() override __.Write(s: string) = tw.Write s override __.WriteT(()) = () - override __.CaptureInterpoland(_spec, _s) = failwith "no interpolands expected" } - + override __.CaptureInterpoland(_s) = failwith "no interpolands expected" } [] module Printf = @@ -1728,43 +1756,43 @@ module Printf = open System.Text open PrintfImpl - type BuilderFormat<'T,'Result> = Format<'T, StringBuilder, unit, 'Result> - type StringFormat<'T,'Result> = Format<'T, unit, string, 'Result> + type BuilderFormat<'T,'Result> = Format<'T, StringBuilder, unit, 'Result> + type StringFormat<'T,'Result> = Format<'T, unit, string, 'Result> type TextWriterFormat<'T,'Result> = Format<'T, TextWriter, unit, 'Result> - type BuilderFormat<'T> = BuilderFormat<'T,unit> -//#if NETSTANDARD2_0 + type BuilderFormat<'T> = BuilderFormat<'T,unit> +#if NETSTANDARD type FormattableStringFormat<'T> = StringFormat<'T,FormattableString> -//#endif - type StringFormat<'T> = StringFormat<'T,string> +#endif + type StringFormat<'T> = StringFormat<'T,string> type TextWriterFormat<'T> = TextWriterFormat<'T,unit> [] let ksprintf continuation (format: StringFormat<'T, 'Result>) : 'T = let cacheItem = Cache.Get (format, false, false) - let initial() = LargeStringPrintfEnv (continuation, cacheItem.blockCount) :> PrintfEnv<_,_,_> - cacheItem.factory initial + let initial() = LargeStringPrintfEnv (continuation, cacheItem.BlockCount) :> PrintfEnv<_,_,_> + cacheItem.FunctionFactory initial [] let sprintf (format: StringFormat<'T>) = let cacheItem = Cache.Get (format, false, false) - let initial() = StringPrintfEnv cacheItem.blockCount - cacheItem.factory initial + let initial() = StringPrintfEnv cacheItem.BlockCount + cacheItem.FunctionFactory initial [] [] let isprintf (format: StringFormat<'T>) = let cacheItem = Cache.Get (format, true, false) - let initial() = StringPrintfEnv cacheItem.blockCount - cacheItem.factory initial + let initial() = StringPrintfEnv cacheItem.BlockCount + cacheItem.FunctionFactory initial -//#if NETSTANDARD2_0 +#if NETSTANDARD [] [] let ifsprintf (format: FormattableStringFormat<'T>) = let cacheItem = Cache.Get (format, true, true) - let initial() = FormattableStringPrintfEnv cacheItem.blockCount - cacheItem.factory initial -//#endif + let initial() = FormattableStringPrintfEnv (cacheItem.FormattableStringFormat, cacheItem.FormattableStringHoleCount) + cacheItem.FunctionFactory initial +#endif [] let kprintf continuation format = ksprintf continuation format @@ -1773,34 +1801,42 @@ module Printf = let kbprintf continuation (builder: StringBuilder) format = let cacheItem = Cache.Get (format, false, false) let initial() = StringBuilderPrintfEnv(continuation, builder) - cacheItem.factory initial + cacheItem.FunctionFactory initial [] let kfprintf continuation textWriter format = let cacheItem = Cache.Get (format, false, false) let initial() = TextWriterPrintfEnv(continuation, textWriter) - cacheItem.factory initial + cacheItem.FunctionFactory initial [] - let bprintf builder format = kbprintf ignore builder format + let bprintf builder format = + kbprintf ignore builder format [] - let fprintf (textWriter: TextWriter) format = kfprintf ignore textWriter format + let fprintf (textWriter: TextWriter) format = + kfprintf ignore textWriter format [] - let fprintfn (textWriter: TextWriter) format = kfprintf (fun _ -> textWriter.WriteLine()) textWriter format + let fprintfn (textWriter: TextWriter) format = + kfprintf (fun _ -> textWriter.WriteLine()) textWriter format [] - let failwithf format = ksprintf failwith format + let failwithf format = + ksprintf failwith format [] - let printf format = fprintf Console.Out format + let printf format = + fprintf Console.Out format [] - let eprintf format = fprintf Console.Error format + let eprintf format = + fprintf Console.Error format [] - let printfn format = fprintfn Console.Out format + let printfn format = + fprintfn Console.Out format [] - let eprintfn format = fprintfn Console.Error format + let eprintfn format = + fprintfn Console.Error format diff --git a/src/fsharp/FSharp.Core/printf.fsi b/src/fsharp/FSharp.Core/printf.fsi index 4d1664ce34a..dd72df81a4b 100644 --- a/src/fsharp/FSharp.Core/printf.fsi +++ b/src/fsharp/FSharp.Core/printf.fsi @@ -154,11 +154,11 @@ module Printf = /// arguments and return type of the format operation. type BuilderFormat<'T> = BuilderFormat<'T, unit> -//#if NETSTANDARD2_0 +#if NETSTANDARD /// Represents a statically-analyzed format when formatting builds a string. The type parameter indicates the /// arguments and return type of the format operation. type FormattableStringFormat<'T> = StringFormat<'T,FormattableString> -//#endif +#endif /// Represents a statically-analyzed format when formatting builds a string. The type parameter indicates the /// arguments and return type of the format operation. @@ -228,7 +228,7 @@ module Printf = [] val isprintf: format:StringFormat<'T> -> 'T -//#if NETSTANDARD2_0 +#if NETSTANDARD /// Interpolated print to a string via an internal string buffer and return /// the result as a string. Helper printers must return strings. /// The input formatter. @@ -236,7 +236,7 @@ module Printf = [] [] val ifsprintf: format:FormattableStringFormat<'T> -> 'T -//#endif +#endif /// bprintf, but call the given 'final' function to generate the result. /// See kprintf. diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index 9eeef3abc01..366f06be88c 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -139,7 +139,7 @@ let tname_Exception = "System.Exception" [] let tname_Missing = "System.Reflection.Missing" [] -let tname_Activator = "System.Activator" +let tname_FormattableString = "System.FormattableString" [] let tname_SerializationInfo = "System.Runtime.Serialization.SerializationInfo" [] @@ -334,6 +334,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_bool_ty = mkNonGenericTy v_bool_tcr let v_char_ty = mkNonGenericTy v_char_tcr let v_obj_ty = mkNonGenericTy v_obj_tcr + let v_FormattableString_ty = mkSysNonGenericTy sys "FormattableString" let v_string_ty = mkNonGenericTy v_string_tcr let v_decimal_ty = mkSysNonGenericTy sys "Decimal" let v_unit_ty = mkNonGenericTy v_unit_tcr_nice @@ -706,6 +707,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_new_format_info = makeIntrinsicValRef(fslib_MFCore_nleref, ".ctor" , Some "PrintfFormat`5", None , [vara;varb;varc;vard;vare], ([[v_string_ty]], mkPrintfFormatTy varaTy varbTy varcTy vardTy vareTy)) let v_sprintf_info = makeIntrinsicValRef(fslib_MFExtraTopLevelOperators_nleref, "sprintf" , None , Some "PrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) let v_isprintf_info = makeIntrinsicValRef(fslib_MFPrintfModule_nleref, "isprintf" , None , Some "InterpolatedPrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) + let v_ifsprintf_info = makeIntrinsicValRef(fslib_MFPrintfModule_nleref, "ifsprintf" , None , Some "InterpolatedPrintFormatToFormattableStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_FormattableString_ty]], varaTy)) let v_lazy_force_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Force" , Some "Lazy`1" , None , [vara], ([[mkLazyTy varaTy]; []], varaTy)) let v_lazy_create_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Create" , Some "Lazy`1" , None , [vara], ([[v_unit_ty --> varaTy]], mkLazyTy varaTy)) @@ -1013,6 +1015,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.bool_ty = v_bool_ty member __.int_ty = v_int_ty member __.string_ty = v_string_ty + member __.system_FormattableString_ty = v_FormattableString_ty member __.unit_ty = v_unit_ty member __.obj_ty = v_obj_ty member __.char_ty = v_char_ty @@ -1369,6 +1372,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val new_format_vref = ValRefForIntrinsic v_new_format_info member val sprintf_vref = ValRefForIntrinsic v_sprintf_info member val isprintf_vref = ValRefForIntrinsic v_isprintf_info + member val ifsprintf_vref = ValRefForIntrinsic v_ifsprintf_info member val unbox_vref = ValRefForIntrinsic v_unbox_info member val unbox_fast_vref = ValRefForIntrinsic v_unbox_fast_info member val istype_vref = ValRefForIntrinsic v_istype_info @@ -1390,17 +1394,18 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.seq_append_info = v_seq_append_info member __.seq_generated_info = v_seq_generated_info member __.seq_finally_info = v_seq_finally_info - member __.seq_of_functions_info = v_seq_of_functions_info + member __.seq_of_functions_info = v_seq_of_functions_info member __.seq_map_info = v_seq_map_info member __.seq_singleton_info = v_seq_singleton_info member __.seq_empty_info = v_seq_empty_info member __.sprintf_info = v_sprintf_info - member __.isprintf_info = v_isprintf_info + member __.isprintf_info = v_isprintf_info + member __.ifsprintf_info = v_ifsprintf_info member __.new_format_info = v_new_format_info member __.unbox_info = v_unbox_info - member __.get_generic_comparer_info = v_get_generic_comparer_info - member __.get_generic_er_equality_comparer_info = v_get_generic_er_equality_comparer_info - member __.get_generic_per_equality_comparer_info = v_get_generic_per_equality_comparer_info + member __.get_generic_comparer_info = v_get_generic_comparer_info + member __.get_generic_er_equality_comparer_info = v_get_generic_er_equality_comparer_info + member __.get_generic_per_equality_comparer_info = v_get_generic_per_equality_comparer_info member __.dispose_info = v_dispose_info member __.getstring_info = v_getstring_info member __.unbox_fast_info = v_unbox_fast_info diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 16469f83d06..298d49c122d 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -5935,7 +5935,7 @@ and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) = let formatText = match format with None -> "()" | Some n -> "(" + n.idText + ")" "%" + alignText + "P" + formatText ) |> String.concat "" - TcConstStringFormatExpr cenv overallTy env m tpenv (Some fillExprs) stringText + TcInterpolatedStringExpr cenv overallTy env m tpenv fillExprs stringText | SynExpr.Const (synConst, m) -> CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) @@ -7120,36 +7120,74 @@ and TcConstStringExpr cenv overallTy env m tpenv s = if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.string_ty) then mkString cenv.g m s, tpenv else - TcConstStringFormatExpr cenv overallTy env m tpenv None s + TcConstStringFormatExpr cenv overallTy env m tpenv s -and TcConstStringFormatExpr cenv overallTy env m tpenv (optFills: SynExpr list option) s = +and TcConstStringFormatExpr cenv overallTy env m tpenv (fmtString: string) = let g = cenv.g - let isInterp = optFills.IsSome let aty = NewInferenceType () - let bty = if isInterp then g.unit_ty else NewInferenceType () - let cty = if isInterp then g.string_ty else NewInferenceType () - let dty = if isInterp then g.string_ty else NewInferenceType () + let bty = NewInferenceType () + let cty = NewInferenceType () + let dty = NewInferenceType () let ety = NewInferenceType () + let formatTy = mkPrintfFormatTy g aty bty cty dty ety + // This might qualify as a format string - check via a type directed rule + let ok = not (isObjTy g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy formatTy - let ok = - match optFills with - | Some _ -> - // If this is an interpolated string then the result must be a string - UnifyTypes cenv env m overallTy g.string_ty - true - | None -> - // This might qualify as a format string - check via a type directed rule - not (isObjTy g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy formatTy + if ok then + // Parse the format string to work out the phantom types + let formatStringCheckContext = match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.FormatStringCheckContext + let normalizedString = (fmtString.Replace("\r\n", "\n").Replace("\r", "\n")) + + let (_argtys, atyRequired, etyRequired), specifierLocations = + try CheckFormatStrings.ParseFormatString m g false false formatStringCheckContext normalizedString bty cty dty + with Failure errString -> error (Error(FSComp.SR.tcUnableToParseFormatString errString, m)) + + match cenv.tcSink.CurrentSink with + | None -> () + | Some sink -> + for specifierLocation, numArgs in specifierLocations do + sink.NotifyFormatSpecifierLocation(specifierLocation, numArgs) + + UnifyTypes cenv env m aty atyRequired + UnifyTypes cenv env m ety etyRequired + let fmtExpr = mkCallNewFormat g m aty bty cty dty ety (mkString g m fmtString) + fmtExpr, tpenv + + else + UnifyTypes cenv env m overallTy g.string_ty + mkString g m fmtString, tpenv + +/// Check an interpolated string expression +and TcInterpolatedStringExpr cenv overallTy env m tpenv (synFillExprs: SynExpr list) (fmtString: string) = + let g = cenv.g + + let ok, isFormattableString = + // If this is an interpolated string then the result must be a string + if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.string_ty) then + true, false + else + // ... or if that fails then may be a FormattableString by a type-directed rule.... + if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.system_FormattableString_ty) then + true, true + else + UnifyTypes cenv env m overallTy g.string_ty + true, false + + let aty = NewInferenceType () + let bty = g.unit_ty + let cty = g.string_ty + let dty = (if isFormattableString then g.system_FormattableString_ty else g.string_ty) + let ety = NewInferenceType () if ok then // Parse the format string to work out the phantom types let formatStringCheckContext = match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.FormatStringCheckContext - let normalizedString = (s.Replace("\r\n", "\n").Replace("\r", "\n")) + let normalizedString = (fmtString.Replace("\r\n", "\n").Replace("\r", "\n")) let (argtys, atyRequired, etyRequired), specifierLocations = - try CheckFormatStrings.ParseFormatString m g optFills.IsSome formatStringCheckContext normalizedString bty cty dty - with Failure s -> error (Error(FSComp.SR.tcUnableToParseFormatString s, m)) + try CheckFormatStrings.ParseFormatString m g true isFormattableString formatStringCheckContext normalizedString bty cty dty + with Failure errString -> error (Error(FSComp.SR.tcUnableToParseFormatString errString, m)) match cenv.tcSink.CurrentSink with | None -> () @@ -7159,21 +7197,21 @@ and TcConstStringFormatExpr cenv overallTy env m tpenv (optFills: SynExpr list o UnifyTypes cenv env m aty atyRequired UnifyTypes cenv env m ety etyRequired - let fmtExpr = mkCallNewFormat g m aty bty cty dty ety (mkString g m s) - match optFills with - | None -> fmtExpr, tpenv - | Some synFillExprs -> - // Check the expressions filling the holes - if argtys.Length <> synFillExprs.Length then - error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) - let flexes = argtys |> List.map (fun _ -> false) - let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs - // Make the call to isprintf + let fmtExpr = mkCallNewFormat g m aty bty cty dty ety (mkString g m fmtString) + // Check the expressions filling the holes + if argtys.Length <> synFillExprs.Length then + error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) + let flexes = argtys |> List.map (fun _ -> false) + let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs + // Make the call to isprintf + if isFormattableString then + mkCall_ifsprintf g m aty fmtExpr fillExprs, tpenv + else mkCall_isprintf g m aty fmtExpr fillExprs, tpenv else UnifyTypes cenv env m overallTy g.string_ty - mkString g m s, tpenv + mkString g m fmtString, tpenv //------------------------------------------------------------------------- // TcConstExpr diff --git a/src/fsharp/TypedTreeOps.fs b/src/fsharp/TypedTreeOps.fs index 978f151ae6c..07f19b61b62 100644 --- a/src/fsharp/TypedTreeOps.fs +++ b/src/fsharp/TypedTreeOps.fs @@ -6975,6 +6975,9 @@ let mkCallSeqEmpty g m ty1 = let mkCall_isprintf (g: TcGlobals) m aty fmt es = mkApps g (typedExprForIntrinsic g m g.isprintf_info, [[aty]], fmt::es , m) +let mkCall_ifsprintf (g: TcGlobals) m aty fmt es = + mkApps g (typedExprForIntrinsic g m g.ifsprintf_info, [[aty]], fmt::es , m) + let mkCallDeserializeQuotationFSharp20Plus g m e1 e2 e3 e4 = let args = [ e1; e2; e3; e4 ] mkApps g (typedExprForIntrinsic g m g.deserialize_quoted_FSharp_20_plus_info, [], [ mkRefTupledNoTypes g m args ], m) diff --git a/src/fsharp/TypedTreeOps.fsi b/src/fsharp/TypedTreeOps.fsi index 98c4e7eeaee..042365fb0e0 100755 --- a/src/fsharp/TypedTreeOps.fsi +++ b/src/fsharp/TypedTreeOps.fsi @@ -1969,8 +1969,12 @@ val mkCallSeqSingleton : TcGlobals -> range -> TType -> Expr -> Expr val mkCallSeqEmpty : TcGlobals -> range -> TType -> Expr +/// Make a call to the 'isprintf' function for string interpolation val mkCall_isprintf: g: TcGlobals -> m: range -> funcTy: TType -> fmtExpr: Expr -> fillExprs: Expr list -> Expr +/// Make a call to the 'isprintf' function for string interpolation for FormattableString +val mkCall_ifsprintf: g: TcGlobals -> m: range -> funcTy: TType -> fmtExpr: Expr -> fillExprs: Expr list -> Expr + val mkILAsmCeq : TcGlobals -> range -> Expr -> Expr -> Expr val mkILAsmClt : TcGlobals -> range -> Expr -> Expr -> Expr diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index f3ca2c88611..9e42dc61c17 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -261,6 +261,28 @@ check \"vcewweh22n2\" END;\"\"\" " + [] + let ``String interpolation to FormattableString`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +let fmt (x: FormattableString) = x.ToString() +let fmt_us (x: FormattableString) = x.ToString(System.Globalization.CultureInfo("en-US")) +let fmt_de (x: FormattableString) = x.ToString(System.Globalization.CultureInfo("de-DE")) + +check "fwejwflpej1" (fmt $"") "" +check "fwejwflpej2" (fmt $"abc") "abc" +check "fwejwflpej3" (fmt $"abc{1}") "abc1" +check "fwejwflpej4" (fmt $"abc %d{box 1} def") "abc 1 def" +check "fwejwflpej6" (fmt_us $"abc {box 30000} def") "abc 30000 def" +check "fwejwflpej7" (fmt_de $"abc {box 30000} def") "abc 30000 def" +check "fwejwflpej8" (fmt_us $"abc {box 30000:N} def") "abc 30,000.00 def" +check "fwejwflpej9" (fmt_de $"abc {box 30000:N} def") "abc 30.000,00 def" + + """ + [] let ``String interpolation using .NET Formats`` () = CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] From d7388cc2e5a1facbbc4b0b9d54cf5186e3b138de Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 22 Apr 2020 20:20:03 +0100 Subject: [PATCH 20/87] negative error checking --- src/fsharp/FSComp.txt | 1 + src/fsharp/TcGlobals.fs | 2 + src/fsharp/TypeChecker.fs | 20 ++++---- .../SurfaceArea.coreclr.fs | 1 + .../SurfaceArea.net40.fs | 1 + tests/fsharp/Compiler/CompilerAssert.fs | 6 ++- .../Compiler/Language/StringInterpolation.fs | 46 ++++++++++++++++++- 7 files changed, 65 insertions(+), 12 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 35ac5b484fa..5b9271cbe97 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1493,6 +1493,7 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3351,chkFeatureNotRuntimeSupported,"Feature '%s' is not supported by target runtime." 3352,typrelInterfaceMemberNoMostSpecificImplementation,"Interface member '%s' does not have a most specific implementation." 3353,chkFeatureNotSupportedInLibrary,"Feature '%s' requires the F# library for language version %s or greater." +3354,tcUnableToParseInterpolatedString,"Unable to parse interpolated string '%s'" 3360,lexByteStringMayNotBeInterpolated,"a byte string may not be interpolated" useSdkRefs,"Use reference assemblies for .NET framework references when available (Enabled by default)." fSharpBannerVersion,"%s for F# %s" diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index 366f06be88c..9992bf79b8f 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -334,6 +334,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_bool_ty = mkNonGenericTy v_bool_tcr let v_char_ty = mkNonGenericTy v_char_tcr let v_obj_ty = mkNonGenericTy v_obj_tcr + let v_FormattableString_tcref = findSysTyconRef sys "FormattableString" let v_FormattableString_ty = mkSysNonGenericTy sys "FormattableString" let v_string_ty = mkNonGenericTy v_string_tcr let v_decimal_ty = mkSysNonGenericTy sys "Decimal" @@ -1015,6 +1016,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.bool_ty = v_bool_ty member __.int_ty = v_int_ty member __.string_ty = v_string_ty + member __.system_FormattableString_tcref = v_FormattableString_tcref member __.system_FormattableString_ty = v_FormattableString_ty member __.unit_ty = v_unit_ty member __.obj_ty = v_obj_ty diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 298d49c122d..79e4cb84f5d 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7120,17 +7120,17 @@ and TcConstStringExpr cenv overallTy env m tpenv s = if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.string_ty) then mkString cenv.g m s, tpenv else - TcConstStringFormatExpr cenv overallTy env m tpenv s + TcFormatStringExpr cenv overallTy env m tpenv s -and TcConstStringFormatExpr cenv overallTy env m tpenv (fmtString: string) = +and TcFormatStringExpr cenv overallTy env m tpenv (fmtString: string) = let g = cenv.g let aty = NewInferenceType () let bty = NewInferenceType () let cty = NewInferenceType () let dty = NewInferenceType () let ety = NewInferenceType () - let formatTy = mkPrintfFormatTy g aty bty cty dty ety + // This might qualify as a format string - check via a type directed rule let ok = not (isObjTy g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy formatTy @@ -7168,12 +7168,16 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (synFillExprs: SynExpr l true, false else // ... or if that fails then may be a FormattableString by a type-directed rule.... - if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.system_FormattableString_ty) then + if (not (isObjTy g overallTy) && + cenv.g.system_FormattableString_tcref.CanDeref && + cenv.g.ifsprintf_vref.TryDeref.IsSome && + AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.system_FormattableString_ty) then true, true else UnifyTypes cenv env m overallTy g.string_ty true, false + printfn "fmtString = <<<%s>>>, isFormattableString = %b" fmtString isFormattableString let aty = NewInferenceType () let bty = g.unit_ty let cty = g.string_ty @@ -7182,12 +7186,12 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (synFillExprs: SynExpr l if ok then // Parse the format string to work out the phantom types - let formatStringCheckContext = match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.FormatStringCheckContext - let normalizedString = (fmtString.Replace("\r\n", "\n").Replace("\r", "\n")) + let formatStringCheckContext = None //match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.FormatStringCheckContext + //let normalizedString = (fmtString.Replace("\r\n", "\n").Replace("\r", "\n")) let (argtys, atyRequired, etyRequired), specifierLocations = - try CheckFormatStrings.ParseFormatString m g true isFormattableString formatStringCheckContext normalizedString bty cty dty - with Failure errString -> error (Error(FSComp.SR.tcUnableToParseFormatString errString, m)) + try CheckFormatStrings.ParseFormatString m g true isFormattableString formatStringCheckContext fmtString bty cty dty + with Failure errString -> error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) match cenv.tcSink.CurrentSink with | None -> () diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index 2815d8594cc..362307ba44c 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -2247,6 +2247,7 @@ Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThenFail[T,TResult](Mic Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[System.String,TResult], Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) +Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToFormattableStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.FormattableString]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriterThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,TResult], System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index cb77401ddfc..9fd7f737e14 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -2247,6 +2247,7 @@ Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThenFail[T,TResult](Mic Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[System.String,TResult], Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) +Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToFormattableStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.FormattableString]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriterThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,TResult], System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/fsharp/Compiler/CompilerAssert.fs b/tests/fsharp/Compiler/CompilerAssert.fs index 4f8a634bb47..64fe6bfc7e9 100644 --- a/tests/fsharp/Compiler/CompilerAssert.fs +++ b/tests/fsharp/Compiler/CompilerAssert.fs @@ -263,8 +263,10 @@ let main argv = 0""" Array.zip errors expectedErrors |> Array.iter (fun (actualError, expectedError) -> - let (expectedSeverity, expectedErrorNumber, expectedErrorRange, expectedErrorMsg) = expectedError - let (actualSeverity, actualErrorNumber, actualErrorRange, actualErrorMsg) = actualError + let (expectedSeverity, expectedErrorNumber, expectedErrorRange, expectedErrorMsg: string) = expectedError + let (actualSeverity, actualErrorNumber, actualErrorRange, actualErrorMsg: string) = actualError + let expectedErrorMsg = expectedErrorMsg.Replace("\r\n", "\n") + let actualErrorMsg = actualErrorMsg.Replace("\r\n", "\n") checkEqual "Severity" expectedSeverity actualSeverity checkEqual "ErrorNumber" expectedErrorNumber actualErrorNumber checkEqual "ErrorRange" expectedErrorRange actualErrorRange diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 9e42dc61c17..b7d5fc3daf6 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -265,12 +265,14 @@ check \"vcewweh22n2\" let ``String interpolation to FormattableString`` () = CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] """ +open System +open System.Globalization let check msg a b = if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a let fmt (x: FormattableString) = x.ToString() -let fmt_us (x: FormattableString) = x.ToString(System.Globalization.CultureInfo("en-US")) -let fmt_de (x: FormattableString) = x.ToString(System.Globalization.CultureInfo("de-DE")) +let fmt_us (x: FormattableString) = x.ToString(CultureInfo("en-US")) +let fmt_de (x: FormattableString) = x.ToString(CultureInfo("de-DE")) check "fwejwflpej1" (fmt $"") "" check "fwejwflpej2" (fmt $"abc") "abc" @@ -500,3 +502,43 @@ let x = $"one" """ [|(FSharpErrorSeverity.Error, 3350, (2, 9, 2, 15), "Feature 'string interpolation' is not available in F# 4.7. Please use language version 'preview' or greater.")|] + + + [] + let ``Basic string interpolation negative`` () = + let s = """ +let x1 = $"one %d{System.String.Empty}" // mismatched types +let x2 = $"one %s{1}" // mismatched types +let x3 = $"one %s" // naked percent in interpolated +let x4 = $"one %d" // naked percent in interpolated +let x5 = $"one %A" // naked percent in interpolated +let x6 = $"one %P" // interpolation hole marker in interploation +let x7 = $"one %P()" // interpolation hole marker in interploation +let x8 = $"one %f" // naked percent in interpolated +let x9 = $"one %d{3:N}" // mix of formats +""" + System.IO.File.WriteAllText(@"c:\misc\a.fs", s) + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + s + [|(FSharpErrorSeverity.Error, 1, (2, 19, 2, 38), + "The type 'string' is not compatible with any of the types byte,int16,int32,int64,sbyte,uint16,uint32,uint64,nativeint,unativeint, arising from the use of a printf-style format string"); + (FSharpErrorSeverity.Error, 1, (3, 19, 3, 20), + """This expression was expected to have type + 'string' +but here has type + 'int' """); + (FSharpErrorSeverity.Error, 3354, (4, 10, 4, 19), + "Unable to parse interpolated string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'"); + (FSharpErrorSeverity.Error, 3354, (5, 10, 5, 19), + "Unable to parse interpolated string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'"); + (FSharpErrorSeverity.Error, 3354, (6, 10, 6, 19), + "Unable to parse interpolated string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'"); + (FSharpErrorSeverity.Error, 3354, (7, 10, 7, 19), + "Unable to parse interpolated string 'Invalid interpolated string. The '%P' specifier may not be used explicitly.'"); + (FSharpErrorSeverity.Error, 3361, (8, 10, 8, 21), + "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); + (FSharpErrorSeverity.Error, 3354, (9, 10, 9, 19), + "Unable to parse interpolated string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'"); + (FSharpErrorSeverity.Error, 3354, (10, 10, 10, 24), + "Unable to parse interpolated string 'Invalid interpolated string. .NET-style format specifiers such as '{x,3}' or '{x:N5}' may not be mixed with '%' format specifiers.'")|] + \ No newline at end of file From b2d1cc9f1ee01165770067c502088a9919f97224 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 22 Apr 2020 21:17:02 +0100 Subject: [PATCH 21/87] remove diagnostics --- src/fsharp/TypeChecker.fs | 1 - tests/fsharp/Compiler/Language/StringInterpolation.fs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 79e4cb84f5d..ecbcbd50bf5 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7177,7 +7177,6 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (synFillExprs: SynExpr l UnifyTypes cenv env m overallTy g.string_ty true, false - printfn "fmtString = <<<%s>>>, isFormattableString = %b" fmtString isFormattableString let aty = NewInferenceType () let bty = g.unit_ty let cty = g.string_ty diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index b7d5fc3daf6..d6074401a6e 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -517,7 +517,6 @@ let x7 = $"one %P()" // interpolation hole marker in interploation let x8 = $"one %f" // naked percent in interpolated let x9 = $"one %d{3:N}" // mix of formats """ - System.IO.File.WriteAllText(@"c:\misc\a.fs", s) CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] s [|(FSharpErrorSeverity.Error, 1, (2, 19, 2, 38), From ad2e050806dd0017c7d04308cafed0451ed6762c Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Apr 2020 14:52:52 +0100 Subject: [PATCH 22/87] simpler FormattableString implementation --- src/fsharp/CheckFormatStrings.fs | 86 ++++---- src/fsharp/CheckFormatStrings.fsi | 2 +- src/fsharp/FSComp.txt | 10 +- src/fsharp/FSharp.Core/printf.fs | 186 +++--------------- src/fsharp/FSharp.Core/printf.fsi | 16 -- src/fsharp/TcGlobals.fs | 9 +- src/fsharp/TypeChecker.fs | 135 +++++++------ src/fsharp/TypedTreeOps.fs | 3 - src/fsharp/TypedTreeOps.fsi | 3 - .../SurfaceArea.coreclr.fs | 1 - .../SurfaceArea.net40.fs | 1 - .../Compiler/Language/StringInterpolation.fs | 49 +++-- 12 files changed, 199 insertions(+), 302 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index aa39faa23c6..c87655e77bf 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -2,6 +2,7 @@ module internal FSharp.Compiler.CheckFormatStrings +open System.Text open FSharp.Compiler open FSharp.Compiler.AbstractIL.Internal.Library open FSharp.Compiler.ConstraintSolver @@ -71,6 +72,13 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta let specifierLocations = ResizeArray() + // For FormattableString we collect a .NET Format String with {0} etc. replacing text. '%%' are replaced + // by '%', we check there are no '%' formats, and '{{' and '}}' are *not* replaced since the subsequent + // call to String.Format etc. will process them. + let dotnetFormatString = StringBuilder() + let appendToDotnetFormatString (s: string) = dotnetFormatString.Append(s) |> ignore + let mutable dotnetFormatStringInterpolationHoleCount = 0 + let rec parseLoop acc (i, relLine, relCol) = if i >= len then let argtys = @@ -80,6 +88,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta raise (Failure (FSComp.SR.forPositionalSpecifiersNotPermitted())) argtys elif System.Char.IsSurrogatePair(fmt,i) then + appendToDotnetFormatString (fmt.[i..i+1]) parseLoop acc (i+2, relLine, relCol+2) else let c = fmt.[i] @@ -132,18 +141,18 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta | '.' -> precision (i+1) | _ -> false,i - let rec digitsWidthAndPrecision i = + let rec digitsWidthAndPrecision n i = if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) match fmt.[i] with - | c when System.Char.IsDigit c -> digitsWidthAndPrecision (i+1) - | _ -> optionalDotAndPrecision i + | c when System.Char.IsDigit c -> digitsWidthAndPrecision (n*10 + int c - int '0') (i+1) + | _ -> Some n, optionalDotAndPrecision i let widthAndPrecision i = if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) match fmt.[i] with - | c when System.Char.IsDigit c -> false,digitsWidthAndPrecision i - | '*' -> true,optionalDotAndPrecision (i+1) - | _ -> false,optionalDotAndPrecision i + | c when System.Char.IsDigit c -> false,digitsWidthAndPrecision 0 i + | '*' -> true, (None, optionalDotAndPrecision (i+1)) + | _ -> false, (None, optionalDotAndPrecision i) let rec digitsPosition n i = if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) @@ -168,7 +177,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta let relCol = relCol + i - oldI let oldI = i - let widthArg,(precisionArg,i) = widthAndPrecision i + let widthArg,(widthValue, (precisionArg,i)) = widthAndPrecision i let relCol = relCol + i - oldI if i >= len then raise (Failure (FSComp.SR.forBadPrecision())) @@ -195,30 +204,34 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta // Explicitly typed holes in interpolated strings "....%d{x}..." get additional '%P()' as a hole place marker let skipPossibleInterpolationHole i = - if isInterpolated then - if i+1 < len && fmt.[i] = '%' && fmt.[i+1] = 'P' then - let i = i + 2 - if i+1 < len && fmt.[i] = '(' && fmt.[i+1] = ')' then - if isFormattableString then - raise (Failure (FSComp.SR.forFormatInvalidForInterpolated4())) - i + 2 - else - raise (Failure (FSComp.SR.forFormatInvalidForInterpolated2())) - else - raise (Failure (FSComp.SR.forFormatInvalidForInterpolated())) - else i + if isInterpolated then + if i+1 < len && fmt.[i] = '%' && fmt.[i+1] = 'P' then + let i = i + 2 + if i+1 < len && fmt.[i] = '(' && fmt.[i+1] = ')' then + if isFormattableString then + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated4())) + i + 2 + else + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated2())) + else + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated())) + else i // Implicitly typed holes in interpolated strings are translated to '... %P(...)...' in the // type checker. They should always have '(...)' after for format string. let requireAndSkipInterpolationHoleFormat i = - if i < len && fmt.[i] = '(' then - let i2 = fmt.IndexOf(")", i+1) - if i2 = -1 then - raise (Failure (FSComp.SR.forFormatInvalidForInterpolated3())) - else - i2+1 - else - raise (Failure (FSComp.SR.forFormatInvalidForInterpolated3())) + if i < len && fmt.[i] = '(' then + let i2 = fmt.IndexOf(")", i+1) + if i2 = -1 then + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated3())) + else + let dotnetAlignment = match widthValue with None -> "" | Some w -> "," + (if info.leftJustify then "-" else "") + string w + let dotnetNumberFormat = match fmt.[i+1..i2-1] with "" -> "" | s -> ":" + s + appendToDotnetFormatString ("{" + string dotnetFormatStringInterpolationHoleCount + dotnetAlignment + dotnetNumberFormat + "}") + dotnetFormatStringInterpolationHoleCount <- dotnetFormatStringInterpolationHoleCount + 1 + i2+1 + else + raise (Failure (FSComp.SR.forFormatInvalidForInterpolated3())) let collectSpecifierLocation relLine relCol numStdArgs = let numArgsForSpecifier = @@ -239,6 +252,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta match ch with | '%' -> collectSpecifierLocation relLine relCol 0 + appendToDotnetFormatString "%" parseLoop acc (i+1, relLine, relCol+1) | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> @@ -305,7 +319,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta | 'P' when isInterpolated -> checkOtherFlags ch let i = requireAndSkipInterpolationHoleFormat (i+1) - parseLoop ((posi, NewInferenceType ()) :: acc) (i+1, relLine, relCol+1) + parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) | 'A' -> match info.numPrefixIfPos with @@ -332,21 +346,25 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta | c -> raise (Failure (FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c))) - | '\n' -> parseLoop acc (i+1, relLine+1, 0) - | _ -> parseLoop acc (i+1, relLine, relCol+1) + | '\n' -> + appendToDotnetFormatString fmt.[i..i] + parseLoop acc (i+1, relLine+1, 0) + | _ -> + appendToDotnetFormatString fmt.[i..i] + parseLoop acc (i+1, relLine, relCol+1) let results = parseLoop [] (0, 0, m.StartColumn) - results, Seq.toList specifierLocations + results, Seq.toList specifierLocations, dotnetFormatString.ToString() let ParseFormatString m g isInterpolated isFormattableString formatStringCheckContext fmt bty cty dty = - let argtys, specifierLocations = parseFormatStringInternal m g isInterpolated isFormattableString formatStringCheckContext fmt bty cty + let argtys, specifierLocations, dotnetFormatString = parseFormatStringInternal m g isInterpolated isFormattableString formatStringCheckContext fmt bty cty let aty = List.foldBack (-->) argtys dty let ety = mkRefTupledTy g argtys - (argtys, aty, ety), specifierLocations + (argtys, aty, ety), specifierLocations, dotnetFormatString let TryCountFormatStringArguments m g isInterpolated fmt bty cty = try - let argtys, _specifierLocations = parseFormatStringInternal m g isInterpolated false None fmt bty cty + let argtys, _specifierLocations, _dotnetFormatString = parseFormatStringInternal m g isInterpolated false None fmt bty cty Some argtys.Length with _ -> None diff --git a/src/fsharp/CheckFormatStrings.fsi b/src/fsharp/CheckFormatStrings.fsi index ec338db1513..20788d6674c 100644 --- a/src/fsharp/CheckFormatStrings.fsi +++ b/src/fsharp/CheckFormatStrings.fsi @@ -13,6 +13,6 @@ open FSharp.Compiler.Range open FSharp.Compiler.TypedTree open FSharp.Compiler.TcGlobals -val ParseFormatString : m: range -> g: TcGlobals -> isInterpolated: bool -> isFormattableString: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType list * TType * TType) * (range * int) list +val ParseFormatString : m: range -> g: TcGlobals -> isInterpolated: bool -> isFormattableString: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType list * TType * TType) * (range * int) list * string val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> isInterpolated: bool -> fmt:string -> bty:TType -> cty:TType -> int option diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 5b9271cbe97..65f503abc89 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1493,7 +1493,7 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3351,chkFeatureNotRuntimeSupported,"Feature '%s' is not supported by target runtime." 3352,typrelInterfaceMemberNoMostSpecificImplementation,"Interface member '%s' does not have a most specific implementation." 3353,chkFeatureNotSupportedInLibrary,"Feature '%s' requires the F# library for language version %s or greater." -3354,tcUnableToParseInterpolatedString,"Unable to parse interpolated string '%s'" +3354,tcUnableToParseInterpolatedString,"Invalid interpolated string. %s" 3360,lexByteStringMayNotBeInterpolated,"a byte string may not be interpolated" useSdkRefs,"Use reference assemblies for .NET framework references when available (Enabled by default)." fSharpBannerVersion,"%s for F# %s" @@ -1517,8 +1517,8 @@ featureNullableOptionalInterop,"nullable optional interop" featureDefaultInterfaceMemberConsumption,"default interface member consumption" featureStringInterpolation,"string interpolation" 3361,tcInterpolationMixedWithPercent,"Mismatch in interpolated string. Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'" -forFormatInvalidForInterpolated,"Invalid interpolated string. Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'." -forFormatInvalidForInterpolated2,"Invalid interpolated string. .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%%' format specifiers." -forFormatInvalidForInterpolated3,"Invalid interpolated string. The '%%P' specifier may not be used explicitly." -forFormatInvalidForInterpolated4,"Invalid interpolated string. Interpolated strings used as the .NET FormattableStrings may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." +forFormatInvalidForInterpolated,"Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'." +forFormatInvalidForInterpolated2,".NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%%' format specifiers." +forFormatInvalidForInterpolated3,"The '%%P' specifier may not be used explicitly." +forFormatInvalidForInterpolated4,"Interpolated strings used as type FormattableString may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." 3362,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 9a0e426bdf8..c7cbdaba9a0 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -131,7 +131,7 @@ module internal PrintfImpl = let parseTypeChar (s: string) i = s.[i], (i + 1) - let parseInteropHoleDotNetFormat typeChar (s: string) i = + let parseInterpolatedHoleDotNetFormat typeChar (s: string) i = if typeChar = 'P' then if i < s.Length && s.[i] = '(' then let i2 = s.IndexOf(")", i) @@ -145,12 +145,13 @@ module internal PrintfImpl = None, i // Skip %P() added for hole in "...%d{x}..." - let skipInterpolationHole isInterpolatedString (s:string) i = - if isInterpolatedString && i+3 < s.Length && - s.[i] = '%' && - s.[i+1] = 'P' && - s.[i+2] = '(' && - s.[i+3] = ')' then i+4 + let skipInterpolationHole (fmt:string) i = + if i+1 < fmt.Length && fmt.[i] = '%' && fmt.[i+1] = 'P' then + let i = i + 2 + if i+1 < fmt.Length && fmt.[i] = '(' && fmt.[i+1] = ')' then + i+2 + else + i else i let findNextFormatSpecifier (s: string) i = @@ -189,9 +190,6 @@ module internal PrintfImpl = abstract Write: string -> unit - /// Write a captured interpolation value - abstract CaptureInterpoland: obj -> unit - /// Write the result of a '%t' format. If this is a string it is written. If it is a 'unit' value /// the side effect has already happened abstract WriteT: 'Residue -> unit @@ -261,20 +259,6 @@ module internal PrintfImpl = type Specializations<'State, 'Residue, 'Result> = /// + + - static member FinalInterpoland1<'A>(s0, s1) = - //Console.WriteLine("FinalInterpoland1 (build part 0)") // TODO remove me - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - //Console.WriteLine("FinalInterpoland1 (build part1)") // TODO remove me - (fun (arg1: 'A) -> - let env = prev() - //Console.WriteLine("FinalInterpoland1 (execute): arg1 = {0}", arg1) // TODO remove me - env.Write(s0) - env.CaptureInterpoland(box arg1) - env.Write(s1) - env.Finish() - ) - ) - /// + + static member Final1<'A>(s0, conv1, s1) = (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> @@ -468,22 +452,6 @@ module internal PrintfImpl = ) ) - static member ChainedInterpoland1<'A, 'Tail>(s0, next) = - //Console.WriteLine("ChainedInterpoland1 (build part 0)") // TODO remove me - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - //Console.WriteLine("ChainedInterpoland1 (build part 1)") // TODO remove me - (fun (arg1: 'A) -> - //Console.WriteLine("ChainedInterpoland1 (arg capture), arg1 = {0}", arg1) // TODO remove me - let env() = - let env = prev() - //Console.WriteLine("ChainedInterpoland1 (execute), arg1 = {0}", arg1) // TODO remove me - env.Write(s0) - env.CaptureInterpoland(arg1) - env - next env : 'Tail - ) - ) - static member ChainedNoPrefix1<'A, 'Tail>(conv1, next) = (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> @@ -1275,12 +1243,6 @@ module internal PrintfImpl = /// environment-creating function. FunctionFactory: PrintfFactory<'Printer, 'State, 'Residue, 'Result> - /// The format for the FormattableString, if we're building one of those - FormattableStringFormat: string - - /// Ther number of holes in the FormattableString, if we're building one of those - FormattableStringHoleCount: int - /// The maximum number of slots needed in the environment including string fragments and output strings from position holders BlockCount: int } @@ -1291,48 +1253,19 @@ module internal PrintfImpl = /// /// The idea of implementation is very simple: every step can either push argument to the stack (if current block of 5 format specifiers is not yet filled) // or grab the content of stack, build intermediate printer and push it back to stack (so it can later be consumed by as argument) - type private FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt: string, isInterpolatedString, isFormattableString) = + type private FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt: string) = let mutable count = 0 let mutable optimizedArgCount = 0 - // If we're building a formattable string, we build the resulting format string during the first pass - let ffmtb = if isFormattableString then StringBuilder() else null - let mutable ffmtCount = 0 - #if DEBUG let verifyMethodInfoWasTaken (mi: System.Reflection.MemberInfo) = if isNull mi then ignore (System.Diagnostics.Debugger.Launch()) #endif - let addFormattableFormatStringPlaceholder (spec: FormatSpecifier) = - ffmtb.Append "{" |> ignore - ffmtb.Append (string ffmtCount) |> ignore - match spec.InteropHoleDotNetFormat with - | None -> () - | Some txt -> - ffmtb.Append ":" |> ignore - ffmtb.Append txt |> ignore - ffmtb.Append "}" |> ignore - ffmtCount <- ffmtCount + 1 - let buildSpecialChained(spec: FormatSpecifier, argTys: Type[], prefix: string, tail: obj, retTy) = - if isFormattableString then - let mi = typeof>.GetMethod("ChainedInterpoland1", NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - ffmtb.Append prefix |> ignore - addFormattableFormatStringPlaceholder spec - - let argTy = argTys.[0] - //Console.WriteLine("buildSpecialChained: argTy = {0}", argTy) // TODO remove me - let mi = mi.MakeGenericMethod ([| argTy; retTy |]) - let args = [| box prefix |] - mi.Invoke(null, args) - - elif spec.TypeChar = 'a' then + if spec.TypeChar = 'a' then let mi = typeof>.GetMethod("LittleAChained", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi @@ -1372,23 +1305,7 @@ module internal PrintfImpl = mi.Invoke(null, args) let buildSpecialFinal(spec: FormatSpecifier, argTys: Type[], prefix: string, suffix: string) = - if isFormattableString then - // Every hole in a formattable string captures the interpoland - let mi = typeof>.GetMethod("FinalInterpoland1", NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - ffmtb.Append prefix |> ignore - addFormattableFormatStringPlaceholder spec - ffmtb.Append suffix |> ignore - - let argTy = argTys.[0] - //Console.WriteLine("buildSpecialFinal: argTy = {0}", argTy) // TODO remove me - let mi = mi.MakeGenericMethod [| argTy |] - let args = [| box prefix; box suffix |] - mi.Invoke(null, args) - - elif spec.TypeChar = 'a' then + if spec.TypeChar = 'a' then let mi = typeof>.GetMethod("LittleAFinal", NonPublicStatics) #if DEBUG verifyMethodInfoWasTaken mi @@ -1510,10 +1427,10 @@ module internal PrintfImpl = let width, i = FormatString.parseWidth s i let precision, i = FormatString.parsePrecision s i let typeChar, i = FormatString.parseTypeChar s i - let interpHoleDotnetFormat, i = FormatString.parseInteropHoleDotNetFormat typeChar s i + let interpHoleDotnetFormat, i = FormatString.parseInterpolatedHoleDotNetFormat typeChar s i // Skip %P insertion points added after %d{...} etc. in interpolated strings - let i = FormatString.skipInterpolationHole isInterpolatedString s i + let i = FormatString.skipInterpolationHole s i let spec = { TypeChar = typeChar @@ -1542,7 +1459,7 @@ module internal PrintfImpl = let numberOfArgs = parseFromFormatSpecifier suffix s retTy next - if isFormattableString || spec.TypeChar = 'a' || spec.TypeChar = 't' || spec.IsStarWidth || spec.IsStarPrecision then + if spec.TypeChar = 'a' || spec.TypeChar = 't' || spec.IsStarWidth || spec.IsStarPrecision then // Every hole in a formattable string captures the interpoland if numberOfArgs = ContinuationOnStack then @@ -1611,9 +1528,6 @@ module internal PrintfImpl = let prefixPos, prefix = FormatString.findNextFormatSpecifier fmt 0 if prefixPos = fmt.Length then - if isFormattableString then - ffmtb.Append prefix |> ignore - // If there are not format specifiers then take a simple path box (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> let env = env() @@ -1631,8 +1545,6 @@ module internal PrintfImpl = let result = { FormatString = fmt - FormattableStringFormat = (if isFormattableString then ffmtb.ToString() else null) - FormattableStringHoleCount = ffmtCount FunctionFactory = factoryObj :?> PrintfFactory<'Printer, 'State, 'Residue, 'Result> // second component is used in SprintfEnv as value for internal buffer BlockCount = (2 * count + 1) - optimizedArgCount @@ -1640,11 +1552,7 @@ module internal PrintfImpl = member _.Result = result - /// 2-level cache. - /// - /// We can use the same caches for both interpolated and non-interpolated strings - /// since interpolated strings contain %P and don't overlap with non-interpolation strings, and if an interpolated - /// string doesn't contain %P then the processing of the format strings is semantically identical. + /// 2-level cache, keyed by format string and index types type Cache<'Printer, 'State, 'Residue, 'Result>() = /// 1st level cache (type-indexed). Stores last value that was consumed by the current @@ -1656,7 +1564,7 @@ module internal PrintfImpl = // 2nd level cache (type-indexed). Dictionary that maps format string to the corresponding cache entry static let mutable dict : ConcurrentDictionary> = null - static member Get(format: Format<'Printer, 'State, 'Residue, 'Result>, isInterpolatedString, isFormattableString) = + static member Get(format: Format<'Printer, 'State, 'Residue, 'Result>) = let cacheEntry = Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent let fmt = format.Value if not (cacheEntry === null) && fmt.Equals cacheEntry.FormatString then @@ -1671,9 +1579,11 @@ module internal PrintfImpl = match dict.TryGetValue(fmt) with | true, res -> res | _ -> - let parser = FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt, isInterpolatedString, isFormattableString) + let parser = FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt) let result = parser.Result - // Note there's a race condition but it doesn't matter if lose one entry + // Note there's a race condition - the dictionary entry may be re-created by another thread + // but it doesn't matter if lose one entry, as long as the dictionary only ends up holding + // a valid entry. dict.TryAdd(fmt, result) |> ignore result Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent <- v @@ -1695,8 +1605,6 @@ module internal PrintfImpl = buf.[ptr] <- s ptr <- ptr + 1 - override __.CaptureInterpoland(_s) = failwith "no interpolands expected" - type SmallStringPrintfEnv() = inherit PrintfEnv(()) let mutable c = null @@ -1704,7 +1612,6 @@ module internal PrintfImpl = override __.Finish() : string = c override __.Write(s: string) = if isNull c then c <- s else c <- c + s override __.WriteT s = if isNull c then c <- s else c <- c + s - override __.CaptureInterpoland(_s) = failwith "no interpolands expected" let StringPrintfEnv n = if n <= 2 then @@ -1712,41 +1619,17 @@ module internal PrintfImpl = else LargeStringPrintfEnv(id, n) :> PrintfEnv<_,_,_> -#if NETSTANDARD - let FormattableStringPrintfEnv(ffmt: string, n) = - let args: obj[] = Array.zeroCreate n - let mutable ptr = 0 - - { new PrintfEnv(()) with - - override __.Finish() : FormattableString = - //Console.WriteLine("FormattableStringPrintfEnv - fmt = {0}", ffmt) - System.Runtime.CompilerServices.FormattableStringFactory.Create(ffmt, args) - - override __.Write(s: string) = () - - override __.WriteT s = failwith "no %t formats in FormattableString" - - override __.CaptureInterpoland (value) = - //Console.WriteLine("FormattableStringPrintfEnv - CaptureInterpoland({0})", value) - args.[ptr] <- value - ptr <- ptr + 1 - } -#endif - let StringBuilderPrintfEnv<'Result>(k, buf) = { new PrintfEnv(buf) with override __.Finish() : 'Result = k () override __.Write(s: string) = ignore(buf.Append s) - override __.WriteT(()) = () - override __.CaptureInterpoland(_s) = failwith "no interpolands expected" } + override __.WriteT(()) = () } let TextWriterPrintfEnv<'Result>(k, tw: IO.TextWriter) = { new PrintfEnv(tw) with override __.Finish() : 'Result = k() override __.Write(s: string) = tw.Write s - override __.WriteT(()) = () - override __.CaptureInterpoland(_s) = failwith "no interpolands expected" } + override __.WriteT(()) = () } [] module Printf = @@ -1760,52 +1643,37 @@ module Printf = type StringFormat<'T,'Result> = Format<'T, unit, string, 'Result> type TextWriterFormat<'T,'Result> = Format<'T, TextWriter, unit, 'Result> type BuilderFormat<'T> = BuilderFormat<'T,unit> -#if NETSTANDARD - type FormattableStringFormat<'T> = StringFormat<'T,FormattableString> -#endif type StringFormat<'T> = StringFormat<'T,string> type TextWriterFormat<'T> = TextWriterFormat<'T,unit> [] let ksprintf continuation (format: StringFormat<'T, 'Result>) : 'T = - let cacheItem = Cache.Get (format, false, false) + let cacheItem = Cache.Get format let initial() = LargeStringPrintfEnv (continuation, cacheItem.BlockCount) :> PrintfEnv<_,_,_> cacheItem.FunctionFactory initial [] let sprintf (format: StringFormat<'T>) = - let cacheItem = Cache.Get (format, false, false) + let cacheItem = Cache.Get format let initial() = StringPrintfEnv cacheItem.BlockCount cacheItem.FunctionFactory initial [] [] - let isprintf (format: StringFormat<'T>) = - let cacheItem = Cache.Get (format, true, false) - let initial() = StringPrintfEnv cacheItem.BlockCount - cacheItem.FunctionFactory initial - -#if NETSTANDARD - [] - [] - let ifsprintf (format: FormattableStringFormat<'T>) = - let cacheItem = Cache.Get (format, true, true) - let initial() = FormattableStringPrintfEnv (cacheItem.FormattableStringFormat, cacheItem.FormattableStringHoleCount) - cacheItem.FunctionFactory initial -#endif + let isprintf (format: StringFormat<'T>) = sprintf format [] let kprintf continuation format = ksprintf continuation format [] let kbprintf continuation (builder: StringBuilder) format = - let cacheItem = Cache.Get (format, false, false) + let cacheItem = Cache.Get format let initial() = StringBuilderPrintfEnv(continuation, builder) cacheItem.FunctionFactory initial [] let kfprintf continuation textWriter format = - let cacheItem = Cache.Get (format, false, false) + let cacheItem = Cache.Get format let initial() = TextWriterPrintfEnv(continuation, textWriter) cacheItem.FunctionFactory initial diff --git a/src/fsharp/FSharp.Core/printf.fsi b/src/fsharp/FSharp.Core/printf.fsi index dd72df81a4b..97140add11e 100644 --- a/src/fsharp/FSharp.Core/printf.fsi +++ b/src/fsharp/FSharp.Core/printf.fsi @@ -154,12 +154,6 @@ module Printf = /// arguments and return type of the format operation. type BuilderFormat<'T> = BuilderFormat<'T, unit> -#if NETSTANDARD - /// Represents a statically-analyzed format when formatting builds a string. The type parameter indicates the - /// arguments and return type of the format operation. - type FormattableStringFormat<'T> = StringFormat<'T,FormattableString> -#endif - /// Represents a statically-analyzed format when formatting builds a string. The type parameter indicates the /// arguments and return type of the format operation. type StringFormat<'T> = StringFormat<'T,string> @@ -228,16 +222,6 @@ module Printf = [] val isprintf: format:StringFormat<'T> -> 'T -#if NETSTANDARD - /// Interpolated print to a string via an internal string buffer and return - /// the result as a string. Helper printers must return strings. - /// The input formatter. - /// The formatted string. - [] - [] - val ifsprintf: format:FormattableStringFormat<'T> -> 'T -#endif - /// bprintf, but call the given 'final' function to generate the result. /// See kprintf. /// The function called after formatting to generate the format result. diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index 9992bf79b8f..4fa40c917ec 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -335,7 +335,9 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_char_ty = mkNonGenericTy v_char_tcr let v_obj_ty = mkNonGenericTy v_obj_tcr let v_FormattableString_tcref = findSysTyconRef sys "FormattableString" - let v_FormattableString_ty = mkSysNonGenericTy sys "FormattableString" + let v_FormattableString_ty = mkNonGenericTy v_FormattableString_tcref + let v_FormattableStringFactory_tcref = findSysTyconRef sysCompilerServices "FormattableStringFactory" + let v_FormattableStringFactory_ty = mkNonGenericTy v_FormattableStringFactory_tcref let v_string_ty = mkNonGenericTy v_string_tcr let v_decimal_ty = mkSysNonGenericTy sys "Decimal" let v_unit_ty = mkNonGenericTy v_unit_tcr_nice @@ -708,7 +710,6 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_new_format_info = makeIntrinsicValRef(fslib_MFCore_nleref, ".ctor" , Some "PrintfFormat`5", None , [vara;varb;varc;vard;vare], ([[v_string_ty]], mkPrintfFormatTy varaTy varbTy varcTy vardTy vareTy)) let v_sprintf_info = makeIntrinsicValRef(fslib_MFExtraTopLevelOperators_nleref, "sprintf" , None , Some "PrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) let v_isprintf_info = makeIntrinsicValRef(fslib_MFPrintfModule_nleref, "isprintf" , None , Some "InterpolatedPrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) - let v_ifsprintf_info = makeIntrinsicValRef(fslib_MFPrintfModule_nleref, "ifsprintf" , None , Some "InterpolatedPrintFormatToFormattableStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_FormattableString_ty]], varaTy)) let v_lazy_force_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Force" , Some "Lazy`1" , None , [vara], ([[mkLazyTy varaTy]; []], varaTy)) let v_lazy_create_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Create" , Some "Lazy`1" , None , [vara], ([[v_unit_ty --> varaTy]], mkLazyTy varaTy)) @@ -1017,7 +1018,9 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.int_ty = v_int_ty member __.string_ty = v_string_ty member __.system_FormattableString_tcref = v_FormattableString_tcref + member __.system_FormattableStringFactory_tcref = v_FormattableStringFactory_tcref member __.system_FormattableString_ty = v_FormattableString_ty + member __.system_FormattableStringFactory_ty = v_FormattableStringFactory_ty member __.unit_ty = v_unit_ty member __.obj_ty = v_obj_ty member __.char_ty = v_char_ty @@ -1374,7 +1377,6 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val new_format_vref = ValRefForIntrinsic v_new_format_info member val sprintf_vref = ValRefForIntrinsic v_sprintf_info member val isprintf_vref = ValRefForIntrinsic v_isprintf_info - member val ifsprintf_vref = ValRefForIntrinsic v_ifsprintf_info member val unbox_vref = ValRefForIntrinsic v_unbox_info member val unbox_fast_vref = ValRefForIntrinsic v_unbox_fast_info member val istype_vref = ValRefForIntrinsic v_istype_info @@ -1402,7 +1404,6 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.seq_empty_info = v_seq_empty_info member __.sprintf_info = v_sprintf_info member __.isprintf_info = v_isprintf_info - member __.ifsprintf_info = v_ifsprintf_info member __.new_format_info = v_new_format_info member __.unbox_info = v_unbox_info member __.get_generic_comparer_info = v_get_generic_comparer_info diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index ecbcbd50bf5..16f63bdc24b 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -5903,39 +5903,9 @@ and TcExprUndelayed cenv overallTy env tpenv (synExpr: SynExpr) = | SynExpr.InterpolatedString (parts, m) -> tryLanguageFeatureError cenv.g.langVersion LanguageFeature.StringInterpolation m - // CHeck the library support is available in the referenced FSharp.Core - if cenv.g.isprintf_vref.TryDeref.IsNone then - languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m - CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) - let fillExprs = - parts - |> List.choose (function - | Choice1Of2 _ -> None - | Choice2Of2 (fillExpr, _) -> - match fillExpr with - // Detect "x" part of "...{x,3}..." - | SynExpr.Tuple (false, [e; SynExpr.Const (SynConst.Int32 _align, _)], _, _) -> Some e - | e -> Some e) - - let stringText = - parts - |> List.map (function - | Choice1Of2 s -> s - | Choice2Of2 (fillExpr, format) -> - let alignText = - match fillExpr with - // Validate and detect ",3" part of "...{x,3}..." - | SynExpr.Tuple (false, args, _, _) -> - match args with - | [_; SynExpr.Const (SynConst.Int32 align, _)] -> string align - | _ -> errorR(Error(FSComp.SR.tcInvalidAlignmentInInterpolatedString(), m)); "" - | _ -> "" - let formatText = match format with None -> "()" | Some n -> "(" + n.idText + ")" - "%" + alignText + "P" + formatText ) - |> String.concat "" - TcInterpolatedStringExpr cenv overallTy env m tpenv fillExprs stringText + TcInterpolatedStringExpr cenv overallTy env m tpenv parts | SynExpr.Const (synConst, m) -> CallExprHasTypeSink cenv.tcSink (m, env.NameEnv, overallTy, env.AccessRights) @@ -7139,7 +7109,7 @@ and TcFormatStringExpr cenv overallTy env m tpenv (fmtString: string) = let formatStringCheckContext = match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.FormatStringCheckContext let normalizedString = (fmtString.Replace("\r\n", "\n").Replace("\r", "\n")) - let (_argtys, atyRequired, etyRequired), specifierLocations = + let (_argtys, atyRequired, etyRequired), specifierLocations, _dotnetFormatString = try CheckFormatStrings.ParseFormatString m g false false formatStringCheckContext normalizedString bty cty dty with Failure errString -> error (Error(FSComp.SR.tcUnableToParseFormatString errString, m)) @@ -7159,62 +7129,101 @@ and TcFormatStringExpr cenv overallTy env m tpenv (fmtString: string) = mkString g m fmtString, tpenv /// Check an interpolated string expression -and TcInterpolatedStringExpr cenv overallTy env m tpenv (synFillExprs: SynExpr list) (fmtString: string) = +and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice list) = let g = cenv.g - let ok, isFormattableString = + let synFillExprs = + parts + |> List.choose (function + | Choice1Of2 _ -> None + | Choice2Of2 (fillExpr, _) -> + match fillExpr with + // Detect "x" part of "...{x,3}..." + | SynExpr.Tuple (false, [e; SynExpr.Const (SynConst.Int32 _align, _)], _, _) -> Some e + | e -> Some e) + + let formattableStringInfo = // If this is an interpolated string then the result must be a string - if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.string_ty) then - true, false + if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy g.string_ty) then + None else // ... or if that fails then may be a FormattableString by a type-directed rule.... if (not (isObjTy g overallTy) && - cenv.g.system_FormattableString_tcref.CanDeref && - cenv.g.ifsprintf_vref.TryDeref.IsSome && - AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy cenv.g.system_FormattableString_ty) then - true, true + g.system_FormattableString_tcref.CanDeref && + AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy g.system_FormattableString_ty) then + + let ad = env.eAccessRights + let createMethod = + match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env m ad "Create" g.system_FormattableStringFactory_ty with + | [x] -> Some x + | _ -> None + + createMethod + else + // this should fail and produce an error UnifyTypes cenv env m overallTy g.string_ty - true, false + None + let isFormattableString = formattableStringInfo.IsSome let aty = NewInferenceType () let bty = g.unit_ty let cty = g.string_ty let dty = (if isFormattableString then g.system_FormattableString_ty else g.string_ty) let ety = NewInferenceType () - if ok then - // Parse the format string to work out the phantom types - let formatStringCheckContext = None //match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.FormatStringCheckContext - //let normalizedString = (fmtString.Replace("\r\n", "\n").Replace("\r", "\n")) - - let (argtys, atyRequired, etyRequired), specifierLocations = - try CheckFormatStrings.ParseFormatString m g true isFormattableString formatStringCheckContext fmtString bty cty dty - with Failure errString -> error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) + // The format string used for checking in CheckFormatStrings. This replaces interpolation holes with %P + let printfFormatString = + parts + |> List.map (function + | Choice1Of2 s -> s + | Choice2Of2 (fillExpr, format) -> + let alignText = + match fillExpr with + // Validate and detect ",3" part of "...{x,3}..." + | SynExpr.Tuple (false, args, _, _) -> + match args with + | [_; SynExpr.Const (SynConst.Int32 align, _)] -> string align + | _ -> errorR(Error(FSComp.SR.tcInvalidAlignmentInInterpolatedString(), m)); "" + | _ -> "" + let formatText = match format with None -> "()" | Some n -> "(" + n.idText + ")" + "%" + alignText + "P" + formatText ) + |> String.concat "" + + // Parse the format string to work out the phantom types and check for absence of '%' specifiers in FormattableString + let (argtys, atyRequired, etyRequired), _specifierLocations, dotnetFormatString = + try CheckFormatStrings.ParseFormatString m g true isFormattableString None printfFormatString bty cty dty + with Failure errString -> error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) + + match formattableStringInfo with + | Some createMethInfo -> + + // FormattableString are *always* turned into FormattableStringFactory.Create calls, boxing each argument + let flexes = argtys |> List.map (fun _ -> false) + let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs + let fillExprsBoxed = (argtys, fillExprs) ||> List.map2 (mkCallBox g m) + let dotnetFormatStringExpr = mkString g m dotnetFormatString + let argsExpr = mkArray (g.obj_ty, fillExprsBoxed, m) + let createExpr, _ = BuildPossiblyConditionalMethodCall cenv env NeverMutates m false createMethInfo NormalValUse [] [dotnetFormatStringExpr; argsExpr] [] + createExpr, tpenv - match cenv.tcSink.CurrentSink with - | None -> () - | Some sink -> - for specifierLocation, numArgs in specifierLocations do - sink.NotifyFormatSpecifierLocation(specifierLocation, numArgs) + | None -> UnifyTypes cenv env m aty atyRequired UnifyTypes cenv env m ety etyRequired - let fmtExpr = mkCallNewFormat g m aty bty cty dty ety (mkString g m fmtString) + let fmtExpr = mkCallNewFormat g m aty bty cty dty ety (mkString g m printfFormatString) // Check the expressions filling the holes if argtys.Length <> synFillExprs.Length then error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) let flexes = argtys |> List.map (fun _ -> false) let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs - // Make the call to isprintf - if isFormattableString then - mkCall_ifsprintf g m aty fmtExpr fillExprs, tpenv - else - mkCall_isprintf g m aty fmtExpr fillExprs, tpenv - else - UnifyTypes cenv env m overallTy g.string_ty - mkString g m fmtString, tpenv + // Check the library support is available in the referenced FSharp.Core + if cenv.g.isprintf_vref.TryDeref.IsNone then + languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m + + // Make the call to isprintf + mkCall_isprintf g m aty fmtExpr fillExprs, tpenv //------------------------------------------------------------------------- // TcConstExpr diff --git a/src/fsharp/TypedTreeOps.fs b/src/fsharp/TypedTreeOps.fs index 07f19b61b62..978f151ae6c 100644 --- a/src/fsharp/TypedTreeOps.fs +++ b/src/fsharp/TypedTreeOps.fs @@ -6975,9 +6975,6 @@ let mkCallSeqEmpty g m ty1 = let mkCall_isprintf (g: TcGlobals) m aty fmt es = mkApps g (typedExprForIntrinsic g m g.isprintf_info, [[aty]], fmt::es , m) -let mkCall_ifsprintf (g: TcGlobals) m aty fmt es = - mkApps g (typedExprForIntrinsic g m g.ifsprintf_info, [[aty]], fmt::es , m) - let mkCallDeserializeQuotationFSharp20Plus g m e1 e2 e3 e4 = let args = [ e1; e2; e3; e4 ] mkApps g (typedExprForIntrinsic g m g.deserialize_quoted_FSharp_20_plus_info, [], [ mkRefTupledNoTypes g m args ], m) diff --git a/src/fsharp/TypedTreeOps.fsi b/src/fsharp/TypedTreeOps.fsi index 042365fb0e0..dba712efe56 100755 --- a/src/fsharp/TypedTreeOps.fsi +++ b/src/fsharp/TypedTreeOps.fsi @@ -1972,9 +1972,6 @@ val mkCallSeqEmpty : TcGlobals -> range -> TType -> Expr /// Make a call to the 'isprintf' function for string interpolation val mkCall_isprintf: g: TcGlobals -> m: range -> funcTy: TType -> fmtExpr: Expr -> fillExprs: Expr list -> Expr -/// Make a call to the 'isprintf' function for string interpolation for FormattableString -val mkCall_ifsprintf: g: TcGlobals -> m: range -> funcTy: TType -> fmtExpr: Expr -> fillExprs: Expr list -> Expr - val mkILAsmCeq : TcGlobals -> range -> Expr -> Expr -> Expr val mkILAsmClt : TcGlobals -> range -> Expr -> Expr -> Expr diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index 362307ba44c..2815d8594cc 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -2247,7 +2247,6 @@ Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThenFail[T,TResult](Mic Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[System.String,TResult], Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) -Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToFormattableStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.FormattableString]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriterThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,TResult], System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index 9fd7f737e14..cb77401ddfc 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -2247,7 +2247,6 @@ Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThenFail[T,TResult](Mic Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[System.String,TResult], Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) -Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToFormattableStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.FormattableString]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriterThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,TResult], System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index d6074401a6e..0abd5cc2a5f 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -277,11 +277,18 @@ let fmt_de (x: FormattableString) = x.ToString(CultureInfo("de-DE")) check "fwejwflpej1" (fmt $"") "" check "fwejwflpej2" (fmt $"abc") "abc" check "fwejwflpej3" (fmt $"abc{1}") "abc1" -check "fwejwflpej4" (fmt $"abc %d{box 1} def") "abc 1 def" -check "fwejwflpej6" (fmt_us $"abc {box 30000} def") "abc 30000 def" -check "fwejwflpej7" (fmt_de $"abc {box 30000} def") "abc 30000 def" -check "fwejwflpej8" (fmt_us $"abc {box 30000:N} def") "abc 30,000.00 def" -check "fwejwflpej9" (fmt_de $"abc {box 30000:N} def") "abc 30.000,00 def" +check "fwejwflpej6" (fmt_us $"abc {30000} def") "abc 30000 def" +check "fwejwflpej7" (fmt_de $"abc {30000} def") "abc 30000 def" +check "fwejwflpej8" (fmt_us $"abc {30000:N} def") "abc 30,000.00 def" +check "fwejwflpej9" (fmt_de $"abc {30000:N} def") "abc 30.000,00 def" +check "fwejwflpej10" (fmt_us $"abc {30000} def {40000} hij") "abc 30000 def 40000 hij" +check "fwejwflpej11" (fmt_us $"abc {30000,-10} def {40000} hij") "abc 30000 def 40000 hij" +check "fwejwflpej12" (fmt_us $"abc {30000,10} def {40000} hij") "abc 30000 def 40000 hij" +check "fwejwflpej13" (fmt_de $"abc {30000} def {40000} hij") "abc 30000 def 40000 hij" +check "fwejwflpej14" (fmt_us $"abc {30000:N} def {40000:N} hij") "abc 30,000.00 def 40,000.00 hij" +check "fwejwflpej15" (fmt_de $"abc {30000:N} def {40000:N} hij") "abc 30.000,00 def 40.000,00 hij" +check "fwejwflpej16" (fmt_de $"abc {30000,10:N} def {40000:N} hij") "abc 30.000,00 def 40.000,00 hij" +check "fwejwflpej17" (fmt_de $"abc {30000,-10:N} def {40000:N} hij") "abc 30.000,00 def 40.000,00 hij" """ @@ -527,17 +534,35 @@ let x9 = $"one %d{3:N}" // mix of formats but here has type 'int' """); (FSharpErrorSeverity.Error, 3354, (4, 10, 4, 19), - "Unable to parse interpolated string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'"); + "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); (FSharpErrorSeverity.Error, 3354, (5, 10, 5, 19), - "Unable to parse interpolated string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'"); + "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); (FSharpErrorSeverity.Error, 3354, (6, 10, 6, 19), - "Unable to parse interpolated string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'"); + "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); (FSharpErrorSeverity.Error, 3354, (7, 10, 7, 19), - "Unable to parse interpolated string 'Invalid interpolated string. The '%P' specifier may not be used explicitly.'"); + "Invalid interpolated string. The '%P' specifier may not be used explicitly."); (FSharpErrorSeverity.Error, 3361, (8, 10, 8, 21), - "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); + "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}"); (FSharpErrorSeverity.Error, 3354, (9, 10, 9, 19), - "Unable to parse interpolated string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'"); + "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); (FSharpErrorSeverity.Error, 3354, (10, 10, 10, 24), - "Unable to parse interpolated string 'Invalid interpolated string. .NET-style format specifiers such as '{x,3}' or '{x:N5}' may not be mixed with '%' format specifiers.'")|] + "Invalid interpolated string. .NET-style format specifiers such as '{x,3}' or '{x:N5}' may not be mixed with '%' format specifiers.")|] + + [] + let ``String interpolation FormattableString negative`` () = + let s = """ + +open System +let x1 : FormattableString = $"one %d{100}" // no %d in FormattableString +let x2 : FormattableString = $"one %s{String.Empty}" // no %s in FormattableString +let x3 : FormattableString = $"one %10s{String.Empty}" // no %10s in FormattableString +""" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + s + [|(FSharpErrorSeverity.Error, 3354, (4, 30, 4, 44), + "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); + (FSharpErrorSeverity.Error, 3354, (5, 30, 5, 53), + "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); + (FSharpErrorSeverity.Error, 3354, (6, 30, 6, 55), + "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used.")|] \ No newline at end of file From 279c05de539f215f360ef44b9dda472dcdf9a0ba Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Apr 2020 14:56:56 +0100 Subject: [PATCH 23/87] fix test --- tests/fsharp/Compiler/Language/StringInterpolation.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 0abd5cc2a5f..f1c7a4cbcb5 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -542,7 +542,7 @@ but here has type (FSharpErrorSeverity.Error, 3354, (7, 10, 7, 19), "Invalid interpolated string. The '%P' specifier may not be used explicitly."); (FSharpErrorSeverity.Error, 3361, (8, 10, 8, 21), - "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}"); + "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); (FSharpErrorSeverity.Error, 3354, (9, 10, 9, 19), "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); (FSharpErrorSeverity.Error, 3354, (10, 10, 10, 24), From 500ecf70f0fba1f83ff907ff0e8aec4d99f65316 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Apr 2020 15:32:28 +0100 Subject: [PATCH 24/87] add testing for nested --- src/fsharp/FSComp.txt | 2 + src/fsharp/lex.fsl | 46 ++++++++- .../Compiler/Language/StringInterpolation.fs | 97 ++++++++++++++----- 3 files changed, 120 insertions(+), 25 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 65f503abc89..4bb0f12d580 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1522,3 +1522,5 @@ forFormatInvalidForInterpolated2,".NET-style format specifiers such as '{{x,3}}' forFormatInvalidForInterpolated3,"The '%%P' specifier may not be used explicitly." forFormatInvalidForInterpolated4,"Interpolated strings used as type FormattableString may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." 3362,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" +3363,lexSingleQuoteInSingleQuote,"Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal." +3364,lexTripleQuoteInTripleQuote,"Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression." diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index b405d167801..b21883f1942 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -543,26 +543,66 @@ rule token args skip = parse | '"' { let buf, fin, m = startString args lexbuf + + // Single quote in triple quote ok, others disallowed + match args.interpolatedStringNesting with + | (_, LexerStringStyle.TripleQuote) :: _ -> () + | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) + | [] -> () + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, LexerStringKind.String, m))) else singleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } | '$' '"' '"' '"' { let buf, fin, m = startString args lexbuf + + // Single quote in triple quote ok, others disallowed + match args.interpolatedStringNesting with + | _ :: _ -> errorR(Error(FSComp.SR.lexTripleQuoteInTripleQuote(), lexbuf.LexemeRange)) + | [] -> () + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, LexerStringKind.InterpolatedStringFirst, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | '$' '"' { let buf,fin,m = startString args lexbuf + + // Single quote in triple quote ok, others disallowed + match args.interpolatedStringNesting with + | (_, LexerStringStyle.TripleQuote) :: _ -> () + | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) + | _ -> () + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, LexerStringKind.InterpolatedStringFirst, m))) else singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | '"' '"' '"' { let buf, fin, m = startString args lexbuf + + // Single quote in triple quote ok, others disallowed + match args.interpolatedStringNesting with + | _ :: _ -> errorR(Error(FSComp.SR.lexTripleQuoteInTripleQuote(), lexbuf.LexemeRange)) + | _ -> () + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, LexerStringKind.String, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } | '@' '"' { let buf, fin, m = startString args lexbuf + + // Single quote in triple quote ok, others disallowed + match args.interpolatedStringNesting with + | (_, LexerStringStyle.TripleQuote) :: _ -> () + | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) + | _ -> () + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, LexerStringKind.String, m))) else verbatimString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } | ("$@" | "@$") '"' { let buf, fin, m = startString args lexbuf + + // Single quote in triple quote ok, others disallowed + match args.interpolatedStringNesting with + | (_, LexerStringStyle.TripleQuote) :: _ -> () + | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) + | _ -> () + if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, LexerStringKind.InterpolatedStringFirst, m))) else verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | truewhite+ @@ -570,7 +610,7 @@ rule token args skip = parse else WHITESPACE (LexCont.Token args.ifdefStack) } | offwhite+ - { if args.lightSyntaxStatus.Status then errorR(Error(FSComp.SR.lexTabsNotAllowed(),lexbuf.LexemeRange)) + { if args.lightSyntaxStatus.Status then errorR(Error(FSComp.SR.lexTabsNotAllowed(), lexbuf.LexemeRange)) if not skip then (WHITESPACE (LexCont.Token args.ifdefStack)) else token args skip lexbuf } | "////" op_char* @@ -616,7 +656,7 @@ rule token args skip = parse | _ -> let text = (String.sub s start (n-start)) let lineNumber = try int32 text - with err -> errorR(Error(FSComp.SR.lexInvalidLineNumber(text),lexbuf.LexemeRange)); 0 + with err -> errorR(Error(FSComp.SR.lexInvalidLineNumber(text), lexbuf.LexemeRange)); 0 lineNumber, parseWhitespaceBeforeFile n // goto the next state and parseWhitespaceBeforeFile n = @@ -803,7 +843,7 @@ rule token args skip = parse | "#light" anywhite* | ("#indent" | "#light") anywhite+ "\"on\"" { if args.lightSyntaxStatus.ExplicitlySet && args.lightSyntaxStatus.WarnOnMultipleTokens then - warning(Error((0,"#light should only occur as the first non-comment text in an F# source file"),lexbuf.LexemeRange)) + warning(Error((0,"#light should only occur as the first non-comment text in an F# source file"), lexbuf.LexemeRange)) // TODO unreachable error above, I think? - brianmcn args.lightSyntaxStatus.Status <- true if not skip then (HASH_LIGHT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index f1c7a4cbcb5..98dc77e13be 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -28,8 +28,6 @@ check "vcewweh5" $"this is {1}" "this is 1" check "vcewweh6" $"123{456}789{012}345" "12345678912345" -check "vcewweh7" $"123{456}789{"012"}345" "123456789012345" - check "vcewweh8" $"{1} {2}" "1 2" @@ -187,27 +185,21 @@ check "vcewweh17" $"abc{while false do ()}def" "abcdef" [] let ``String interpolation using nested string`` () = CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] - """ + " let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + if a = b then printfn \"%s succeeded\" msg else failwithf \"%s failed, expected %A, got %A\" msg b a // check nested string -check "vcewweh22m1" $"x = {"1"} " "x = 1 " - -check "vcewweh22m2" $"x = {$""} " "x = " +check \"vcewweh22m1\" $\"\"\"x = {\"1\"} \"\"\" \"x = 1 \" -check "vcewweh22m3" $"x = {$"1 {"2" + $"{3}" + "2"} 1"} " "x = 1 232 1 " - -check "vcewweh22m4" $"x = {$"1 {"2" + $"{3}" + "2"} 11 {2} 1"} " "x = 1 232 11 2 1 " - -check "vcewweh22m5" $"x = {$"1 {"2" + $"{3}" + "2"} 11 { $"{{%A{seq{yield 2; yield 2;} }}}" } 1"} " "x = 1 232 11 {seq [2; 2]} 1 " +check \"vcewweh22m2\" $\"\"\"x = {$\"\"} \"\"\" \"x = \" do let genreSpecified = true - let getGenre() = "comedy" - check "vcewweh22m6" $"/api/movie/{if not genreSpecified then "" else $"q?genre={getGenre()}"}" "/api/movie/q?genre=comedy" + let getGenre() = \"comedy\" + check \"vcewweh22m6\" $\"\"\"/api/movie/{if not genreSpecified then \"\" else $\"q?genre={getGenre()}\"}\"\"\" \"/api/movie/q?genre=comedy\" -""" +" [] let ``Triple quote string interpolation using nested string`` () = @@ -450,9 +442,6 @@ check "vcewweh22f" $"x = %s{s}" "x = sixsix" // check %A of string check "vcewweh22g" $"x = %A{s}" "x = \"sixsix\"" -// check nested string with %s -check "vcewweh22l" $"x = %s{"1"}" "x = 1" - check "vcewweh20" $"x = %A{1}" "x = 1" """ @@ -513,7 +502,7 @@ let x = $"one" [] let ``Basic string interpolation negative`` () = - let s = """ + let code = """ let x1 = $"one %d{System.String.Empty}" // mismatched types let x2 = $"one %s{1}" // mismatched types let x3 = $"one %s" // naked percent in interpolated @@ -525,7 +514,7 @@ let x8 = $"one %f" // naked percent in interpolated let x9 = $"one %d{3:N}" // mix of formats """ CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] - s + code [|(FSharpErrorSeverity.Error, 1, (2, 19, 2, 38), "The type 'string' is not compatible with any of the types byte,int16,int32,int64,sbyte,uint16,uint32,uint64,nativeint,unativeint, arising from the use of a printf-style format string"); (FSharpErrorSeverity.Error, 1, (3, 19, 3, 20), @@ -550,7 +539,7 @@ but here has type [] let ``String interpolation FormattableString negative`` () = - let s = """ + let code = """ open System let x1 : FormattableString = $"one %d{100}" // no %d in FormattableString @@ -558,11 +547,75 @@ let x2 : FormattableString = $"one %s{String.Empty}" // no %s in FormattableStri let x3 : FormattableString = $"one %10s{String.Empty}" // no %10s in FormattableString """ CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] - s + code [|(FSharpErrorSeverity.Error, 3354, (4, 30, 4, 44), "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); (FSharpErrorSeverity.Error, 3354, (5, 30, 5, 53), "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); (FSharpErrorSeverity.Error, 3354, (6, 30, 6, 55), "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used.")|] + + + [] + let ``String interpolation negative nested in single`` () = + let code = """ + +open System +let s1 = $"123{456}789{"012"}345" +let s2 = $"123{456}789{@"012"}345" +let s3 = $"123{456}789{$"012"}345" +let s4 = $@"123{456}789{"012"}345" +let s5 = @$"123{456}789{"012"}345" +let s6 = $@"123{456}789{@"012"}345" +let s7 = @$"123{456}789{$"012"}345" +let s8 = $@"123{456}789{@$"012"}345" +let s9 = @$"123{456}789{$@"012"}345" +""" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 3363, (4, 24, 4, 25), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); + (FSharpErrorSeverity.Error, 3363, (5, 24, 5, 26), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); + (FSharpErrorSeverity.Error, 3363, (6, 24, 6, 26), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); + (FSharpErrorSeverity.Error, 3363, (7, 25, 7, 26), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); + (FSharpErrorSeverity.Error, 3363, (8, 25, 8, 26), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); + (FSharpErrorSeverity.Error, 3363, (9, 25, 9, 27), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); + (FSharpErrorSeverity.Error, 3363, (10, 25, 10, 27), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); + (FSharpErrorSeverity.Error, 3363, (11, 25, 11, 28), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); + (FSharpErrorSeverity.Error, 3363, (12, 25, 12, 28), + "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal.")|] + + [] + let ``String interpolation negative nested in triple`` () = + let code = " + +open System +let TripleInTripleInterpolated = $\"\"\"123{456}789{\"\"\"012\"\"\"}345\"\"\" +let TripleInSingleInterpolated = $\"123{456}789{\"\"\"012\"\"\"}345\" +let TripleInVerbatimInterpolated = $\"123{456}789{\"\"\"012\"\"\"}345\" +let TripleInterpolatedInTripleInterpolated = $\"\"\"123{456}789{$\"\"\"012\"\"\"}345\"\"\" +let TripleInterpolatedInSingleInterpolated = $\"123{456}789{$\"\"\"012\"\"\"}345\" +let TripleInterpolatedInVerbatimInterpolated = $\"123{456}789{$\"\"\"012\"\"\"}345\" +" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 3364, (4, 52, 4, 55), + "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); + (FSharpErrorSeverity.Error, 3364, (5, 50, 5, 53), + "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); + (FSharpErrorSeverity.Error, 3364, (6, 50, 6, 53), + "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); + (FSharpErrorSeverity.Error, 3364, (7, 64, 7, 68), + "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); + (FSharpErrorSeverity.Error, 3364, (8, 62, 8, 66), + "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); + (FSharpErrorSeverity.Error, 3364, (9, 62, 9, 66), + "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression.")|] \ No newline at end of file From 9bf95ab496de0f743b3de721f585316f386c1b5b Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Apr 2020 16:03:31 +0100 Subject: [PATCH 25/87] add IFormattable support --- src/fsharp/TcGlobals.fs | 4 +++ src/fsharp/TypeChecker.fs | 13 +++++--- .../Compiler/Language/StringInterpolation.fs | 31 +++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index 4fa40c917ec..b47b8c17611 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -334,7 +334,9 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_bool_ty = mkNonGenericTy v_bool_tcr let v_char_ty = mkNonGenericTy v_char_tcr let v_obj_ty = mkNonGenericTy v_obj_tcr + let v_IFormattable_tcref = findSysTyconRef sys "IFormattable" let v_FormattableString_tcref = findSysTyconRef sys "FormattableString" + let v_IFormattable_ty = mkNonGenericTy v_IFormattable_tcref let v_FormattableString_ty = mkNonGenericTy v_FormattableString_tcref let v_FormattableStringFactory_tcref = findSysTyconRef sysCompilerServices "FormattableStringFactory" let v_FormattableStringFactory_ty = mkNonGenericTy v_FormattableStringFactory_tcref @@ -1017,8 +1019,10 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.bool_ty = v_bool_ty member __.int_ty = v_int_ty member __.string_ty = v_string_ty + member __.system_IFormattable_tcref = v_IFormattable_tcref member __.system_FormattableString_tcref = v_FormattableString_tcref member __.system_FormattableStringFactory_tcref = v_FormattableStringFactory_tcref + member __.system_IFormattable_ty = v_IFormattable_ty member __.system_FormattableString_ty = v_FormattableString_ty member __.system_FormattableStringFactory_ty = v_FormattableStringFactory_ty member __.unit_ty = v_unit_ty diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 16f63bdc24b..62d971551c8 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7149,8 +7149,8 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 98dc77e13be..e088bd3ebb1 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -266,6 +266,37 @@ let fmt (x: FormattableString) = x.ToString() let fmt_us (x: FormattableString) = x.ToString(CultureInfo("en-US")) let fmt_de (x: FormattableString) = x.ToString(CultureInfo("de-DE")) +check "fwejwflpej1" (fmt $"") "" +check "fwejwflpej2" (fmt $"abc") "abc" +check "fwejwflpej3" (fmt $"abc{1}") "abc1" +check "fwejwflpej6" (fmt_us $"abc {30000} def") "abc 30000 def" +check "fwejwflpej7" (fmt_de $"abc {30000} def") "abc 30000 def" +check "fwejwflpej8" (fmt_us $"abc {30000:N} def") "abc 30,000.00 def" +check "fwejwflpej9" (fmt_de $"abc {30000:N} def") "abc 30.000,00 def" +check "fwejwflpej10" (fmt_us $"abc {30000} def {40000} hij") "abc 30000 def 40000 hij" +check "fwejwflpej11" (fmt_us $"abc {30000,-10} def {40000} hij") "abc 30000 def 40000 hij" +check "fwejwflpej12" (fmt_us $"abc {30000,10} def {40000} hij") "abc 30000 def 40000 hij" +check "fwejwflpej13" (fmt_de $"abc {30000} def {40000} hij") "abc 30000 def 40000 hij" +check "fwejwflpej14" (fmt_us $"abc {30000:N} def {40000:N} hij") "abc 30,000.00 def 40,000.00 hij" +check "fwejwflpej15" (fmt_de $"abc {30000:N} def {40000:N} hij") "abc 30.000,00 def 40.000,00 hij" +check "fwejwflpej16" (fmt_de $"abc {30000,10:N} def {40000:N} hij") "abc 30.000,00 def 40.000,00 hij" +check "fwejwflpej17" (fmt_de $"abc {30000,-10:N} def {40000:N} hij") "abc 30.000,00 def 40.000,00 hij" + + """ + + [] + let ``String interpolation to IFormattable`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +open System +open System.Globalization +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +let fmt (x: IFormattable) = x.ToString() +let fmt_us (x: IFormattable) = x.ToString("", CultureInfo("en-US")) +let fmt_de (x: IFormattable) = x.ToString("", CultureInfo("de-DE")) + check "fwejwflpej1" (fmt $"") "" check "fwejwflpej2" (fmt $"abc") "abc" check "fwejwflpej3" (fmt $"abc{1}") "abc1" From 47b61ec7c420dc95a43984b613fce73f75341bbb Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Apr 2020 16:05:22 +0100 Subject: [PATCH 26/87] tweak error message --- src/fsharp/FSComp.txt | 2 +- tests/fsharp/Compiler/Language/StringInterpolation.fs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 4bb0f12d580..5aa048ee5bf 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1520,7 +1520,7 @@ featureStringInterpolation,"string interpolation" forFormatInvalidForInterpolated,"Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'." forFormatInvalidForInterpolated2,".NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%%' format specifiers." forFormatInvalidForInterpolated3,"The '%%P' specifier may not be used explicitly." -forFormatInvalidForInterpolated4,"Interpolated strings used as type FormattableString may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." +forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable or FormattableString may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." 3362,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" 3363,lexSingleQuoteInSingleQuote,"Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal." 3364,lexTripleQuoteInTripleQuote,"Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression." diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index e088bd3ebb1..7eab99ac1c6 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -580,11 +580,11 @@ let x3 : FormattableString = $"one %10s{String.Empty}" // no %10s in Formattable CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] code [|(FSharpErrorSeverity.Error, 3354, (4, 30, 4, 44), - "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); + "Invalid interpolated string. Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); (FSharpErrorSeverity.Error, 3354, (5, 30, 5, 53), - "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); + "Invalid interpolated string. Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); (FSharpErrorSeverity.Error, 3354, (6, 30, 6, 55), - "Invalid interpolated string. Interpolated strings used as type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used.")|] + "Invalid interpolated string. Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used.")|] [] From 455b568022eeba8cc36bdec9ac25addb2cb8e088 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Fri, 24 Apr 2020 18:56:42 +0800 Subject: [PATCH 27/87] tests: StringInterpolation: fix case errors --- tests/fsharp/Compiler/Language/StringInterpolation.fs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index a2142641680..9f2317a8f3b 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -574,10 +574,10 @@ but here has type "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); (FSharpErrorSeverity.Error, 3354, (7, 10, 7, 19), "Invalid interpolated string. The '%P' specifier may not be used explicitly."); - (fsharperrorseverity.error, 3361, (8, 10, 8, 21), - "mismatch in interpolated string. interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); - (fsharperrorseverity.error, 3361, (9, 10, 9, 31), - "mismatch in interpolated string. interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); + (FSharpErrorSeverity.Error, 3361, (8, 10, 8, 21), + "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); + (FSharpErrorSeverity.Error, 3361, (9, 10, 9, 31), + "Invalid interpolated string. The '%P' specifier may not be used explicitly."); (FSharpErrorSeverity.Error, 3354, (10, 10, 10, 19), "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); (FSharpErrorSeverity.Error, 3354, (11, 10, 11, 24), From 7b79c987b52599502ba2e51831b0338987e01ace Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Apr 2020 13:12:40 +0100 Subject: [PATCH 28/87] fix error message --- src/fsharp/FSComp.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 5aa048ee5bf..8db4c2d7055 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1520,7 +1520,7 @@ featureStringInterpolation,"string interpolation" forFormatInvalidForInterpolated,"Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'." forFormatInvalidForInterpolated2,".NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%%' format specifiers." forFormatInvalidForInterpolated3,"The '%%P' specifier may not be used explicitly." -forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable or FormattableString may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." +forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable or type FormattableString may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." 3362,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" 3363,lexSingleQuoteInSingleQuote,"Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal." 3364,lexTripleQuoteInTripleQuote,"Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression." From 39070daaf43a4886f579eb709a5fc05bbd0a316a Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Apr 2020 13:20:10 +0100 Subject: [PATCH 29/87] check number of values matches --- src/fsharp/TypeChecker.fs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 62d971551c8..6749da5227a 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7195,16 +7195,25 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) + // Check the expressions filling the holes + if argtys.Length <> synFillExprs.Length then + error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) + match formattableStringInfo with | Some createMethInfo -> - // FormattableString are *always* turned into FormattableStringFactory.Create calls, boxing each argument + // Type check the expressions filling the holes let flexes = argtys |> List.map (fun _ -> false) let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs + let fillExprsBoxed = (argtys, fillExprs) ||> List.map2 (mkCallBox g m) + let dotnetFormatStringExpr = mkString g m dotnetFormatString let argsExpr = mkArray (g.obj_ty, fillExprsBoxed, m) + + // FormattableString are *always* turned into FormattableStringFactory.Create calls, boxing each argument let createExpr, _ = BuildPossiblyConditionalMethodCall cenv env NeverMutates m false createMethInfo NormalValUse [] [dotnetFormatStringExpr; argsExpr] [] + let resultExpr = if typeEquiv g overallTy g.system_IFormattable_ty then mkCoerceIfNeeded g g.system_IFormattable_ty g.system_FormattableString_ty createExpr @@ -7216,10 +7225,10 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice synFillExprs.Length then - error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) + + // Type check the expressions filling the holes let flexes = argtys |> List.map (fun _ -> false) let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs From 373ccf371c1c0b7f8dd4cb41eecbddccbb6e2b94 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 1 May 2020 17:33:43 +0100 Subject: [PATCH 30/87] allow use of format strings with printf and friends --- src/fsharp/FSharp.Core/printf.fs | 1721 +++++++---------- src/fsharp/FSharp.Core/printf.fsi | 22 +- src/fsharp/MethodCalls.fs | 3 +- src/fsharp/TcGlobals.fs | 14 +- src/fsharp/TypeChecker.fs | 124 +- src/fsharp/TypedTreeOps.fs | 10 +- src/fsharp/TypedTreeOps.fsi | 6 +- .../fsharp/core/printf-interpolated/test.fsx | 268 +++ tests/fsharp/core/printf/test.fsx | 1481 +++++++------- tests/fsharp/tests.fs | 3 + 10 files changed, 1781 insertions(+), 1871 deletions(-) create mode 100644 tests/fsharp/core/printf-interpolated/test.fsx diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index c7cbdaba9a0..ab0c4e39e8e 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -2,16 +2,25 @@ namespace Microsoft.FSharp.Core -type PrintfFormat<'Printer,'State,'Residue,'Result>(value:string) = - member x.Value = value +type PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value:string, captures: obj[]) = + + new (value) = new PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value, null) + + member x.Value = value + + member x.Captures = captures - override __.ToString() = value + override __.ToString() = value -type PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple>(value:string) = - inherit PrintfFormat<'Printer,'State,'Residue,'Result>(value) +type PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple>(value:string, captures) = + + inherit PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value, captures) + + new (value) = new PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple>(value, null) -type Format<'Printer,'State,'Residue,'Result> = PrintfFormat<'Printer,'State,'Residue,'Result> -type Format<'Printer,'State,'Residue,'Result,'Tuple> = PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> +type Format<'Printer, 'State, 'Residue, 'Result> = PrintfFormat<'Printer, 'State, 'Residue, 'Result> + +type Format<'Printer, 'State, 'Residue, 'Result, 'Tuple> = PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple> module internal PrintfImpl = @@ -37,11 +46,9 @@ module internal PrintfImpl = /// 2. we can make combinable parts independent from particular printf implementation. Thus final result can be cached and shared. /// i.e when first call to printf "%s %s" will trigger creation of the specialization. Subsequent calls will pick existing specialization open System - open System.IO - open System.Text - open System.Collections.Generic open System.Collections.Concurrent + open System.Globalization open System.Reflection open Microsoft.FSharp.Core open Microsoft.FSharp.Core.Operators @@ -79,21 +86,53 @@ module internal PrintfImpl = Flags: FormatFlags InteropHoleDotNetFormat: string option } - member this.IsStarPrecision = this.Precision = StarValue - member this.IsPrecisionSpecified = this.Precision <> NotSpecifiedValue - member this.IsStarWidth = this.Width = StarValue - member this.IsWidthSpecified = this.Width <> NotSpecifiedValue + member spec.IsStarPrecision = (spec.Precision = StarValue) + + member spec.IsPrecisionSpecified = (spec.Precision <> NotSpecifiedValue) - override this.ToString() = + member spec.IsStarWidth = (spec.Width = StarValue) + + member spec.IsWidthSpecified = (spec.Width <> NotSpecifiedValue) + + member spec.ArgCount = + let n = + if spec.TypeChar = 'a' then 2 + elif spec.IsStarWidth || spec.IsStarPrecision then + if spec.IsStarWidth = spec.IsStarPrecision then 3 + else 2 + else 1 + + let n = if spec.TypeChar = '%' then n - 1 else n + + System.Diagnostics.Debug.Assert(n <> 0, "n <> 0") + + n + + override spec.ToString() = let valueOf n = match n with StarValue -> "*" | NotSpecifiedValue -> "-" | n -> n.ToString() System.String.Format ( "'{0}', Precision={1}, Width={2}, Flags={3}", - this.TypeChar, - (valueOf this.Precision), - (valueOf this.Width), - this.Flags + spec.TypeChar, + (valueOf spec.Precision), + (valueOf spec.Width), + spec.Flags ) + + member spec.IsDecimalFormat = + spec.TypeChar = 'M' + + member spec.GetPadAndPrefix allowZeroPadding = + let padChar = if allowZeroPadding && isPadWithZeros spec.Flags then '0' else ' '; + let prefix = + if isPlusForPositives spec.Flags then "+" + elif isSpaceForPositives spec.Flags then " " + else "" + padChar, prefix + + member spec.IsGFormat = + spec.IsDecimalFormat || System.Char.ToLower(spec.TypeChar) = 'g' + /// Set of helpers to parse format string module private FormatString = @@ -181,6 +220,48 @@ module internal PrintfImpl = go (i + 1) buf go i (Text.StringBuilder()) + [] + /// Represents one step in the execution of a format string + type Step = + | Step of prefix: string * conv1: (obj -> string) + | StepString of prefix: string + | StepLittleT of prefix: string + | StepLittleA of prefix: string + | StepStar1 of prefix: string * conv: (obj -> int -> string) + | StepPercentStar1 of prefix: string + | StepStar2 of prefix: string * conv: (obj -> int -> int -> string) + | StepPercentStar2 of prefix: string + + static member BlockCount(steps) = + let mutable count = 0 + for step in steps do + match step with + | Step (prefix, _conv1) -> + if not (String.IsNullOrEmpty prefix) then count <- count + 1 + count <- count + 1 + | StepString prefix -> + if not (String.IsNullOrEmpty prefix) then count <- count + 1 + | StepLittleT(prefix) -> + if String.IsNullOrEmpty prefix then count <- count + 1 + count <- count + 1 + | StepLittleA(prefix) -> + if String.IsNullOrEmpty prefix then count <- count + 1 + count <- count + 1 + | StepStar1(prefix, _conv) -> + if String.IsNullOrEmpty prefix then count <- count + 1 + count <- count + 1 + | StepPercentStar1(prefix) -> + if String.IsNullOrEmpty prefix then count <- count + 1 + count <- count + 1 + | StepStar2(prefix, _conv) -> + if String.IsNullOrEmpty prefix then count <- count + 1 + count <- count + 1 + | StepPercentStar2(prefix) -> + if String.IsNullOrEmpty prefix then count <- count + 1 + count <- count + 1 + count + + /// Abstracts generated printer from the details of particular environment: how to write text, how to produce results etc... [] type PrintfEnv<'State, 'Residue, 'Result>(state: 'State) = @@ -193,52 +274,76 @@ module internal PrintfImpl = /// Write the result of a '%t' format. If this is a string it is written. If it is a 'unit' value /// the side effect has already happened abstract WriteT: 'Residue -> unit + + member env.WriteSkipEmpty(s: string) = + if not (String.IsNullOrEmpty s) then + env.Write s + + member inline env.Write(argIndex: byref, args: obj[], conv: obj -> string) = + let arg = args.[argIndex] + argIndex <- argIndex + 1 + env.Write(conv arg) - type Utils = - static member inline Write (env: PrintfEnv<_, _, _>, a, b) = - env.Write a - env.Write b - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c) = - Utils.Write(env, a, b) - env.Write c - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d) = - Utils.Write(env, a, b) - Utils.Write(env, c, d) - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e) = - Utils.Write(env, a, b, c) - Utils.Write(env, d, e) - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f) = - Utils.Write(env, a, b, c, d) - Utils.Write(env, e, f) - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g) = - Utils.Write(env, a, b, c, d, e) - Utils.Write(env, f, g) - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h) = - Utils.Write(env, a, b, c, d, e, f) - Utils.Write(env, g, h) - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h, i) = - Utils.Write(env, a, b, c, d, e, f, g) - Utils.Write(env, h, i) - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h, i, j) = - Utils.Write(env, a, b, c, d, e, f, g, h) - Utils.Write(env, i, j) - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h, i, j, k) = - Utils.Write(env, a, b, c, d, e, f, g, h, i) - Utils.Write(env, j, k) - - static member inline Write (env: PrintfEnv<_, _, _>, a, b, c, d, e, f, g, h, i, j, k, l, m) = - Utils.Write(env, a, b, c, d, e, f, g, h, i, j, k) - Utils.Write(env, l, m) + member env.RunSteps (args: obj[], steps: Step[]) = + let mutable argIndex = 0 + + for step in steps do + match step with + | Step (prefix, conv1) -> + env.WriteSkipEmpty(prefix) + env.Write(&argIndex, args, conv1) + + | StepString prefix -> + env.WriteSkipEmpty(prefix) + + | StepLittleT(prefix) -> + env.WriteSkipEmpty prefix + let farg = args.[argIndex] + argIndex <- argIndex + 1 + let f = farg :?> ('State -> 'Residue) + env.WriteT(f env.State) + + | StepLittleA(prefix) -> + env.WriteSkipEmpty prefix + let farg = args.[argIndex] + argIndex <- argIndex + 1 + let arg = args.[argIndex] + argIndex <- argIndex + 1 + let f = farg :?> ('State -> obj -> 'Residue) + env.WriteT(f env.State arg) + + | StepStar1(prefix, conv) -> + env.WriteSkipEmpty prefix + let star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let arg1 = args.[argIndex] + argIndex <- argIndex + 1 + env.Write (conv arg1 star1) + + | StepPercentStar1(prefix) -> + //let _star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + env.WriteSkipEmpty prefix + env.Write("%") + + | StepStar2(prefix, conv) -> + env.WriteSkipEmpty prefix + let star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let star2 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let arg1 = args.[argIndex] + argIndex <- argIndex + 1 + env.Write (conv arg1 star1 star2) + + | StepPercentStar2(prefix) -> + env.WriteSkipEmpty prefix + //let _star1 = args.[argIndex] :?> int + argIndex <- argIndex + 2 + env.Write("%") + env.Finish() + /// Type of results produced by specialization. /// /// This is a function that accepts a thunk to create PrintfEnv on demand (at the very last @@ -246,454 +351,98 @@ module internal PrintfImpl = /// /// After all arguments are collected, specialization obtains concrete PrintfEnv from the thunk /// and uses it to output collected data. - type PrintfFactory<'Printer, 'State, 'Residue, 'Result> = (unit -> PrintfEnv<'State, 'Residue, 'Result>) -> 'Printer + type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> (obj list * PrintfEnv<'State, 'Residue, 'Result>) + type PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result> = PrintfFuncContext<'State, 'Residue, 'Result> -> 'Printer [] - let MaxArgumentsInSpecialization = 5 - - /// Specializations are created via factory methods. These methods accepts 2 kinds of arguments - /// - parts of format string that corresponds to raw text - /// - functions that can transform collected values to strings - /// basic shape of the signature of specialization - /// + + + ... + - type Specializations<'State, 'Residue, 'Result> = - - /// + + - static member Final1<'A>(s0, conv1, s1) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) -> - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1) - env.Finish() - ) - ) - - /// + - static member FinalNoSuffix1<'A>(s0, conv1) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) -> - let env = prev() - Utils.Write(env, s0, conv1 arg1) - env.Finish() - ) - ) + let MaxArgumentsInSpecialization = 3 - /// + - static member FinalNoPrefix1<'A>(conv1, s1) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) -> - let env = prev() - Utils.Write(env, conv1 arg1, s1) - env.Finish() - ) + type Specializations<'State, 'Residue, 'Result>() = + + static let finalizeSteps steps = steps |> List.rev |> List.toArray + static let finalizeArgs args = args |> List.rev |> List.toArray + + static member Final0(steps) = + let allSteps = finalizeSteps steps + (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + let (args, env) = prev() + env.RunSteps(finalizeArgs args, allSteps) ) - static member FinalNoPrefixOrSuffix1<'A>(conv1) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member CaptureFinal1<'A>(steps) = + let allSteps = finalizeSteps steps + (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> - let env = prev() - env.Write (conv1 arg1) - env.Finish() + let (args, env) = prev() + let finalArgs = box arg1 :: args + env.RunSteps(finalizeArgs finalArgs, allSteps) ) ) - static member Final2<'A, 'B>(s0, conv1, s1, conv2, s2) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) -> - let env = prev() - Utils.Write(env, s0, conv1 a, s1, conv2 b, s2) - env.Finish() - ) - ) - - static member FinalNoSuffix2<'A, 'B>(s0, conv1, s1, conv2) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) -> - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2) - env.Finish() - ) - ) - - static member FinalNoPrefix2<'A, 'B>(conv1, s1, conv2, s2) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member CaptureFinal2<'A, 'B>(steps) = + let allSteps = finalizeSteps steps + (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) -> - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2) - env.Finish() - ) - ) - - static member FinalNoPrefixOrSuffix2<'A, 'B>(conv1, s1, conv2) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (a: 'A) (b: 'B) -> - let env = prev() - Utils.Write(env, conv1 a, s1, conv2 b) - env.Finish() + let (args, env) = prev() + let finalArgs = box arg2 :: box arg1 :: args + env.RunSteps(finalizeArgs finalArgs, allSteps) ) ) - static member Final3<'A, 'B, 'C>(s0, conv1, s1, conv2, s2, conv3, s3) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member CaptureFinal3<'A, 'B, 'C>(steps) = + let allSteps = finalizeSteps steps + (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3) - env.Finish() - ) - ) - - static member FinalNoSuffix3<'A, 'B, 'C>(s0, conv1, s1, conv2, s2, conv3) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3) - env.Finish() - ) - ) - - static member FinalNoPrefix3<'A, 'B, 'C>(conv1, s1, conv2, s2, conv3, s3) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3) - env.Finish() - ) - ) - - static member FinalNoPrefixOrSuffix3<'A, 'B, 'C>(conv1, s1, conv2, s2, conv3) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3) - env.Finish() - ) - ) - - static member Final4<'A, 'B, 'C, 'D>(s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4) - env.Finish() + let (args, env) = prev() + let finalArgs = box arg3 :: box arg2 :: box arg1 :: args + env.RunSteps(finalizeArgs finalArgs, allSteps) ) ) - static member FinalNoSuffix4<'A, 'B, 'C, 'D>(s0, conv1, s1, conv2, s2, conv3, s3, conv4) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4) - env.Finish() - ) - ) - - static member FinalNoPrefix4<'A, 'B, 'C, 'D>(conv1, s1, conv2, s2, conv3, s3, conv4, s4) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4) - env.Finish() - ) - ) - - static member FinalNoPrefixOrSuffix4<'A, 'B, 'C, 'D>(conv1, s1, conv2, s2, conv3, s3, conv4) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4) - env.Finish() - ) - ) - - static member Final5<'A, 'B, 'C, 'D, 'E>(s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, s5) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5, s5) - env.Finish() - ) - ) - - static member FinalNoSuffix5<'A, 'B, 'C, 'D, 'E>(s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5) - env.Finish() - ) - ) - - static member FinalNoPrefix5<'A, 'B, 'C, 'D, 'E>(conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, s5) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5, s5) - env.Finish() - ) - ) - - static member FinalNoPrefixOrSuffix5<'A, 'B, 'C, 'D, 'E>(conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5) - env.Finish() - ) - ) - - static member Chained1<'A, 'Tail>(s0, conv1, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) -> - let env() = - let env = prev() - Utils.Write(env, s0, conv1 arg1) - env - next env : 'Tail - ) - ) - - static member ChainedNoPrefix1<'A, 'Tail>(conv1, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member Capture1<'A, 'Tail>(next) = + (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> let curr() = - let env = prev() - env.Write(conv1 arg1) - env + let (args, env) = prev() + (box arg1 :: args), env next curr : 'Tail ) ) - static member Chained2<'A, 'B, 'Tail>(s0, conv1, s1, conv2, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) -> + static member CaptureLittleA<'A, 'Tail>(next) = + (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + (fun (f: 'State -> 'A -> 'Residue) (arg1: 'A) -> let curr() = - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2) - env + let (args, env) = prev() + (box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args), env next curr : 'Tail ) ) - static member ChainedNoPrefix2<'A, 'B, 'Tail>(conv1, s1, conv2, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member Capture2<'A, 'B, 'Tail>(next) = + (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) -> let curr() = - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2) - env + let (args, env) = prev() + (box arg2 :: box arg1 :: args), env next curr : 'Tail ) ) - static member Chained3<'A, 'B, 'C, 'Tail> (s0, conv1, s1, conv2, s2, conv3, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> + static member Capture3<'A, 'B, 'C, 'Tail> (next) = + (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> let curr() = - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3) - env + let (args, env) = prev() + (box arg3 :: box arg2 :: box arg1 :: args), env next curr : 'Tail ) ) - static member ChainedNoPrefix3<'A, 'B, 'C, 'Tail> (conv1, s1, conv2, s2, conv3, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> - let curr() = - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3) - env - next curr : 'Tail - ) - ) - - static member Chained4<'A, 'B, 'C, 'D, 'Tail> (s0, conv1, s1, conv2, s2, conv3, s3, conv4, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> - let curr() = - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4) - env - next curr : 'Tail - ) - ) - - static member ChainedNoPrefix4<'A, 'B, 'C, 'D, 'Tail> (conv1, s1, conv2, s2, conv3, s3, conv4, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D)-> - let curr() = - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4) - env - next curr : 'Tail - ) - ) - - static member Chained5<'A, 'B, 'C, 'D, 'E, 'Tail> (s0, conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> - let curr() = - let env = prev() - Utils.Write(env, s0, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5) - env - next curr : 'Tail - ) - ) - - static member ChainedNoPrefix5<'A, 'B, 'C, 'D, 'E, 'Tail> (conv1, s1, conv2, s2, conv3, s3, conv4, s4, conv5, next) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) (arg4: 'D) (arg5: 'E)-> - let env() = - let env = prev() - Utils.Write(env, conv1 arg1, s1, conv2 arg2, s2, conv3 arg3, s3, conv4 arg4, s4, conv5 arg5) - env - next env : 'Tail - ) - ) - - static member TFinal(s1: string, s2: string) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (f: 'State -> 'Residue) -> - let env = prev() - env.Write s1 - env.WriteT(f env.State) - env.Write s2 - env.Finish() - ) - ) - static member TChained<'Tail>(s1: string, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (f: 'State -> 'Residue) -> - let curr() = - let env = prev() - env.Write s1 - env.WriteT(f env.State) - env - next curr: 'Tail - ) - ) - - static member LittleAFinal<'A>(s1: string, s2: string) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (f: 'State -> 'A ->'Residue) (a: 'A) -> - let env = prev() - env.Write s1 - env.WriteT(f env.State a) - env.Write s2 - env.Finish() - ) - ) - static member LittleAChained<'A, 'Tail>(s1: string, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (f: 'State -> 'A ->'Residue) (a: 'A) -> - let curr() = - let env = prev() - env.Write s1 - env.WriteT(f env.State a) - env - next curr: 'Tail - ) - ) - - static member StarFinal1<'A>(s1: string, conv, s2: string) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (star1: int) (a: 'A) -> - let env = prev() - env.Write s1 - env.Write (conv a star1: string) - env.Write s2 - env.Finish() - ) - ) - - static member PercentStarFinal1(s1: string, s2: string) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (_star1 : int) -> - let env = prev() - env.Write s1 - env.Write("%") - env.Write s2 - env.Finish() - ) - ) - - static member StarFinal2<'A>(s1: string, conv, s2: string) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (star1: int) (star2: int) (arg1: 'A) -> - let env = prev() - env.Write s1 - env.Write (conv arg1 star1 star2: string) - env.Write s2 - env.Finish() - ) - ) - - /// Handles case when '%*.*%' is used at the end of string - static member PercentStarFinal2(s1: string, s2: string) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (_star1 : int) (_star2 : int) -> - let env = prev() - env.Write s1 - env.Write("%") - env.Write s2 - env.Finish() - ) - ) - - static member StarChained1<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (star1: int) (arg1: 'A) -> - let curr() = - let env = prev() - env.Write s1 - env.Write(conv arg1 star1 : string) - env - next curr : 'Tail - ) - ) - - /// Handles case when '%*%' is used in the middle of the string so it needs to be chained to another printing block - static member PercentStarChained1<'Tail>(s1: string, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (_star1 : int) -> - let curr() = - let env = prev() - env.Write s1 - env.Write("%") - env - next curr: 'Tail - ) - ) - - static member StarChained2<'A, 'Tail>(s1: string, conv, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = - (fun (prev: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (star1: int) (star2: int) (arg1: 'A) -> - let curr() = - let env = prev() - env.Write s1 - env.Write(conv arg1 star1 star2 : string) - env - next curr : 'Tail - ) - ) - - /// Handles case when '%*.*%' is used in the middle of the string so it needs to be chained to another printing block - static member PercentStarChained2<'Tail>(s1: string, next: PrintfFactory<'Tail, 'State, 'Residue, 'Result>) = - (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - (fun (_star1 : int) (_star2 : int) -> - let env() = - let env = env() - env.Write s1 - env.Write("%") - env - next env : 'Tail - ) - ) - let inline (===) a b = Object.ReferenceEquals(a, b) - let invariantCulture = System.Globalization.CultureInfo.InvariantCulture let inline boolToString v = if v then "true" else "false" + let inline stringToSafeString v = match v with | null -> "" @@ -708,9 +457,9 @@ module internal PrintfImpl = type ValueConverter private (f: obj) = member x.FuncObj = f - static member inline Make (f: 'T1 -> string) = ValueConverter(box f) - static member inline Make (f: 'T1 -> 'T2 -> string) = ValueConverter(box f) - static member inline Make (f: 'T1 -> 'T2 -> 'T3 -> string) = ValueConverter(box f) + static member inline Make (f: obj -> string) = ValueConverter(box f) + static member inline Make (f: obj -> int -> string) = ValueConverter(box f) + static member inline Make (f: obj -> int-> int -> string) = ValueConverter(box f) let getFormatForFloat (ch: char) (prec: int) = ch.ToString() + prec.ToString() @@ -728,7 +477,7 @@ module internal PrintfImpl = /// pad here is function that converts T to string with respect of justification /// basic - function that converts T to string without applying justification rules /// adaptPaddedFormatted returns boxed function that has various number of arguments depending on if width\precision flags has '*' value - let inline adaptPaddedFormatted (spec: FormatSpecifier) getFormat (basic: string -> 'T -> string) (pad: string -> int -> 'T -> string) : ValueConverter = + let adaptPaddedFormatted (spec: FormatSpecifier) getFormat (basic: string -> obj -> string) (pad: string -> int -> obj -> string) : ValueConverter = if spec.IsStarWidth then if spec.IsStarPrecision then // width=*, prec=* @@ -768,7 +517,7 @@ module internal PrintfImpl = /// pad here is function that converts T to string with respect of justification /// basic - function that converts T to string without applying justification rules /// adaptPadded returns boxed function that has various number of arguments depending on if width flags has '*' value - let inline adaptPadded (spec: FormatSpecifier) (basic: 'T -> string) (pad: int -> 'T -> string) : ValueConverter = + let adaptPadded (spec: FormatSpecifier) (basic: obj -> string) (pad: int -> obj -> string) : ValueConverter = if spec.IsStarWidth then // width=*, prec=? ValueConverter.Make (fun v width -> @@ -783,7 +532,7 @@ module internal PrintfImpl = ValueConverter.Make (fun v -> basic v) - let inline withPaddingFormatted (spec: FormatSpecifier) getFormat (defaultFormat: string) (f: string -> 'T -> string) left right : ValueConverter = + let withPaddingFormatted (spec: FormatSpecifier) getFormat (defaultFormat: string) (f: string -> obj -> string) left right : ValueConverter = if not (spec.IsWidthSpecified || spec.IsPrecisionSpecified) then ValueConverter.Make (f defaultFormat) else @@ -792,7 +541,7 @@ module internal PrintfImpl = else adaptPaddedFormatted spec getFormat f right - let inline withPadding (spec: FormatSpecifier) (f: 'T -> string) left right : ValueConverter = + let withPadding (spec: FormatSpecifier) (f: obj -> string) left right : ValueConverter = if not spec.IsWidthSpecified then ValueConverter.Make f else @@ -801,29 +550,40 @@ module internal PrintfImpl = else adaptPadded spec f right - let inline isNumber (x: ^T) = - not (^T: (static member IsPositiveInfinity: 'T -> bool) x) && - not (^T: (static member IsNegativeInfinity: 'T -> bool) x) && - not (^T: (static member IsNaN: 'T -> bool) x) - - let inline isInteger n = - n % LanguagePrimitives.GenericOne = LanguagePrimitives.GenericZero - - let inline isPositive n = - n >= LanguagePrimitives.GenericZero - /// contains functions to handle left\right justifications for non-numeric types (strings\bools) module Basic = - let inline leftJustify (f: 'T -> string) padChar = + let leftJustify (f: obj -> string) padChar = fun (w: int) v -> (f v).PadRight(w, padChar) - let inline rightJustify (f: 'T -> string) padChar = + let rightJustify (f: obj -> string) padChar = fun (w: int) v -> (f v).PadLeft(w, padChar) + let withPadding (spec: FormatSpecifier) f = + let padChar, _ = spec.GetPadAndPrefix false + Padding.withPadding spec f (leftJustify f padChar) (rightJustify f padChar) + /// contains functions to handle left\right and no justification case for numbers module GenericNumber = + + let isPositive (n: obj) = + match n with + | :? int8 as n -> n >= 0y + | :? uint8 -> true + | :? int16 as n -> n >= 0s + | :? uint16 -> true + | :? int32 as n -> n >= 0 + | :? uint32 -> true + | :? int64 as n -> n >= 0L + | :? uint64 -> true + | :? nativeint as n -> n >= 0n + | :? unativeint -> true + | :? single as n -> n >= 0.0f + | :? double as n -> n >= 0.0 + | :? decimal as n -> n >= 0.0M + | _ -> failwith "isPositive: unreachable" + /// handles right justification when pad char = '0' /// this case can be tricky: /// - negative numbers, -7 should be printed as '-007', not '00-7' @@ -843,12 +603,12 @@ module internal PrintfImpl = str.PadLeft(w, ' ') /// handler right justification when pad char = ' ' - let inline rightJustifyWithSpaceAsPadChar (str: string) isNumber isPositive w (prefixForPositives: string) = + let rightJustifyWithSpaceAsPadChar (str: string) isNumber isPositive w (prefixForPositives: string) = System.Diagnostics.Debug.Assert(prefixForPositives.Length = 0 || prefixForPositives.Length = 1) (if isNumber && isPositive then prefixForPositives + str else str).PadLeft(w, ' ') /// handles left justification with formatting with 'G'\'g' - either for decimals or with 'g'\'G' is explicitly set - let inline leftJustifyWithGFormat (str: string) isNumber isInteger isPositive w (prefixForPositives: string) padChar = + let leftJustifyWithGFormat (str: string) isNumber isInteger isPositive w (prefixForPositives: string) padChar = if isNumber then let str = if isPositive then prefixForPositives + str else str // NOTE: difference - for 'g' format we use isInt check to detect situations when '5.0' is printed as '5' @@ -860,7 +620,7 @@ module internal PrintfImpl = else str.PadRight(w, ' ') // pad NaNs with ' ' - let inline leftJustifyWithNonGFormat (str: string) isNumber isPositive w (prefixForPositives: string) padChar = + let leftJustifyWithNonGFormat (str: string) isNumber isPositive w (prefixForPositives: string) padChar = if isNumber then let str = if isPositive then prefixForPositives + str else str str.PadRight(w, padChar) @@ -868,169 +628,200 @@ module internal PrintfImpl = str.PadRight(w, ' ') // pad NaNs with ' ' /// processes given string based depending on values isNumber\isPositive - let inline noJustificationCore (str: string) isNumber isPositive prefixForPositives = + let noJustificationCore (str: string) isNumber isPositive prefixForPositives = if isNumber && isPositive then prefixForPositives + str else str /// noJustification handler for f: 'T -> string - basic integer types - let inline noJustification f (prefix: string) isUnsigned = + let noJustification (f: obj -> string) (prefix: string) isUnsigned = if isUnsigned then - fun v -> noJustificationCore (f v) true true prefix + fun (v: obj) -> noJustificationCore (f v) true true prefix else - fun v -> noJustificationCore (f v) true (isPositive v) prefix + fun (v: obj) -> noJustificationCore (f v) true (isPositive v) prefix - /// noJustification handler for f: string -> 'T -> string - floating point types - let inline noJustificationWithFormat f (prefix: string) = - fun (fmt: string) v -> noJustificationCore (f fmt v) true (isPositive v) prefix - - /// leftJustify handler for f: 'T -> string - basic integer types - let inline leftJustify isGFormat f (prefix: string) padChar isUnsigned = + /// contains functions to handle left\right and no justification case for numbers + module Integer = + + let eliminateNative (v: obj) = + match v with + | :? nativeint as n -> + if IntPtr.Size = 4 then box (n.ToInt32()) + else box (n.ToInt64()) + | :? unativeint as n -> + if IntPtr.Size = 4 then box (uint32 (n.ToUInt32())) + else box (uint64 (n.ToUInt64())) + | _ -> v + + let rec toString (v: obj) = + match v with + | :? int32 as n -> n.ToString(CultureInfo.InvariantCulture) + | :? int64 as n -> n.ToString(CultureInfo.InvariantCulture) + | :? sbyte as n -> n.ToString(CultureInfo.InvariantCulture) + | :? byte as n -> n.ToString(CultureInfo.InvariantCulture) + | :? int16 as n -> n.ToString(CultureInfo.InvariantCulture) + | :? uint16 as n -> n.ToString(CultureInfo.InvariantCulture) + | :? uint32 as n -> n.ToString(CultureInfo.InvariantCulture) + | :? uint64 as n -> n.ToString(CultureInfo.InvariantCulture) + | :? nativeint | :? unativeint -> toString (eliminateNative v) + | _ -> failwith "toString: unreachable" + + let rec toFormattedString fmt (v: obj) = + match v with + | :? int32 as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? int64 as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? sbyte as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? byte as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? int16 as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? uint16 as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? uint32 as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? uint64 as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? nativeint | :? unativeint -> toFormattedString fmt (eliminateNative v) + | _ -> failwith "toString: unreachable" + + let rec toUnsigned (v: obj) = + match v with + | :? int32 as n -> box (uint32 n) + | :? int64 as n -> box (uint64 n) + | :? sbyte as n -> box (byte n) + | :? int16 as n -> box (uint16 n) + | :? nativeint | :? unativeint -> toUnsigned (eliminateNative v) + | _ -> v + + /// Left justification handler for f: 'T -> string - basic integer types + let leftJustify isGFormat (f: obj -> string) (prefix: string) padChar isUnsigned = if isUnsigned then if isGFormat then - fun (w: int) v -> - leftJustifyWithGFormat (f v) true (isInteger v) true w prefix padChar + fun (w: int) (v: obj) -> + GenericNumber.leftJustifyWithGFormat (f v) true true true w prefix padChar else - fun (w: int) v -> - leftJustifyWithNonGFormat (f v) true true w prefix padChar + fun (w: int) (v: obj) -> + GenericNumber.leftJustifyWithNonGFormat (f v) true true w prefix padChar else if isGFormat then - fun (w: int) v -> - leftJustifyWithGFormat (f v) true (isInteger v) (isPositive v) w prefix padChar + fun (w: int) (v: obj) -> + GenericNumber.leftJustifyWithGFormat (f v) true true (GenericNumber.isPositive v) w prefix padChar else - fun (w: int) v -> - leftJustifyWithNonGFormat (f v) true (isPositive v) w prefix padChar + fun (w: int) (v: obj) -> + GenericNumber.leftJustifyWithNonGFormat (f v) true (GenericNumber.isPositive v) w prefix padChar - /// leftJustify handler for f: string -> 'T -> string - floating point types - let inline leftJustifyWithFormat isGFormat f (prefix: string) padChar = - if isGFormat then - fun (fmt: string) (w: int) v -> - leftJustifyWithGFormat (f fmt v) true (isInteger v) (isPositive v) w prefix padChar - else - fun (fmt: string) (w: int) v -> - leftJustifyWithNonGFormat (f fmt v) true (isPositive v) w prefix padChar - - /// rightJustify handler for f: 'T -> string - basic integer types - let inline rightJustify f (prefixForPositives: string) padChar isUnsigned = + /// Right justification handler for f: 'T -> string - basic integer types + let rightJustify f (prefixForPositives: string) padChar isUnsigned = if isUnsigned then if padChar = '0' then - fun (w: int) v -> - rightJustifyWithZeroAsPadChar (f v) true true w prefixForPositives + fun (w: int) (v: obj) -> + GenericNumber.rightJustifyWithZeroAsPadChar (f v) true true w prefixForPositives else System.Diagnostics.Debug.Assert((padChar = ' ')) - fun (w: int) v -> - rightJustifyWithSpaceAsPadChar (f v) true true w prefixForPositives + fun (w: int) (v: obj) -> + GenericNumber.rightJustifyWithSpaceAsPadChar (f v) true true w prefixForPositives else if padChar = '0' then - fun (w: int) v -> - rightJustifyWithZeroAsPadChar (f v) true (isPositive v) w prefixForPositives + fun (w: int) (v: obj) -> + GenericNumber.rightJustifyWithZeroAsPadChar (f v) true (GenericNumber.isPositive v) w prefixForPositives else System.Diagnostics.Debug.Assert((padChar = ' ')) fun (w: int) v -> - rightJustifyWithSpaceAsPadChar (f v) true (isPositive v) w prefixForPositives - - /// rightJustify handler for f: string -> 'T -> string - floating point types - let inline rightJustifyWithFormat f (prefixForPositives: string) padChar = - if padChar = '0' then - fun (fmt: string) (w: int) v -> - rightJustifyWithZeroAsPadChar (f fmt v) true (isPositive v) w prefixForPositives - - else - System.Diagnostics.Debug.Assert((padChar = ' ')) - fun (fmt: string) (w: int) v -> - rightJustifyWithSpaceAsPadChar (f fmt v) true (isPositive v) w prefixForPositives - - module Float = - let inline noJustification f (prefixForPositives: string) = - fun (fmt: string) v -> - GenericNumber.noJustificationCore (f fmt v) (isNumber v) (isPositive v) prefixForPositives + GenericNumber.rightJustifyWithSpaceAsPadChar (f v) true (GenericNumber.isPositive v) w prefixForPositives + + let withPadding (spec: FormatSpecifier) isUnsigned (f: obj -> string) = + let allowZeroPadding = not (isLeftJustify spec.Flags) || spec.IsDecimalFormat + let padChar, prefix = spec.GetPadAndPrefix allowZeroPadding + Padding.withPadding spec + (GenericNumber.noJustification f prefix isUnsigned) + (leftJustify spec.IsGFormat f prefix padChar isUnsigned) + (rightJustify f prefix padChar isUnsigned) + + let getValueConverter (spec: FormatSpecifier) : ValueConverter = + let c = spec.TypeChar + if c = 'd' || c = 'i' then + withPadding spec false toString + elif c = 'u' then + withPadding spec true (toUnsigned >> toString) + elif c = 'x' then + withPadding spec true (toFormattedString "x") + elif c = 'X' then + withPadding spec true (toFormattedString "X") + elif c = 'o' then + withPadding spec true (fun (v: obj) -> + match toUnsigned v with + | :? uint64 as u -> Convert.ToString(int64 u, 8) + | u -> Convert.ToString(Convert.ToInt64 u, 8)) + else raise (ArgumentException()) - let inline leftJustify isGFormat f (prefix: string) padChar = + module FloatAndDecimal = + + let rec toFormattedString fmt (v: obj) = + match v with + | :? single as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? double as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | :? decimal as n -> n.ToString(fmt, CultureInfo.InvariantCulture) + | _ -> failwith "toFormattedString: unreachable" + + let isNumber (x: obj) = + match x with + | :? single as x -> + not (Single.IsPositiveInfinity(x)) && + not (Single.IsNegativeInfinity(x)) && + not (Single.IsNaN(x)) + | :? double as x -> + not (Double.IsPositiveInfinity(x)) && + not (Double.IsNegativeInfinity(x)) && + not (Double.IsNaN(x)) + | :? decimal -> true + | _ -> failwith "isNumber: unreachable" + + let isInteger (n: obj) = + match n with + | :? single as n -> n % 1.0f = 0.0f + | :? double as n -> n % 1. = 0. + | :? decimal as n -> n % 1.0M = 0.0M + | _ -> failwith "isInteger: unreachable" + + let noJustification (prefixForPositives: string) = + fun (fmt: string) (v: obj) -> + GenericNumber.noJustificationCore (toFormattedString fmt v) (isNumber v) (GenericNumber.isPositive v) prefixForPositives + + let leftJustify isGFormat (prefix: string) padChar = if isGFormat then - fun (fmt: string) (w: int) v -> - GenericNumber.leftJustifyWithGFormat (f fmt v) (isNumber v) (isInteger v) (isPositive v) w prefix padChar + fun (fmt: string) (w: int) (v: obj) -> + GenericNumber.leftJustifyWithGFormat (toFormattedString fmt v) (isNumber v) (isInteger v) (GenericNumber.isPositive v) w prefix padChar else - fun (fmt: string) (w: int) v -> - GenericNumber.leftJustifyWithNonGFormat (f fmt v) (isNumber v) (isPositive v) w prefix padChar + fun (fmt: string) (w: int) (v: obj) -> + GenericNumber.leftJustifyWithNonGFormat (toFormattedString fmt v) (isNumber v) (GenericNumber.isPositive v) w prefix padChar - let inline rightJustify f (prefixForPositives: string) padChar = + let rightJustify (prefixForPositives: string) padChar = if padChar = '0' then - fun (fmt: string) (w: int) v -> - GenericNumber.rightJustifyWithZeroAsPadChar (f fmt v) (isNumber v) (isPositive v) w prefixForPositives + fun (fmt: string) (w: int) (v: obj) -> + GenericNumber.rightJustifyWithZeroAsPadChar (toFormattedString fmt v) (isNumber v) (GenericNumber.isPositive v) w prefixForPositives else System.Diagnostics.Debug.Assert((padChar = ' ')) - fun (fmt: string) (w: int) v -> - GenericNumber.rightJustifyWithSpaceAsPadChar (f fmt v) (isNumber v) (isPositive v) w prefixForPositives - - let isDecimalFormatSpecifier (spec: FormatSpecifier) = - spec.TypeChar = 'M' - - let getPadAndPrefix allowZeroPadding (spec: FormatSpecifier) = - let padChar = if allowZeroPadding && isPadWithZeros spec.Flags then '0' else ' '; - let prefix = - if isPlusForPositives spec.Flags then "+" - elif isSpaceForPositives spec.Flags then " " - else "" - padChar, prefix - - let isGFormat(spec: FormatSpecifier) = - isDecimalFormatSpecifier spec || System.Char.ToLower(spec.TypeChar) = 'g' - - let inline basicWithPadding (spec: FormatSpecifier) f = - let padChar, _ = getPadAndPrefix false spec - Padding.withPadding spec f (Basic.leftJustify f padChar) (Basic.rightJustify f padChar) - - let inline numWithPadding (spec: FormatSpecifier) isUnsigned f = - let allowZeroPadding = not (isLeftJustify spec.Flags) || isDecimalFormatSpecifier spec - let padChar, prefix = getPadAndPrefix allowZeroPadding spec - let isGFormat = isGFormat spec - Padding.withPadding spec (GenericNumber.noJustification f prefix isUnsigned) (GenericNumber.leftJustify isGFormat f prefix padChar isUnsigned) (GenericNumber.rightJustify f prefix padChar isUnsigned) - - let inline decimalWithPadding (spec: FormatSpecifier) getFormat defaultFormat f : ValueConverter = - let padChar, prefix = getPadAndPrefix true spec - let isGFormat = isGFormat spec - Padding.withPaddingFormatted spec getFormat defaultFormat (GenericNumber.noJustificationWithFormat f prefix) (GenericNumber.leftJustifyWithFormat isGFormat f prefix padChar) (GenericNumber.rightJustifyWithFormat f prefix padChar) - - let inline floatWithPadding (spec: FormatSpecifier) getFormat defaultFormat f = - let padChar, prefix = getPadAndPrefix true spec - let isGFormat = isGFormat spec - Padding.withPaddingFormatted spec getFormat defaultFormat (Float.noJustification f prefix) (Float.leftJustify isGFormat f prefix padChar) (Float.rightJustify f prefix padChar) - - let inline identity v = v - - let inline toString v = (^T : (member ToString: IFormatProvider -> string)(v, invariantCulture)) - - let inline toFormattedString fmt = fun (v: ^T) -> (^T: (member ToString: string * IFormatProvider -> string)(v, fmt, invariantCulture)) - - let inline numberToString c spec alt unsignedConv = - if c = 'd' || c = 'i' then - numWithPadding spec false (alt >> toString: ^T -> string) - elif c = 'u' then - numWithPadding spec true (alt >> unsignedConv >> toString: ^T -> string) - elif c = 'x' then - numWithPadding spec true (alt >> toFormattedString "x": ^T -> string) - elif c = 'X' then - numWithPadding spec true (alt >> toFormattedString "X": ^T -> string ) - elif c = 'o' then - numWithPadding spec true (fun (v: ^T) -> Convert.ToString(int64(unsignedConv (alt v)), 8)) - else raise (ArgumentException()) - + fun (fmt: string) (w: int) (v: obj) -> + GenericNumber.rightJustifyWithSpaceAsPadChar (toFormattedString fmt v) (isNumber v) (GenericNumber.isPositive v) w prefixForPositives + + let withPadding (spec: FormatSpecifier) getFormat defaultFormat = + let padChar, prefix = spec.GetPadAndPrefix true + Padding.withPaddingFormatted spec getFormat defaultFormat + (noJustification prefix) + (leftJustify spec.IsGFormat prefix padChar) + (rightJustify prefix padChar) + type ObjectPrinter = - static member ObjectToString<'T>(spec: FormatSpecifier) : ValueConverter = - basicWithPadding spec (fun (v: 'T) -> - match box v with + static member ObjectToString(spec: FormatSpecifier) : ValueConverter = + Basic.withPadding spec (fun (v: obj) -> + match v with | null -> "" | x -> x.ToString()) /// Convert an interpoland to a string - static member InterpolandToString<'T>(spec: FormatSpecifier) : ValueConverter = + static member InterpolandToString(spec: FormatSpecifier) : ValueConverter = let fmt = match spec.InteropHoleDotNetFormat with | None -> null | Some fmt -> "{0:" + fmt + "}" - basicWithPadding spec (fun (v: 'T) -> - match box v with + Basic.withPadding spec (fun (vobj: obj) -> + match vobj with | null -> "" | x -> match fmt with @@ -1061,103 +852,67 @@ module internal PrintfImpl = match spec.IsStarWidth, spec.IsStarPrecision with | true, true -> - ValueConverter.Make (fun (v: 'T) (width: int) (prec: int) -> + ValueConverter.Make (fun (vobj: obj) (width: int) (prec: int) -> + let v = unbox<'T> vobj let opts = { opts with PrintSize = prec } let opts = if not useZeroWidth then { opts with PrintWidth = width} else opts ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) ) | true, false -> - ValueConverter.Make (fun (v: 'T) (width: int) -> + ValueConverter.Make (fun (vobj: obj) (width: int) -> + let v = unbox<'T> vobj let opts = if not useZeroWidth then { opts with PrintWidth = width} else opts ObjectPrinter.GenericToStringCore(v, opts, bindingFlags)) | false, true -> - ValueConverter.Make (fun (v: 'T) (prec: int) -> + ValueConverter.Make (fun (vobj: obj) (prec: int) -> + let v = unbox<'T> vobj let opts = { opts with PrintSize = prec } ObjectPrinter.GenericToStringCore(v, opts, bindingFlags) ) | false, false -> - ValueConverter.Make (fun (v: 'T) -> + ValueConverter.Make (fun (vobj: obj) -> + let v = unbox<'T> vobj ObjectPrinter.GenericToStringCore(v, opts, bindingFlags)) - let basicNumberToString (ty: Type) (spec: FormatSpecifier) = - System.Diagnostics.Debug.Assert(not spec.IsPrecisionSpecified, "not spec.IsPrecisionSpecified") - - let ch = spec.TypeChar - - match Type.GetTypeCode ty with - | TypeCode.Int32 -> numberToString ch spec identity (uint32: int -> uint32) - | TypeCode.Int64 -> numberToString ch spec identity (uint64: int64 -> uint64) - | TypeCode.Byte -> numberToString ch spec identity (byte: byte -> byte) - | TypeCode.SByte -> numberToString ch spec identity (byte: sbyte -> byte) - | TypeCode.Int16 -> numberToString ch spec identity (uint16: int16 -> uint16) - | TypeCode.UInt16 -> numberToString ch spec identity (uint16: uint16 -> uint16) - | TypeCode.UInt32 -> numberToString ch spec identity (uint32: uint32 -> uint32) - | TypeCode.UInt64 -> numberToString ch spec identity (uint64: uint64 -> uint64) - | _ -> - if ty === typeof then - if IntPtr.Size = 4 then - numberToString ch spec (fun (v: IntPtr) -> v.ToInt32()) uint32 - else - numberToString ch spec (fun (v: IntPtr) -> v.ToInt64()) uint64 - elif ty === typeof then - if IntPtr.Size = 4 then - numberToString ch spec (fun (v: UIntPtr) -> v.ToUInt32()) uint32 - else - numberToString ch spec (fun (v: UIntPtr) -> v.ToUInt64()) uint64 - - else raise (ArgumentException(ty.Name + " not a basic integer type")) - - let basicFloatToString ty spec = + let basicFloatToString spec = let defaultFormat = getFormatForFloat spec.TypeChar DefaultPrecision - match Type.GetTypeCode ty with - | TypeCode.Single -> floatWithPadding spec (getFormatForFloat spec.TypeChar) defaultFormat (fun fmt (v: float32) -> toFormattedString fmt v) - | TypeCode.Double -> floatWithPadding spec (getFormatForFloat spec.TypeChar) defaultFormat (fun fmt (v: float) -> toFormattedString fmt v) - | TypeCode.Decimal -> decimalWithPadding spec (getFormatForFloat spec.TypeChar) defaultFormat (fun fmt (v: decimal) -> toFormattedString fmt v) - | _ -> raise (ArgumentException(ty.Name + " not a basic floating point type")) + FloatAndDecimal.withPadding spec (getFormatForFloat spec.TypeChar) defaultFormat let private NonPublicStatics = BindingFlags.NonPublic ||| BindingFlags.Static let mi_GenericToString = typeof.GetMethod("GenericToString", NonPublicStatics) - let mi_ObjectToString = typeof.GetMethod("ObjectToString", NonPublicStatics) - let mi_InterpolandToString = typeof.GetMethod("InterpolandToString", NonPublicStatics) let private getValueConverter (ty: Type) (spec: FormatSpecifier) : ValueConverter = match spec.TypeChar with | 'b' -> - System.Diagnostics.Debug.Assert(ty === typeof, "ty === typeof") - basicWithPadding spec boolToString + Basic.withPadding spec (unbox >> boolToString) | 's' -> - System.Diagnostics.Debug.Assert(ty === typeof, "ty === typeof") - basicWithPadding spec stringToSafeString + Basic.withPadding spec (unbox >> stringToSafeString) | 'c' -> - System.Diagnostics.Debug.Assert(ty === typeof, "ty === typeof") - basicWithPadding spec (fun (c: char) -> c.ToString()) + Basic.withPadding spec (fun (c: obj) -> (unbox c).ToString()) | 'M' -> - System.Diagnostics.Debug.Assert(ty === typeof, "ty === typeof") - decimalWithPadding spec (fun _ -> "G") "G" (fun fmt (v: decimal) -> toFormattedString fmt v) // %M ignores precision + FloatAndDecimal.withPadding spec (fun _ -> "G") "G" // %M ignores precision | 'd' | 'i' | 'x' | 'X' | 'u' | 'o'-> - basicNumberToString ty spec + Integer.getValueConverter spec | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' -> - basicFloatToString ty spec + basicFloatToString spec | 'A' -> let mi = mi_GenericToString.MakeGenericMethod ty mi.Invoke(null, [| box spec |]) |> unbox | 'O' -> - let mi = mi_ObjectToString.MakeGenericMethod ty - mi.Invoke(null, [| box spec |]) |> unbox + ObjectPrinter.ObjectToString(spec) | 'P' -> - let mi = mi_InterpolandToString.MakeGenericMethod ty - mi.Invoke(null, [| box spec |]) |> unbox + ObjectPrinter.InterpolandToString(spec) | _ -> raise (ArgumentException(SR.GetString(SR.printfBadFormatSpecifier))) let extractCurriedArguments (ty: Type) n = System.Diagnostics.Debug.Assert(n = 1 || n = 2 || n = 3, "n = 1 || n = 2 || n = 3") - let buf = Array.zeroCreate (n + 1) + let buf = Array.zeroCreate n let rec go (ty: Type) i = if i < n then match ty.GetGenericArguments() with @@ -1167,407 +922,212 @@ module internal PrintfImpl = | _ -> failwith (String.Format("Expected function with {0} arguments", n)) else System.Diagnostics.Debug.Assert((i = n), "i = n") - buf.[i] <- ty - buf + buf, ty go ty 0 - - type private PrintfBuilderStack() = - // Note this 'obj' is an untagged union of type "string | value converter function | continuation function" - let args = Stack 10 - let types = Stack 5 - - let stackToArray size start count (s: Stack<_>) = - let arr = Array.zeroCreate size - for i = 0 to count - 1 do - arr.[start + i] <- s.Pop() - arr - - member __.GetArgumentAndTypesAsArrays - ( - argsArraySize, argsArrayStartPos, argsArrayTotalCount, - typesArraySize, typesArrayStartPos, typesArrayTotalCount - ) = - let argsArray = stackToArray argsArraySize argsArrayStartPos argsArrayTotalCount args - let typesArray = stackToArray typesArraySize typesArrayStartPos typesArrayTotalCount types - argsArray, typesArray - - member __.PopContinuation() = - System.Diagnostics.Debug.Assert(args.Count = 1, "args.Count = 1") - System.Diagnostics.Debug.Assert(types.Count = 1, "types.Count = 1") - - let cont = args.Pop() - let contTy = types.Pop() - - cont, contTy - member __.PopValueUnsafe() = args.Pop() + let MAX_CAPTURE = 3 - member __.PushString(value: string) = - args.Push (box value) - - member __.PushValueConverter(value: ValueConverter, ty) = - args.Push value.FuncObj - types.Push ty - - member this.PushContinuation (cont: obj, contTy: Type) = - System.Diagnostics.Debug.Assert(this.IsEmpty, "this.IsEmpty") - System.Diagnostics.Debug.Assert( - ( - let _arg, retTy = Microsoft.FSharp.Reflection.FSharpType.GetFunctionElements(cont.GetType()) - contTy.IsAssignableFrom retTy - ), - "incorrect type" - ) - - args.Push cont - types.Push contTy - - member __.HasContinuationOnStack expectedNumberOfArguments = - (types.Count = expectedNumberOfArguments + 1) - - member __.IsEmpty = - System.Diagnostics.Debug.Assert(args.Count = types.Count, "args.Count = types.Count") - (args.Count = 0) - - /// Type of element that is stored in cache. This is the residue of the parse of the format string. - /// - /// Pair: factory for the printer + number of text blocks that printer will produce (used to preallocate buffers) - [] - type CachedItem<'Printer, 'State, 'Residue, 'Result> = - { - /// The format string, used to help identify the cache entry (the cache index types are taken - /// into account as well). - FormatString: string - - /// The factory function used to generate the result or the resulting function. it is passed an - /// environment-creating function. - FunctionFactory: PrintfFactory<'Printer, 'State, 'Residue, 'Result> - - /// The maximum number of slots needed in the environment including string fragments and output strings from position holders - BlockCount: int - } - - /// Parses format string and creates result printer factory function. - /// - /// First it recursively consumes format string up to the end, then during unwinding builds printer using PrintfBuilderStack as storage for arguments. - /// - /// The idea of implementation is very simple: every step can either push argument to the stack (if current block of 5 format specifiers is not yet filled) - // or grab the content of stack, build intermediate printer and push it back to stack (so it can later be consumed by as argument) - type private FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt: string) = + /// Parses format string and creates resulting step list and printer factory function. + type FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt: string) = - let mutable count = 0 - let mutable optimizedArgCount = 0 - -#if DEBUG - let verifyMethodInfoWasTaken (mi: System.Reflection.MemberInfo) = - if isNull mi then - ignore (System.Diagnostics.Debugger.Launch()) -#endif - - let buildSpecialChained(spec: FormatSpecifier, argTys: Type[], prefix: string, tail: obj, retTy) = + let buildCaptureFunc (spec: FormatSpecifier, steps, argTys: Type[], retTy, nextInfo) = + let (next:obj, nextCanCombine: bool, nextArgTys: Type[], nextRetTy, nextNextOpt) = nextInfo + assert (argTys.Length > 0) + + // See if we can compress a capture to a multi-capture + // CaptureN + Final --> CaptureFinalN + // Capture1 + Capture1 --> Capture2 + // Capture1 + Capture2 --> Capture3 + // Capture2 + Capture1 --> Capture3 + match argTys.Length, nextArgTys.Length with + | _ when spec.TypeChar = 'a' -> + // %a has an existential type which must be converted to obj + assert (argTys.Length = 2) + let captureMethName = "CaptureLittleA" + let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) + let mi = mi.MakeGenericMethod([| argTys.[1]; retTy |]) + let funcObj = mi.Invoke(null, [| next |]) + funcObj, false, argTys, retTy, Some next + + | n1, n2 when nextCanCombine && n1 + n2 <= MAX_CAPTURE -> + // 'next' is thrown away on this path and replaced by a combined Capture + let captureCount = n1 + n2 + let combinedArgTys = Array.append argTys nextArgTys + match nextNextOpt with + | None -> + let captureMethName = "CaptureFinal" + string captureCount + let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) + let mi = mi.MakeGenericMethod(combinedArgTys) + let funcObj = mi.Invoke(null, [| steps |]) + funcObj, true, combinedArgTys, nextRetTy, None + | Some nextNext -> + let captureMethName = "Capture" + string captureCount + let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) + let mi = mi.MakeGenericMethod(Array.append combinedArgTys [| nextRetTy |]) + let funcObj = mi.Invoke(null, [| nextNext |]) + funcObj, true, combinedArgTys, nextRetTy, nextNextOpt + + | captureCount, _ -> + let captureMethName = "Capture" + string captureCount + let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) + let mi = mi.MakeGenericMethod(Array.append argTys [| retTy |]) + let funcObj = mi.Invoke(null, [| next |]) + funcObj, true, argTys, retTy, Some next + + let buildStep (spec: FormatSpecifier) (argTys: Type[]) prefix = if spec.TypeChar = 'a' then - let mi = typeof>.GetMethod("LittleAChained", NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - - let mi = mi.MakeGenericMethod([| argTys.[1]; retTy |]) - let args = [| box prefix; tail |] - mi.Invoke(null, args) + StepLittleA prefix elif spec.TypeChar = 't' then - let mi = typeof>.GetMethod("TChained", NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - let mi = mi.MakeGenericMethod([| retTy |]) - let args = [| box prefix; tail |] - mi.Invoke(null, args) - else - System.Diagnostics.Debug.Assert(spec.IsStarPrecision || spec.IsStarWidth, "spec.IsStarPrecision || spec.IsStarWidth ") - - let mi = - let n = if spec.IsStarWidth = spec.IsStarPrecision then 2 else 1 - let prefix = if spec.TypeChar = '%' then "PercentStarChained" else "StarChained" - let name = prefix + (string n) - typeof>.GetMethod(name, NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - let argTypes, args = - if spec.TypeChar = '%' then - [| retTy |], [| box prefix; tail |] + StepLittleT prefix + elif spec.IsStarPrecision || spec.IsStarWidth then + let isTwoStar = (spec.IsStarWidth = spec.IsStarPrecision) + match isTwoStar, spec.TypeChar with + | false, '%' -> StepPercentStar1 prefix + | true, '%' -> StepPercentStar2 prefix + | _ -> + let argTy = (match argTys with null -> typeof | _ -> argTys.[argTys.Length - 1]) + let conv = getValueConverter argTy spec + if isTwoStar then + let convFunc = conv.FuncObj :?> (obj -> int -> int -> string) + StepStar2 (prefix, convFunc) else - let argTy = argTys.[argTys.Length - 2] - let conv = getValueConverter argTy spec - [| argTy; retTy |], [| box prefix; conv.FuncObj; tail |] - - let mi = mi.MakeGenericMethod argTypes - mi.Invoke(null, args) - - let buildSpecialFinal(spec: FormatSpecifier, argTys: Type[], prefix: string, suffix: string) = - if spec.TypeChar = 'a' then - let mi = typeof>.GetMethod("LittleAFinal", NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - let mi = mi.MakeGenericMethod(argTys.[1] : Type) - let args = [| box prefix; box suffix |] - mi.Invoke(null, args) - elif spec.TypeChar = 't' then - let mi = typeof>.GetMethod("TFinal", NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - let args = [| box prefix; box suffix |] - mi.Invoke(null, args) + let convFunc = conv.FuncObj :?> (obj -> int -> string) + StepStar1 (prefix, convFunc) else - System.Diagnostics.Debug.Assert(spec.IsStarPrecision || spec.IsStarWidth, "spec.IsStarPrecision || spec.IsStarWidth ") - - let mi = - let n = if spec.IsStarWidth = spec.IsStarPrecision then 2 else 1 - let prefix = if spec.TypeChar = '%' then "PercentStarFinal" else "StarFinal" - let name = prefix + (string n) - typeof>.GetMethod(name, NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - - let mi, args = - if spec.TypeChar = '%' then - mi, [| box prefix; box suffix |] - else - let argTy = argTys.[argTys.Length - 2] - let mi = mi.MakeGenericMethod argTy - let conv = getValueConverter argTy spec - mi, [| box prefix; conv.FuncObj; box suffix |] - - mi.Invoke(null, args) - - let argIsEmptyString (arg: obj) = - match arg with - | :? string as s -> String.IsNullOrEmpty(s) - | _ -> false - - let buildPlainFinal(args: obj[], argTypes: Type[]) = - let argsCount = args.Length - let methodName,args = - // check if the prefix is empty - if argsCount > 0 && argIsEmptyString args.[0] then - // check if both prefix and suffix are empty - if argsCount > 1 && argIsEmptyString args.[argsCount - 1] then - let args = Array.sub args 1 (argsCount - 2) - optimizedArgCount <- optimizedArgCount + 2 - "FinalNoPrefixOrSuffix", args - else - optimizedArgCount <- optimizedArgCount + 1 - "FinalNoPrefix", args |> Array.skip 1 - // check if suffix is empty - elif argsCount > 0 && argIsEmptyString args.[argsCount - 1] then - let args = Array.sub args 0 (argsCount - 1) - optimizedArgCount <- optimizedArgCount + 1 - "FinalNoSuffix", args - else - "Final",args - - let mi = typeof>.GetMethod(methodName + string argTypes.Length, NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - let mi = mi.MakeGenericMethod argTypes - mi.Invoke(null, args) - - let buildPlainChained(args: obj[], argTypes: Type[]) = - let argsCount = args.Length - let methodName,args = - // check if the prefix is empty - if argsCount > 0 && argIsEmptyString args.[0] then - optimizedArgCount <- optimizedArgCount + 1 - "ChainedNoPrefix", args |> Array.skip 1 - else - "Chained", args - - let mi = typeof>.GetMethod(methodName + string (argTypes.Length - 1), NonPublicStatics) -#if DEBUG - verifyMethodInfoWasTaken mi -#endif - let mi = mi.MakeGenericMethod argTypes - mi.Invoke(null, args) - - let builderStack = PrintfBuilderStack() - - let ContinuationOnStack = -1 - - let buildPlain numberOfArgs prefix = - let n = numberOfArgs * 2 - let hasCont = builderStack.HasContinuationOnStack numberOfArgs - - let extra = if hasCont then 1 else 0 - let plainArgs, plainTypes = - builderStack.GetArgumentAndTypesAsArrays(n + 1, 1, n, numberOfArgs + extra, 0, numberOfArgs) - - plainArgs.[0] <- box prefix - - if hasCont then - let cont, contTy = builderStack.PopContinuation() - plainArgs.[plainArgs.Length - 1] <- cont - plainTypes.[plainTypes.Length - 1] <- contTy - - buildPlainChained(plainArgs, plainTypes) - else - buildPlainFinal(plainArgs, plainTypes) - - let rec parseFromFormatSpecifier (prefix: string) (s: string) (funcTy: Type) i: int = - - if i >= s.Length then 0 else + let argTy = (match argTys with null -> typeof | _ -> argTys.[0]) + let conv = getValueConverter argTy spec + let convFunc = conv.FuncObj :?> (obj -> string) + Step (prefix, convFunc) - System.Diagnostics.Debug.Assert(s.[i] = '%', "s.[i] = '%'") - count <- count + 1 - - let flags, i = FormatString.parseFlags s (i + 1) - let width, i = FormatString.parseWidth s i - let precision, i = FormatString.parsePrecision s i - let typeChar, i = FormatString.parseTypeChar s i - let interpHoleDotnetFormat, i = FormatString.parseInterpolatedHoleDotNetFormat typeChar s i + let parseSpec i = + let flags, i = FormatString.parseFlags fmt (i + 1) + let width, i = FormatString.parseWidth fmt i + let precision, i = FormatString.parsePrecision fmt i + let typeChar, i = FormatString.parseTypeChar fmt i + let interpHoleDotnetFormat, i = FormatString.parseInterpolatedHoleDotNetFormat typeChar fmt i // Skip %P insertion points added after %d{...} etc. in interpolated strings - let i = FormatString.skipInterpolationHole s i + let i = FormatString.skipInterpolationHole fmt i - let spec = + let spec = { TypeChar = typeChar Precision = precision Flags = flags Width = width InteropHoleDotNetFormat = interpHoleDotnetFormat } + i, spec - let next, suffix = FormatString.findNextFormatSpecifier s i - - let argTys = - let n = - if spec.TypeChar = 'a' then 2 - elif spec.IsStarWidth || spec.IsStarPrecision then - if spec.IsStarWidth = spec.IsStarPrecision then 3 - else 2 - else 1 - - let n = if spec.TypeChar = '%' then n - 1 else n - - System.Diagnostics.Debug.Assert(n <> 0, "n <> 0") - - extractCurriedArguments funcTy n - - let retTy = argTys.[argTys.Length - 1] - - let numberOfArgs = parseFromFormatSpecifier suffix s retTy next - - if spec.TypeChar = 'a' || spec.TypeChar = 't' || spec.IsStarWidth || spec.IsStarPrecision then - // Every hole in a formattable string captures the interpoland - if numberOfArgs = ContinuationOnStack then - - let cont, contTy = builderStack.PopContinuation() - let currentCont = buildSpecialChained(spec, argTys, prefix, cont, contTy) - builderStack.PushContinuation(currentCont, funcTy) - - ContinuationOnStack - else - if numberOfArgs = 0 then - System.Diagnostics.Debug.Assert(builderStack.IsEmpty, "builderStack.IsEmpty") - - let currentCont = buildSpecialFinal(spec, argTys, prefix, suffix) - builderStack.PushContinuation(currentCont, funcTy) - ContinuationOnStack - else - let hasCont = builderStack.HasContinuationOnStack numberOfArgs - - let expectedNumberOfItemsOnStack = numberOfArgs * 2 - let sizeOfTypesArray = - if hasCont then numberOfArgs + 1 - else numberOfArgs - - let plainArgs, plainTypes = - builderStack.GetArgumentAndTypesAsArrays(expectedNumberOfItemsOnStack + 1, 1, expectedNumberOfItemsOnStack, sizeOfTypesArray, 0, numberOfArgs ) - - plainArgs.[0] <- box suffix - - let next = - if hasCont then - let nextCont, nextContTy = builderStack.PopContinuation() - plainArgs.[plainArgs.Length - 1] <- nextCont - plainTypes.[plainTypes.Length - 1] <- nextContTy - buildPlainChained(plainArgs, plainTypes) - else - buildPlainFinal(plainArgs, plainTypes) - - let next = buildSpecialChained(spec, argTys, prefix, next, retTy) - builderStack.PushContinuation(next, funcTy) - - ContinuationOnStack + // A simplified parser. For the case where the string is being used with interpolands captured in the Format object. + let rec parseStepsAux steps (prefix: string) i = + if i >= fmt.Length then + let step = StepString(prefix) + (step :: steps) else - if numberOfArgs = ContinuationOnStack then - let idx = argTys.Length - 2 - builderStack.PushString suffix - let conv = getValueConverter argTys.[idx] spec - builderStack.PushValueConverter(conv, argTys.[idx]) - 1 - else - builderStack.PushString suffix - let conv = getValueConverter argTys.[0] spec - builderStack.PushValueConverter(conv, argTys.[0]) - - if numberOfArgs = MaxArgumentsInSpecialization - 1 then - let cont = buildPlain (numberOfArgs + 1) prefix - builderStack.PushContinuation(cont, funcTy) - ContinuationOnStack - else - numberOfArgs + 1 - - let funcTy = typeof<'Printer> - - let factoryObj = - - // Find the format specifier + let i, spec = parseSpec i + let next, suffix = FormatString.findNextFormatSpecifier fmt i + let step = buildStep spec null prefix + parseStepsAux (step::steps) suffix next + + let parseSteps () = + let i, prefix = FormatString.findNextFormatSpecifier fmt 0 + let steps = parseStepsAux [] prefix i + let count = Step.BlockCount steps + count, steps + + /// The more advanced parser which both builds the steps (with accurate %A types), + /// and produces a curried function value of the right type guided by funcTy + let rec parseAndCreateFuncFactoryAux steps (prefix: string) (funcTy: Type) i = + + if i >= fmt.Length then + let step = StepString(prefix) + let last = Specializations<'State, 'Residue, 'Result>.Final0(steps) + (step :: steps), (box last, true, [| |], funcTy, None) + else + System.Diagnostics.Debug.Assert(fmt.[i] = '%', "s.[i] = '%'") + let i, spec = parseSpec i + let next, suffix = FormatString.findNextFormatSpecifier fmt i + let argTys, retTy = extractCurriedArguments funcTy spec.ArgCount + let step = buildStep spec argTys prefix + let allSteps, next = parseAndCreateFuncFactoryAux (step::steps) suffix retTy next + let nextNew = buildCaptureFunc (spec, allSteps, argTys, retTy, next) + allSteps, nextNew + + let parseAndCreateFuncFactory () = + let funcTy = typeof<'Printer> + + // Find the first format specifier let prefixPos, prefix = FormatString.findNextFormatSpecifier fmt 0 + // If there are not format specifiers then take a simple path if prefixPos = fmt.Length then - // If there are not format specifiers then take a simple path - box (fun (env: unit -> PrintfEnv<'State, 'Residue, 'Result>) -> - let env = env() - env.Write prefix + 0, box (fun (initial: PrintfFuncContext<'State, 'Residue, 'Result>) -> + let (_args, env) = initial() + env.WriteSkipEmpty prefix env.Finish()) else - let n = parseFromFormatSpecifier prefix fmt funcTy prefixPos - - if n = ContinuationOnStack || n = 0 then - builderStack.PopValueUnsafe() - else - buildPlain n prefix - - //do System.Console.WriteLine("factoryObj.GetType() = {0}", factoryObj.GetType()) - let result = - { - FormatString = fmt - FunctionFactory = factoryObj :?> PrintfFactory<'Printer, 'State, 'Residue, 'Result> - // second component is used in SprintfEnv as value for internal buffer - BlockCount = (2 * count + 1) - optimizedArgCount - } - - member _.Result = result + let steps, (factoryObj, _, _, _, _) = parseAndCreateFuncFactoryAux [] prefix funcTy prefixPos + let count = Step.BlockCount steps + count, factoryObj + + // The simple steps, populated on-demand, for the case where the string is being used with interpolands captured in the Format object. + let mutable allSteps = Unchecked.defaultof<_> + + // The function factory, populated on-demand + let mutable functionFactory = Unchecked.defaultof<_> + + // The function factory, populated on-demand + let mutable stringCount = 0 + + /// The format string, used to help identify the cache entry (the cache index types are taken + /// into account as well). + member _.FormatString = fmt + + /// The steps involved in executing the format string when interpolands are captured + member _.Steps = + match allSteps with + | null -> + // We may initialize this twice, but the assignment is atomic and the computation will give functionally + // identical results each time it is ok + let count, steps = parseSteps () + stringCount <- count + allSteps <- steps |> List.rev |> List.toArray + | _ -> () + allSteps + + /// The number of strings produced for a sprintf + member _.BlockCount = stringCount + + /// The factory function used to generate the result or the resulting function. + member _.FunctionFactory = + match box functionFactory with + | null -> + let count, funcObj = parseAndCreateFuncFactory () + // We may initialize this twice, but the assignment is atomic and the computation will give functionally + // identical results each time it is ok + functionFactory <- (funcObj :?> PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result>) + stringCount <- (2 * count + 1) + | _ -> () + functionFactory /// 2-level cache, keyed by format string and index types type Cache<'Printer, 'State, 'Residue, 'Result>() = - /// 1st level cache (type-indexed). Stores last value that was consumed by the current - /// thread in thread-static field thus providing shortcuts for scenarios when printf is - /// called in tight loop. + /// 1st level cache (type-indexed). Stores last value that was consumed by the current thread in + /// thread-static field thus providing shortcuts for scenarios when printf is called in tight loop. [] - static val mutable private mostRecent: CachedItem<'Printer, 'State, 'Residue, 'Result> + static val mutable private mostRecent: FormatParser<'Printer, 'State, 'Residue, 'Result> // 2nd level cache (type-indexed). Dictionary that maps format string to the corresponding cache entry - static let mutable dict : ConcurrentDictionary> = null + static let mutable dict : ConcurrentDictionary> = null - static member Get(format: Format<'Printer, 'State, 'Residue, 'Result>) = + static member GetParser(format: Format<'Printer, 'State, 'Residue, 'Result>) = let cacheEntry = Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent let fmt = format.Value - if not (cacheEntry === null) && fmt.Equals cacheEntry.FormatString then + if cacheEntry === null then + let parser = FormatParser(fmt) + Cache.mostRecent <- parser + parser + elif fmt.Equals cacheEntry.FormatString then cacheEntry else // Initialize the 2nd level cache if necessary. Note there's a race condition but it doesn't @@ -1575,27 +1135,23 @@ module internal PrintfImpl = if isNull dict then dict <- ConcurrentDictionary<_,_>() - let v = + let parser = match dict.TryGetValue(fmt) with | true, res -> res | _ -> - let parser = FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt) - let result = parser.Result - // Note there's a race condition - the dictionary entry may be re-created by another thread - // but it doesn't matter if lose one entry, as long as the dictionary only ends up holding - // a valid entry. - dict.TryAdd(fmt, result) |> ignore - result - Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent <- v - v - - type LargeStringPrintfEnv<'Result>(k, n) = + let parser = FormatParser(fmt) + // There's a race condition - but the computation is functional and it doesn't matter if we do it twice + dict.TryAdd(fmt, parser) |> ignore + parser + Cache.mostRecent <- parser + parser + + type LargeStringPrintfEnv<'Result>(continuation, blockSize) = inherit PrintfEnv(()) - let buf: string[] = Array.zeroCreate n + let buf: string[] = Array.zeroCreate blockSize let mutable ptr = 0 - - override __.Finish() : 'Result = k (String.Concat buf) + override __.Finish() : 'Result = continuation (String.Concat buf) override __.Write(s: string) = buf.[ptr] <- s @@ -1613,11 +1169,11 @@ module internal PrintfImpl = override __.Write(s: string) = if isNull c then c <- s else c <- c + s override __.WriteT s = if isNull c then c <- s else c <- c + s - let StringPrintfEnv n = - if n <= 2 then + let StringPrintfEnv blockSize = + if blockSize <= 2 then SmallStringPrintfEnv() :> PrintfEnv<_,_,_> else - LargeStringPrintfEnv(id, n) :> PrintfEnv<_,_,_> + LargeStringPrintfEnv(id, blockSize) :> PrintfEnv<_,_,_> let StringBuilderPrintfEnv<'Result>(k, buf) = { new PrintfEnv(buf) with @@ -1639,43 +1195,60 @@ module Printf = open System.Text open PrintfImpl - type BuilderFormat<'T,'Result> = Format<'T, StringBuilder, unit, 'Result> - type StringFormat<'T,'Result> = Format<'T, unit, string, 'Result> - type TextWriterFormat<'T,'Result> = Format<'T, TextWriter, unit, 'Result> + type BuilderFormat<'T, 'Result> = Format<'T, StringBuilder, unit, 'Result> + type StringFormat<'T, 'Result> = Format<'T, unit, string, 'Result> + type TextWriterFormat<'T, 'Result> = Format<'T, TextWriter, unit, 'Result> type BuilderFormat<'T> = BuilderFormat<'T,unit> type StringFormat<'T> = StringFormat<'T,string> type TextWriterFormat<'T> = TextWriterFormat<'T,unit> + let gprintf envf (format: Format<'Printer, 'State, 'Residue, 'Result>) = + let cacheItem = Cache.GetParser format + match format.Captures with + | null -> + // The ksprintf "...%d ...." arg path, producing a function + let factory = cacheItem.FunctionFactory + let initial() = ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>) + factory initial + | captures -> + // The ksprintf $"...%d{3}...." path, running the steps straight away to produce a string + let steps = cacheItem.Steps + let env = envf cacheItem.BlockCount :> PrintfEnv<_,_,_> + let res = env.RunSteps(captures, steps) + unbox res // prove 'T = 'Result + //continuation res + [] let ksprintf continuation (format: StringFormat<'T, 'Result>) : 'T = - let cacheItem = Cache.Get format - let initial() = LargeStringPrintfEnv (continuation, cacheItem.BlockCount) :> PrintfEnv<_,_,_> - cacheItem.FunctionFactory initial + gprintf (fun stringCount -> LargeStringPrintfEnv(continuation, stringCount)) format [] let sprintf (format: StringFormat<'T>) = - let cacheItem = Cache.Get format - let initial() = StringPrintfEnv cacheItem.BlockCount - cacheItem.FunctionFactory initial - - [] - [] - let isprintf (format: StringFormat<'T>) = sprintf format + // We inline gprintf by hand here to be sure to remove a few allocations + let cacheItem = Cache.GetParser format + match format.Captures with + | null -> + // The sprintf "...%d ...." arg path, producing a function + let factory = cacheItem.FunctionFactory + let initial() = ([], StringPrintfEnv cacheItem.BlockCount) + factory initial + | captures -> + // The sprintf $"...%d{3}...." path, running the steps straight away to produce a string + let steps = cacheItem.Steps + let env = StringPrintfEnv cacheItem.BlockCount + let res = env.RunSteps(captures, steps) + unbox res // proves 'T = string [] let kprintf continuation format = ksprintf continuation format [] - let kbprintf continuation (builder: StringBuilder) format = - let cacheItem = Cache.Get format - let initial() = StringBuilderPrintfEnv(continuation, builder) - cacheItem.FunctionFactory initial + let kbprintf continuation (builder: StringBuilder) (format: BuilderFormat<'T, 'Result>) : 'T = + gprintf (fun _stringCount -> StringBuilderPrintfEnv(continuation, builder)) format [] - let kfprintf continuation textWriter format = - let cacheItem = Cache.Get format - let initial() = TextWriterPrintfEnv(continuation, textWriter) - cacheItem.FunctionFactory initial + let kfprintf continuation textWriter (format: TextWriterFormat<'T, 'Result>) = + gprintf (fun _stringCount -> TextWriterPrintfEnv(continuation, textWriter)) format [] let bprintf builder format = diff --git a/src/fsharp/FSharp.Core/printf.fsi b/src/fsharp/FSharp.Core/printf.fsi index 97140add11e..6e56342ea66 100644 --- a/src/fsharp/FSharp.Core/printf.fsi +++ b/src/fsharp/FSharp.Core/printf.fsi @@ -21,8 +21,16 @@ type PrintfFormat<'Printer,'State,'Residue,'Result> = /// The PrintfFormat containing the formatted result. new : value:string -> PrintfFormat<'Printer,'State,'Residue,'Result> + /// Construct a format string + /// The input string. + /// The captured expressions in an interpolation string. + /// The PrintfFormat containing the formatted result. + new : value:string * captures: obj[] -> PrintfFormat<'Printer,'State,'Residue,'Result> + /// The raw text of the format string. member Value : string + + member Captures: obj[] /// Type of a formatting expression. /// Function type generated by printf. @@ -39,6 +47,12 @@ type PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> = /// The created format string. new: value:string -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> + /// Construct a format string + /// The input string. + /// The captured expressions in an interpolation string. + /// The created format string. + new: value:string * captures: obj[] -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> + /// Type of a formatting expression. /// Function type generated by printf. /// Type argument passed to %a formatters @@ -214,14 +228,6 @@ module Printf = [] val sprintf: format:StringFormat<'T> -> 'T - /// Interpolated print to a string via an internal string buffer and return - /// the result as a string. Helper printers must return strings. - /// The input formatter. - /// The formatted string. - [] - [] - val isprintf: format:StringFormat<'T> -> 'T - /// bprintf, but call the given 'final' function to generate the result. /// See kprintf. /// The function called after formatting to generate the format result. diff --git a/src/fsharp/MethodCalls.fs b/src/fsharp/MethodCalls.fs index 821f458e59a..70af5858d07 100644 --- a/src/fsharp/MethodCalls.fs +++ b/src/fsharp/MethodCalls.fs @@ -851,7 +851,8 @@ let TryImportProvidedMethodBaseAsLibraryIntrinsic (amap: Import.ImportMap, m: ra if isAppTy amap.g declaringType then let declaringEntity = tcrefOfAppTy amap.g declaringType if not declaringEntity.IsLocalRef && ccuEq declaringEntity.nlr.Ccu amap.g.fslibCcu then - match amap.g.knownIntrinsics.TryGetValue ((declaringEntity.LogicalName, methodName)) with + let n = mbase.PUntaint((fun x -> x.GetParameters().Length), m) + match amap.g.knownIntrinsics.TryGetValue ((declaringEntity.LogicalName, None, methodName, n)) with | true, vref -> Some vref | _ -> match amap.g.knownFSharpCoreModules.TryGetValue declaringEntity.LogicalName with diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index b47b8c17611..418308d67b5 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -360,7 +360,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let mkForallTyIfNeeded d r = match d with [] -> r | tps -> TType_forall(tps, r) // A table of all intrinsics that the compiler cares about - let v_knownIntrinsics = Dictionary<(string*string), ValRef>(HashIdentity.Structural) + let v_knownIntrinsics = Dictionary<(string * string option * string * int), ValRef>(HashIdentity.Structural) let makeIntrinsicValRef (enclosingEntity, logicalName, memberParentName, compiledNameOpt, typars, (argtys, rty)) = let ty = mkForallTyIfNeeded typars (mkIteratedFunTy (List.map mkSmallRefTupledTy argtys) rty) @@ -370,10 +370,12 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let key = ValLinkageFullKey({ MemberParentMangledName=memberParentName; MemberIsOverride=false; LogicalName=logicalName; TotalArgCount= argCount }, linkageType) let vref = IntrinsicValRef(enclosingEntity, logicalName, isMember, ty, key) let compiledName = defaultArg compiledNameOpt logicalName - v_knownIntrinsics.Add((enclosingEntity.LastItemMangledName, compiledName), ValRefForIntrinsic vref) + let key = (enclosingEntity.LastItemMangledName, memberParentName, compiledName, argCount) + assert not (v_knownIntrinsics.ContainsKey(key)) + if not (v_knownIntrinsics.ContainsKey(key)) then + v_knownIntrinsics.Add(key, ValRefForIntrinsic vref) vref - let v_IComparer_ty = mkSysNonGenericTy sysCollections "IComparer" let v_IEqualityComparer_ty = mkSysNonGenericTy sysCollections "IEqualityComparer" @@ -710,8 +712,8 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_seq_singleton_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "singleton" , None , Some "Singleton" , [vara], ([[varaTy]], mkSeqTy varaTy)) let v_seq_empty_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "empty" , None , Some "Empty" , [vara], ([], mkSeqTy varaTy)) let v_new_format_info = makeIntrinsicValRef(fslib_MFCore_nleref, ".ctor" , Some "PrintfFormat`5", None , [vara;varb;varc;vard;vare], ([[v_string_ty]], mkPrintfFormatTy varaTy varbTy varcTy vardTy vareTy)) + let v_new_format_ext_info = makeIntrinsicValRef(fslib_MFCore_nleref, ".ctor" , Some "PrintfFormat`5", None , [vara;varb;varc;vard;vare], ([[v_string_ty; mkArrayType 1 v_obj_ty]], mkPrintfFormatTy varaTy varbTy varcTy vardTy vareTy)) let v_sprintf_info = makeIntrinsicValRef(fslib_MFExtraTopLevelOperators_nleref, "sprintf" , None , Some "PrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) - let v_isprintf_info = makeIntrinsicValRef(fslib_MFPrintfModule_nleref, "isprintf" , None , Some "InterpolatedPrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) let v_lazy_force_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Force" , Some "Lazy`1" , None , [vara], ([[mkLazyTy varaTy]; []], varaTy)) let v_lazy_create_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Create" , Some "Lazy`1" , None , [vara], ([[v_unit_ty --> varaTy]], mkLazyTy varaTy)) @@ -1380,7 +1382,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val seq_empty_vref = ValRefForIntrinsic v_seq_empty_info member val new_format_vref = ValRefForIntrinsic v_new_format_info member val sprintf_vref = ValRefForIntrinsic v_sprintf_info - member val isprintf_vref = ValRefForIntrinsic v_isprintf_info + member val new_format_ext_vref = ValRefForIntrinsic v_new_format_ext_info member val unbox_vref = ValRefForIntrinsic v_unbox_info member val unbox_fast_vref = ValRefForIntrinsic v_unbox_fast_info member val istype_vref = ValRefForIntrinsic v_istype_info @@ -1407,8 +1409,8 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.seq_singleton_info = v_seq_singleton_info member __.seq_empty_info = v_seq_empty_info member __.sprintf_info = v_sprintf_info - member __.isprintf_info = v_isprintf_info member __.new_format_info = v_new_format_info + member __.new_format_ext_info = v_new_format_ext_info member __.unbox_info = v_unbox_info member __.get_generic_comparer_info = v_get_generic_comparer_info member __.get_generic_er_equality_comparer_info = v_get_generic_er_equality_comparer_info diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 6749da5227a..be040490c81 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7142,35 +7142,60 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice Some e | e -> Some e) - let formattableStringInfo = - // If this is an interpolated string then the result must be a string + let printerTy = NewInferenceType () + let printerArgTy = NewInferenceType () + let printerResidueTy = NewInferenceType () + let printerResultTy = NewInferenceType () + let printerTupleTy = NewInferenceType () + let formatTy = mkPrintfFormatTy g printerTy printerArgTy printerResidueTy printerResultTy printerTupleTy + + let stringKind = + // If this is an interpolated string then try to force the result to be a string if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy g.string_ty) then - None - else - // ... or if that fails then may be a FormattableString by a type-directed rule.... - if (not (isObjTy g overallTy) && - ((g.system_FormattableString_tcref.CanDeref && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy g.system_FormattableString_ty) - || (g.system_IFormattable_tcref.CanDeref && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy g.system_IFormattable_ty))) then - - let ad = env.eAccessRights - let createMethod = - match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env m ad "Create" g.system_FormattableStringFactory_ty with - | [x] -> Some x - | _ -> None - createMethod + // And if that succeeds, the result of printing is a string + UnifyTypes cenv env m printerArgTy g.unit_ty + UnifyTypes cenv env m printerResidueTy g.string_ty + UnifyTypes cenv env m printerResultTy overallTy + Choice1Of3 () - else - // this should fail and produce an error - UnifyTypes cenv env m overallTy g.string_ty - None + // ... or if that fails then may be a FormattableString by a type-directed rule.... + elif (not (isObjTy g overallTy) && + ((g.system_FormattableString_tcref.CanDeref && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy g.system_FormattableString_ty) + || (g.system_IFormattable_tcref.CanDeref && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy g.system_IFormattable_ty))) then - let isFormattableString = formattableStringInfo.IsSome - let aty = NewInferenceType () - let bty = g.unit_ty - let cty = g.string_ty - let dty = overallTy - let ety = NewInferenceType () + // And if that succeeds, the result of printing is a string + // We work out printerTy further below + UnifyTypes cenv env m printerArgTy g.unit_ty + UnifyTypes cenv env m printerResidueTy g.string_ty + UnifyTypes cenv env m printerResultTy overallTy + + // Find the FormattableStringFactor.Create method in the .NET libraries + let ad = env.eAccessRights + let createMethodOpt = + match TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AllResults cenv env m ad "Create" g.system_FormattableStringFactory_ty with + | [x] -> Some x + | _ -> None + + match createMethodOpt with + | Some createMethod -> Choice2Of3 createMethod + | None -> Choice1Of3 () + + // ... or if that fails then may be a PrintfFormat by a type-directed rule.... + elif not (isObjTy g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy formatTy then + + // And if that succeeds, the printerTy and printerResultTy must be the same (there are no curried arguments) + UnifyTypes cenv env m printerTy printerResultTy + match GetIntrinsicConstructorInfosOfType cenv.infoReader m formatTy |> List.filter (fun minfo -> minfo.NumArgs = [2]) with + | [ctorInfo] -> Choice3Of3 ctorInfo + | _ -> Choice1Of3 () + + else + // this should fail and produce an error + UnifyTypes cenv env m overallTy g.string_ty + Choice1Of3 () + + let isFormattableString = (match stringKind with Choice2Of3 _ -> true | _ -> false) // The format string used for checking in CheckFormatStrings. This replaces interpolation holes with %P let printfFormatString = @@ -7191,16 +7216,37 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice String.concat "" // Parse the format string to work out the phantom types and check for absence of '%' specifiers in FormattableString - let (argtys, atyRequired, etyRequired), _specifierLocations, dotnetFormatString = - try CheckFormatStrings.ParseFormatString m g true isFormattableString None printfFormatString bty cty dty + let (argtys, printerTyRequired, printerTupleTyRequired), _specifierLocations, dotnetFormatString = + try CheckFormatStrings.ParseFormatString m g true isFormattableString None printfFormatString printerArgTy printerResidueTy printerResultTy with Failure errString -> error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) // Check the expressions filling the holes if argtys.Length <> synFillExprs.Length then error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) - match formattableStringInfo with - | Some createMethInfo -> + match stringKind with + + // The case for $"..." used as type string + | Choice1Of3 () -> + + UnifyTypes cenv env m printerTy printerTyRequired + UnifyTypes cenv env m printerTupleTy printerTupleTyRequired + + let fmtExpr = mkCallNewFormat g m printerTy printerArgTy printerResidueTy printerResultTy printerTupleTy (mkString g m printfFormatString) + + // Type check the expressions filling the holes + let flexes = argtys |> List.map (fun _ -> false) + let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs + + // Check the library support is available in the referenced FSharp.Core + if cenv.g.new_format_ext_vref.TryDeref.IsNone then + languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m + + // Make the call to isprintf + mkCall_sprintf g m printerTy fmtExpr fillExprs, tpenv + + // The case for $"..." used as type FormattableString or IFormattable + | Choice2Of3 createMethInfo -> // Type check the expressions filling the holes let flexes = argtys |> List.map (fun _ -> false) @@ -7221,23 +7267,27 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice - - UnifyTypes cenv env m aty atyRequired - UnifyTypes cenv env m ety etyRequired + // The case for $"...%d{x}..." used as type PrintfFormat - giving a PrintfFormat that captures + // is arguments + | Choice3Of3 ctor -> - let fmtExpr = mkCallNewFormat g m aty bty cty dty ety (mkString g m printfFormatString) + UnifyTypes cenv env m printerTupleTy printerTupleTyRequired // Type check the expressions filling the holes let flexes = argtys |> List.map (fun _ -> false) let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs + let fillExprsBoxed = (argtys, fillExprs) ||> List.map2 (mkCallBox g m) + + let argsExpr = mkArray (g.obj_ty, fillExprsBoxed, m) + // Check the library support is available in the referenced FSharp.Core - if cenv.g.isprintf_vref.TryDeref.IsNone then + if cenv.g.new_format_ext_vref.TryDeref.IsNone then languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m - // Make the call to isprintf - mkCall_isprintf g m aty fmtExpr fillExprs, tpenv + let fmtExpr = MakeMethInfoCall cenv.amap m ctor [] [mkString g m printfFormatString; argsExpr] + + fmtExpr, tpenv //------------------------------------------------------------------------- // TcConstExpr diff --git a/src/fsharp/TypedTreeOps.fs b/src/fsharp/TypedTreeOps.fs index 978f151ae6c..c628cb6a3b8 100644 --- a/src/fsharp/TypedTreeOps.fs +++ b/src/fsharp/TypedTreeOps.fs @@ -6914,7 +6914,11 @@ let mkCallRaise (g: TcGlobals) m ty e1 = mkApps g (typedExprForIntrinsic g m g.r let mkCallNewDecimal (g: TcGlobals) m (e1, e2, e3, e4, e5) = mkApps g (typedExprForIntrinsic g m g.new_decimal_info, [], [ e1;e2;e3;e4;e5 ], m) -let mkCallNewFormat (g: TcGlobals) m aty bty cty dty ety e1 = mkApps g (typedExprForIntrinsic g m g.new_format_info, [[aty;bty;cty;dty;ety]], [ e1 ], m) +let mkCallNewFormat (g: TcGlobals) m aty bty cty dty ety e1 = + mkApps g (typedExprForIntrinsic g m g.new_format_info, [[aty;bty;cty;dty;ety]], [ e1 ], m) + +let mkCallNewFormatWithExprs (g: TcGlobals) m aty bty cty dty ety fmtStringExpr captureExprs = + mkApps g (typedExprForIntrinsic g m g.new_format_ext_info, [[aty;bty;cty;dty;ety]], [fmtStringExpr;captureExprs ], m) let TryEliminateDesugaredConstants g m c = match c with @@ -6972,8 +6976,8 @@ let mkCallSeqSingleton g m ty1 arg1 = let mkCallSeqEmpty g m ty1 = mkApps g (typedExprForIntrinsic g m g.seq_empty_info, [[ty1]], [ ], m) -let mkCall_isprintf (g: TcGlobals) m aty fmt es = - mkApps g (typedExprForIntrinsic g m g.isprintf_info, [[aty]], fmt::es , m) +let mkCall_sprintf (g: TcGlobals) m aty fmt es = + mkApps g (typedExprForIntrinsic g m g.sprintf_info, [[aty]], fmt::es , m) let mkCallDeserializeQuotationFSharp20Plus g m e1 e2 e3 e4 = let args = [ e1; e2; e3; e4 ] diff --git a/src/fsharp/TypedTreeOps.fsi b/src/fsharp/TypedTreeOps.fsi index dba712efe56..222a97eb40e 100755 --- a/src/fsharp/TypedTreeOps.fsi +++ b/src/fsharp/TypedTreeOps.fsi @@ -1773,7 +1773,9 @@ val mkInvalidCastExnNewobj: TcGlobals -> ILInstr // Construct calls to some intrinsic functions //------------------------------------------------------------------------- -val mkCallNewFormat : TcGlobals -> range -> TType -> TType -> TType -> TType -> TType -> Expr -> Expr +val mkCallNewFormat: TcGlobals -> range -> TType -> TType -> TType -> TType -> TType -> formatStringExpr: Expr -> Expr + +val mkCallNewFormatWithExprs: TcGlobals -> range -> TType -> TType -> TType -> TType -> TType -> formatStringExpr: Expr -> captureExprs: Expr -> Expr val mkCallUnbox : TcGlobals -> range -> TType -> Expr -> Expr @@ -1970,7 +1972,7 @@ val mkCallSeqSingleton : TcGlobals -> range -> TType -> Expr -> Expr val mkCallSeqEmpty : TcGlobals -> range -> TType -> Expr /// Make a call to the 'isprintf' function for string interpolation -val mkCall_isprintf: g: TcGlobals -> m: range -> funcTy: TType -> fmtExpr: Expr -> fillExprs: Expr list -> Expr +val mkCall_sprintf: g: TcGlobals -> m: range -> funcTy: TType -> fmtExpr: Expr -> fillExprs: Expr list -> Expr val mkILAsmCeq : TcGlobals -> range -> Expr -> Expr -> Expr diff --git a/tests/fsharp/core/printf-interpolated/test.fsx b/tests/fsharp/core/printf-interpolated/test.fsx new file mode 100644 index 00000000000..baf5989741d --- /dev/null +++ b/tests/fsharp/core/printf-interpolated/test.fsx @@ -0,0 +1,268 @@ +// #Conformance #Printing + +#if TESTS_AS_APP +module Core_printf_interp +#endif + +#light + +open Printf + +let failures = ref [] + +let report_failure (s : string) = + stderr.Write" NO: " + stderr.WriteLine s + failures := !failures @ [s] + +// change this to true to run every test case +// leave as false to randomly execute a subset of cases (this is a very expensive test area) +let runEveryTest = true + +let test t (s1:Lazy) s2 = + stdout.WriteLine ("running test "+t+"...") + let s1 = s1.Force() + if s1 <> s2 then + report_failure ("test "+t+": expected \n\t'"+s2+"' but produced \n\t'"+s1+"'") + else + stdout.WriteLine ("test "+t+": correctly produced '"+s1+"'") + +let verify actual expected = test expected actual expected + +let adjust1 obj n1 = unbox ((unbox obj) n1) + +let _ = test "cewoui2a" (lazy(sprintf $"%o{0}")) "0" +let _ = test "cewoui2b" (lazy(sprintf $"%o{0}")) "0" +let _ = test "cewoui2c" (lazy(sprintf $"%o{5}")) "5" +let _ = test "cewoui2q" (lazy(sprintf $"%o{8}")) "10" +let _ = test "cewoui2w" (lazy(sprintf $"%o{15}")) "17" +let _ = test "cewoui2e" (lazy(sprintf $"%o{System.Int32.MinValue}" )) "20000000000" +let _ = test "cewoui2r" (lazy(sprintf $"%o{0xffffffff}" )) "37777777777" +let _ = test "cewoui2t" (lazy(sprintf $"%o{System.Int32.MinValue+1}")) "20000000001" +let _ = test "cewoui2y" (lazy(sprintf $"%o{System.Int32.MaxValue}")) "17777777777" + +let _ = test "cewoui2u" (lazy(sprintf $"%o{-1}" )) "37777777777" + +let f z = sprintf $"%o{z}" + +let _ = test "cewoui2a" (lazy(f 0)) "0" +let _ = test "cewoui2s" (lazy(f 0)) "0" +let _ = test "cewoui2d" (lazy(f 5)) "5" +let _ = test "cewoui2f" (lazy(f 8)) "10" +let _ = test "cewoui2g" (lazy(f 15)) "17" +let _ = test "cewoui2h" (lazy(f System.Int32.MinValue)) "20000000000" +let _ = test "cewoui2j" (lazy(f 0xffffffff)) "37777777777" +let _ = test "cewoui2lk" (lazy(f (System.Int32.MinValue+1))) "20000000001" +let _ = test "cewoui2l" (lazy(f System.Int32.MaxValue)) "17777777777" + +let _ = test "cewoui212" (lazy(f (-1))) "37777777777" + +// check bprintf +let _ = test "csd3oui2!" (lazy(let buf = new System.Text.StringBuilder() in ignore (bprintf buf $"%x{0}%x{1}"); buf.ToString())) "01" + +// check kbprintf +let _ = test "csd3oui2!1" (lazy(let buf = new System.Text.StringBuilder() in kbprintf (fun () -> buf.ToString()) buf $"%x{0}%x{1}")) "01" + +let _ = test "cewoui2!" (lazy(sprintf $"%x{0}")) "0" +let _ = test "cewoui26" (lazy(sprintf $"%x{5}")) "5" +let _ = test "cewoui2f" (lazy(sprintf $"%x{8}")) "8" +let _ = test "cewoui29" (lazy(sprintf $"%x{15}")) "f" +let _ = test "cewoui2Z" (lazy(sprintf $"%x{System.Int32.MinValue}" )) "80000000" +let _ = test "cewoui2X" (lazy(sprintf $"%x{0xffffffff}" )) "ffffffff" +let _ = test "cewoui2A" (lazy(sprintf $"%x{System.Int32.MinValue+1}" )) "80000001" +let _ = test "cewoui2Q" (lazy(sprintf $"%x{System.Int32.MaxValue}" )) "7fffffff" + +let fx z = sprintf $"%x{z}" +let _ = test "cewoui2W" (lazy(fx 0)) "0" +let _ = test "cewoui2E" (lazy(fx 5)) "5" +let _ = test "cewoui2R" (lazy(fx 8)) "8" +let _ = test "cewoui2T" (lazy(fx 15)) "f" +let _ = test "cewoui2Y" (lazy(fx System.Int32.MinValue)) "80000000" +let _ = test "cewoui2U" (lazy(fx 0xffffffff)) "ffffffff" +let _ = test "cewoui2I" (lazy(fx (System.Int32.MinValue+1))) "80000001" +let _ = test "cewoui2O" (lazy(fx System.Int32.MaxValue)) "7fffffff" + +let _ = test "cewoui2Z" (lazy(sprintf $"%X{0}")) "0" +let _ = test "cewoui2X" (lazy(sprintf $"%X{5}")) "5" +let _ = test "cewoui2C" (lazy(sprintf $"%X{8}")) "8" +let _ = test "cewoui2V" (lazy(sprintf $"%X{15}")) "F" +let _ = test "cewoui2v" (lazy(sprintf $"%X{System.Int32.MinValue}" )) "80000000" +let _ = test "cewoui2B" (lazy(sprintf $"%X{0xffffffff}" )) "FFFFFFFF" +let _ = test "cewoui2N" (lazy(sprintf $"%X{System.Int32.MinValue+1}")) "80000001" +let _ = test "cewoui2M" (lazy(sprintf $"%X{System.Int32.MaxValue}" )) "7FFFFFFF" + +let _ = test "cewou44a" (lazy(sprintf $"%u{0}")) "0" +let _ = test "cewou44b" (lazy(sprintf $"%u{5}" )) "5" +let _ = test "cewou44c" (lazy(sprintf $"%u{8}" )) "8" +let _ = test "cewou44d" (lazy(sprintf $"%u{15}" )) "15" +let _ = test "cewou44e" (lazy(sprintf $"%u{System.Int32.MinValue}" )) "2147483648" +let _ = test "cewou44f" (lazy(sprintf $"%u{0xffffffff}" )) "4294967295" +let _ = test "cewou44g" (lazy(sprintf $"%u{System.Int32.MinValue+1}" )) "2147483649" +let _ = test "cewou44h" (lazy(sprintf $"%u{System.Int32.MaxValue}" )) "2147483647" + +let _ = test "cewou45a" (lazy(sprintf $"%d{0ul}" )) "0" +let _ = test "cewou45b" (lazy(sprintf $"%d{5ul}" )) "5" +let _ = test "cewou45c" (lazy(sprintf $"%d{8ul}" )) "8" +let _ = test "cewou45d" (lazy(sprintf $"%d{15ul}" )) "15" +let _ = test "cewou45e" (lazy(sprintf $"%d{2147483648ul}" )) "2147483648" +let _ = test "cewou45f" (lazy(sprintf $"%d{4294967295ul}" )) "4294967295" +let _ = test "cewou45g" (lazy(sprintf $"%d{2147483649ul}" )) "2147483649" +let _ = test "cewou45h" (lazy(sprintf $"%d{2147483647ul}" )) "2147483647" + +let _ = test "cewou46a" (lazy(sprintf $"%d{0ul}" )) "0" +let _ = test "cewou46b" (lazy(sprintf $"%d{5ul}" )) "5" +let _ = test "cewou46c" (lazy(sprintf $"%d{8ul}" )) "8" +let _ = test "cewou46d" (lazy(sprintf $"%d{15ul}" )) "15" +let _ = test "cewou46e" (lazy(sprintf $"%d{2147483648ul}" )) "2147483648" +let _ = test "cewou46f" (lazy(sprintf $"%d{4294967295ul}" )) "4294967295" +let _ = test "cewou46g" (lazy(sprintf $"%d{2147483649ul}" )) "2147483649" +let _ = test "cewou46h" (lazy(sprintf $"%d{2147483647ul}" )) "2147483647" + +let _ = test "ceew903" (lazy(sprintf $"%u{System.Int64.MaxValue}" )) "9223372036854775807" +let _ = test "ceew903" (lazy(sprintf $"%u{System.Int64.MinValue}" )) "9223372036854775808" +let _ = test "ceew903" (lazy(sprintf $"%d{System.Int64.MaxValue}" )) "9223372036854775807" +let _ = test "ceew903" (lazy(sprintf $"%d{System.Int64.MinValue}" )) "-9223372036854775808" + +let _ = test "ceew903" (lazy(sprintf $"%u{System.Int64.MaxValue}" )) "9223372036854775807" +let _ = test "ceew903" (lazy(sprintf $"%u{System.Int64.MinValue}" )) "9223372036854775808" +let _ = test "ceew903" (lazy(sprintf $"%d{System.Int64.MaxValue}" )) "9223372036854775807" +let _ = test "ceew903" (lazy(sprintf $"%d{System.Int64.MinValue}" )) "-9223372036854775808" + +let _ = test "cewou47a" (lazy(sprintf $"%d{0n}" )) "0" +let _ = test "cewou47b" (lazy(sprintf $"%d{5n}" )) "5" +let _ = test "cewou47c" (lazy(sprintf $"%d{8n}" )) "8" +let _ = test "cewou47d" (lazy(sprintf $"%d{15n}" )) "15" +let _ = test "cewou47e" (lazy(sprintf $"%u{2147483648n}" )) "2147483648" +let _ = test "cewou47f" (lazy(sprintf $"%u{4294967295n}" )) "4294967295" +let _ = test "cewou47g" (lazy(sprintf $"%u{2147483649n}" )) "2147483649" +let _ = test "cewou47h" (lazy(sprintf $"%u{2147483647n}" )) "2147483647" + +let _ = test "cewou47a" (lazy(sprintf $"%d{0n}" )) "0" +let _ = test "cewou47b" (lazy(sprintf $"%d{5n}" )) "5" +let _ = test "cewou47c" (lazy(sprintf $"%d{8n}" )) "8" +let _ = test "cewou47d" (lazy(sprintf $"%d{15n}" )) "15" +let _ = test "cewou47e" (lazy(sprintf $"%u{2147483648n}" )) "2147483648" +let _ = test "cewou47f" (lazy(sprintf $"%u{4294967295n}" )) "4294967295" +let _ = test "cewou47g" (lazy(sprintf $"%u{2147483649n}" )) "2147483649" +let _ = test "cewou47h" (lazy(sprintf $"%u{2147483647n}" )) "2147483647" + +let _ = test "cewou48a" (lazy(sprintf $"%d{0un}" )) "0" +let _ = test "cewou48b" (lazy(sprintf $"%d{5un}" )) "5" +let _ = test "cewou48c" (lazy(sprintf $"%d{8un}" )) "8" +let _ = test "cewou48d" (lazy(sprintf $"%d{15un}" )) "15" +let _ = test "cewou48e" (lazy(sprintf $"%u{2147483648un}" )) "2147483648" +let _ = test "cewou48f" (lazy(sprintf $"%u{4294967295un}" )) "4294967295" +let _ = test "cewou48g" (lazy(sprintf $"%u{2147483649un}" )) "2147483649" +let _ = test "cewou48h" (lazy(sprintf $"%u{2147483647un}" )) "2147483647" + +let _ = test "cewou49c" (lazy(sprintf $"%+d{5}" )) "+5" +let _ = test "cewou49d" (lazy(sprintf $"% d{5}" )) " 5" +let _ = test "cewou49e" (lazy(sprintf $"%+4d{5}" )) " +5" +let _ = test "cewou49f" (lazy(sprintf $"%-+4d{5}" )) "+5 " +let _ = test "cewou49g" (lazy(sprintf $"%-4d{5}" )) "5 " +let _ = test "cewou49h" (lazy(sprintf $"%- 4d{5}" )) " 5 " +let _ = test "cewou49i" (lazy(sprintf $"%- d{5}" )) " 5" +let _ = test "cewou49j" (lazy(sprintf $"% d{5}" )) " 5" +let _ = test "weioj31" (lazy(sprintf $"%3d{5}")) " 5" +let _ = test "weioj32" (lazy(sprintf $"%1d{5}")) "5" +let _ = test "weioj33" (lazy(sprintf $"%2d{500}")) "500" + +let _ = + test "test8535" (lazy(sprintf $"""%t{(fun _ -> "???")}""" )) "???" + test "test8536" (lazy(sprintf $"A%d{0}B")) "A0B" + test "test8537" (lazy(sprintf $"A%d{0}B%d{1}C")) "A0B1C" + test "test8538" (lazy(sprintf $"A%d{0}B%d{1}C%d{2}D")) "A0B1C2D" + test "test8539" (lazy(sprintf $"A%d{0}B%d{1}C%d{2}D%d{3}E")) "A0B1C2D3E" + test "test8540" (lazy(sprintf $"A%d{0}B%d{1}C%d{2}D%d{3}E%d{4}F")) "A0B1C2D3E4F" + test "test8541" (lazy(sprintf $"A%d{0}B%d{1}C%d{2}D%d{3}E%d{4}F%d{5}G")) "A0B1C2D3E4F5G" + test "test8542" (lazy(sprintf $"A%d{0}B%d{1}C%d{2}D%d{3}E%d{4}F%d{5}G%d{6}H")) "A0B1C2D3E4F5G6H" + test "test8543" (lazy(sprintf $"A%d{0}B%d{1}C%d{2}D%d{3}E%d{4}F%d{5}G%d{6}H%d{7}I")) "A0B1C2D3E4F5G6H7I" + + +let _ = + test "test8361" (lazy(sprintf $"""%A{"abc"}""")) "\"abc\"" + test "test8362" (lazy(sprintf $"""%5A{"abc"}""")) "\"abc\"" + test "test8363" (lazy(sprintf $"""%1A{"abc"}""")) "\"abc\"" + test "test8365" (lazy(sprintf $"""%.5A{"abc"}""")) "\"abc\"" + test "test8368" (lazy(sprintf $"""%-A{"abc"}""")) "\"abc\"" + test "test8369" (lazy(sprintf $"""%-5A{"abc"}""")) "\"abc\"" + test "test8370" (lazy(sprintf $"""%-1A{"abc"}""")) "\"abc\"" + test "test8372" (lazy(sprintf $"""%-.5A{"abc"}""")) "\"abc\"" + test "test8375" (lazy(sprintf $"""%+A{"abc"}""")) "\"abc\"" + test "test8376" (lazy(sprintf $"""%+5A{"abc"}""")) "\"abc\"" + test "test8377" (lazy(sprintf $"""%+1A{"abc"}""")) "\"abc\"" + test "test8379" (lazy(sprintf $"""%+.5A{"abc"}""")) "\"abc\"" + test "test8382" (lazy(sprintf $"""%-+A{"abc"}""")) "\"abc\"" + test "test8383" (lazy(sprintf $"""%-+5A{"abc"}""")) "\"abc\"" + test "test8384" (lazy(sprintf $"""%-+1A{"abc"}""")) "\"abc\"" + test "test8386" (lazy(sprintf $"""%-+.5A{"abc"}""")) "\"abc\"" + test "test8389" (lazy(sprintf $"""%A{15}""")) "15" + test "test8390" (lazy(sprintf $"""%5A{15}""")) "15" + test "test8391" (lazy(sprintf $"""%1A{15}""")) "15" + test "test8393" (lazy(sprintf $"""%.5A{15}""")) "15" + test "test8396" (lazy(sprintf $"""%-A{15}""")) "15" + test "test8397" (lazy(sprintf $"""%-5A{15}""")) "15" + test "test8398" (lazy(sprintf $"""%-1A{15}""")) "15" + test "test8400" (lazy(sprintf $"""%-.5A{15}""")) "15" + test "test8403" (lazy(sprintf $"""%+A{15}""")) "15" + test "test8404" (lazy(sprintf $"""%+5A{15}""")) "15" + test "test8405" (lazy(sprintf $"""%+1A{15}""")) "15" + test "test8407" (lazy(sprintf $"""%+.5A{15}""")) "15" + test "test8410" (lazy(sprintf $"""%-+A{15}""")) "15" + test "test8411" (lazy(sprintf $"""%-+5A{15}""")) "15" + test "test8412" (lazy(sprintf $"""%-+1A{15}""")) "15" + test "test8414" (lazy(sprintf $"""%-+.5A{15}""")) "15" + + test "test8417" (lazy(sprintf $"""%A{-10}""")) "-10" + test "test8418" (lazy(sprintf $"""%5A{-10}""")) "-10" + test "test8419" (lazy(sprintf $"""%1A{-10}""")) "-10" + test "test8421" (lazy(sprintf $"""%.5A{-10}""")) "-10" + test "test8424" (lazy(sprintf $"""%-A{-10}""")) "-10" + test "test8425" (lazy(sprintf $"""%-5A{-10}""")) "-10" + test "test8426" (lazy(sprintf $"""%-1A{-10}""")) "-10" + test "test8428" (lazy(sprintf $"""%-.5A{-10}""")) "-10" + test "test8431" (lazy(sprintf $"""%+A{-10}""")) "-10" + test "test8432" (lazy(sprintf $"""%+5A{-10}""")) "-10" + test "test8433" (lazy(sprintf $"""%+1A{-10}""")) "-10" + test "test8435" (lazy(sprintf $"""%+.5A{-10}""")) "-10" + test "test8438" (lazy(sprintf $"""%-+A{-10}""")) "-10" + test "test8439" (lazy(sprintf $"""%-+5A{-10}""")) "-10" + test "test8440" (lazy(sprintf $"""%-+1A{-10}""")) "-10" + test "test8442" (lazy(sprintf $"""%-+.5A{-10}""")) "-10" + + // NOTE: %A in $"..." does *not* pick up the static type, which makes a difference for 'None' + test "test8445a" (lazy(sprintf "%A" (None: int option))) "None" + test "test8445b" (lazy(sprintf $"""%A{(None: int option)}""")) "" + + test "test8445" (lazy(sprintf $"""%A{null}""")) "" + test "test8446" (lazy(sprintf $"""%5A{null}""")) "" + test "test8447" (lazy(sprintf $"""%1A{null}""")) "" + test "test8449" (lazy(sprintf $"""%.5A{null}""")) "" + test "test8452" (lazy(sprintf $"""%-A{null}""")) "" + test "test8453" (lazy(sprintf $"""%-5A{null}""")) "" + test "test8454" (lazy(sprintf $"""%-1A{null}""")) "" + test "test8456" (lazy(sprintf $"""%-.5A{null}""")) "" + test "test8459" (lazy(sprintf $"""%+A{null}""")) "" + test "test8460" (lazy(sprintf $"""%+5A{null}""")) "" + test "test8461" (lazy(sprintf $"""%+1A{null}""")) "" + test "test8463" (lazy(sprintf $"""%+.5A{null}""")) "" + test "test8466" (lazy(sprintf $"""%-+A{null}""")) "" + test "test8467" (lazy(sprintf $"""%-+5A{null}""")) "" + test "test8468" (lazy(sprintf $"""%-+1A{null}""")) "" + test "test8470" (lazy(sprintf $"""%-+.5A{null}""")) "" + + +#if TESTS_AS_APP +let RUN() = !failures +#else +let aa = + match !failures with + | [] -> + stdout.WriteLine "Test Passed" + System.IO.File.WriteAllText("test.ok","ok") + exit 0 + | _ -> + stdout.WriteLine "Test Failed" + exit 1 +#endif + diff --git a/tests/fsharp/core/printf/test.fsx b/tests/fsharp/core/printf/test.fsx index fe48744f84e..c2f08c7fe8a 100644 --- a/tests/fsharp/core/printf/test.fsx +++ b/tests/fsharp/core/printf/test.fsx @@ -17,7 +17,7 @@ let report_failure (s : string) = // change this to true to run every test case // leave as false to randomly execute a subset of cases (this is a very expensive test area) -let runEveryTest = false +let runEveryTest = true if runEveryTest then stdout.WriteLine "Running every test. Set \"runEveryTest\" to false to reduce runtime" else stdout.WriteLine "Running a random subset of tests. Set \"runEveryTest\" to true to run all tests" @@ -25,6 +25,7 @@ let rnd = System.Random() let test t (s1:Lazy) s2 = if runEveryTest || (rnd.Next() % 10) = 0 then + stdout.WriteLine ("running test "+t+"...") let s1 = s1.Force() if s1 <> s2 then report_failure ("test "+t+": expected \n\t'"+s2+"' but produced \n\t'"+s1+"'") @@ -8546,744 +8547,744 @@ let func7000()= test "test7998" (lazy(sprintf "%-0M" 10M)) "10" test "test7999" (lazy(sprintf "%-05M" 10M)) "10 "//"10000" test "test8000" (lazy(sprintf "%-01M" 10M)) "10" -let func8000()= - test "test8001" (lazy(sprintf "%-0*M" 7 10M)) "10 "//"1000000" - test "test8002" (lazy(sprintf "%-0.5M" 10M)) "10" - test "test8003" (lazy(sprintf "%-0.*M" 4 10M)) "10" - test "test8004" (lazy(sprintf "%-0*.*M" 5 4 10M)) "10 "//"10000" - test "test8005" (lazy(sprintf "%+M" 10M)) "+10" - test "test8006" (lazy(sprintf "%+5M" 10M)) " +10" - test "test8007" (lazy(sprintf "%+1M" 10M)) "+10" - test "test8008" (lazy(sprintf "%+*M" 7 10M)) " +10" - test "test8009" (lazy(sprintf "%+.5M" 10M)) "+10" - test "test8010" (lazy(sprintf "%+.*M" 4 10M)) "+10" - test "test8011" (lazy(sprintf "%+*.*M" 5 4 10M)) " +10" - test "test8012" (lazy(sprintf "%-+M" 10M)) "+10" - test "test8013" (lazy(sprintf "%-+5M" 10M)) "+10 " - test "test8014" (lazy(sprintf "%-+1M" 10M)) "+10" - test "test8015" (lazy(sprintf "%-+*M" 7 10M)) "+10 " - test "test8016" (lazy(sprintf "%-+.5M" 10M)) "+10" - test "test8017" (lazy(sprintf "%-+.*M" 4 10M)) "+10" - test "test8018" (lazy(sprintf "%-+*.*M" 5 4 10M)) "+10 " - test "test8019" (lazy(sprintf "%+0M" 10M)) "+10" - test "test8020" (lazy(sprintf "%+05M" 10M)) "+0010"// "00+10" - test "test8021" (lazy(sprintf "%+01M" 10M)) "+10" - test "test8022" (lazy(sprintf "%+0*M" 7 10M)) "+000010"// "0000+10" - test "test8023" (lazy(sprintf "%+0.5M" 10M)) "+10" - test "test8024" (lazy(sprintf "%+0.*M" 4 10M)) "+10" - test "test8025" (lazy(sprintf "%+0*.*M" 5 4 10M)) "+0010"// "00+10" - test "test8026" (lazy(sprintf "%-+0M" 10M)) "+10" - test "test8027" (lazy(sprintf "%-+05M" 10M)) "+10 "//"+1000" - test "test8028" (lazy(sprintf "%-+01M" 10M)) "+10" - test "test8029" (lazy(sprintf "%-+0*M" 7 10M)) "+10 "// "+100000" - test "test8030" (lazy(sprintf "%-+0.5M" 10M)) "+10" - test "test8031" (lazy(sprintf "%-+0.*M" 4 10M)) "+10" - test "test8032" (lazy(sprintf "%-+0*.*M" 5 4 10M)) "+10 "//"+1000" - test "test8033" (lazy(sprintf "% M" 10M)) " 10" - test "test8034" (lazy(sprintf "% 5M" 10M)) " 10" - test "test8035" (lazy(sprintf "% 1M" 10M)) " 10" - test "test8036" (lazy(sprintf "% *M" 7 10M)) " 10" - test "test8037" (lazy(sprintf "% .5M" 10M)) " 10" - test "test8038" (lazy(sprintf "% .*M" 4 10M)) " 10" - test "test8039" (lazy(sprintf "% *.*M" 5 4 10M)) " 10" - test "test8040" (lazy(sprintf "%- M" 10M)) " 10" - test "test8041" (lazy(sprintf "%- 5M" 10M)) " 10 " - test "test8042" (lazy(sprintf "%- 1M" 10M)) " 10" - test "test8043" (lazy(sprintf "%- *M" 7 10M)) " 10 " - test "test8044" (lazy(sprintf "%- .5M" 10M)) " 10" - test "test8045" (lazy(sprintf "%- .*M" 4 10M)) " 10" - test "test8046" (lazy(sprintf "%- *.*M" 5 4 10M)) " 10 " - test "test8047" (lazy(sprintf "% 0M" 10M)) " 10" - test "test8048" (lazy(sprintf "% 05M" 10M)) " 0010"// "00 10" - test "test8049" (lazy(sprintf "% 01M" 10M)) " 10" - test "test8050" (lazy(sprintf "% 0*M" 7 10M)) " 000010"// "0000 10" - test "test8051" (lazy(sprintf "% 0.5M" 10M)) " 10" - test "test8052" (lazy(sprintf "% 0.*M" 4 10M)) " 10" - test "test8053" (lazy(sprintf "% 0*.*M" 5 4 10M)) " 0010"// "00 10" - test "test8054" (lazy(sprintf "%- 0M" 10M)) " 10" - test "test8055" (lazy(sprintf "%- 05M" 10M)) " 10 "// " 1000" - test "test8056" (lazy(sprintf "%- 01M" 10M)) " 10" - test "test8057" (lazy(sprintf "%- 0*M" 7 10M)) " 10 "//" 100000" - test "test8058" (lazy(sprintf "%- 0.5M" 10M)) " 10" - test "test8059" (lazy(sprintf "%- 0.*M" 4 10M)) " 10" - test "test8060" (lazy(sprintf "%- 0*.*M" 5 4 10M)) " 10 "//" 1000" - test "test8061" (lazy(sprintf "%M" 1.3M)) "1.3" - test "test8062" (lazy(sprintf "%5M" 1.3M)) " 1.3" - test "test8063" (lazy(sprintf "%1M" 1.3M)) "1.3" - test "test8064" (lazy(sprintf "%*M" 7 1.3M)) " 1.3" - test "test8065" (lazy(sprintf "%.5M" 1.3M)) "1.3" - test "test8066" (lazy(sprintf "%.*M" 4 1.3M)) "1.3" - test "test8067" (lazy(sprintf "%*.*M" 5 4 1.3M)) " 1.3" - test "test8068" (lazy(sprintf "%-M" 1.3M)) "1.3" - test "test8069" (lazy(sprintf "%-5M" 1.3M)) "1.3 " - test "test8070" (lazy(sprintf "%-1M" 1.3M)) "1.3" - test "test8071" (lazy(sprintf "%-*M" 7 1.3M)) "1.3 " - test "test8072" (lazy(sprintf "%-.5M" 1.3M)) "1.3" - test "test8073" (lazy(sprintf "%-.*M" 4 1.3M)) "1.3" - test "test8074" (lazy(sprintf "%-*.*M" 5 4 1.3M)) "1.3 " - test "test8075" (lazy(sprintf "%0M" 1.3M)) "1.3" - test "test8076" (lazy(sprintf "%05M" 1.3M)) "001.3" - test "test8077" (lazy(sprintf "%01M" 1.3M)) "1.3" - test "test8078" (lazy(sprintf "%0*M" 7 1.3M)) "00001.3" - test "test8079" (lazy(sprintf "%0.5M" 1.3M)) "1.3" - test "test8080" (lazy(sprintf "%0.*M" 4 1.3M)) "1.3" - test "test8081" (lazy(sprintf "%0*.*M" 5 4 1.3M)) "001.3" - test "test8082" (lazy(sprintf "%-0M" 1.3M)) "1.3" - test "test8083" (lazy(sprintf "%-05M" 1.3M)) "1.300" - test "test8084" (lazy(sprintf "%-01M" 1.3M)) "1.3" - test "test8085" (lazy(sprintf "%-0*M" 7 1.3M)) "1.30000" - test "test8086" (lazy(sprintf "%-0.5M" 1.3M)) "1.3" - test "test8087" (lazy(sprintf "%-0.*M" 4 1.3M)) "1.3" - test "test8088" (lazy(sprintf "%-0*.*M" 5 4 1.3M)) "1.300" - test "test8089" (lazy(sprintf "%+M" 1.3M)) "+1.3" - test "test8090" (lazy(sprintf "%+5M" 1.3M)) " +1.3" - test "test8091" (lazy(sprintf "%+1M" 1.3M)) "+1.3" - test "test8092" (lazy(sprintf "%+*M" 7 1.3M)) " +1.3" - test "test8093" (lazy(sprintf "%+.5M" 1.3M)) "+1.3" - test "test8094" (lazy(sprintf "%+.*M" 4 1.3M)) "+1.3" - test "test8095" (lazy(sprintf "%+*.*M" 5 4 1.3M)) " +1.3" - test "test8096" (lazy(sprintf "%-+M" 1.3M)) "+1.3" - test "test8097" (lazy(sprintf "%-+5M" 1.3M)) "+1.3 " - test "test8098" (lazy(sprintf "%-+1M" 1.3M)) "+1.3" - test "test8099" (lazy(sprintf "%-+*M" 7 1.3M)) "+1.3 " - test "test8100" (lazy(sprintf "%-+.5M" 1.3M)) "+1.3" - test "test8101" (lazy(sprintf "%-+.*M" 4 1.3M)) "+1.3" - test "test8102" (lazy(sprintf "%-+*.*M" 5 4 1.3M)) "+1.3 " - test "test8103" (lazy(sprintf "%+0M" 1.3M)) "+1.3" - test "test8104" (lazy(sprintf "%+05M" 1.3M)) "+01.3"//"0+1.3" - test "test8105" (lazy(sprintf "%+01M" 1.3M)) "+1.3" - test "test8106" (lazy(sprintf "%+0*M" 7 1.3M)) "+0001.3"//"000+1.3" - test "test8107" (lazy(sprintf "%+0.5M" 1.3M)) "+1.3" - test "test8108" (lazy(sprintf "%+0.*M" 4 1.3M)) "+1.3" - test "test8109" (lazy(sprintf "%+0*.*M" 5 4 1.3M)) "+01.3"//"0+1.3" - test "test8110" (lazy(sprintf "%-+0M" 1.3M)) "+1.3" - test "test8111" (lazy(sprintf "%-+05M" 1.3M)) "+1.30" - test "test8112" (lazy(sprintf "%-+01M" 1.3M)) "+1.3" - test "test8113" (lazy(sprintf "%-+0*M" 7 1.3M)) "+1.3000" - test "test8114" (lazy(sprintf "%-+0.5M" 1.3M)) "+1.3" - test "test8115" (lazy(sprintf "%-+0.*M" 4 1.3M)) "+1.3" - test "test8116" (lazy(sprintf "%-+0*.*M" 5 4 1.3M)) "+1.30" - test "test8117" (lazy(sprintf "% M" 1.3M)) " 1.3" - test "test8118" (lazy(sprintf "% 5M" 1.3M)) " 1.3" - test "test8119" (lazy(sprintf "% 1M" 1.3M)) " 1.3" - test "test8120" (lazy(sprintf "% *M" 7 1.3M)) " 1.3" - test "test8121" (lazy(sprintf "% .5M" 1.3M)) " 1.3" - test "test8122" (lazy(sprintf "% .*M" 4 1.3M)) " 1.3" - test "test8123" (lazy(sprintf "% *.*M" 5 4 1.3M)) " 1.3" - test "test8124" (lazy(sprintf "%- M" 1.3M)) " 1.3" - test "test8125" (lazy(sprintf "%- 5M" 1.3M)) " 1.3 " - test "test8126" (lazy(sprintf "%- 1M" 1.3M)) " 1.3" - test "test8127" (lazy(sprintf "%- *M" 7 1.3M)) " 1.3 " - test "test8128" (lazy(sprintf "%- .5M" 1.3M)) " 1.3" - test "test8129" (lazy(sprintf "%- .*M" 4 1.3M)) " 1.3" - test "test8130" (lazy(sprintf "%- *.*M" 5 4 1.3M)) " 1.3 " - test "test8131" (lazy(sprintf "% 0M" 1.3M)) " 1.3" - test "test8132" (lazy(sprintf "% 05M" 1.3M)) " 01.3"//"0 1.3" - test "test8133" (lazy(sprintf "% 01M" 1.3M)) " 1.3" - test "test8134" (lazy(sprintf "% 0*M" 7 1.3M)) " 0001.3"// "000 1.3" - test "test8135" (lazy(sprintf "% 0.5M" 1.3M)) " 1.3" - test "test8136" (lazy(sprintf "% 0.*M" 4 1.3M)) " 1.3" - test "test8137" (lazy(sprintf "% 0*.*M" 5 4 1.3M)) " 01.3"// "0 1.3" - test "test8138" (lazy(sprintf "%- 0M" 1.3M)) " 1.3" - test "test8139" (lazy(sprintf "%- 05M" 1.3M)) " 1.30" - test "test8140" (lazy(sprintf "%- 01M" 1.3M)) " 1.3" - test "test8141" (lazy(sprintf "%- 0*M" 7 1.3M)) " 1.3000" - test "test8142" (lazy(sprintf "%- 0.5M" 1.3M)) " 1.3" - test "test8143" (lazy(sprintf "%- 0.*M" 4 1.3M)) " 1.3" - test "test8144" (lazy(sprintf "%- 0*.*M" 5 4 1.3M)) " 1.30" - test "test8145" (lazy(sprintf "%M" -15.5M)) "-15.5" - test "test8146" (lazy(sprintf "%5M" -15.5M)) "-15.5" - test "test8147" (lazy(sprintf "%1M" -15.5M)) "-15.5" - test "test8148" (lazy(sprintf "%*M" 7 -15.5M)) " -15.5" - test "test8149" (lazy(sprintf "%.5M" -15.5M)) "-15.5" - test "test8150" (lazy(sprintf "%.*M" 4 -15.5M)) "-15.5" - test "test8151" (lazy(sprintf "%*.*M" 5 4 -15.5M)) "-15.5" - test "test8152" (lazy(sprintf "%-M" -15.5M)) "-15.5" - test "test8153" (lazy(sprintf "%-5M" -15.5M)) "-15.5" - test "test8154" (lazy(sprintf "%-1M" -15.5M)) "-15.5" - test "test8155" (lazy(sprintf "%-*M" 7 -15.5M)) "-15.5 " - test "test8156" (lazy(sprintf "%-.5M" -15.5M)) "-15.5" - test "test8157" (lazy(sprintf "%-.*M" 4 -15.5M)) "-15.5" - test "test8158" (lazy(sprintf "%-*.*M" 5 4 -15.5M)) "-15.5" - test "test8159" (lazy(sprintf "%0M" -15.5M)) "-15.5" - test "test8160" (lazy(sprintf "%05M" -15.5M)) "-15.5" - test "test8161" (lazy(sprintf "%01M" -15.5M)) "-15.5" - test "test8162" (lazy(sprintf "%0*M" 7 -15.5M)) "-0015.5"// "00-15.5" - test "test8163" (lazy(sprintf "%0.5M" -15.5M)) "-15.5" - test "test8164" (lazy(sprintf "%0.*M" 4 -15.5M)) "-15.5" - test "test8165" (lazy(sprintf "%0*.*M" 5 4 -15.5M)) "-15.5" - test "test8166" (lazy(sprintf "%-0M" -15.5M)) "-15.5" - test "test8167" (lazy(sprintf "%-05M" -15.5M)) "-15.5" - test "test8168" (lazy(sprintf "%-01M" -15.5M)) "-15.5" - test "test8169" (lazy(sprintf "%-0*M" 7 -15.5M)) "-15.500" - test "test8170" (lazy(sprintf "%-0.5M" -15.5M)) "-15.5" - test "test8171" (lazy(sprintf "%-0.*M" 4 -15.5M)) "-15.5" - test "test8172" (lazy(sprintf "%-0*.*M" 5 4 -15.5M)) "-15.5" - test "test8173" (lazy(sprintf "%+M" -15.5M)) "-15.5" - test "test8174" (lazy(sprintf "%+5M" -15.5M)) "-15.5" - test "test8175" (lazy(sprintf "%+1M" -15.5M)) "-15.5" - test "test8176" (lazy(sprintf "%+*M" 7 -15.5M)) " -15.5" - test "test8177" (lazy(sprintf "%+.5M" -15.5M)) "-15.5" - test "test8178" (lazy(sprintf "%+.*M" 4 -15.5M)) "-15.5" - test "test8179" (lazy(sprintf "%+*.*M" 5 4 -15.5M)) "-15.5" - test "test8180" (lazy(sprintf "%-+M" -15.5M)) "-15.5" - test "test8181" (lazy(sprintf "%-+5M" -15.5M)) "-15.5" - test "test8182" (lazy(sprintf "%-+1M" -15.5M)) "-15.5" - test "test8183" (lazy(sprintf "%-+*M" 7 -15.5M)) "-15.5 " - test "test8184" (lazy(sprintf "%-+.5M" -15.5M)) "-15.5" - test "test8185" (lazy(sprintf "%-+.*M" 4 -15.5M)) "-15.5" - test "test8186" (lazy(sprintf "%-+*.*M" 5 4 -15.5M)) "-15.5" - test "test8187" (lazy(sprintf "%+0M" -15.5M)) "-15.5" - test "test8188" (lazy(sprintf "%+05M" -15.5M)) "-15.5" - test "test8189" (lazy(sprintf "%+01M" -15.5M)) "-15.5" - test "test8190" (lazy(sprintf "%+0*M" 7 -15.5M)) "-0015.5"//"00-15.5" - test "test8191" (lazy(sprintf "%+0.5M" -15.5M)) "-15.5" - test "test8192" (lazy(sprintf "%+0.*M" 4 -15.5M)) "-15.5" - test "test8193" (lazy(sprintf "%+0*.*M" 5 4 -15.5M)) "-15.5" - test "test8194" (lazy(sprintf "%-+0M" -15.5M)) "-15.5" - test "test8195" (lazy(sprintf "%-+05M" -15.5M)) "-15.5" - test "test8196" (lazy(sprintf "%-+01M" -15.5M)) "-15.5" - test "test8197" (lazy(sprintf "%-+0*M" 7 -15.5M)) "-15.500" - test "test8198" (lazy(sprintf "%-+0.5M" -15.5M)) "-15.5" - test "test8199" (lazy(sprintf "%-+0.*M" 4 -15.5M)) "-15.5" - test "test8200" (lazy(sprintf "%-+0*.*M" 5 4 -15.5M)) "-15.5" - test "test8201" (lazy(sprintf "% M" -15.5M)) "-15.5" - test "test8202" (lazy(sprintf "% 5M" -15.5M)) "-15.5" - test "test8203" (lazy(sprintf "% 1M" -15.5M)) "-15.5" - test "test8204" (lazy(sprintf "% *M" 7 -15.5M)) " -15.5" - test "test8205" (lazy(sprintf "% .5M" -15.5M)) "-15.5" - test "test8206" (lazy(sprintf "% .*M" 4 -15.5M)) "-15.5" - test "test8207" (lazy(sprintf "% *.*M" 5 4 -15.5M)) "-15.5" - test "test8208" (lazy(sprintf "%- M" -15.5M)) "-15.5" - test "test8209" (lazy(sprintf "%- 5M" -15.5M)) "-15.5" - test "test8210" (lazy(sprintf "%- 1M" -15.5M)) "-15.5" - test "test8211" (lazy(sprintf "%- *M" 7 -15.5M)) "-15.5 " - test "test8212" (lazy(sprintf "%- .5M" -15.5M)) "-15.5" - test "test8213" (lazy(sprintf "%- .*M" 4 -15.5M)) "-15.5" - test "test8214" (lazy(sprintf "%- *.*M" 5 4 -15.5M)) "-15.5" - test "test8215" (lazy(sprintf "% 0M" -15.5M)) "-15.5" - test "test8216" (lazy(sprintf "% 05M" -15.5M)) "-15.5" - test "test8217" (lazy(sprintf "% 01M" -15.5M)) "-15.5" - test "test8218" (lazy(sprintf "% 0*M" 7 -15.5M)) "-0015.5"//"00-15.5" - test "test8219" (lazy(sprintf "% 0.5M" -15.5M)) "-15.5" - test "test8220" (lazy(sprintf "% 0.*M" 4 -15.5M)) "-15.5" - test "test8221" (lazy(sprintf "% 0*.*M" 5 4 -15.5M)) "-15.5" - test "test8222" (lazy(sprintf "%- 0M" -15.5M)) "-15.5" - test "test8223" (lazy(sprintf "%- 05M" -15.5M)) "-15.5" - test "test8224" (lazy(sprintf "%- 01M" -15.5M)) "-15.5" - test "test8225" (lazy(sprintf "%- 0*M" 7 -15.5M)) "-15.500" - test "test8226" (lazy(sprintf "%- 0.5M" -15.5M)) "-15.5" - test "test8227" (lazy(sprintf "%- 0.*M" 4 -15.5M)) "-15.5" - test "test8228" (lazy(sprintf "%- 0*.*M" 5 4 -15.5M)) "-15.5" - test "test8229" (lazy(sprintf "%M" -7M)) "-7" - test "test8230" (lazy(sprintf "%5M" -7M)) " -7" - test "test8231" (lazy(sprintf "%1M" -7M)) "-7" - test "test8232" (lazy(sprintf "%*M" 7 -7M)) " -7" - test "test8233" (lazy(sprintf "%.5M" -7M)) "-7" - test "test8234" (lazy(sprintf "%.*M" 4 -7M)) "-7" - test "test8235" (lazy(sprintf "%*.*M" 5 4 -7M)) " -7" - test "test8236" (lazy(sprintf "%-M" -7M)) "-7" - test "test8237" (lazy(sprintf "%-5M" -7M)) "-7 " - test "test8238" (lazy(sprintf "%-1M" -7M)) "-7" - test "test8239" (lazy(sprintf "%-*M" 7 -7M)) "-7 " - test "test8240" (lazy(sprintf "%-.5M" -7M)) "-7" - test "test8241" (lazy(sprintf "%-.*M" 4 -7M)) "-7" - test "test8242" (lazy(sprintf "%-*.*M" 5 4 -7M)) "-7 " - test "test8243" (lazy(sprintf "%0M" -7M)) "-7" - test "test8244" (lazy(sprintf "%05M" -7M)) "-0007"//"000-7" - test "test8245" (lazy(sprintf "%01M" -7M)) "-7" - test "test8246" (lazy(sprintf "%0*M" 7 -7M)) "-000007"// "00000-7" - test "test8247" (lazy(sprintf "%0.5M" -7M)) "-7" - test "test8248" (lazy(sprintf "%0.*M" 4 -7M)) "-7" - test "test8249" (lazy(sprintf "%0*.*M" 5 4 -7M)) "-0007"// "000-7" - test "test8250" (lazy(sprintf "%-0M" -7M)) "-7" - test "test8251" (lazy(sprintf "%-05M" -7M)) "-7 "// "-7000" - test "test8252" (lazy(sprintf "%-01M" -7M)) "-7" - test "test8253" (lazy(sprintf "%-0*M" 7 -7M)) "-7 "//"-700000" - test "test8254" (lazy(sprintf "%-0.5M" -7M)) "-7" - test "test8255" (lazy(sprintf "%-0.*M" 4 -7M)) "-7" - test "test8256" (lazy(sprintf "%-0*.*M" 5 4 -7M)) "-7 "//"-7000" - test "test8257" (lazy(sprintf "%+M" -7M)) "-7" - test "test8258" (lazy(sprintf "%+5M" -7M)) " -7" - test "test8259" (lazy(sprintf "%+1M" -7M)) "-7" - test "test8260" (lazy(sprintf "%+*M" 7 -7M)) " -7" - test "test8261" (lazy(sprintf "%+.5M" -7M)) "-7" - test "test8262" (lazy(sprintf "%+.*M" 4 -7M)) "-7" - test "test8263" (lazy(sprintf "%+*.*M" 5 4 -7M)) " -7" - test "test8264" (lazy(sprintf "%-+M" -7M)) "-7" - test "test8265" (lazy(sprintf "%-+5M" -7M)) "-7 " - test "test8266" (lazy(sprintf "%-+1M" -7M)) "-7" - test "test8267" (lazy(sprintf "%-+*M" 7 -7M)) "-7 " - test "test8268" (lazy(sprintf "%-+.5M" -7M)) "-7" - test "test8269" (lazy(sprintf "%-+.*M" 4 -7M)) "-7" - test "test8270" (lazy(sprintf "%-+*.*M" 5 4 -7M)) "-7 " - test "test8271" (lazy(sprintf "%+0M" -7M)) "-7" - test "test8272" (lazy(sprintf "%+05M" -7M)) "-0007"// "000-7" - test "test8273" (lazy(sprintf "%+01M" -7M)) "-7" - test "test8274" (lazy(sprintf "%+0*M" 7 -7M)) "-000007"//"00000-7" - test "test8275" (lazy(sprintf "%+0.5M" -7M)) "-7" - test "test8276" (lazy(sprintf "%+0.*M" 4 -7M)) "-7" - test "test8277" (lazy(sprintf "%+0*.*M" 5 4 -7M)) "-0007"//"000-7" - test "test8278" (lazy(sprintf "%-+0M" -7M)) "-7" - test "test8279" (lazy(sprintf "%-+05M" -7M)) "-7 "// "-7000" - test "test8280" (lazy(sprintf "%-+01M" -7M)) "-7" - test "test8281" (lazy(sprintf "%-+0*M" 7 -7M)) "-7 "// "-700000" - test "test8282" (lazy(sprintf "%-+0.5M" -7M)) "-7" - test "test8283" (lazy(sprintf "%-+0.*M" 4 -7M)) "-7" - test "test8284" (lazy(sprintf "%-+0*.*M" 5 4 -7M)) "-7 "//"-7000" - test "test8285" (lazy(sprintf "% M" -7M)) "-7" - test "test8286" (lazy(sprintf "% 5M" -7M)) " -7" - test "test8287" (lazy(sprintf "% 1M" -7M)) "-7" - test "test8288" (lazy(sprintf "% *M" 7 -7M)) " -7" - test "test8289" (lazy(sprintf "% .5M" -7M)) "-7" - test "test8290" (lazy(sprintf "% .*M" 4 -7M)) "-7" - test "test8291" (lazy(sprintf "% *.*M" 5 4 -7M)) " -7" - test "test8292" (lazy(sprintf "%- M" -7M)) "-7" - test "test8293" (lazy(sprintf "%- 5M" -7M)) "-7 " - test "test8294" (lazy(sprintf "%- 1M" -7M)) "-7" - test "test8295" (lazy(sprintf "%- *M" 7 -7M)) "-7 " - test "test8296" (lazy(sprintf "%- .5M" -7M)) "-7" - test "test8297" (lazy(sprintf "%- .*M" 4 -7M)) "-7" - test "test8298" (lazy(sprintf "%- *.*M" 5 4 -7M)) "-7 " - test "test8299" (lazy(sprintf "% 0M" -7M)) "-7" - test "test8300" (lazy(sprintf "% 05M" -7M)) "-0007"//"000-7" - test "test8301" (lazy(sprintf "% 01M" -7M)) "-7" - test "test8302" (lazy(sprintf "% 0*M" 7 -7M)) "-000007"//"00000-7" - test "test8303" (lazy(sprintf "% 0.5M" -7M)) "-7" - test "test8304" (lazy(sprintf "% 0.*M" 4 -7M)) "-7" - test "test8305" (lazy(sprintf "% 0*.*M" 5 4 -7M)) "-0007"// "000-7" - test "test8306" (lazy(sprintf "%- 0M" -7M)) "-7" - test "test8307" (lazy(sprintf "%- 05M" -7M)) "-7 "// "-7000" - test "test8308" (lazy(sprintf "%- 01M" -7M)) "-7" - test "test8309" (lazy(sprintf "%- 0*M" 7 -7M)) "-7 "//"-700000" - test "test8310" (lazy(sprintf "%- 0.5M" -7M)) "-7" - test "test8311" (lazy(sprintf "%- 0.*M" 4 -7M)) "-7" - test "test8312" (lazy(sprintf "%- 0*.*M" 5 4 -7M)) "-7 "///"-7000" - test "test8313" (lazy(sprintf "%O" "abc")) "abc" - test "test8314" (lazy(sprintf "%5O" "abc")) " abc" - test "test8315" (lazy(sprintf "%1O" "abc")) "abc" - test "test8316" (lazy(sprintf "%*O" 7 "abc")) " abc" - test "test8317" (lazy(sprintf "%-O" "abc")) "abc" - test "test8318" (lazy(sprintf "%-5O" "abc")) "abc " - test "test8319" (lazy(sprintf "%-1O" "abc")) "abc" - test "test8320" (lazy(sprintf "%-*O" 7 "abc")) "abc " - test "test8321" (lazy(sprintf "%O" 15)) "15" - test "test8322" (lazy(sprintf "%5O" 15)) " 15" - test "test8323" (lazy(sprintf "%1O" 15)) "15" - test "test8324" (lazy(sprintf "%*O" 7 15)) " 15" - test "test8325" (lazy(sprintf "%-O" 15)) "15" - test "test8326" (lazy(sprintf "%-5O" 15)) "15 " - test "test8327" (lazy(sprintf "%-1O" 15)) "15" - test "test8328" (lazy(sprintf "%-*O" 7 15)) "15 " - test "test8329" (lazy(sprintf "%O" -10)) "-10" - test "test8330" (lazy(sprintf "%5O" -10)) " -10" - test "test8331" (lazy(sprintf "%1O" -10)) "-10" - test "test8332" (lazy(sprintf "%*O" 7 -10)) " -10" - test "test8333" (lazy(sprintf "%-O" -10)) "-10" - test "test8334" (lazy(sprintf "%-5O" -10)) "-10 " - test "test8335" (lazy(sprintf "%-1O" -10)) "-10" - test "test8336" (lazy(sprintf "%-*O" 7 -10)) "-10 " - test "test8337" (lazy(sprintf "%O" null)) "" - test "test8338" (lazy(sprintf "%5O" null)) "" - test "test8339" (lazy(sprintf "%1O" null)) "" - test "test8340" (lazy(sprintf "%*O" 7 null)) " " - test "test8341" (lazy(sprintf "%-O" null)) "" - test "test8342" (lazy(sprintf "%-5O" null)) "" - test "test8343" (lazy(sprintf "%-1O" null)) "" - test "test8344" (lazy(sprintf "%-*O" 7 null)) " " - test "test8345" (lazy(sprintf "%O" 'P')) "P" - test "test8346" (lazy(sprintf "%5O" 'P')) " P" - test "test8347" (lazy(sprintf "%1O" 'P')) "P" - test "test8348" (lazy(sprintf "%*O" 7 'P')) " P" - test "test8349" (lazy(sprintf "%-O" 'P')) "P" - test "test8350" (lazy(sprintf "%-5O" 'P')) "P " - test "test8351" (lazy(sprintf "%-1O" 'P')) "P" - test "test8352" (lazy(sprintf "%-*O" 7 'P')) "P " - test "test8353" (lazy(sprintf "%O" System.IO.FileShare.None)) "None" - test "test8354" (lazy(sprintf "%5O" System.IO.FileShare.None)) " None" - test "test8355" (lazy(sprintf "%1O" System.IO.FileShare.None)) "None" - test "test8356" (lazy(sprintf "%*O" 7 System.IO.FileShare.None)) " None" - test "test8357" (lazy(sprintf "%-O" System.IO.FileShare.None)) "None" - test "test8358" (lazy(sprintf "%-5O" System.IO.FileShare.None)) "None " - test "test8359" (lazy(sprintf "%-1O" System.IO.FileShare.None)) "None" - test "test8360" (lazy(sprintf "%-*O" 7 System.IO.FileShare.None)) "None " - test "test8361" (lazy(sprintf "%A" "abc")) "\"abc\"" - test "test8362" (lazy(sprintf "%5A" "abc")) "\"abc\"" - test "test8363" (lazy(sprintf "%1A" "abc")) "\"abc\"" - test "test8364" (lazy(sprintf "%*A" 7 "abc")) "\"abc\"" - test "test8365" (lazy(sprintf "%.5A" "abc")) "\"abc\"" - test "test8366" (lazy(sprintf "%.*A" 4 "abc")) "\"abc\"" - test "test8367" (lazy(sprintf "%*.*A" 5 4 "abc")) "\"abc\"" - test "test8368" (lazy(sprintf "%-A" "abc")) "\"abc\"" - test "test8369" (lazy(sprintf "%-5A" "abc")) "\"abc\"" - test "test8370" (lazy(sprintf "%-1A" "abc")) "\"abc\"" - test "test8371" (lazy(sprintf "%-*A" 7 "abc")) "\"abc\"" - test "test8372" (lazy(sprintf "%-.5A" "abc")) "\"abc\"" - test "test8373" (lazy(sprintf "%-.*A" 4 "abc")) "\"abc\"" - test "test8374" (lazy(sprintf "%-*.*A" 5 4 "abc")) "\"abc\"" - test "test8375" (lazy(sprintf "%+A" "abc")) "\"abc\"" - test "test8376" (lazy(sprintf "%+5A" "abc")) "\"abc\"" - test "test8377" (lazy(sprintf "%+1A" "abc")) "\"abc\"" - test "test8378" (lazy(sprintf "%+*A" 7 "abc")) "\"abc\"" - test "test8379" (lazy(sprintf "%+.5A" "abc")) "\"abc\"" - test "test8380" (lazy(sprintf "%+.*A" 4 "abc")) "\"abc\"" - test "test8381" (lazy(sprintf "%+*.*A" 5 4 "abc")) "\"abc\"" - test "test8382" (lazy(sprintf "%-+A" "abc")) "\"abc\"" - test "test8383" (lazy(sprintf "%-+5A" "abc")) "\"abc\"" - test "test8384" (lazy(sprintf "%-+1A" "abc")) "\"abc\"" - test "test8385" (lazy(sprintf "%-+*A" 7 "abc")) "\"abc\"" - test "test8386" (lazy(sprintf "%-+.5A" "abc")) "\"abc\"" - test "test8387" (lazy(sprintf "%-+.*A" 4 "abc")) "\"abc\"" - test "test8388" (lazy(sprintf "%-+*.*A" 5 4 "abc")) "\"abc\"" - test "test8389" (lazy(sprintf "%A" 15)) "15" - test "test8390" (lazy(sprintf "%5A" 15)) "15" - test "test8391" (lazy(sprintf "%1A" 15)) "15" - test "test8392" (lazy(sprintf "%*A" 7 15)) "15" - test "test8393" (lazy(sprintf "%.5A" 15)) "15" - test "test8394" (lazy(sprintf "%.*A" 4 15)) "15" - test "test8395" (lazy(sprintf "%*.*A" 5 4 15)) "15" - test "test8396" (lazy(sprintf "%-A" 15)) "15" - test "test8397" (lazy(sprintf "%-5A" 15)) "15" - test "test8398" (lazy(sprintf "%-1A" 15)) "15" - test "test8399" (lazy(sprintf "%-*A" 7 15)) "15" - test "test8400" (lazy(sprintf "%-.5A" 15)) "15" - test "test8401" (lazy(sprintf "%-.*A" 4 15)) "15" - test "test8402" (lazy(sprintf "%-*.*A" 5 4 15)) "15" - test "test8403" (lazy(sprintf "%+A" 15)) "15" - test "test8404" (lazy(sprintf "%+5A" 15)) "15" - test "test8405" (lazy(sprintf "%+1A" 15)) "15" - test "test8406" (lazy(sprintf "%+*A" 7 15)) "15" - test "test8407" (lazy(sprintf "%+.5A" 15)) "15" - test "test8408" (lazy(sprintf "%+.*A" 4 15)) "15" - test "test8409" (lazy(sprintf "%+*.*A" 5 4 15)) "15" - test "test8410" (lazy(sprintf "%-+A" 15)) "15" - test "test8411" (lazy(sprintf "%-+5A" 15)) "15" - test "test8412" (lazy(sprintf "%-+1A" 15)) "15" - test "test8413" (lazy(sprintf "%-+*A" 7 15)) "15" - test "test8414" (lazy(sprintf "%-+.5A" 15)) "15" - test "test8415" (lazy(sprintf "%-+.*A" 4 15)) "15" - test "test8416" (lazy(sprintf "%-+*.*A" 5 4 15)) "15" - test "test8417" (lazy(sprintf "%A" -10)) "-10" - test "test8418" (lazy(sprintf "%5A" -10)) "-10" - test "test8419" (lazy(sprintf "%1A" -10)) "-10" - test "test8420" (lazy(sprintf "%*A" 7 -10)) "-10" - test "test8421" (lazy(sprintf "%.5A" -10)) "-10" - test "test8422" (lazy(sprintf "%.*A" 4 -10)) "-10" - test "test8423" (lazy(sprintf "%*.*A" 5 4 -10)) "-10" - test "test8424" (lazy(sprintf "%-A" -10)) "-10" - test "test8425" (lazy(sprintf "%-5A" -10)) "-10" - test "test8426" (lazy(sprintf "%-1A" -10)) "-10" - test "test8427" (lazy(sprintf "%-*A" 7 -10)) "-10" - test "test8428" (lazy(sprintf "%-.5A" -10)) "-10" - test "test8429" (lazy(sprintf "%-.*A" 4 -10)) "-10" - test "test8430" (lazy(sprintf "%-*.*A" 5 4 -10)) "-10" - test "test8431" (lazy(sprintf "%+A" -10)) "-10" - test "test8432" (lazy(sprintf "%+5A" -10)) "-10" - test "test8433" (lazy(sprintf "%+1A" -10)) "-10" - test "test8434" (lazy(sprintf "%+*A" 7 -10)) "-10" - test "test8435" (lazy(sprintf "%+.5A" -10)) "-10" - test "test8436" (lazy(sprintf "%+.*A" 4 -10)) "-10" - test "test8437" (lazy(sprintf "%+*.*A" 5 4 -10)) "-10" - test "test8438" (lazy(sprintf "%-+A" -10)) "-10" - test "test8439" (lazy(sprintf "%-+5A" -10)) "-10" - test "test8440" (lazy(sprintf "%-+1A" -10)) "-10" - test "test8441" (lazy(sprintf "%-+*A" 7 -10)) "-10" - test "test8442" (lazy(sprintf "%-+.5A" -10)) "-10" - test "test8443" (lazy(sprintf "%-+.*A" 4 -10)) "-10" - test "test8444" (lazy(sprintf "%-+*.*A" 5 4 -10)) "-10" - test "test8445" (lazy(sprintf "%A" null)) "" - test "test8446" (lazy(sprintf "%5A" null)) "" - test "test8447" (lazy(sprintf "%1A" null)) "" - test "test8448" (lazy(sprintf "%*A" 7 null)) "" - test "test8449" (lazy(sprintf "%.5A" null)) "" - test "test8450" (lazy(sprintf "%.*A" 4 null)) "" - test "test8451" (lazy(sprintf "%*.*A" 5 4 null)) "" - test "test8452" (lazy(sprintf "%-A" null)) "" - test "test8453" (lazy(sprintf "%-5A" null)) "" - test "test8454" (lazy(sprintf "%-1A" null)) "" - test "test8455" (lazy(sprintf "%-*A" 7 null)) "" - test "test8456" (lazy(sprintf "%-.5A" null)) "" - test "test8457" (lazy(sprintf "%-.*A" 4 null)) "" - test "test8458" (lazy(sprintf "%-*.*A" 5 4 null)) "" - test "test8459" (lazy(sprintf "%+A" null)) "" - test "test8460" (lazy(sprintf "%+5A" null)) "" - test "test8461" (lazy(sprintf "%+1A" null)) "" - test "test8462" (lazy(sprintf "%+*A" 7 null)) "" - test "test8463" (lazy(sprintf "%+.5A" null)) "" - test "test8464" (lazy(sprintf "%+.*A" 4 null)) "" - test "test8465" (lazy(sprintf "%+*.*A" 5 4 null)) "" - test "test8466" (lazy(sprintf "%-+A" null)) "" - test "test8467" (lazy(sprintf "%-+5A" null)) "" - test "test8468" (lazy(sprintf "%-+1A" null)) "" - test "test8469" (lazy(sprintf "%-+*A" 7 null)) "" - test "test8470" (lazy(sprintf "%-+.5A" null)) "" - test "test8471" (lazy(sprintf "%-+.*A" 4 null)) "" - test "test8472" (lazy(sprintf "%-+*.*A" 5 4 null)) "" - test "test8473" (lazy(sprintf "%A" 'P')) "'P'" - test "test8474" (lazy(sprintf "%5A" 'P')) "'P'" - test "test8475" (lazy(sprintf "%1A" 'P')) "'P'" - test "test8476" (lazy(sprintf "%*A" 7 'P')) "'P'" - test "test8477" (lazy(sprintf "%.5A" 'P')) "'P'" - test "test8478" (lazy(sprintf "%.*A" 4 'P')) "'P'" - test "test8479" (lazy(sprintf "%*.*A" 5 4 'P')) "'P'" - test "test8480" (lazy(sprintf "%-A" 'P')) "'P'" - test "test8481" (lazy(sprintf "%-5A" 'P')) "'P'" - test "test8482" (lazy(sprintf "%-1A" 'P')) "'P'" - test "test8483" (lazy(sprintf "%-*A" 7 'P')) "'P'" - test "test8484" (lazy(sprintf "%-.5A" 'P')) "'P'" - test "test8485" (lazy(sprintf "%-.*A" 4 'P')) "'P'" - test "test8486" (lazy(sprintf "%-*.*A" 5 4 'P')) "'P'" - test "test8487" (lazy(sprintf "%+A" 'P')) "'P'" - test "test8488" (lazy(sprintf "%+5A" 'P')) "'P'" - test "test8489" (lazy(sprintf "%+1A" 'P')) "'P'" - test "test8490" (lazy(sprintf "%+*A" 7 'P')) "'P'" - test "test8491" (lazy(sprintf "%+.5A" 'P')) "'P'" - test "test8492" (lazy(sprintf "%+.*A" 4 'P')) "'P'" - test "test8493" (lazy(sprintf "%+*.*A" 5 4 'P')) "'P'" - test "test8494" (lazy(sprintf "%-+A" 'P')) "'P'" - test "test8495" (lazy(sprintf "%-+5A" 'P')) "'P'" - test "test8496" (lazy(sprintf "%-+1A" 'P')) "'P'" - test "test8497" (lazy(sprintf "%-+*A" 7 'P')) "'P'" - test "test8498" (lazy(sprintf "%-+.5A" 'P')) "'P'" - test "test8499" (lazy(sprintf "%-+.*A" 4 'P')) "'P'" - test "test8500" (lazy(sprintf "%-+*.*A" 5 4 'P')) "'P'" - test "test8501" (lazy(sprintf "%A" System.IO.FileShare.None)) "None" - test "test8502" (lazy(sprintf "%5A" System.IO.FileShare.None)) "None" - test "test8503" (lazy(sprintf "%1A" System.IO.FileShare.None)) "None" - test "test8504" (lazy(sprintf "%*A" 7 System.IO.FileShare.None)) "None" - test "test8505" (lazy(sprintf "%.5A" System.IO.FileShare.None)) "None" - test "test8506" (lazy(sprintf "%.*A" 4 System.IO.FileShare.None)) "None" - test "test8507" (lazy(sprintf "%*.*A" 5 4 System.IO.FileShare.None)) "None" - test "test8508" (lazy(sprintf "%-A" System.IO.FileShare.None)) "None" - test "test8509" (lazy(sprintf "%-5A" System.IO.FileShare.None)) "None" - test "test8510" (lazy(sprintf "%-1A" System.IO.FileShare.None)) "None" - test "test8511" (lazy(sprintf "%-*A" 7 System.IO.FileShare.None)) "None" - test "test8512" (lazy(sprintf "%-.5A" System.IO.FileShare.None)) "None" - test "test8513" (lazy(sprintf "%-.*A" 4 System.IO.FileShare.None)) "None" - test "test8514" (lazy(sprintf "%-*.*A" 5 4 System.IO.FileShare.None)) "None" - test "test8515" (lazy(sprintf "%+A" System.IO.FileShare.None)) "None" - test "test8516" (lazy(sprintf "%+5A" System.IO.FileShare.None)) "None" - test "test8517" (lazy(sprintf "%+1A" System.IO.FileShare.None)) "None" - test "test8518" (lazy(sprintf "%+*A" 7 System.IO.FileShare.None)) "None" - test "test8519" (lazy(sprintf "%+.5A" System.IO.FileShare.None)) "None" - test "test8520" (lazy(sprintf "%+.*A" 4 System.IO.FileShare.None)) "None" - test "test8521" (lazy(sprintf "%+*.*A" 5 4 System.IO.FileShare.None)) "None" - test "test8522" (lazy(sprintf "%-+A" System.IO.FileShare.None)) "None" - test "test8523" (lazy(sprintf "%-+5A" System.IO.FileShare.None)) "None" - test "test8524" (lazy(sprintf "%-+1A" System.IO.FileShare.None)) "None" - test "test8525" (lazy(sprintf "%-+*A" 7 System.IO.FileShare.None)) "None" - test "test8526" (lazy(sprintf "%-+.5A" System.IO.FileShare.None)) "None" - test "test8527" (lazy(sprintf "%-+.*A" 4 System.IO.FileShare.None)) "None" - test "test8528" (lazy(sprintf "%-+*.*A" 5 4 System.IO.FileShare.None)) "None" - test "test8529" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") "abc")) "abc!!!" - test "test8530" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") 15)) "15!!!" - test "test8531" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") -10)) "-10!!!" - test "test8532" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") null)) "!!!" - test "test8533" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") 'P')) "P!!!" - test "test8534" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") System.IO.FileShare.None)) "None!!!" - test "test8535" (lazy(sprintf "%t" (fun _ -> "???"))) "???" - test "test8536" (lazy(sprintf "A%dB" 0)) "A0B" - test "test8537" (lazy(sprintf "A%dB%dC" 0 1)) "A0B1C" - test "test8538" (lazy(sprintf "A%dB%dC%dD" 0 1 2)) "A0B1C2D" - test "test8539" (lazy(sprintf "A%dB%dC%dD%dE" 0 1 2 3)) "A0B1C2D3E" - test "test8540" (lazy(sprintf "A%dB%dC%dD%dE%dF" 0 1 2 3 4)) "A0B1C2D3E4F" - test "test8541" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG" 0 1 2 3 4 5)) "A0B1C2D3E4F5G" - test "test8542" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH" 0 1 2 3 4 5 6)) "A0B1C2D3E4F5G6H" - test "test8543" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI" 0 1 2 3 4 5 6 7)) "A0B1C2D3E4F5G6H7I" - test "test8544" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ" 0 1 2 3 4 5 6 7 8)) "A0B1C2D3E4F5G6H7I8J" - test "test8545" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK" 0 1 2 3 4 5 6 7 8 9)) "A0B1C2D3E4F5G6H7I8J9K" - test "test8546" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL" 0 1 2 3 4 5 6 7 8 9 10)) "A0B1C2D3E4F5G6H7I8J9K10L" - test "test8547" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM" 0 1 2 3 4 5 6 7 8 9 10 11)) "A0B1C2D3E4F5G6H7I8J9K10L11M" - test "test8548" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN" 0 1 2 3 4 5 6 7 8 9 10 11 12)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N" - test "test8549" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO" 0 1 2 3 4 5 6 7 8 9 10 11 12 13)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O" - test "test8550" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P" - test "test8551" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q" - test "test8552" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ%dR" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R" - test "test8553" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ%dR%dS" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S" - test "test8554" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ%dR%dS%dT" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T" - test "test8555" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ%dR%dS%dT%dU" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T19U" - test "test8556" (lazy(sprintf "A%aB" (fun _ v -> string v) 0)) "A0B" - test "test8557" (lazy(sprintf "A%aB%aC" (fun _ v -> string v) 0 (fun _ v -> string v) 1)) "A0B1C" - test "test8558" (lazy(sprintf "A%aB%aC%aD" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2)) "A0B1C2D" - test "test8559" (lazy(sprintf "A%aB%aC%aD%aE" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3)) "A0B1C2D3E" - test "test8560" (lazy(sprintf "A%aB%aC%aD%aE%aF" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4)) "A0B1C2D3E4F" - test "test8561" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5)) "A0B1C2D3E4F5G" - test "test8562" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6)) "A0B1C2D3E4F5G6H" - test "test8563" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7)) "A0B1C2D3E4F5G6H7I" - test "test8564" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8)) "A0B1C2D3E4F5G6H7I8J" - test "test8565" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9)) "A0B1C2D3E4F5G6H7I8J9K" - test "test8566" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10)) "A0B1C2D3E4F5G6H7I8J9K10L" - test "test8567" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11)) "A0B1C2D3E4F5G6H7I8J9K10L11M" - test "test8568" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N" - test "test8569" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O" - test "test8570" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P" - test "test8571" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q" - test "test8572" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ%aR" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15 (fun _ v -> string v) 16)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R" - test "test8573" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ%aR%aS" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15 (fun _ v -> string v) 16 (fun _ v -> string v) 17)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S" - test "test8574" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ%aR%aS%aT" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15 (fun _ v -> string v) 16 (fun _ v -> string v) 17 (fun _ v -> string v) 18)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T" - test "test8575" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ%aR%aS%aT%aU" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15 (fun _ v -> string v) 16 (fun _ v -> string v) 17 (fun _ v -> string v) 18 (fun _ v -> string v) 19)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T19U" - test "test8576" (lazy(sprintf "01-00%d01-01%a11-10%d11-11" 0 (fun _ v -> (string v) + "X") 1 10 )) "01-00001-011X11-101011-11" - test "test8577" (lazy(sprintf "01-00%d01-01%a11-10%d11-11%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-011X11-101011-11ReadX_TAIL" - test "test8578" (lazy(sprintf "01-00%d01-01%d01-02%a11-10%d11-11%d11-12" 0 1 (fun _ v -> (string v) + "X") 1 10 11 )) "01-00001-01101-021X11-101011-111111-12" - test "test8579" (lazy(sprintf "01-00%d01-01%d01-02%a11-10%d11-11%d11-12%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-021X11-101011-111111-12ReadX_TAIL" - test "test8580" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%a11-10%d11-11%d11-12%d11-13" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 )) "01-00001-01101-02201-031X11-101011-111111-121211-13" - test "test8581" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%a11-10%d11-11%d11-12%d11-13%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-031X11-101011-111111-121211-13ReadX_TAIL" - test "test8582" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%a11-10%d11-11%d11-12%d11-13%d11-14" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 )) "01-00001-01101-02201-03301-041X11-101011-111111-121211-131311-14" - test "test8583" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%a11-10%d11-11%d11-12%d11-13%d11-14%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-041X11-101011-111111-121211-131311-14ReadX_TAIL" - test "test8584" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 )) "01-00001-01101-02201-03301-04401-051X11-101011-111111-121211-131311-141411-15" - test "test8585" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-051X11-101011-111111-121211-131311-141411-15ReadX_TAIL" - test "test8586" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 )) "01-00001-01101-02201-03301-04401-05501-061X11-101011-111111-121211-131311-141411-151511-16" - test "test8587" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-05501-061X11-101011-111111-121211-131311-141411-151511-16ReadX_TAIL" - test "test8588" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 )) "01-00001-01101-02201-03301-04401-05501-06601-071X11-101011-111111-121211-131311-141411-151511-161611-17" - test "test8589" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-05501-06601-071X11-101011-111111-121211-131311-141411-151511-161611-17ReadX_TAIL" - test "test8590" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%d01-08%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%d11-18" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 )) "01-00001-01101-02201-03301-04401-05501-06601-07701-081X11-101011-111111-121211-131311-141411-151511-161611-171711-18" - test "test8591" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%d01-08%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%d11-18%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-05501-06601-07701-081X11-101011-111111-121211-131311-141411-151511-161611-171711-18ReadX_TAIL" - test "test8592" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%d01-08%d01-09%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%d11-18%d11-19" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 )) "01-00001-01101-02201-03301-04401-05501-06601-07701-08801-091X11-101011-111111-121211-131311-141411-151511-161611-171711-181811-19" - test "test8593" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%d01-08%d01-09%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%d11-18%d11-19%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-05501-06601-07701-08801-091X11-101011-111111-121211-131311-141411-151511-161611-171711-181811-19ReadX_TAIL" - test "test8594" (lazy(sprintf "02-00%d02-01%a12-10%d12-11%a22-20%d22-21" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 )) "02-00002-011X12-101012-112X22-202022-21" - test "test8595" (lazy(sprintf "02-00%d02-01%a12-10%d12-11%a22-20%d22-21%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-011X12-101012-112X22-202022-21ReadX_TAIL" - test "test8596" (lazy(sprintf "02-00%d02-01%d02-02%a12-10%d12-11%d12-12%a22-20%d22-21%d22-22" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 )) "02-00002-01102-021X12-101012-111112-122X22-202022-212122-22" - test "test8597" (lazy(sprintf "02-00%d02-01%d02-02%a12-10%d12-11%d12-12%a22-20%d22-21%d22-22%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-021X12-101012-111112-122X22-202022-212122-22ReadX_TAIL" - test "test8598" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%a12-10%d12-11%d12-12%d12-13%a22-20%d22-21%d22-22%d22-23" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 )) "02-00002-01102-02202-031X12-101012-111112-121212-132X22-202022-212122-222222-23" - test "test8599" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%a12-10%d12-11%d12-12%d12-13%a22-20%d22-21%d22-22%d22-23%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-031X12-101012-111112-121212-132X22-202022-212122-222222-23ReadX_TAIL" - test "test8600" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%a12-10%d12-11%d12-12%d12-13%d12-14%a22-20%d22-21%d22-22%d22-23%d22-24" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 )) "02-00002-01102-02202-03302-041X12-101012-111112-121212-131312-142X22-202022-212122-222222-232322-24" - test "test8601" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%a12-10%d12-11%d12-12%d12-13%d12-14%a22-20%d22-21%d22-22%d22-23%d22-24%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-041X12-101012-111112-121212-131312-142X22-202022-212122-222222-232322-24ReadX_TAIL" - test "test8602" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 )) "02-00002-01102-02202-03302-04402-051X12-101012-111112-121212-131312-141412-152X22-202022-212122-222222-232322-242422-25" - test "test8603" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-051X12-101012-111112-121212-131312-141412-152X22-202022-212122-222222-232322-242422-25ReadX_TAIL" - test "test8604" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 )) "02-00002-01102-02202-03302-04402-05502-061X12-101012-111112-121212-131312-141412-151512-162X22-202022-212122-222222-232322-242422-252522-26" - test "test8605" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-05502-061X12-101012-111112-121212-131312-141412-151512-162X22-202022-212122-222222-232322-242422-252522-26ReadX_TAIL" - test "test8606" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 )) "02-00002-01102-02202-03302-04402-05502-06602-071X12-101012-111112-121212-131312-141412-151512-161612-172X22-202022-212122-222222-232322-242422-252522-262622-27" - test "test8607" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-05502-06602-071X12-101012-111112-121212-131312-141412-151512-161612-172X22-202022-212122-222222-232322-242422-252522-262622-27ReadX_TAIL" - test "test8608" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%d02-08%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%d12-18%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%d22-28" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 )) "02-00002-01102-02202-03302-04402-05502-06602-07702-081X12-101012-111112-121212-131312-141412-151512-161612-171712-182X22-202022-212122-222222-232322-242422-252522-262622-272722-28" - test "test8609" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%d02-08%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%d12-18%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%d22-28%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-05502-06602-07702-081X12-101012-111112-121212-131312-141412-151512-161612-171712-182X22-202022-212122-222222-232322-242422-252522-262622-272722-28ReadX_TAIL" - test "test8610" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%d02-08%d02-09%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%d12-18%d12-19%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%d22-28%d22-29" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 )) "02-00002-01102-02202-03302-04402-05502-06602-07702-08802-091X12-101012-111112-121212-131312-141412-151512-161612-171712-181812-192X22-202022-212122-222222-232322-242422-252522-262622-272722-282822-29" - test "test8611" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%d02-08%d02-09%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%d12-18%d12-19%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%d22-28%d22-29%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-05502-06602-07702-08802-091X12-101012-111112-121212-131312-141412-151512-161612-171712-181812-192X22-202022-212122-222222-232322-242422-252522-262622-272722-282822-29ReadX_TAIL" - test "test8612" (lazy(sprintf "03-00%d03-01%a13-10%d13-11%a23-20%d23-21%a33-30%d33-31" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 )) "03-00003-011X13-101013-112X23-202023-213X33-303033-31" - test "test8613" (lazy(sprintf "03-00%d03-01%a13-10%d13-11%a23-20%d23-21%a33-30%d33-31%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-011X13-101013-112X23-202023-213X33-303033-31ReadX_TAIL" - test "test8614" (lazy(sprintf "03-00%d03-01%d03-02%a13-10%d13-11%d13-12%a23-20%d23-21%d23-22%a33-30%d33-31%d33-32" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 )) "03-00003-01103-021X13-101013-111113-122X23-202023-212123-223X33-303033-313133-32" - test "test8615" (lazy(sprintf "03-00%d03-01%d03-02%a13-10%d13-11%d13-12%a23-20%d23-21%d23-22%a33-30%d33-31%d33-32%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-021X13-101013-111113-122X23-202023-212123-223X33-303033-313133-32ReadX_TAIL" - test "test8616" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%a13-10%d13-11%d13-12%d13-13%a23-20%d23-21%d23-22%d23-23%a33-30%d33-31%d33-32%d33-33" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 )) "03-00003-01103-02203-031X13-101013-111113-121213-132X23-202023-212123-222223-233X33-303033-313133-323233-33" - test "test8617" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%a13-10%d13-11%d13-12%d13-13%a23-20%d23-21%d23-22%d23-23%a33-30%d33-31%d33-32%d33-33%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-031X13-101013-111113-121213-132X23-202023-212123-222223-233X33-303033-313133-323233-33ReadX_TAIL" - test "test8618" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%a13-10%d13-11%d13-12%d13-13%d13-14%a23-20%d23-21%d23-22%d23-23%d23-24%a33-30%d33-31%d33-32%d33-33%d33-34" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 )) "03-00003-01103-02203-03303-041X13-101013-111113-121213-131313-142X23-202023-212123-222223-232323-243X33-303033-313133-323233-333333-34" - test "test8619" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%a13-10%d13-11%d13-12%d13-13%d13-14%a23-20%d23-21%d23-22%d23-23%d23-24%a33-30%d33-31%d33-32%d33-33%d33-34%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-041X13-101013-111113-121213-131313-142X23-202023-212123-222223-232323-243X33-303033-313133-323233-333333-34ReadX_TAIL" - test "test8620" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 )) "03-00003-01103-02203-03303-04403-051X13-101013-111113-121213-131313-141413-152X23-202023-212123-222223-232323-242423-253X33-303033-313133-323233-333333-343433-35" - test "test8621" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-051X13-101013-111113-121213-131313-141413-152X23-202023-212123-222223-232323-242423-253X33-303033-313133-323233-333333-343433-35ReadX_TAIL" - test "test8622" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 )) "03-00003-01103-02203-03303-04403-05503-061X13-101013-111113-121213-131313-141413-151513-162X23-202023-212123-222223-232323-242423-252523-263X33-303033-313133-323233-333333-343433-353533-36" - test "test8623" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-05503-061X13-101013-111113-121213-131313-141413-151513-162X23-202023-212123-222223-232323-242423-252523-263X33-303033-313133-323233-333333-343433-353533-36ReadX_TAIL" - test "test8624" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 )) "03-00003-01103-02203-03303-04403-05503-06603-071X13-101013-111113-121213-131313-141413-151513-161613-172X23-202023-212123-222223-232323-242423-252523-262623-273X33-303033-313133-323233-333333-343433-353533-363633-37" - test "test8625" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-05503-06603-071X13-101013-111113-121213-131313-141413-151513-161613-172X23-202023-212123-222223-232323-242423-252523-262623-273X33-303033-313133-323233-333333-343433-353533-363633-37ReadX_TAIL" - test "test8626" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%d03-08%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%d13-18%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%d23-28%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%d33-38" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 )) "03-00003-01103-02203-03303-04403-05503-06603-07703-081X13-101013-111113-121213-131313-141413-151513-161613-171713-182X23-202023-212123-222223-232323-242423-252523-262623-272723-283X33-303033-313133-323233-333333-343433-353533-363633-373733-38" - test "test8627" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%d03-08%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%d13-18%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%d23-28%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%d33-38%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-05503-06603-07703-081X13-101013-111113-121213-131313-141413-151513-161613-171713-182X23-202023-212123-222223-232323-242423-252523-262623-272723-283X33-303033-313133-323233-333333-343433-353533-363633-373733-38ReadX_TAIL" - test "test8628" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%d03-08%d03-09%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%d13-18%d13-19%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%d23-28%d23-29%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%d33-38%d33-39" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 )) "03-00003-01103-02203-03303-04403-05503-06603-07703-08803-091X13-101013-111113-121213-131313-141413-151513-161613-171713-181813-192X23-202023-212123-222223-232323-242423-252523-262623-272723-282823-293X33-303033-313133-323233-333333-343433-353533-363633-373733-383833-39" - test "test8629" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%d03-08%d03-09%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%d13-18%d13-19%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%d23-28%d23-29%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%d33-38%d33-39%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-05503-06603-07703-08803-091X13-101013-111113-121213-131313-141413-151513-161613-171713-181813-192X23-202023-212123-222223-232323-242423-252523-262623-272723-282823-293X33-303033-313133-323233-333333-343433-353533-363633-373733-383833-39ReadX_TAIL" - test "test8630" (lazy(sprintf "04-00%d04-01%a14-10%d14-11%a24-20%d24-21%a34-30%d34-31%a44-40%d44-41" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 )) "04-00004-011X14-101014-112X24-202024-213X34-303034-314X44-404044-41" - test "test8631" (lazy(sprintf "04-00%d04-01%a14-10%d14-11%a24-20%d24-21%a34-30%d34-31%a44-40%d44-41%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-011X14-101014-112X24-202024-213X34-303034-314X44-404044-41ReadX_TAIL" - test "test8632" (lazy(sprintf "04-00%d04-01%d04-02%a14-10%d14-11%d14-12%a24-20%d24-21%d24-22%a34-30%d34-31%d34-32%a44-40%d44-41%d44-42" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 )) "04-00004-01104-021X14-101014-111114-122X24-202024-212124-223X34-303034-313134-324X44-404044-414144-42" - test "test8633" (lazy(sprintf "04-00%d04-01%d04-02%a14-10%d14-11%d14-12%a24-20%d24-21%d24-22%a34-30%d34-31%d34-32%a44-40%d44-41%d44-42%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-021X14-101014-111114-122X24-202024-212124-223X34-303034-313134-324X44-404044-414144-42ReadX_TAIL" - test "test8634" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%a14-10%d14-11%d14-12%d14-13%a24-20%d24-21%d24-22%d24-23%a34-30%d34-31%d34-32%d34-33%a44-40%d44-41%d44-42%d44-43" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 )) "04-00004-01104-02204-031X14-101014-111114-121214-132X24-202024-212124-222224-233X34-303034-313134-323234-334X44-404044-414144-424244-43" - test "test8635" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%a14-10%d14-11%d14-12%d14-13%a24-20%d24-21%d24-22%d24-23%a34-30%d34-31%d34-32%d34-33%a44-40%d44-41%d44-42%d44-43%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-031X14-101014-111114-121214-132X24-202024-212124-222224-233X34-303034-313134-323234-334X44-404044-414144-424244-43ReadX_TAIL" - test "test8636" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%a14-10%d14-11%d14-12%d14-13%d14-14%a24-20%d24-21%d24-22%d24-23%d24-24%a34-30%d34-31%d34-32%d34-33%d34-34%a44-40%d44-41%d44-42%d44-43%d44-44" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 )) "04-00004-01104-02204-03304-041X14-101014-111114-121214-131314-142X24-202024-212124-222224-232324-243X34-303034-313134-323234-333334-344X44-404044-414144-424244-434344-44" - test "test8637" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%a14-10%d14-11%d14-12%d14-13%d14-14%a24-20%d24-21%d24-22%d24-23%d24-24%a34-30%d34-31%d34-32%d34-33%d34-34%a44-40%d44-41%d44-42%d44-43%d44-44%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-041X14-101014-111114-121214-131314-142X24-202024-212124-222224-232324-243X34-303034-313134-323234-333334-344X44-404044-414144-424244-434344-44ReadX_TAIL" - test "test8638" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 )) "04-00004-01104-02204-03304-04404-051X14-101014-111114-121214-131314-141414-152X24-202024-212124-222224-232324-242424-253X34-303034-313134-323234-333334-343434-354X44-404044-414144-424244-434344-444444-45" - test "test8639" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-051X14-101014-111114-121214-131314-141414-152X24-202024-212124-222224-232324-242424-253X34-303034-313134-323234-333334-343434-354X44-404044-414144-424244-434344-444444-45ReadX_TAIL" - test "test8640" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 )) "04-00004-01104-02204-03304-04404-05504-061X14-101014-111114-121214-131314-141414-151514-162X24-202024-212124-222224-232324-242424-252524-263X34-303034-313134-323234-333334-343434-353534-364X44-404044-414144-424244-434344-444444-454544-46" - test "test8641" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-05504-061X14-101014-111114-121214-131314-141414-151514-162X24-202024-212124-222224-232324-242424-252524-263X34-303034-313134-323234-333334-343434-353534-364X44-404044-414144-424244-434344-444444-454544-46ReadX_TAIL" - test "test8642" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 )) "04-00004-01104-02204-03304-04404-05504-06604-071X14-101014-111114-121214-131314-141414-151514-161614-172X24-202024-212124-222224-232324-242424-252524-262624-273X34-303034-313134-323234-333334-343434-353534-363634-374X44-404044-414144-424244-434344-444444-454544-464644-47" - test "test8643" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-05504-06604-071X14-101014-111114-121214-131314-141414-151514-161614-172X24-202024-212124-222224-232324-242424-252524-262624-273X34-303034-313134-323234-333334-343434-353534-363634-374X44-404044-414144-424244-434344-444444-454544-464644-47ReadX_TAIL" - test "test8644" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%d04-08%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%d14-18%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%d24-28%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%d34-38%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%d44-48" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 )) "04-00004-01104-02204-03304-04404-05504-06604-07704-081X14-101014-111114-121214-131314-141414-151514-161614-171714-182X24-202024-212124-222224-232324-242424-252524-262624-272724-283X34-303034-313134-323234-333334-343434-353534-363634-373734-384X44-404044-414144-424244-434344-444444-454544-464644-474744-48" - test "test8645" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%d04-08%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%d14-18%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%d24-28%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%d34-38%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%d44-48%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-05504-06604-07704-081X14-101014-111114-121214-131314-141414-151514-161614-171714-182X24-202024-212124-222224-232324-242424-252524-262624-272724-283X34-303034-313134-323234-333334-343434-353534-363634-373734-384X44-404044-414144-424244-434344-444444-454544-464644-474744-48ReadX_TAIL" - test "test8646" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%d04-08%d04-09%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%d14-18%d14-19%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%d24-28%d24-29%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%d34-38%d34-39%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%d44-48%d44-49" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 )) "04-00004-01104-02204-03304-04404-05504-06604-07704-08804-091X14-101014-111114-121214-131314-141414-151514-161614-171714-181814-192X24-202024-212124-222224-232324-242424-252524-262624-272724-282824-293X34-303034-313134-323234-333334-343434-353534-363634-373734-383834-394X44-404044-414144-424244-434344-444444-454544-464644-474744-484844-49" - test "test8647" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%d04-08%d04-09%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%d14-18%d14-19%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%d24-28%d24-29%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%d34-38%d34-39%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%d44-48%d44-49%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-05504-06604-07704-08804-091X14-101014-111114-121214-131314-141414-151514-161614-171714-181814-192X24-202024-212124-222224-232324-242424-252524-262624-272724-282824-293X34-303034-313134-323234-333334-343434-353534-363634-373734-383834-394X44-404044-414144-424244-434344-444444-454544-464644-474744-484844-49ReadX_TAIL" - test "test8648" (lazy(sprintf "05-00%d05-01%a15-10%d15-11%a25-20%d25-21%a35-30%d35-31%a45-40%d45-41%a55-50%d55-51" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 )) "05-00005-011X15-101015-112X25-202025-213X35-303035-314X45-404045-415X55-505055-51" - test "test8649" (lazy(sprintf "05-00%d05-01%a15-10%d15-11%a25-20%d25-21%a35-30%d35-31%a45-40%d45-41%a55-50%d55-51%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-011X15-101015-112X25-202025-213X35-303035-314X45-404045-415X55-505055-51ReadX_TAIL" - test "test8650" (lazy(sprintf "05-00%d05-01%d05-02%a15-10%d15-11%d15-12%a25-20%d25-21%d25-22%a35-30%d35-31%d35-32%a45-40%d45-41%d45-42%a55-50%d55-51%d55-52" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 )) "05-00005-01105-021X15-101015-111115-122X25-202025-212125-223X35-303035-313135-324X45-404045-414145-425X55-505055-515155-52" - test "test8651" (lazy(sprintf "05-00%d05-01%d05-02%a15-10%d15-11%d15-12%a25-20%d25-21%d25-22%a35-30%d35-31%d35-32%a45-40%d45-41%d45-42%a55-50%d55-51%d55-52%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-021X15-101015-111115-122X25-202025-212125-223X35-303035-313135-324X45-404045-414145-425X55-505055-515155-52ReadX_TAIL" - test "test8652" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%a15-10%d15-11%d15-12%d15-13%a25-20%d25-21%d25-22%d25-23%a35-30%d35-31%d35-32%d35-33%a45-40%d45-41%d45-42%d45-43%a55-50%d55-51%d55-52%d55-53" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 )) "05-00005-01105-02205-031X15-101015-111115-121215-132X25-202025-212125-222225-233X35-303035-313135-323235-334X45-404045-414145-424245-435X55-505055-515155-525255-53" - test "test8653" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%a15-10%d15-11%d15-12%d15-13%a25-20%d25-21%d25-22%d25-23%a35-30%d35-31%d35-32%d35-33%a45-40%d45-41%d45-42%d45-43%a55-50%d55-51%d55-52%d55-53%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-031X15-101015-111115-121215-132X25-202025-212125-222225-233X35-303035-313135-323235-334X45-404045-414145-424245-435X55-505055-515155-525255-53ReadX_TAIL" - test "test8654" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%a15-10%d15-11%d15-12%d15-13%d15-14%a25-20%d25-21%d25-22%d25-23%d25-24%a35-30%d35-31%d35-32%d35-33%d35-34%a45-40%d45-41%d45-42%d45-43%d45-44%a55-50%d55-51%d55-52%d55-53%d55-54" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 )) "05-00005-01105-02205-03305-041X15-101015-111115-121215-131315-142X25-202025-212125-222225-232325-243X35-303035-313135-323235-333335-344X45-404045-414145-424245-434345-445X55-505055-515155-525255-535355-54" - test "test8655" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%a15-10%d15-11%d15-12%d15-13%d15-14%a25-20%d25-21%d25-22%d25-23%d25-24%a35-30%d35-31%d35-32%d35-33%d35-34%a45-40%d45-41%d45-42%d45-43%d45-44%a55-50%d55-51%d55-52%d55-53%d55-54%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-041X15-101015-111115-121215-131315-142X25-202025-212125-222225-232325-243X35-303035-313135-323235-333335-344X45-404045-414145-424245-434345-445X55-505055-515155-525255-535355-54ReadX_TAIL" - test "test8656" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 )) "05-00005-01105-02205-03305-04405-051X15-101015-111115-121215-131315-141415-152X25-202025-212125-222225-232325-242425-253X35-303035-313135-323235-333335-343435-354X45-404045-414145-424245-434345-444445-455X55-505055-515155-525255-535355-545455-55" - test "test8657" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-051X15-101015-111115-121215-131315-141415-152X25-202025-212125-222225-232325-242425-253X35-303035-313135-323235-333335-343435-354X45-404045-414145-424245-434345-444445-455X55-505055-515155-525255-535355-545455-55ReadX_TAIL" - test "test8658" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 )) "05-00005-01105-02205-03305-04405-05505-061X15-101015-111115-121215-131315-141415-151515-162X25-202025-212125-222225-232325-242425-252525-263X35-303035-313135-323235-333335-343435-353535-364X45-404045-414145-424245-434345-444445-454545-465X55-505055-515155-525255-535355-545455-555555-56" - test "test8659" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-05505-061X15-101015-111115-121215-131315-141415-151515-162X25-202025-212125-222225-232325-242425-252525-263X35-303035-313135-323235-333335-343435-353535-364X45-404045-414145-424245-434345-444445-454545-465X55-505055-515155-525255-535355-545455-555555-56ReadX_TAIL" - test "test8660" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 )) "05-00005-01105-02205-03305-04405-05505-06605-071X15-101015-111115-121215-131315-141415-151515-161615-172X25-202025-212125-222225-232325-242425-252525-262625-273X35-303035-313135-323235-333335-343435-353535-363635-374X45-404045-414145-424245-434345-444445-454545-464645-475X55-505055-515155-525255-535355-545455-555555-565655-57" - test "test8661" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-05505-06605-071X15-101015-111115-121215-131315-141415-151515-161615-172X25-202025-212125-222225-232325-242425-252525-262625-273X35-303035-313135-323235-333335-343435-353535-363635-374X45-404045-414145-424245-434345-444445-454545-464645-475X55-505055-515155-525255-535355-545455-555555-565655-57ReadX_TAIL" - test "test8662" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%d05-08%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%d15-18%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%d25-28%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%d35-38%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%d45-48%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%d55-58" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 )) "05-00005-01105-02205-03305-04405-05505-06605-07705-081X15-101015-111115-121215-131315-141415-151515-161615-171715-182X25-202025-212125-222225-232325-242425-252525-262625-272725-283X35-303035-313135-323235-333335-343435-353535-363635-373735-384X45-404045-414145-424245-434345-444445-454545-464645-474745-485X55-505055-515155-525255-535355-545455-555555-565655-575755-58" - test "test8663" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%d05-08%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%d15-18%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%d25-28%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%d35-38%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%d45-48%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%d55-58%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-05505-06605-07705-081X15-101015-111115-121215-131315-141415-151515-161615-171715-182X25-202025-212125-222225-232325-242425-252525-262625-272725-283X35-303035-313135-323235-333335-343435-353535-363635-373735-384X45-404045-414145-424245-434345-444445-454545-464645-474745-485X55-505055-515155-525255-535355-545455-555555-565655-575755-58ReadX_TAIL" - test "test8664" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%d05-08%d05-09%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%d15-18%d15-19%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%d25-28%d25-29%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%d35-38%d35-39%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%d45-48%d45-49%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%d55-58%d55-59" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 )) "05-00005-01105-02205-03305-04405-05505-06605-07705-08805-091X15-101015-111115-121215-131315-141415-151515-161615-171715-181815-192X25-202025-212125-222225-232325-242425-252525-262625-272725-282825-293X35-303035-313135-323235-333335-343435-353535-363635-373735-383835-394X45-404045-414145-424245-434345-444445-454545-464645-474745-484845-495X55-505055-515155-525255-535355-545455-555555-565655-575755-585855-59" - test "test8665" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%d05-08%d05-09%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%d15-18%d15-19%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%d25-28%d25-29%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%d35-38%d35-39%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%d45-48%d45-49%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%d55-58%d55-59%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-05505-06605-07705-08805-091X15-101015-111115-121215-131315-141415-151515-161615-171715-181815-192X25-202025-212125-222225-232325-242425-252525-262625-272725-282825-293X35-303035-313135-323235-333335-343435-353535-363635-373735-383835-394X45-404045-414145-424245-434345-444445-454545-464645-474745-484845-495X55-505055-515155-525255-535355-545455-555555-565655-575755-585855-59ReadX_TAIL" - test "test8666" (lazy(sprintf "06-00%d06-01%a16-10%d16-11%a26-20%d26-21%a36-30%d36-31%a46-40%d46-41%a56-50%d56-51%a66-60%d66-61" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 )) "06-00006-011X16-101016-112X26-202026-213X36-303036-314X46-404046-415X56-505056-516X66-606066-61" - test "test8667" (lazy(sprintf "06-00%d06-01%a16-10%d16-11%a26-20%d26-21%a36-30%d36-31%a46-40%d46-41%a56-50%d56-51%a66-60%d66-61%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-011X16-101016-112X26-202026-213X36-303036-314X46-404046-415X56-505056-516X66-606066-61ReadX_TAIL" - test "test8668" (lazy(sprintf "06-00%d06-01%d06-02%a16-10%d16-11%d16-12%a26-20%d26-21%d26-22%a36-30%d36-31%d36-32%a46-40%d46-41%d46-42%a56-50%d56-51%d56-52%a66-60%d66-61%d66-62" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 )) "06-00006-01106-021X16-101016-111116-122X26-202026-212126-223X36-303036-313136-324X46-404046-414146-425X56-505056-515156-526X66-606066-616166-62" - test "test8669" (lazy(sprintf "06-00%d06-01%d06-02%a16-10%d16-11%d16-12%a26-20%d26-21%d26-22%a36-30%d36-31%d36-32%a46-40%d46-41%d46-42%a56-50%d56-51%d56-52%a66-60%d66-61%d66-62%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-021X16-101016-111116-122X26-202026-212126-223X36-303036-313136-324X46-404046-414146-425X56-505056-515156-526X66-606066-616166-62ReadX_TAIL" - test "test8670" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%a16-10%d16-11%d16-12%d16-13%a26-20%d26-21%d26-22%d26-23%a36-30%d36-31%d36-32%d36-33%a46-40%d46-41%d46-42%d46-43%a56-50%d56-51%d56-52%d56-53%a66-60%d66-61%d66-62%d66-63" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 )) "06-00006-01106-02206-031X16-101016-111116-121216-132X26-202026-212126-222226-233X36-303036-313136-323236-334X46-404046-414146-424246-435X56-505056-515156-525256-536X66-606066-616166-626266-63" - test "test8671" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%a16-10%d16-11%d16-12%d16-13%a26-20%d26-21%d26-22%d26-23%a36-30%d36-31%d36-32%d36-33%a46-40%d46-41%d46-42%d46-43%a56-50%d56-51%d56-52%d56-53%a66-60%d66-61%d66-62%d66-63%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-031X16-101016-111116-121216-132X26-202026-212126-222226-233X36-303036-313136-323236-334X46-404046-414146-424246-435X56-505056-515156-525256-536X66-606066-616166-626266-63ReadX_TAIL" - test "test8672" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%a16-10%d16-11%d16-12%d16-13%d16-14%a26-20%d26-21%d26-22%d26-23%d26-24%a36-30%d36-31%d36-32%d36-33%d36-34%a46-40%d46-41%d46-42%d46-43%d46-44%a56-50%d56-51%d56-52%d56-53%d56-54%a66-60%d66-61%d66-62%d66-63%d66-64" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 )) "06-00006-01106-02206-03306-041X16-101016-111116-121216-131316-142X26-202026-212126-222226-232326-243X36-303036-313136-323236-333336-344X46-404046-414146-424246-434346-445X56-505056-515156-525256-535356-546X66-606066-616166-626266-636366-64" - test "test8673" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%a16-10%d16-11%d16-12%d16-13%d16-14%a26-20%d26-21%d26-22%d26-23%d26-24%a36-30%d36-31%d36-32%d36-33%d36-34%a46-40%d46-41%d46-42%d46-43%d46-44%a56-50%d56-51%d56-52%d56-53%d56-54%a66-60%d66-61%d66-62%d66-63%d66-64%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-041X16-101016-111116-121216-131316-142X26-202026-212126-222226-232326-243X36-303036-313136-323236-333336-344X46-404046-414146-424246-434346-445X56-505056-515156-525256-535356-546X66-606066-616166-626266-636366-64ReadX_TAIL" - test "test8674" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 )) "06-00006-01106-02206-03306-04406-051X16-101016-111116-121216-131316-141416-152X26-202026-212126-222226-232326-242426-253X36-303036-313136-323236-333336-343436-354X46-404046-414146-424246-434346-444446-455X56-505056-515156-525256-535356-545456-556X66-606066-616166-626266-636366-646466-65" - test "test8675" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-051X16-101016-111116-121216-131316-141416-152X26-202026-212126-222226-232326-242426-253X36-303036-313136-323236-333336-343436-354X46-404046-414146-424246-434346-444446-455X56-505056-515156-525256-535356-545456-556X66-606066-616166-626266-636366-646466-65ReadX_TAIL" - test "test8676" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 )) "06-00006-01106-02206-03306-04406-05506-061X16-101016-111116-121216-131316-141416-151516-162X26-202026-212126-222226-232326-242426-252526-263X36-303036-313136-323236-333336-343436-353536-364X46-404046-414146-424246-434346-444446-454546-465X56-505056-515156-525256-535356-545456-555556-566X66-606066-616166-626266-636366-646466-656566-66" - test "test8677" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-05506-061X16-101016-111116-121216-131316-141416-151516-162X26-202026-212126-222226-232326-242426-252526-263X36-303036-313136-323236-333336-343436-353536-364X46-404046-414146-424246-434346-444446-454546-465X56-505056-515156-525256-535356-545456-555556-566X66-606066-616166-626266-636366-646466-656566-66ReadX_TAIL" - test "test8678" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 )) "06-00006-01106-02206-03306-04406-05506-06606-071X16-101016-111116-121216-131316-141416-151516-161616-172X26-202026-212126-222226-232326-242426-252526-262626-273X36-303036-313136-323236-333336-343436-353536-363636-374X46-404046-414146-424246-434346-444446-454546-464646-475X56-505056-515156-525256-535356-545456-555556-565656-576X66-606066-616166-626266-636366-646466-656566-666666-67" - test "test8679" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-05506-06606-071X16-101016-111116-121216-131316-141416-151516-161616-172X26-202026-212126-222226-232326-242426-252526-262626-273X36-303036-313136-323236-333336-343436-353536-363636-374X46-404046-414146-424246-434346-444446-454546-464646-475X56-505056-515156-525256-535356-545456-555556-565656-576X66-606066-616166-626266-636366-646466-656566-666666-67ReadX_TAIL" - test "test8680" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%d06-08%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%d16-18%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%d26-28%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%d36-38%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%d46-48%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%d56-58%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%d66-68" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 )) "06-00006-01106-02206-03306-04406-05506-06606-07706-081X16-101016-111116-121216-131316-141416-151516-161616-171716-182X26-202026-212126-222226-232326-242426-252526-262626-272726-283X36-303036-313136-323236-333336-343436-353536-363636-373736-384X46-404046-414146-424246-434346-444446-454546-464646-474746-485X56-505056-515156-525256-535356-545456-555556-565656-575756-586X66-606066-616166-626266-636366-646466-656566-666666-676766-68" - test "test8681" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%d06-08%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%d16-18%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%d26-28%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%d36-38%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%d46-48%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%d56-58%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%d66-68%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-05506-06606-07706-081X16-101016-111116-121216-131316-141416-151516-161616-171716-182X26-202026-212126-222226-232326-242426-252526-262626-272726-283X36-303036-313136-323236-333336-343436-353536-363636-373736-384X46-404046-414146-424246-434346-444446-454546-464646-474746-485X56-505056-515156-525256-535356-545456-555556-565656-575756-586X66-606066-616166-626266-636366-646466-656566-666666-676766-68ReadX_TAIL" - test "test8682" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%d06-08%d06-09%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%d16-18%d16-19%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%d26-28%d26-29%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%d36-38%d36-39%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%d46-48%d46-49%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%d56-58%d56-59%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%d66-68%d66-69" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 )) "06-00006-01106-02206-03306-04406-05506-06606-07706-08806-091X16-101016-111116-121216-131316-141416-151516-161616-171716-181816-192X26-202026-212126-222226-232326-242426-252526-262626-272726-282826-293X36-303036-313136-323236-333336-343436-353536-363636-373736-383836-394X46-404046-414146-424246-434346-444446-454546-464646-474746-484846-495X56-505056-515156-525256-535356-545456-555556-565656-575756-585856-596X66-606066-616166-626266-636366-646466-656566-666666-676766-686866-69" - test "test8683" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%d06-08%d06-09%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%d16-18%d16-19%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%d26-28%d26-29%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%d36-38%d36-39%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%d46-48%d46-49%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%d56-58%d56-59%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%d66-68%d66-69%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-05506-06606-07706-08806-091X16-101016-111116-121216-131316-141416-151516-161616-171716-181816-192X26-202026-212126-222226-232326-242426-252526-262626-272726-282826-293X36-303036-313136-323236-333336-343436-353536-363636-373736-383836-394X46-404046-414146-424246-434346-444446-454546-464646-474746-484846-495X56-505056-515156-525256-535356-545456-555556-565656-575756-585856-596X66-606066-616166-626266-636366-646466-656566-666666-676766-686866-69ReadX_TAIL" - test "test8684" (lazy(sprintf "07-00%d07-01%a17-10%d17-11%a27-20%d27-21%a37-30%d37-31%a47-40%d47-41%a57-50%d57-51%a67-60%d67-61%a77-70%d77-71" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 )) "07-00007-011X17-101017-112X27-202027-213X37-303037-314X47-404047-415X57-505057-516X67-606067-617X77-707077-71" - test "test8685" (lazy(sprintf "07-00%d07-01%a17-10%d17-11%a27-20%d27-21%a37-30%d37-31%a47-40%d47-41%a57-50%d57-51%a67-60%d67-61%a77-70%d77-71%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-011X17-101017-112X27-202027-213X37-303037-314X47-404047-415X57-505057-516X67-606067-617X77-707077-71ReadX_TAIL" - test "test8686" (lazy(sprintf "07-00%d07-01%d07-02%a17-10%d17-11%d17-12%a27-20%d27-21%d27-22%a37-30%d37-31%d37-32%a47-40%d47-41%d47-42%a57-50%d57-51%d57-52%a67-60%d67-61%d67-62%a77-70%d77-71%d77-72" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 )) "07-00007-01107-021X17-101017-111117-122X27-202027-212127-223X37-303037-313137-324X47-404047-414147-425X57-505057-515157-526X67-606067-616167-627X77-707077-717177-72" - test "test8687" (lazy(sprintf "07-00%d07-01%d07-02%a17-10%d17-11%d17-12%a27-20%d27-21%d27-22%a37-30%d37-31%d37-32%a47-40%d47-41%d47-42%a57-50%d57-51%d57-52%a67-60%d67-61%d67-62%a77-70%d77-71%d77-72%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-021X17-101017-111117-122X27-202027-212127-223X37-303037-313137-324X47-404047-414147-425X57-505057-515157-526X67-606067-616167-627X77-707077-717177-72ReadX_TAIL" - test "test8688" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%a17-10%d17-11%d17-12%d17-13%a27-20%d27-21%d27-22%d27-23%a37-30%d37-31%d37-32%d37-33%a47-40%d47-41%d47-42%d47-43%a57-50%d57-51%d57-52%d57-53%a67-60%d67-61%d67-62%d67-63%a77-70%d77-71%d77-72%d77-73" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 )) "07-00007-01107-02207-031X17-101017-111117-121217-132X27-202027-212127-222227-233X37-303037-313137-323237-334X47-404047-414147-424247-435X57-505057-515157-525257-536X67-606067-616167-626267-637X77-707077-717177-727277-73" - test "test8689" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%a17-10%d17-11%d17-12%d17-13%a27-20%d27-21%d27-22%d27-23%a37-30%d37-31%d37-32%d37-33%a47-40%d47-41%d47-42%d47-43%a57-50%d57-51%d57-52%d57-53%a67-60%d67-61%d67-62%d67-63%a77-70%d77-71%d77-72%d77-73%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-031X17-101017-111117-121217-132X27-202027-212127-222227-233X37-303037-313137-323237-334X47-404047-414147-424247-435X57-505057-515157-525257-536X67-606067-616167-626267-637X77-707077-717177-727277-73ReadX_TAIL" - test "test8690" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%a17-10%d17-11%d17-12%d17-13%d17-14%a27-20%d27-21%d27-22%d27-23%d27-24%a37-30%d37-31%d37-32%d37-33%d37-34%a47-40%d47-41%d47-42%d47-43%d47-44%a57-50%d57-51%d57-52%d57-53%d57-54%a67-60%d67-61%d67-62%d67-63%d67-64%a77-70%d77-71%d77-72%d77-73%d77-74" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 )) "07-00007-01107-02207-03307-041X17-101017-111117-121217-131317-142X27-202027-212127-222227-232327-243X37-303037-313137-323237-333337-344X47-404047-414147-424247-434347-445X57-505057-515157-525257-535357-546X67-606067-616167-626267-636367-647X77-707077-717177-727277-737377-74" - test "test8691" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%a17-10%d17-11%d17-12%d17-13%d17-14%a27-20%d27-21%d27-22%d27-23%d27-24%a37-30%d37-31%d37-32%d37-33%d37-34%a47-40%d47-41%d47-42%d47-43%d47-44%a57-50%d57-51%d57-52%d57-53%d57-54%a67-60%d67-61%d67-62%d67-63%d67-64%a77-70%d77-71%d77-72%d77-73%d77-74%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-041X17-101017-111117-121217-131317-142X27-202027-212127-222227-232327-243X37-303037-313137-323237-333337-344X47-404047-414147-424247-434347-445X57-505057-515157-525257-535357-546X67-606067-616167-626267-636367-647X77-707077-717177-727277-737377-74ReadX_TAIL" - test "test8692" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 )) "07-00007-01107-02207-03307-04407-051X17-101017-111117-121217-131317-141417-152X27-202027-212127-222227-232327-242427-253X37-303037-313137-323237-333337-343437-354X47-404047-414147-424247-434347-444447-455X57-505057-515157-525257-535357-545457-556X67-606067-616167-626267-636367-646467-657X77-707077-717177-727277-737377-747477-75" - test "test8693" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-051X17-101017-111117-121217-131317-141417-152X27-202027-212127-222227-232327-242427-253X37-303037-313137-323237-333337-343437-354X47-404047-414147-424247-434347-444447-455X57-505057-515157-525257-535357-545457-556X67-606067-616167-626267-636367-646467-657X77-707077-717177-727277-737377-747477-75ReadX_TAIL" - test "test8694" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 )) "07-00007-01107-02207-03307-04407-05507-061X17-101017-111117-121217-131317-141417-151517-162X27-202027-212127-222227-232327-242427-252527-263X37-303037-313137-323237-333337-343437-353537-364X47-404047-414147-424247-434347-444447-454547-465X57-505057-515157-525257-535357-545457-555557-566X67-606067-616167-626267-636367-646467-656567-667X77-707077-717177-727277-737377-747477-757577-76" - test "test8695" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-05507-061X17-101017-111117-121217-131317-141417-151517-162X27-202027-212127-222227-232327-242427-252527-263X37-303037-313137-323237-333337-343437-353537-364X47-404047-414147-424247-434347-444447-454547-465X57-505057-515157-525257-535357-545457-555557-566X67-606067-616167-626267-636367-646467-656567-667X77-707077-717177-727277-737377-747477-757577-76ReadX_TAIL" - test "test8696" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 )) "07-00007-01107-02207-03307-04407-05507-06607-071X17-101017-111117-121217-131317-141417-151517-161617-172X27-202027-212127-222227-232327-242427-252527-262627-273X37-303037-313137-323237-333337-343437-353537-363637-374X47-404047-414147-424247-434347-444447-454547-464647-475X57-505057-515157-525257-535357-545457-555557-565657-576X67-606067-616167-626267-636367-646467-656567-666667-677X77-707077-717177-727277-737377-747477-757577-767677-77" - test "test8697" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-05507-06607-071X17-101017-111117-121217-131317-141417-151517-161617-172X27-202027-212127-222227-232327-242427-252527-262627-273X37-303037-313137-323237-333337-343437-353537-363637-374X47-404047-414147-424247-434347-444447-454547-464647-475X57-505057-515157-525257-535357-545457-555557-565657-576X67-606067-616167-626267-636367-646467-656567-666667-677X77-707077-717177-727277-737377-747477-757577-767677-77ReadX_TAIL" - test "test8698" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%d07-08%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%d17-18%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%d27-28%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%d37-38%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%d47-48%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%d57-58%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%d67-68%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%d77-78" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 )) "07-00007-01107-02207-03307-04407-05507-06607-07707-081X17-101017-111117-121217-131317-141417-151517-161617-171717-182X27-202027-212127-222227-232327-242427-252527-262627-272727-283X37-303037-313137-323237-333337-343437-353537-363637-373737-384X47-404047-414147-424247-434347-444447-454547-464647-474747-485X57-505057-515157-525257-535357-545457-555557-565657-575757-586X67-606067-616167-626267-636367-646467-656567-666667-676767-687X77-707077-717177-727277-737377-747477-757577-767677-777777-78" - test "test8699" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%d07-08%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%d17-18%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%d27-28%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%d37-38%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%d47-48%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%d57-58%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%d67-68%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%d77-78%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-05507-06607-07707-081X17-101017-111117-121217-131317-141417-151517-161617-171717-182X27-202027-212127-222227-232327-242427-252527-262627-272727-283X37-303037-313137-323237-333337-343437-353537-363637-373737-384X47-404047-414147-424247-434347-444447-454547-464647-474747-485X57-505057-515157-525257-535357-545457-555557-565657-575757-586X67-606067-616167-626267-636367-646467-656567-666667-676767-687X77-707077-717177-727277-737377-747477-757577-767677-777777-78ReadX_TAIL" - test "test8700" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%d07-08%d07-09%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%d17-18%d17-19%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%d27-28%d27-29%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%d37-38%d37-39%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%d47-48%d47-49%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%d57-58%d57-59%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%d67-68%d67-69%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%d77-78%d77-79" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 )) "07-00007-01107-02207-03307-04407-05507-06607-07707-08807-091X17-101017-111117-121217-131317-141417-151517-161617-171717-181817-192X27-202027-212127-222227-232327-242427-252527-262627-272727-282827-293X37-303037-313137-323237-333337-343437-353537-363637-373737-383837-394X47-404047-414147-424247-434347-444447-454547-464647-474747-484847-495X57-505057-515157-525257-535357-545457-555557-565657-575757-585857-596X67-606067-616167-626267-636367-646467-656567-666667-676767-686867-697X77-707077-717177-727277-737377-747477-757577-767677-777777-787877-79" - test "test8701" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%d07-08%d07-09%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%d17-18%d17-19%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%d27-28%d27-29%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%d37-38%d37-39%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%d47-48%d47-49%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%d57-58%d57-59%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%d67-68%d67-69%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%d77-78%d77-79%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-05507-06607-07707-08807-091X17-101017-111117-121217-131317-141417-151517-161617-171717-181817-192X27-202027-212127-222227-232327-242427-252527-262627-272727-282827-293X37-303037-313137-323237-333337-343437-353537-363637-373737-383837-394X47-404047-414147-424247-434347-444447-454547-464647-474747-484847-495X57-505057-515157-525257-535357-545457-555557-565657-575757-585857-596X67-606067-616167-626267-636367-646467-656567-666667-676767-686867-697X77-707077-717177-727277-737377-747477-757577-767677-777777-787877-79ReadX_TAIL" - test "test8702" (lazy(sprintf "08-00%d08-01%a18-10%d18-11%a28-20%d28-21%a38-30%d38-31%a48-40%d48-41%a58-50%d58-51%a68-60%d68-61%a78-70%d78-71%a88-80%d88-81" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") 8 80 )) "08-00008-011X18-101018-112X28-202028-213X38-303038-314X48-404048-415X58-505058-516X68-606068-617X78-707078-718X88-808088-81" - test "test8703" (lazy(sprintf "08-00%d08-01%a18-10%d18-11%a28-20%d28-21%a38-30%d38-31%a48-40%d48-41%a58-50%d58-51%a68-60%d68-61%a78-70%d78-71%a88-80%d88-81%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") 8 80 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-011X18-101018-112X28-202028-213X38-303038-314X48-404048-415X58-505058-516X68-606068-617X78-707078-718X88-808088-81ReadX_TAIL" - test "test8704" (lazy(sprintf "08-00%d08-01%d08-02%a18-10%d18-11%d18-12%a28-20%d28-21%d28-22%a38-30%d38-31%d38-32%a48-40%d48-41%d48-42%a58-50%d58-51%d58-52%a68-60%d68-61%d68-62%a78-70%d78-71%d78-72%a88-80%d88-81%d88-82" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") 8 80 81 )) "08-00008-01108-021X18-101018-111118-122X28-202028-212128-223X38-303038-313138-324X48-404048-414148-425X58-505058-515158-526X68-606068-616168-627X78-707078-717178-728X88-808088-818188-82" - test "test8705" (lazy(sprintf "08-00%d08-01%d08-02%a18-10%d18-11%d18-12%a28-20%d28-21%d28-22%a38-30%d38-31%d38-32%a48-40%d48-41%d48-42%a58-50%d58-51%d58-52%a68-60%d68-61%d68-62%a78-70%d78-71%d78-72%a88-80%d88-81%d88-82%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") 8 80 81 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-021X18-101018-111118-122X28-202028-212128-223X38-303038-313138-324X48-404048-414148-425X58-505058-515158-526X68-606068-616168-627X78-707078-717178-728X88-808088-818188-82ReadX_TAIL" - test "test8706" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%a18-10%d18-11%d18-12%d18-13%a28-20%d28-21%d28-22%d28-23%a38-30%d38-31%d38-32%d38-33%a48-40%d48-41%d48-42%d48-43%a58-50%d58-51%d58-52%d58-53%a68-60%d68-61%d68-62%d68-63%a78-70%d78-71%d78-72%d78-73%a88-80%d88-81%d88-82%d88-83" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") 8 80 81 82 )) "08-00008-01108-02208-031X18-101018-111118-121218-132X28-202028-212128-222228-233X38-303038-313138-323238-334X48-404048-414148-424248-435X58-505058-515158-525258-536X68-606068-616168-626268-637X78-707078-717178-727278-738X88-808088-818188-828288-83" - test "test8707" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%a18-10%d18-11%d18-12%d18-13%a28-20%d28-21%d28-22%d28-23%a38-30%d38-31%d38-32%d38-33%a48-40%d48-41%d48-42%d48-43%a58-50%d58-51%d58-52%d58-53%a68-60%d68-61%d68-62%d68-63%a78-70%d78-71%d78-72%d78-73%a88-80%d88-81%d88-82%d88-83%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") 8 80 81 82 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-031X18-101018-111118-121218-132X28-202028-212128-222228-233X38-303038-313138-323238-334X48-404048-414148-424248-435X58-505058-515158-525258-536X68-606068-616168-626268-637X78-707078-717178-727278-738X88-808088-818188-828288-83ReadX_TAIL" - test "test8708" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%a18-10%d18-11%d18-12%d18-13%d18-14%a28-20%d28-21%d28-22%d28-23%d28-24%a38-30%d38-31%d38-32%d38-33%d38-34%a48-40%d48-41%d48-42%d48-43%d48-44%a58-50%d58-51%d58-52%d58-53%d58-54%a68-60%d68-61%d68-62%d68-63%d68-64%a78-70%d78-71%d78-72%d78-73%d78-74%a88-80%d88-81%d88-82%d88-83%d88-84" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") 8 80 81 82 83 )) "08-00008-01108-02208-03308-041X18-101018-111118-121218-131318-142X28-202028-212128-222228-232328-243X38-303038-313138-323238-333338-344X48-404048-414148-424248-434348-445X58-505058-515158-525258-535358-546X68-606068-616168-626268-636368-647X78-707078-717178-727278-737378-748X88-808088-818188-828288-838388-84" - test "test8709" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%a18-10%d18-11%d18-12%d18-13%d18-14%a28-20%d28-21%d28-22%d28-23%d28-24%a38-30%d38-31%d38-32%d38-33%d38-34%a48-40%d48-41%d48-42%d48-43%d48-44%a58-50%d58-51%d58-52%d58-53%d58-54%a68-60%d68-61%d68-62%d68-63%d68-64%a78-70%d78-71%d78-72%d78-73%d78-74%a88-80%d88-81%d88-82%d88-83%d88-84%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") 8 80 81 82 83 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-041X18-101018-111118-121218-131318-142X28-202028-212128-222228-232328-243X38-303038-313138-323238-333338-344X48-404048-414148-424248-434348-445X58-505058-515158-525258-535358-546X68-606068-616168-626268-636368-647X78-707078-717178-727278-737378-748X88-808088-818188-828288-838388-84ReadX_TAIL" - test "test8710" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 )) "08-00008-01108-02208-03308-04408-051X18-101018-111118-121218-131318-141418-152X28-202028-212128-222228-232328-242428-253X38-303038-313138-323238-333338-343438-354X48-404048-414148-424248-434348-444448-455X58-505058-515158-525258-535358-545458-556X68-606068-616168-626268-636368-646468-657X78-707078-717178-727278-737378-747478-758X88-808088-818188-828288-838388-848488-85" - test "test8711" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-051X18-101018-111118-121218-131318-141418-152X28-202028-212128-222228-232328-242428-253X38-303038-313138-323238-333338-343438-354X48-404048-414148-424248-434348-444448-455X58-505058-515158-525258-535358-545458-556X68-606068-616168-626268-636368-646468-657X78-707078-717178-727278-737378-747478-758X88-808088-818188-828288-838388-848488-85ReadX_TAIL" - test "test8712" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 )) "08-00008-01108-02208-03308-04408-05508-061X18-101018-111118-121218-131318-141418-151518-162X28-202028-212128-222228-232328-242428-252528-263X38-303038-313138-323238-333338-343438-353538-364X48-404048-414148-424248-434348-444448-454548-465X58-505058-515158-525258-535358-545458-555558-566X68-606068-616168-626268-636368-646468-656568-667X78-707078-717178-727278-737378-747478-757578-768X88-808088-818188-828288-838388-848488-858588-86" - test "test8713" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-05508-061X18-101018-111118-121218-131318-141418-151518-162X28-202028-212128-222228-232328-242428-252528-263X38-303038-313138-323238-333338-343438-353538-364X48-404048-414148-424248-434348-444448-454548-465X58-505058-515158-525258-535358-545458-555558-566X68-606068-616168-626268-636368-646468-656568-667X78-707078-717178-727278-737378-747478-757578-768X88-808088-818188-828288-838388-848488-858588-86ReadX_TAIL" - test "test8714" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 )) "08-00008-01108-02208-03308-04408-05508-06608-071X18-101018-111118-121218-131318-141418-151518-161618-172X28-202028-212128-222228-232328-242428-252528-262628-273X38-303038-313138-323238-333338-343438-353538-363638-374X48-404048-414148-424248-434348-444448-454548-464648-475X58-505058-515158-525258-535358-545458-555558-565658-576X68-606068-616168-626268-636368-646468-656568-666668-677X78-707078-717178-727278-737378-747478-757578-767678-778X88-808088-818188-828288-838388-848488-858588-868688-87" - test "test8715" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-05508-06608-071X18-101018-111118-121218-131318-141418-151518-161618-172X28-202028-212128-222228-232328-242428-252528-262628-273X38-303038-313138-323238-333338-343438-353538-363638-374X48-404048-414148-424248-434348-444448-454548-464648-475X58-505058-515158-525258-535358-545458-555558-565658-576X68-606068-616168-626268-636368-646468-656568-666668-677X78-707078-717178-727278-737378-747478-757578-767678-778X88-808088-818188-828288-838388-848488-858588-868688-87ReadX_TAIL" - test "test8716" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%d08-08%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%d18-18%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%d28-28%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%d38-38%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%d48-48%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%d58-58%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%d68-68%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%d78-78%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%d88-88" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 )) "08-00008-01108-02208-03308-04408-05508-06608-07708-081X18-101018-111118-121218-131318-141418-151518-161618-171718-182X28-202028-212128-222228-232328-242428-252528-262628-272728-283X38-303038-313138-323238-333338-343438-353538-363638-373738-384X48-404048-414148-424248-434348-444448-454548-464648-474748-485X58-505058-515158-525258-535358-545458-555558-565658-575758-586X68-606068-616168-626268-636368-646468-656568-666668-676768-687X78-707078-717178-727278-737378-747478-757578-767678-777778-788X88-808088-818188-828288-838388-848488-858588-868688-878788-88" - test "test8717" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%d08-08%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%d18-18%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%d28-28%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%d38-38%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%d48-48%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%d58-58%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%d68-68%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%d78-78%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%d88-88%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-05508-06608-07708-081X18-101018-111118-121218-131318-141418-151518-161618-171718-182X28-202028-212128-222228-232328-242428-252528-262628-272728-283X38-303038-313138-323238-333338-343438-353538-363638-373738-384X48-404048-414148-424248-434348-444448-454548-464648-474748-485X58-505058-515158-525258-535358-545458-555558-565658-575758-586X68-606068-616168-626268-636368-646468-656568-666668-676768-687X78-707078-717178-727278-737378-747478-757578-767678-777778-788X88-808088-818188-828288-838388-848488-858588-868688-878788-88ReadX_TAIL" - test "test8718" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%d08-08%d08-09%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%d18-18%d18-19%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%d28-28%d28-29%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%d38-38%d38-39%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%d48-48%d48-49%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%d58-58%d58-59%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%d68-68%d68-69%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%d78-78%d78-79%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%d88-88%d88-89" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 88 )) "08-00008-01108-02208-03308-04408-05508-06608-07708-08808-091X18-101018-111118-121218-131318-141418-151518-161618-171718-181818-192X28-202028-212128-222228-232328-242428-252528-262628-272728-282828-293X38-303038-313138-323238-333338-343438-353538-363638-373738-383838-394X48-404048-414148-424248-434348-444448-454548-464648-474748-484848-495X58-505058-515158-525258-535358-545458-555558-565658-575758-585858-596X68-606068-616168-626268-636368-646468-656568-666668-676768-686868-697X78-707078-717178-727278-737378-747478-757578-767678-777778-787878-798X88-808088-818188-828288-838388-848488-858588-868688-878788-888888-89" - test "test8719" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%d08-08%d08-09%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%d18-18%d18-19%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%d28-28%d28-29%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%d38-38%d38-39%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%d48-48%d48-49%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%d58-58%d58-59%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%d68-68%d68-69%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%d78-78%d78-79%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%d88-88%d88-89%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 88 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-05508-06608-07708-08808-091X18-101018-111118-121218-131318-141418-151518-161618-171718-181818-192X28-202028-212128-222228-232328-242428-252528-262628-272728-282828-293X38-303038-313138-323238-333338-343438-353538-363638-373738-383838-394X48-404048-414148-424248-434348-444448-454548-464648-474748-484848-495X58-505058-515158-525258-535358-545458-555558-565658-575758-585858-596X68-606068-616168-626268-636368-646468-656568-666668-676768-686868-697X78-707078-717178-727278-737378-747478-757578-767678-777778-787878-798X88-808088-818188-828288-838388-848488-858588-868688-878788-888888-89ReadX_TAIL" - test "test8720" (lazy(sprintf "09-00%d09-01%a19-10%d19-11%a29-20%d29-21%a39-30%d39-31%a49-40%d49-41%a59-50%d59-51%a69-60%d69-61%a79-70%d79-71%a89-80%d89-81%a99-90%d99-91" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") 8 80 (fun _ v -> (string v) + "X") 9 90 )) "09-00009-011X19-101019-112X29-202029-213X39-303039-314X49-404049-415X59-505059-516X69-606069-617X79-707079-718X89-808089-819X99-909099-91" - test "test8721" (lazy(sprintf "09-00%d09-01%a19-10%d19-11%a29-20%d29-21%a39-30%d39-31%a49-40%d49-41%a59-50%d59-51%a69-60%d69-61%a79-70%d79-71%a89-80%d89-81%a99-90%d99-91%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") 8 80 (fun _ v -> (string v) + "X") 9 90 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-011X19-101019-112X29-202029-213X39-303039-314X49-404049-415X59-505059-516X69-606069-617X79-707079-718X89-808089-819X99-909099-91ReadX_TAIL" - test "test8722" (lazy(sprintf "09-00%d09-01%d09-02%a19-10%d19-11%d19-12%a29-20%d29-21%d29-22%a39-30%d39-31%d39-32%a49-40%d49-41%d49-42%a59-50%d59-51%d59-52%a69-60%d69-61%d69-62%a79-70%d79-71%d79-72%a89-80%d89-81%d89-82%a99-90%d99-91%d99-92" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") 8 80 81 (fun _ v -> (string v) + "X") 9 90 91 )) "09-00009-01109-021X19-101019-111119-122X29-202029-212129-223X39-303039-313139-324X49-404049-414149-425X59-505059-515159-526X69-606069-616169-627X79-707079-717179-728X89-808089-818189-829X99-909099-919199-92" - test "test8723" (lazy(sprintf "09-00%d09-01%d09-02%a19-10%d19-11%d19-12%a29-20%d29-21%d29-22%a39-30%d39-31%d39-32%a49-40%d49-41%d49-42%a59-50%d59-51%d59-52%a69-60%d69-61%d69-62%a79-70%d79-71%d79-72%a89-80%d89-81%d89-82%a99-90%d99-91%d99-92%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") 8 80 81 (fun _ v -> (string v) + "X") 9 90 91 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-021X19-101019-111119-122X29-202029-212129-223X39-303039-313139-324X49-404049-414149-425X59-505059-515159-526X69-606069-616169-627X79-707079-717179-728X89-808089-818189-829X99-909099-919199-92ReadX_TAIL" - test "test8724" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%a19-10%d19-11%d19-12%d19-13%a29-20%d29-21%d29-22%d29-23%a39-30%d39-31%d39-32%d39-33%a49-40%d49-41%d49-42%d49-43%a59-50%d59-51%d59-52%d59-53%a69-60%d69-61%d69-62%d69-63%a79-70%d79-71%d79-72%d79-73%a89-80%d89-81%d89-82%d89-83%a99-90%d99-91%d99-92%d99-93" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") 8 80 81 82 (fun _ v -> (string v) + "X") 9 90 91 92 )) "09-00009-01109-02209-031X19-101019-111119-121219-132X29-202029-212129-222229-233X39-303039-313139-323239-334X49-404049-414149-424249-435X59-505059-515159-525259-536X69-606069-616169-626269-637X79-707079-717179-727279-738X89-808089-818189-828289-839X99-909099-919199-929299-93" - test "test8725" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%a19-10%d19-11%d19-12%d19-13%a29-20%d29-21%d29-22%d29-23%a39-30%d39-31%d39-32%d39-33%a49-40%d49-41%d49-42%d49-43%a59-50%d59-51%d59-52%d59-53%a69-60%d69-61%d69-62%d69-63%a79-70%d79-71%d79-72%d79-73%a89-80%d89-81%d89-82%d89-83%a99-90%d99-91%d99-92%d99-93%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") 8 80 81 82 (fun _ v -> (string v) + "X") 9 90 91 92 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-031X19-101019-111119-121219-132X29-202029-212129-222229-233X39-303039-313139-323239-334X49-404049-414149-424249-435X59-505059-515159-525259-536X69-606069-616169-626269-637X79-707079-717179-727279-738X89-808089-818189-828289-839X99-909099-919199-929299-93ReadX_TAIL" - test "test8726" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%a19-10%d19-11%d19-12%d19-13%d19-14%a29-20%d29-21%d29-22%d29-23%d29-24%a39-30%d39-31%d39-32%d39-33%d39-34%a49-40%d49-41%d49-42%d49-43%d49-44%a59-50%d59-51%d59-52%d59-53%d59-54%a69-60%d69-61%d69-62%d69-63%d69-64%a79-70%d79-71%d79-72%d79-73%d79-74%a89-80%d89-81%d89-82%d89-83%d89-84%a99-90%d99-91%d99-92%d99-93%d99-94" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") 8 80 81 82 83 (fun _ v -> (string v) + "X") 9 90 91 92 93 )) "09-00009-01109-02209-03309-041X19-101019-111119-121219-131319-142X29-202029-212129-222229-232329-243X39-303039-313139-323239-333339-344X49-404049-414149-424249-434349-445X59-505059-515159-525259-535359-546X69-606069-616169-626269-636369-647X79-707079-717179-727279-737379-748X89-808089-818189-828289-838389-849X99-909099-919199-929299-939399-94" - test "test8727" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%a19-10%d19-11%d19-12%d19-13%d19-14%a29-20%d29-21%d29-22%d29-23%d29-24%a39-30%d39-31%d39-32%d39-33%d39-34%a49-40%d49-41%d49-42%d49-43%d49-44%a59-50%d59-51%d59-52%d59-53%d59-54%a69-60%d69-61%d69-62%d69-63%d69-64%a79-70%d79-71%d79-72%d79-73%d79-74%a89-80%d89-81%d89-82%d89-83%d89-84%a99-90%d99-91%d99-92%d99-93%d99-94%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") 8 80 81 82 83 (fun _ v -> (string v) + "X") 9 90 91 92 93 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-041X19-101019-111119-121219-131319-142X29-202029-212129-222229-232329-243X39-303039-313139-323239-333339-344X49-404049-414149-424249-434349-445X59-505059-515159-525259-535359-546X69-606069-616169-626269-636369-647X79-707079-717179-727279-737379-748X89-808089-818189-828289-838389-849X99-909099-919199-929299-939399-94ReadX_TAIL" - test "test8728" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 )) "09-00009-01109-02209-03309-04409-051X19-101019-111119-121219-131319-141419-152X29-202029-212129-222229-232329-242429-253X39-303039-313139-323239-333339-343439-354X49-404049-414149-424249-434349-444449-455X59-505059-515159-525259-535359-545459-556X69-606069-616169-626269-636369-646469-657X79-707079-717179-727279-737379-747479-758X89-808089-818189-828289-838389-848489-859X99-909099-919199-929299-939399-949499-95" - test "test8729" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-051X19-101019-111119-121219-131319-141419-152X29-202029-212129-222229-232329-242429-253X39-303039-313139-323239-333339-343439-354X49-404049-414149-424249-434349-444449-455X59-505059-515159-525259-535359-545459-556X69-606069-616169-626269-636369-646469-657X79-707079-717179-727279-737379-747479-758X89-808089-818189-828289-838389-848489-859X99-909099-919199-929299-939399-949499-95ReadX_TAIL" - test "test8730" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 )) "09-00009-01109-02209-03309-04409-05509-061X19-101019-111119-121219-131319-141419-151519-162X29-202029-212129-222229-232329-242429-252529-263X39-303039-313139-323239-333339-343439-353539-364X49-404049-414149-424249-434349-444449-454549-465X59-505059-515159-525259-535359-545459-555559-566X69-606069-616169-626269-636369-646469-656569-667X79-707079-717179-727279-737379-747479-757579-768X89-808089-818189-828289-838389-848489-858589-869X99-909099-919199-929299-939399-949499-959599-96" - test "test8731" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-05509-061X19-101019-111119-121219-131319-141419-151519-162X29-202029-212129-222229-232329-242429-252529-263X39-303039-313139-323239-333339-343439-353539-364X49-404049-414149-424249-434349-444449-454549-465X59-505059-515159-525259-535359-545459-555559-566X69-606069-616169-626269-636369-646469-656569-667X79-707079-717179-727279-737379-747479-757579-768X89-808089-818189-828289-838389-848489-858589-869X99-909099-919199-929299-939399-949499-959599-96ReadX_TAIL" - test "test8732" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 )) "09-00009-01109-02209-03309-04409-05509-06609-071X19-101019-111119-121219-131319-141419-151519-161619-172X29-202029-212129-222229-232329-242429-252529-262629-273X39-303039-313139-323239-333339-343439-353539-363639-374X49-404049-414149-424249-434349-444449-454549-464649-475X59-505059-515159-525259-535359-545459-555559-565659-576X69-606069-616169-626269-636369-646469-656569-666669-677X79-707079-717179-727279-737379-747479-757579-767679-778X89-808089-818189-828289-838389-848489-858589-868689-879X99-909099-919199-929299-939399-949499-959599-969699-97" - test "test8733" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-05509-06609-071X19-101019-111119-121219-131319-141419-151519-161619-172X29-202029-212129-222229-232329-242429-252529-262629-273X39-303039-313139-323239-333339-343439-353539-363639-374X49-404049-414149-424249-434349-444449-454549-464649-475X59-505059-515159-525259-535359-545459-555559-565659-576X69-606069-616169-626269-636369-646469-656569-666669-677X79-707079-717179-727279-737379-747479-757579-767679-778X89-808089-818189-828289-838389-848489-858589-868689-879X99-909099-919199-929299-939399-949499-959599-969699-97ReadX_TAIL" - test "test8734" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%d09-08%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%d19-18%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%d29-28%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%d39-38%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%d49-48%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%d59-58%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%d69-68%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%d79-78%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%d89-88%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%d99-98" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 97 )) "09-00009-01109-02209-03309-04409-05509-06609-07709-081X19-101019-111119-121219-131319-141419-151519-161619-171719-182X29-202029-212129-222229-232329-242429-252529-262629-272729-283X39-303039-313139-323239-333339-343439-353539-363639-373739-384X49-404049-414149-424249-434349-444449-454549-464649-474749-485X59-505059-515159-525259-535359-545459-555559-565659-575759-586X69-606069-616169-626269-636369-646469-656569-666669-676769-687X79-707079-717179-727279-737379-747479-757579-767679-777779-788X89-808089-818189-828289-838389-848489-858589-868689-878789-889X99-909099-919199-929299-939399-949499-959599-969699-979799-98" - test "test8735" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%d09-08%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%d19-18%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%d29-28%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%d39-38%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%d49-48%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%d59-58%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%d69-68%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%d79-78%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%d89-88%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%d99-98%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 97 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-05509-06609-07709-081X19-101019-111119-121219-131319-141419-151519-161619-171719-182X29-202029-212129-222229-232329-242429-252529-262629-272729-283X39-303039-313139-323239-333339-343439-353539-363639-373739-384X49-404049-414149-424249-434349-444449-454549-464649-474749-485X59-505059-515159-525259-535359-545459-555559-565659-575759-586X69-606069-616169-626269-636369-646469-656569-666669-676769-687X79-707079-717179-727279-737379-747479-757579-767679-777779-788X89-808089-818189-828289-838389-848489-858589-868689-878789-889X99-909099-919199-929299-939399-949499-959599-969699-979799-98ReadX_TAIL" - test "test8736" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%d09-08%d09-09%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%d19-18%d19-19%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%d29-28%d29-29%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%d39-38%d39-39%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%d49-48%d49-49%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%d59-58%d59-59%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%d69-68%d69-69%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%d79-78%d79-79%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%d89-88%d89-89%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%d99-98%d99-99" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 88 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 97 98 )) "09-00009-01109-02209-03309-04409-05509-06609-07709-08809-091X19-101019-111119-121219-131319-141419-151519-161619-171719-181819-192X29-202029-212129-222229-232329-242429-252529-262629-272729-282829-293X39-303039-313139-323239-333339-343439-353539-363639-373739-383839-394X49-404049-414149-424249-434349-444449-454549-464649-474749-484849-495X59-505059-515159-525259-535359-545459-555559-565659-575759-585859-596X69-606069-616169-626269-636369-646469-656569-666669-676769-686869-697X79-707079-717179-727279-737379-747479-757579-767679-777779-787879-798X89-808089-818189-828289-838389-848489-858589-868689-878789-888889-899X99-909099-919199-929299-939399-949499-959599-969699-979799-989899-99" - test "test8737" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%d09-08%d09-09%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%d19-18%d19-19%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%d29-28%d29-29%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%d39-38%d39-39%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%d49-48%d49-49%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%d59-58%d59-59%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%d69-68%d69-69%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%d79-78%d79-79%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%d89-88%d89-89%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%d99-98%d99-99%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 88 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 97 98 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-05509-06609-07709-08809-091X19-101019-111119-121219-131319-141419-151519-161619-171719-181819-192X29-202029-212129-222229-232329-242429-252529-262629-272729-282829-293X39-303039-313139-323239-333339-343439-353539-363639-373739-383839-394X49-404049-414149-424249-434349-444449-454549-464649-474749-484849-495X59-505059-515159-525259-535359-545459-555559-565659-575759-585859-596X69-606069-616169-626269-636369-646469-656569-666669-676769-686869-697X79-707079-717179-727279-737379-747479-757579-767679-777779-787879-798X89-808089-818189-828289-838389-848489-858589-868689-878789-888889-899X99-909099-919199-929299-939399-949499-959599-969699-979799-989899-99ReadX_TAIL" +// let func8000()= +// test "test8001" (lazy(sprintf "%-0*M" 7 10M)) "10 "//"1000000" +// test "test8002" (lazy(sprintf "%-0.5M" 10M)) "10" +// test "test8003" (lazy(sprintf "%-0.*M" 4 10M)) "10" +// test "test8004" (lazy(sprintf "%-0*.*M" 5 4 10M)) "10 "//"10000" +// test "test8005" (lazy(sprintf "%+M" 10M)) "+10" +// test "test8006" (lazy(sprintf "%+5M" 10M)) " +10" +// test "test8007" (lazy(sprintf "%+1M" 10M)) "+10" +// test "test8008" (lazy(sprintf "%+*M" 7 10M)) " +10" +// test "test8009" (lazy(sprintf "%+.5M" 10M)) "+10" +// test "test8010" (lazy(sprintf "%+.*M" 4 10M)) "+10" +// test "test8011" (lazy(sprintf "%+*.*M" 5 4 10M)) " +10" +// test "test8012" (lazy(sprintf "%-+M" 10M)) "+10" +// test "test8013" (lazy(sprintf "%-+5M" 10M)) "+10 " +// test "test8014" (lazy(sprintf "%-+1M" 10M)) "+10" +// test "test8015" (lazy(sprintf "%-+*M" 7 10M)) "+10 " +// test "test8016" (lazy(sprintf "%-+.5M" 10M)) "+10" +// test "test8017" (lazy(sprintf "%-+.*M" 4 10M)) "+10" +// test "test8018" (lazy(sprintf "%-+*.*M" 5 4 10M)) "+10 " +// test "test8019" (lazy(sprintf "%+0M" 10M)) "+10" +// test "test8020" (lazy(sprintf "%+05M" 10M)) "+0010"// "00+10" +// test "test8021" (lazy(sprintf "%+01M" 10M)) "+10" +// test "test8022" (lazy(sprintf "%+0*M" 7 10M)) "+000010"// "0000+10" +// test "test8023" (lazy(sprintf "%+0.5M" 10M)) "+10" +// test "test8024" (lazy(sprintf "%+0.*M" 4 10M)) "+10" +// test "test8025" (lazy(sprintf "%+0*.*M" 5 4 10M)) "+0010"// "00+10" +// test "test8026" (lazy(sprintf "%-+0M" 10M)) "+10" +// test "test8027" (lazy(sprintf "%-+05M" 10M)) "+10 "//"+1000" +// test "test8028" (lazy(sprintf "%-+01M" 10M)) "+10" +// test "test8029" (lazy(sprintf "%-+0*M" 7 10M)) "+10 "// "+100000" +// test "test8030" (lazy(sprintf "%-+0.5M" 10M)) "+10" +// test "test8031" (lazy(sprintf "%-+0.*M" 4 10M)) "+10" +// test "test8032" (lazy(sprintf "%-+0*.*M" 5 4 10M)) "+10 "//"+1000" +// test "test8033" (lazy(sprintf "% M" 10M)) " 10" +// test "test8034" (lazy(sprintf "% 5M" 10M)) " 10" +// test "test8035" (lazy(sprintf "% 1M" 10M)) " 10" +// test "test8036" (lazy(sprintf "% *M" 7 10M)) " 10" +// test "test8037" (lazy(sprintf "% .5M" 10M)) " 10" +// test "test8038" (lazy(sprintf "% .*M" 4 10M)) " 10" +// test "test8039" (lazy(sprintf "% *.*M" 5 4 10M)) " 10" +// test "test8040" (lazy(sprintf "%- M" 10M)) " 10" +// test "test8041" (lazy(sprintf "%- 5M" 10M)) " 10 " +// test "test8042" (lazy(sprintf "%- 1M" 10M)) " 10" +// test "test8043" (lazy(sprintf "%- *M" 7 10M)) " 10 " +// test "test8044" (lazy(sprintf "%- .5M" 10M)) " 10" +// test "test8045" (lazy(sprintf "%- .*M" 4 10M)) " 10" +// test "test8046" (lazy(sprintf "%- *.*M" 5 4 10M)) " 10 " +// test "test8047" (lazy(sprintf "% 0M" 10M)) " 10" +// test "test8048" (lazy(sprintf "% 05M" 10M)) " 0010"// "00 10" +// test "test8049" (lazy(sprintf "% 01M" 10M)) " 10" +// test "test8050" (lazy(sprintf "% 0*M" 7 10M)) " 000010"// "0000 10" +// test "test8051" (lazy(sprintf "% 0.5M" 10M)) " 10" +// test "test8052" (lazy(sprintf "% 0.*M" 4 10M)) " 10" +// test "test8053" (lazy(sprintf "% 0*.*M" 5 4 10M)) " 0010"// "00 10" +// test "test8054" (lazy(sprintf "%- 0M" 10M)) " 10" +// test "test8055" (lazy(sprintf "%- 05M" 10M)) " 10 "// " 1000" +// test "test8056" (lazy(sprintf "%- 01M" 10M)) " 10" +// test "test8057" (lazy(sprintf "%- 0*M" 7 10M)) " 10 "//" 100000" +// test "test8058" (lazy(sprintf "%- 0.5M" 10M)) " 10" +// test "test8059" (lazy(sprintf "%- 0.*M" 4 10M)) " 10" +// test "test8060" (lazy(sprintf "%- 0*.*M" 5 4 10M)) " 10 "//" 1000" +// test "test8061" (lazy(sprintf "%M" 1.3M)) "1.3" +// test "test8062" (lazy(sprintf "%5M" 1.3M)) " 1.3" +// test "test8063" (lazy(sprintf "%1M" 1.3M)) "1.3" +// test "test8064" (lazy(sprintf "%*M" 7 1.3M)) " 1.3" +// test "test8065" (lazy(sprintf "%.5M" 1.3M)) "1.3" +// test "test8066" (lazy(sprintf "%.*M" 4 1.3M)) "1.3" +// test "test8067" (lazy(sprintf "%*.*M" 5 4 1.3M)) " 1.3" +// test "test8068" (lazy(sprintf "%-M" 1.3M)) "1.3" +// test "test8069" (lazy(sprintf "%-5M" 1.3M)) "1.3 " +// test "test8070" (lazy(sprintf "%-1M" 1.3M)) "1.3" +// test "test8071" (lazy(sprintf "%-*M" 7 1.3M)) "1.3 " +// test "test8072" (lazy(sprintf "%-.5M" 1.3M)) "1.3" +// test "test8073" (lazy(sprintf "%-.*M" 4 1.3M)) "1.3" +// test "test8074" (lazy(sprintf "%-*.*M" 5 4 1.3M)) "1.3 " +// test "test8075" (lazy(sprintf "%0M" 1.3M)) "1.3" +// test "test8076" (lazy(sprintf "%05M" 1.3M)) "001.3" +// test "test8077" (lazy(sprintf "%01M" 1.3M)) "1.3" +// test "test8078" (lazy(sprintf "%0*M" 7 1.3M)) "00001.3" +// test "test8079" (lazy(sprintf "%0.5M" 1.3M)) "1.3" +// test "test8080" (lazy(sprintf "%0.*M" 4 1.3M)) "1.3" +// test "test8081" (lazy(sprintf "%0*.*M" 5 4 1.3M)) "001.3" +// test "test8082" (lazy(sprintf "%-0M" 1.3M)) "1.3" +// test "test8083" (lazy(sprintf "%-05M" 1.3M)) "1.300" +// test "test8084" (lazy(sprintf "%-01M" 1.3M)) "1.3" +// test "test8085" (lazy(sprintf "%-0*M" 7 1.3M)) "1.30000" +// test "test8086" (lazy(sprintf "%-0.5M" 1.3M)) "1.3" +// test "test8087" (lazy(sprintf "%-0.*M" 4 1.3M)) "1.3" +// test "test8088" (lazy(sprintf "%-0*.*M" 5 4 1.3M)) "1.300" +// test "test8089" (lazy(sprintf "%+M" 1.3M)) "+1.3" +// test "test8090" (lazy(sprintf "%+5M" 1.3M)) " +1.3" +// test "test8091" (lazy(sprintf "%+1M" 1.3M)) "+1.3" +// test "test8092" (lazy(sprintf "%+*M" 7 1.3M)) " +1.3" +// test "test8093" (lazy(sprintf "%+.5M" 1.3M)) "+1.3" +// test "test8094" (lazy(sprintf "%+.*M" 4 1.3M)) "+1.3" +// test "test8095" (lazy(sprintf "%+*.*M" 5 4 1.3M)) " +1.3" +// test "test8096" (lazy(sprintf "%-+M" 1.3M)) "+1.3" +// test "test8097" (lazy(sprintf "%-+5M" 1.3M)) "+1.3 " +// test "test8098" (lazy(sprintf "%-+1M" 1.3M)) "+1.3" +// test "test8099" (lazy(sprintf "%-+*M" 7 1.3M)) "+1.3 " +// test "test8100" (lazy(sprintf "%-+.5M" 1.3M)) "+1.3" +// test "test8101" (lazy(sprintf "%-+.*M" 4 1.3M)) "+1.3" +// test "test8102" (lazy(sprintf "%-+*.*M" 5 4 1.3M)) "+1.3 " +// test "test8103" (lazy(sprintf "%+0M" 1.3M)) "+1.3" +// test "test8104" (lazy(sprintf "%+05M" 1.3M)) "+01.3"//"0+1.3" +// test "test8105" (lazy(sprintf "%+01M" 1.3M)) "+1.3" +// test "test8106" (lazy(sprintf "%+0*M" 7 1.3M)) "+0001.3"//"000+1.3" +// test "test8107" (lazy(sprintf "%+0.5M" 1.3M)) "+1.3" +// test "test8108" (lazy(sprintf "%+0.*M" 4 1.3M)) "+1.3" +// test "test8109" (lazy(sprintf "%+0*.*M" 5 4 1.3M)) "+01.3"//"0+1.3" +// test "test8110" (lazy(sprintf "%-+0M" 1.3M)) "+1.3" +// test "test8111" (lazy(sprintf "%-+05M" 1.3M)) "+1.30" +// test "test8112" (lazy(sprintf "%-+01M" 1.3M)) "+1.3" +// test "test8113" (lazy(sprintf "%-+0*M" 7 1.3M)) "+1.3000" +// test "test8114" (lazy(sprintf "%-+0.5M" 1.3M)) "+1.3" +// test "test8115" (lazy(sprintf "%-+0.*M" 4 1.3M)) "+1.3" +// test "test8116" (lazy(sprintf "%-+0*.*M" 5 4 1.3M)) "+1.30" +// test "test8117" (lazy(sprintf "% M" 1.3M)) " 1.3" +// test "test8118" (lazy(sprintf "% 5M" 1.3M)) " 1.3" +// test "test8119" (lazy(sprintf "% 1M" 1.3M)) " 1.3" +// test "test8120" (lazy(sprintf "% *M" 7 1.3M)) " 1.3" +// test "test8121" (lazy(sprintf "% .5M" 1.3M)) " 1.3" +// test "test8122" (lazy(sprintf "% .*M" 4 1.3M)) " 1.3" +// test "test8123" (lazy(sprintf "% *.*M" 5 4 1.3M)) " 1.3" +// test "test8124" (lazy(sprintf "%- M" 1.3M)) " 1.3" +// test "test8125" (lazy(sprintf "%- 5M" 1.3M)) " 1.3 " +// test "test8126" (lazy(sprintf "%- 1M" 1.3M)) " 1.3" +// test "test8127" (lazy(sprintf "%- *M" 7 1.3M)) " 1.3 " +// test "test8128" (lazy(sprintf "%- .5M" 1.3M)) " 1.3" +// test "test8129" (lazy(sprintf "%- .*M" 4 1.3M)) " 1.3" +// test "test8130" (lazy(sprintf "%- *.*M" 5 4 1.3M)) " 1.3 " +// test "test8131" (lazy(sprintf "% 0M" 1.3M)) " 1.3" +// test "test8132" (lazy(sprintf "% 05M" 1.3M)) " 01.3"//"0 1.3" +// test "test8133" (lazy(sprintf "% 01M" 1.3M)) " 1.3" +// test "test8134" (lazy(sprintf "% 0*M" 7 1.3M)) " 0001.3"// "000 1.3" +// test "test8135" (lazy(sprintf "% 0.5M" 1.3M)) " 1.3" +// test "test8136" (lazy(sprintf "% 0.*M" 4 1.3M)) " 1.3" +// test "test8137" (lazy(sprintf "% 0*.*M" 5 4 1.3M)) " 01.3"// "0 1.3" +// test "test8138" (lazy(sprintf "%- 0M" 1.3M)) " 1.3" +// test "test8139" (lazy(sprintf "%- 05M" 1.3M)) " 1.30" +// test "test8140" (lazy(sprintf "%- 01M" 1.3M)) " 1.3" +// test "test8141" (lazy(sprintf "%- 0*M" 7 1.3M)) " 1.3000" +// test "test8142" (lazy(sprintf "%- 0.5M" 1.3M)) " 1.3" +// test "test8143" (lazy(sprintf "%- 0.*M" 4 1.3M)) " 1.3" +// test "test8144" (lazy(sprintf "%- 0*.*M" 5 4 1.3M)) " 1.30" +// test "test8145" (lazy(sprintf "%M" -15.5M)) "-15.5" +// test "test8146" (lazy(sprintf "%5M" -15.5M)) "-15.5" +// test "test8147" (lazy(sprintf "%1M" -15.5M)) "-15.5" +// test "test8148" (lazy(sprintf "%*M" 7 -15.5M)) " -15.5" +// test "test8149" (lazy(sprintf "%.5M" -15.5M)) "-15.5" +// test "test8150" (lazy(sprintf "%.*M" 4 -15.5M)) "-15.5" +// test "test8151" (lazy(sprintf "%*.*M" 5 4 -15.5M)) "-15.5" +// test "test8152" (lazy(sprintf "%-M" -15.5M)) "-15.5" +// test "test8153" (lazy(sprintf "%-5M" -15.5M)) "-15.5" +// test "test8154" (lazy(sprintf "%-1M" -15.5M)) "-15.5" +// test "test8155" (lazy(sprintf "%-*M" 7 -15.5M)) "-15.5 " +// test "test8156" (lazy(sprintf "%-.5M" -15.5M)) "-15.5" +// test "test8157" (lazy(sprintf "%-.*M" 4 -15.5M)) "-15.5" +// test "test8158" (lazy(sprintf "%-*.*M" 5 4 -15.5M)) "-15.5" +// test "test8159" (lazy(sprintf "%0M" -15.5M)) "-15.5" +// test "test8160" (lazy(sprintf "%05M" -15.5M)) "-15.5" +// test "test8161" (lazy(sprintf "%01M" -15.5M)) "-15.5" +// test "test8162" (lazy(sprintf "%0*M" 7 -15.5M)) "-0015.5"// "00-15.5" +// test "test8163" (lazy(sprintf "%0.5M" -15.5M)) "-15.5" +// test "test8164" (lazy(sprintf "%0.*M" 4 -15.5M)) "-15.5" +// test "test8165" (lazy(sprintf "%0*.*M" 5 4 -15.5M)) "-15.5" +// test "test8166" (lazy(sprintf "%-0M" -15.5M)) "-15.5" +// test "test8167" (lazy(sprintf "%-05M" -15.5M)) "-15.5" +// test "test8168" (lazy(sprintf "%-01M" -15.5M)) "-15.5" +// test "test8169" (lazy(sprintf "%-0*M" 7 -15.5M)) "-15.500" +// test "test8170" (lazy(sprintf "%-0.5M" -15.5M)) "-15.5" +// test "test8171" (lazy(sprintf "%-0.*M" 4 -15.5M)) "-15.5" +// test "test8172" (lazy(sprintf "%-0*.*M" 5 4 -15.5M)) "-15.5" +// test "test8173" (lazy(sprintf "%+M" -15.5M)) "-15.5" +// test "test8174" (lazy(sprintf "%+5M" -15.5M)) "-15.5" +// test "test8175" (lazy(sprintf "%+1M" -15.5M)) "-15.5" +// test "test8176" (lazy(sprintf "%+*M" 7 -15.5M)) " -15.5" +// test "test8177" (lazy(sprintf "%+.5M" -15.5M)) "-15.5" +// test "test8178" (lazy(sprintf "%+.*M" 4 -15.5M)) "-15.5" +// test "test8179" (lazy(sprintf "%+*.*M" 5 4 -15.5M)) "-15.5" +// test "test8180" (lazy(sprintf "%-+M" -15.5M)) "-15.5" +// test "test8181" (lazy(sprintf "%-+5M" -15.5M)) "-15.5" +// test "test8182" (lazy(sprintf "%-+1M" -15.5M)) "-15.5" +// test "test8183" (lazy(sprintf "%-+*M" 7 -15.5M)) "-15.5 " +// test "test8184" (lazy(sprintf "%-+.5M" -15.5M)) "-15.5" +// test "test8185" (lazy(sprintf "%-+.*M" 4 -15.5M)) "-15.5" +// test "test8186" (lazy(sprintf "%-+*.*M" 5 4 -15.5M)) "-15.5" +// test "test8187" (lazy(sprintf "%+0M" -15.5M)) "-15.5" +// test "test8188" (lazy(sprintf "%+05M" -15.5M)) "-15.5" +// test "test8189" (lazy(sprintf "%+01M" -15.5M)) "-15.5" +// test "test8190" (lazy(sprintf "%+0*M" 7 -15.5M)) "-0015.5"//"00-15.5" +// test "test8191" (lazy(sprintf "%+0.5M" -15.5M)) "-15.5" +// test "test8192" (lazy(sprintf "%+0.*M" 4 -15.5M)) "-15.5" +// test "test8193" (lazy(sprintf "%+0*.*M" 5 4 -15.5M)) "-15.5" +// test "test8194" (lazy(sprintf "%-+0M" -15.5M)) "-15.5" +// test "test8195" (lazy(sprintf "%-+05M" -15.5M)) "-15.5" +// test "test8196" (lazy(sprintf "%-+01M" -15.5M)) "-15.5" +// test "test8197" (lazy(sprintf "%-+0*M" 7 -15.5M)) "-15.500" +// test "test8198" (lazy(sprintf "%-+0.5M" -15.5M)) "-15.5" +// test "test8199" (lazy(sprintf "%-+0.*M" 4 -15.5M)) "-15.5" +// test "test8200" (lazy(sprintf "%-+0*.*M" 5 4 -15.5M)) "-15.5" +// test "test8201" (lazy(sprintf "% M" -15.5M)) "-15.5" +// test "test8202" (lazy(sprintf "% 5M" -15.5M)) "-15.5" +// test "test8203" (lazy(sprintf "% 1M" -15.5M)) "-15.5" +// test "test8204" (lazy(sprintf "% *M" 7 -15.5M)) " -15.5" +// test "test8205" (lazy(sprintf "% .5M" -15.5M)) "-15.5" +// test "test8206" (lazy(sprintf "% .*M" 4 -15.5M)) "-15.5" +// test "test8207" (lazy(sprintf "% *.*M" 5 4 -15.5M)) "-15.5" +// test "test8208" (lazy(sprintf "%- M" -15.5M)) "-15.5" +// test "test8209" (lazy(sprintf "%- 5M" -15.5M)) "-15.5" +// test "test8210" (lazy(sprintf "%- 1M" -15.5M)) "-15.5" +// test "test8211" (lazy(sprintf "%- *M" 7 -15.5M)) "-15.5 " +// test "test8212" (lazy(sprintf "%- .5M" -15.5M)) "-15.5" +// test "test8213" (lazy(sprintf "%- .*M" 4 -15.5M)) "-15.5" +// test "test8214" (lazy(sprintf "%- *.*M" 5 4 -15.5M)) "-15.5" +// test "test8215" (lazy(sprintf "% 0M" -15.5M)) "-15.5" +// test "test8216" (lazy(sprintf "% 05M" -15.5M)) "-15.5" +// test "test8217" (lazy(sprintf "% 01M" -15.5M)) "-15.5" +// test "test8218" (lazy(sprintf "% 0*M" 7 -15.5M)) "-0015.5"//"00-15.5" +// test "test8219" (lazy(sprintf "% 0.5M" -15.5M)) "-15.5" +// test "test8220" (lazy(sprintf "% 0.*M" 4 -15.5M)) "-15.5" +// test "test8221" (lazy(sprintf "% 0*.*M" 5 4 -15.5M)) "-15.5" +// test "test8222" (lazy(sprintf "%- 0M" -15.5M)) "-15.5" +// test "test8223" (lazy(sprintf "%- 05M" -15.5M)) "-15.5" +// test "test8224" (lazy(sprintf "%- 01M" -15.5M)) "-15.5" +// test "test8225" (lazy(sprintf "%- 0*M" 7 -15.5M)) "-15.500" +// test "test8226" (lazy(sprintf "%- 0.5M" -15.5M)) "-15.5" +// test "test8227" (lazy(sprintf "%- 0.*M" 4 -15.5M)) "-15.5" +// test "test8228" (lazy(sprintf "%- 0*.*M" 5 4 -15.5M)) "-15.5" +// test "test8229" (lazy(sprintf "%M" -7M)) "-7" +// test "test8230" (lazy(sprintf "%5M" -7M)) " -7" +// test "test8231" (lazy(sprintf "%1M" -7M)) "-7" +// test "test8232" (lazy(sprintf "%*M" 7 -7M)) " -7" +// test "test8233" (lazy(sprintf "%.5M" -7M)) "-7" +// test "test8234" (lazy(sprintf "%.*M" 4 -7M)) "-7" +// test "test8235" (lazy(sprintf "%*.*M" 5 4 -7M)) " -7" +// test "test8236" (lazy(sprintf "%-M" -7M)) "-7" +// test "test8237" (lazy(sprintf "%-5M" -7M)) "-7 " +// test "test8238" (lazy(sprintf "%-1M" -7M)) "-7" +// test "test8239" (lazy(sprintf "%-*M" 7 -7M)) "-7 " +// test "test8240" (lazy(sprintf "%-.5M" -7M)) "-7" +// test "test8241" (lazy(sprintf "%-.*M" 4 -7M)) "-7" +// test "test8242" (lazy(sprintf "%-*.*M" 5 4 -7M)) "-7 " +// test "test8243" (lazy(sprintf "%0M" -7M)) "-7" +// test "test8244" (lazy(sprintf "%05M" -7M)) "-0007"//"000-7" +// test "test8245" (lazy(sprintf "%01M" -7M)) "-7" +// test "test8246" (lazy(sprintf "%0*M" 7 -7M)) "-000007"// "00000-7" +// test "test8247" (lazy(sprintf "%0.5M" -7M)) "-7" +// test "test8248" (lazy(sprintf "%0.*M" 4 -7M)) "-7" +// test "test8249" (lazy(sprintf "%0*.*M" 5 4 -7M)) "-0007"// "000-7" +// test "test8250" (lazy(sprintf "%-0M" -7M)) "-7" +// test "test8251" (lazy(sprintf "%-05M" -7M)) "-7 "// "-7000" +// test "test8252" (lazy(sprintf "%-01M" -7M)) "-7" +// test "test8253" (lazy(sprintf "%-0*M" 7 -7M)) "-7 "//"-700000" +// test "test8254" (lazy(sprintf "%-0.5M" -7M)) "-7" +// test "test8255" (lazy(sprintf "%-0.*M" 4 -7M)) "-7" +// test "test8256" (lazy(sprintf "%-0*.*M" 5 4 -7M)) "-7 "//"-7000" +// test "test8257" (lazy(sprintf "%+M" -7M)) "-7" +// test "test8258" (lazy(sprintf "%+5M" -7M)) " -7" +// test "test8259" (lazy(sprintf "%+1M" -7M)) "-7" +// test "test8260" (lazy(sprintf "%+*M" 7 -7M)) " -7" +// test "test8261" (lazy(sprintf "%+.5M" -7M)) "-7" +// test "test8262" (lazy(sprintf "%+.*M" 4 -7M)) "-7" +// test "test8263" (lazy(sprintf "%+*.*M" 5 4 -7M)) " -7" +// test "test8264" (lazy(sprintf "%-+M" -7M)) "-7" +// test "test8265" (lazy(sprintf "%-+5M" -7M)) "-7 " +// test "test8266" (lazy(sprintf "%-+1M" -7M)) "-7" +// test "test8267" (lazy(sprintf "%-+*M" 7 -7M)) "-7 " +// test "test8268" (lazy(sprintf "%-+.5M" -7M)) "-7" +// test "test8269" (lazy(sprintf "%-+.*M" 4 -7M)) "-7" +// test "test8270" (lazy(sprintf "%-+*.*M" 5 4 -7M)) "-7 " +// test "test8271" (lazy(sprintf "%+0M" -7M)) "-7" +// test "test8272" (lazy(sprintf "%+05M" -7M)) "-0007"// "000-7" +// test "test8273" (lazy(sprintf "%+01M" -7M)) "-7" +// test "test8274" (lazy(sprintf "%+0*M" 7 -7M)) "-000007"//"00000-7" +// test "test8275" (lazy(sprintf "%+0.5M" -7M)) "-7" +// test "test8276" (lazy(sprintf "%+0.*M" 4 -7M)) "-7" +// test "test8277" (lazy(sprintf "%+0*.*M" 5 4 -7M)) "-0007"//"000-7" +// test "test8278" (lazy(sprintf "%-+0M" -7M)) "-7" +// test "test8279" (lazy(sprintf "%-+05M" -7M)) "-7 "// "-7000" +// test "test8280" (lazy(sprintf "%-+01M" -7M)) "-7" +// test "test8281" (lazy(sprintf "%-+0*M" 7 -7M)) "-7 "// "-700000" +// test "test8282" (lazy(sprintf "%-+0.5M" -7M)) "-7" +// test "test8283" (lazy(sprintf "%-+0.*M" 4 -7M)) "-7" +// test "test8284" (lazy(sprintf "%-+0*.*M" 5 4 -7M)) "-7 "//"-7000" +// test "test8285" (lazy(sprintf "% M" -7M)) "-7" +// test "test8286" (lazy(sprintf "% 5M" -7M)) " -7" +// test "test8287" (lazy(sprintf "% 1M" -7M)) "-7" +// test "test8288" (lazy(sprintf "% *M" 7 -7M)) " -7" +// test "test8289" (lazy(sprintf "% .5M" -7M)) "-7" +// test "test8290" (lazy(sprintf "% .*M" 4 -7M)) "-7" +// test "test8291" (lazy(sprintf "% *.*M" 5 4 -7M)) " -7" +// test "test8292" (lazy(sprintf "%- M" -7M)) "-7" +// test "test8293" (lazy(sprintf "%- 5M" -7M)) "-7 " +// test "test8294" (lazy(sprintf "%- 1M" -7M)) "-7" +// test "test8295" (lazy(sprintf "%- *M" 7 -7M)) "-7 " +// test "test8296" (lazy(sprintf "%- .5M" -7M)) "-7" +// test "test8297" (lazy(sprintf "%- .*M" 4 -7M)) "-7" +// test "test8298" (lazy(sprintf "%- *.*M" 5 4 -7M)) "-7 " +// test "test8299" (lazy(sprintf "% 0M" -7M)) "-7" +// test "test8300" (lazy(sprintf "% 05M" -7M)) "-0007"//"000-7" +// test "test8301" (lazy(sprintf "% 01M" -7M)) "-7" +// test "test8302" (lazy(sprintf "% 0*M" 7 -7M)) "-000007"//"00000-7" +// test "test8303" (lazy(sprintf "% 0.5M" -7M)) "-7" +// test "test8304" (lazy(sprintf "% 0.*M" 4 -7M)) "-7" +// test "test8305" (lazy(sprintf "% 0*.*M" 5 4 -7M)) "-0007"// "000-7" +// test "test8306" (lazy(sprintf "%- 0M" -7M)) "-7" +// test "test8307" (lazy(sprintf "%- 05M" -7M)) "-7 "// "-7000" +// test "test8308" (lazy(sprintf "%- 01M" -7M)) "-7" +// test "test8309" (lazy(sprintf "%- 0*M" 7 -7M)) "-7 "//"-700000" +// test "test8310" (lazy(sprintf "%- 0.5M" -7M)) "-7" +// test "test8311" (lazy(sprintf "%- 0.*M" 4 -7M)) "-7" +// test "test8312" (lazy(sprintf "%- 0*.*M" 5 4 -7M)) "-7 "///"-7000" +// test "test8313" (lazy(sprintf "%O" "abc")) "abc" +// test "test8314" (lazy(sprintf "%5O" "abc")) " abc" +// test "test8315" (lazy(sprintf "%1O" "abc")) "abc" +// test "test8316" (lazy(sprintf "%*O" 7 "abc")) " abc" +// test "test8317" (lazy(sprintf "%-O" "abc")) "abc" +// test "test8318" (lazy(sprintf "%-5O" "abc")) "abc " +// test "test8319" (lazy(sprintf "%-1O" "abc")) "abc" +// test "test8320" (lazy(sprintf "%-*O" 7 "abc")) "abc " +// test "test8321" (lazy(sprintf "%O" 15)) "15" +// test "test8322" (lazy(sprintf "%5O" 15)) " 15" +// test "test8323" (lazy(sprintf "%1O" 15)) "15" +// test "test8324" (lazy(sprintf "%*O" 7 15)) " 15" +// test "test8325" (lazy(sprintf "%-O" 15)) "15" +// test "test8326" (lazy(sprintf "%-5O" 15)) "15 " +// test "test8327" (lazy(sprintf "%-1O" 15)) "15" +// test "test8328" (lazy(sprintf "%-*O" 7 15)) "15 " +// test "test8329" (lazy(sprintf "%O" -10)) "-10" +// test "test8330" (lazy(sprintf "%5O" -10)) " -10" +// test "test8331" (lazy(sprintf "%1O" -10)) "-10" +// test "test8332" (lazy(sprintf "%*O" 7 -10)) " -10" +// test "test8333" (lazy(sprintf "%-O" -10)) "-10" +// test "test8334" (lazy(sprintf "%-5O" -10)) "-10 " +// test "test8335" (lazy(sprintf "%-1O" -10)) "-10" +// test "test8336" (lazy(sprintf "%-*O" 7 -10)) "-10 " +// test "test8337" (lazy(sprintf "%O" null)) "" +// test "test8338" (lazy(sprintf "%5O" null)) "" +// test "test8339" (lazy(sprintf "%1O" null)) "" +// test "test8340" (lazy(sprintf "%*O" 7 null)) " " +// test "test8341" (lazy(sprintf "%-O" null)) "" +// test "test8342" (lazy(sprintf "%-5O" null)) "" +// test "test8343" (lazy(sprintf "%-1O" null)) "" +// test "test8344" (lazy(sprintf "%-*O" 7 null)) " " +// test "test8345" (lazy(sprintf "%O" 'P')) "P" +// test "test8346" (lazy(sprintf "%5O" 'P')) " P" +// test "test8347" (lazy(sprintf "%1O" 'P')) "P" +// test "test8348" (lazy(sprintf "%*O" 7 'P')) " P" +// test "test8349" (lazy(sprintf "%-O" 'P')) "P" +// test "test8350" (lazy(sprintf "%-5O" 'P')) "P " +// test "test8351" (lazy(sprintf "%-1O" 'P')) "P" +// test "test8352" (lazy(sprintf "%-*O" 7 'P')) "P " +// test "test8353" (lazy(sprintf "%O" System.IO.FileShare.None)) "None" +// test "test8354" (lazy(sprintf "%5O" System.IO.FileShare.None)) " None" +// test "test8355" (lazy(sprintf "%1O" System.IO.FileShare.None)) "None" +// test "test8356" (lazy(sprintf "%*O" 7 System.IO.FileShare.None)) " None" +// test "test8357" (lazy(sprintf "%-O" System.IO.FileShare.None)) "None" +// test "test8358" (lazy(sprintf "%-5O" System.IO.FileShare.None)) "None " +// test "test8359" (lazy(sprintf "%-1O" System.IO.FileShare.None)) "None" +// test "test8360" (lazy(sprintf "%-*O" 7 System.IO.FileShare.None)) "None " +// test "test8361" (lazy(sprintf "%A" "abc")) "\"abc\"" +// test "test8362" (lazy(sprintf "%5A" "abc")) "\"abc\"" +// test "test8363" (lazy(sprintf "%1A" "abc")) "\"abc\"" +// test "test8364" (lazy(sprintf "%*A" 7 "abc")) "\"abc\"" +// test "test8365" (lazy(sprintf "%.5A" "abc")) "\"abc\"" +// test "test8366" (lazy(sprintf "%.*A" 4 "abc")) "\"abc\"" +// test "test8367" (lazy(sprintf "%*.*A" 5 4 "abc")) "\"abc\"" +// test "test8368" (lazy(sprintf "%-A" "abc")) "\"abc\"" +// test "test8369" (lazy(sprintf "%-5A" "abc")) "\"abc\"" +// test "test8370" (lazy(sprintf "%-1A" "abc")) "\"abc\"" +// test "test8371" (lazy(sprintf "%-*A" 7 "abc")) "\"abc\"" +// test "test8372" (lazy(sprintf "%-.5A" "abc")) "\"abc\"" +// test "test8373" (lazy(sprintf "%-.*A" 4 "abc")) "\"abc\"" +// test "test8374" (lazy(sprintf "%-*.*A" 5 4 "abc")) "\"abc\"" +// test "test8375" (lazy(sprintf "%+A" "abc")) "\"abc\"" +// test "test8376" (lazy(sprintf "%+5A" "abc")) "\"abc\"" +// test "test8377" (lazy(sprintf "%+1A" "abc")) "\"abc\"" +// test "test8378" (lazy(sprintf "%+*A" 7 "abc")) "\"abc\"" +// test "test8379" (lazy(sprintf "%+.5A" "abc")) "\"abc\"" +// test "test8380" (lazy(sprintf "%+.*A" 4 "abc")) "\"abc\"" +// test "test8381" (lazy(sprintf "%+*.*A" 5 4 "abc")) "\"abc\"" +// test "test8382" (lazy(sprintf "%-+A" "abc")) "\"abc\"" +// test "test8383" (lazy(sprintf "%-+5A" "abc")) "\"abc\"" +// test "test8384" (lazy(sprintf "%-+1A" "abc")) "\"abc\"" +// test "test8385" (lazy(sprintf "%-+*A" 7 "abc")) "\"abc\"" +// test "test8386" (lazy(sprintf "%-+.5A" "abc")) "\"abc\"" +// test "test8387" (lazy(sprintf "%-+.*A" 4 "abc")) "\"abc\"" +// test "test8388" (lazy(sprintf "%-+*.*A" 5 4 "abc")) "\"abc\"" +// test "test8389" (lazy(sprintf "%A" 15)) "15" +// test "test8390" (lazy(sprintf "%5A" 15)) "15" +// test "test8391" (lazy(sprintf "%1A" 15)) "15" +// test "test8392" (lazy(sprintf "%*A" 7 15)) "15" +// test "test8393" (lazy(sprintf "%.5A" 15)) "15" +// test "test8394" (lazy(sprintf "%.*A" 4 15)) "15" +// test "test8395" (lazy(sprintf "%*.*A" 5 4 15)) "15" +// test "test8396" (lazy(sprintf "%-A" 15)) "15" +// test "test8397" (lazy(sprintf "%-5A" 15)) "15" +// test "test8398" (lazy(sprintf "%-1A" 15)) "15" +// test "test8399" (lazy(sprintf "%-*A" 7 15)) "15" +// test "test8400" (lazy(sprintf "%-.5A" 15)) "15" +// test "test8401" (lazy(sprintf "%-.*A" 4 15)) "15" +// test "test8402" (lazy(sprintf "%-*.*A" 5 4 15)) "15" +// test "test8403" (lazy(sprintf "%+A" 15)) "15" +// test "test8404" (lazy(sprintf "%+5A" 15)) "15" +// test "test8405" (lazy(sprintf "%+1A" 15)) "15" +// test "test8406" (lazy(sprintf "%+*A" 7 15)) "15" +// test "test8407" (lazy(sprintf "%+.5A" 15)) "15" +// test "test8408" (lazy(sprintf "%+.*A" 4 15)) "15" +// test "test8409" (lazy(sprintf "%+*.*A" 5 4 15)) "15" +// test "test8410" (lazy(sprintf "%-+A" 15)) "15" +// test "test8411" (lazy(sprintf "%-+5A" 15)) "15" +// test "test8412" (lazy(sprintf "%-+1A" 15)) "15" +// test "test8413" (lazy(sprintf "%-+*A" 7 15)) "15" +// test "test8414" (lazy(sprintf "%-+.5A" 15)) "15" +// test "test8415" (lazy(sprintf "%-+.*A" 4 15)) "15" +// test "test8416" (lazy(sprintf "%-+*.*A" 5 4 15)) "15" +// test "test8417" (lazy(sprintf "%A" -10)) "-10" +// test "test8418" (lazy(sprintf "%5A" -10)) "-10" +// test "test8419" (lazy(sprintf "%1A" -10)) "-10" +// test "test8420" (lazy(sprintf "%*A" 7 -10)) "-10" +// test "test8421" (lazy(sprintf "%.5A" -10)) "-10" +// test "test8422" (lazy(sprintf "%.*A" 4 -10)) "-10" +// test "test8423" (lazy(sprintf "%*.*A" 5 4 -10)) "-10" +// test "test8424" (lazy(sprintf "%-A" -10)) "-10" +// test "test8425" (lazy(sprintf "%-5A" -10)) "-10" +// test "test8426" (lazy(sprintf "%-1A" -10)) "-10" +// test "test8427" (lazy(sprintf "%-*A" 7 -10)) "-10" +// test "test8428" (lazy(sprintf "%-.5A" -10)) "-10" +// test "test8429" (lazy(sprintf "%-.*A" 4 -10)) "-10" +// test "test8430" (lazy(sprintf "%-*.*A" 5 4 -10)) "-10" +// test "test8431" (lazy(sprintf "%+A" -10)) "-10" +// test "test8432" (lazy(sprintf "%+5A" -10)) "-10" +// test "test8433" (lazy(sprintf "%+1A" -10)) "-10" +// test "test8434" (lazy(sprintf "%+*A" 7 -10)) "-10" +// test "test8435" (lazy(sprintf "%+.5A" -10)) "-10" +// test "test8436" (lazy(sprintf "%+.*A" 4 -10)) "-10" +// test "test8437" (lazy(sprintf "%+*.*A" 5 4 -10)) "-10" +// test "test8438" (lazy(sprintf "%-+A" -10)) "-10" +// test "test8439" (lazy(sprintf "%-+5A" -10)) "-10" +// test "test8440" (lazy(sprintf "%-+1A" -10)) "-10" +// test "test8441" (lazy(sprintf "%-+*A" 7 -10)) "-10" +// test "test8442" (lazy(sprintf "%-+.5A" -10)) "-10" +// test "test8443" (lazy(sprintf "%-+.*A" 4 -10)) "-10" +// test "test8444" (lazy(sprintf "%-+*.*A" 5 4 -10)) "-10" +// test "test8445" (lazy(sprintf "%A" null)) "" +// test "test8446" (lazy(sprintf "%5A" null)) "" +// test "test8447" (lazy(sprintf "%1A" null)) "" +// test "test8448" (lazy(sprintf "%*A" 7 null)) "" +// test "test8449" (lazy(sprintf "%.5A" null)) "" +// test "test8450" (lazy(sprintf "%.*A" 4 null)) "" +// test "test8451" (lazy(sprintf "%*.*A" 5 4 null)) "" +// test "test8452" (lazy(sprintf "%-A" null)) "" +// test "test8453" (lazy(sprintf "%-5A" null)) "" +// test "test8454" (lazy(sprintf "%-1A" null)) "" +// test "test8455" (lazy(sprintf "%-*A" 7 null)) "" +// test "test8456" (lazy(sprintf "%-.5A" null)) "" +// test "test8457" (lazy(sprintf "%-.*A" 4 null)) "" +// test "test8458" (lazy(sprintf "%-*.*A" 5 4 null)) "" +// test "test8459" (lazy(sprintf "%+A" null)) "" +// test "test8460" (lazy(sprintf "%+5A" null)) "" +// test "test8461" (lazy(sprintf "%+1A" null)) "" +// test "test8462" (lazy(sprintf "%+*A" 7 null)) "" +// test "test8463" (lazy(sprintf "%+.5A" null)) "" +// test "test8464" (lazy(sprintf "%+.*A" 4 null)) "" +// test "test8465" (lazy(sprintf "%+*.*A" 5 4 null)) "" +// test "test8466" (lazy(sprintf "%-+A" null)) "" +// test "test8467" (lazy(sprintf "%-+5A" null)) "" +// test "test8468" (lazy(sprintf "%-+1A" null)) "" +// test "test8469" (lazy(sprintf "%-+*A" 7 null)) "" +// test "test8470" (lazy(sprintf "%-+.5A" null)) "" +// test "test8471" (lazy(sprintf "%-+.*A" 4 null)) "" +// test "test8472" (lazy(sprintf "%-+*.*A" 5 4 null)) "" +// test "test8473" (lazy(sprintf "%A" 'P')) "'P'" +// test "test8474" (lazy(sprintf "%5A" 'P')) "'P'" +// test "test8475" (lazy(sprintf "%1A" 'P')) "'P'" +// test "test8476" (lazy(sprintf "%*A" 7 'P')) "'P'" +// test "test8477" (lazy(sprintf "%.5A" 'P')) "'P'" +// test "test8478" (lazy(sprintf "%.*A" 4 'P')) "'P'" +// test "test8479" (lazy(sprintf "%*.*A" 5 4 'P')) "'P'" +// test "test8480" (lazy(sprintf "%-A" 'P')) "'P'" +// test "test8481" (lazy(sprintf "%-5A" 'P')) "'P'" +// test "test8482" (lazy(sprintf "%-1A" 'P')) "'P'" +// test "test8483" (lazy(sprintf "%-*A" 7 'P')) "'P'" +// test "test8484" (lazy(sprintf "%-.5A" 'P')) "'P'" +// test "test8485" (lazy(sprintf "%-.*A" 4 'P')) "'P'" +// test "test8486" (lazy(sprintf "%-*.*A" 5 4 'P')) "'P'" +// test "test8487" (lazy(sprintf "%+A" 'P')) "'P'" +// test "test8488" (lazy(sprintf "%+5A" 'P')) "'P'" +// test "test8489" (lazy(sprintf "%+1A" 'P')) "'P'" +// test "test8490" (lazy(sprintf "%+*A" 7 'P')) "'P'" +// test "test8491" (lazy(sprintf "%+.5A" 'P')) "'P'" +// test "test8492" (lazy(sprintf "%+.*A" 4 'P')) "'P'" +// test "test8493" (lazy(sprintf "%+*.*A" 5 4 'P')) "'P'" +// test "test8494" (lazy(sprintf "%-+A" 'P')) "'P'" +// test "test8495" (lazy(sprintf "%-+5A" 'P')) "'P'" +// test "test8496" (lazy(sprintf "%-+1A" 'P')) "'P'" +// test "test8497" (lazy(sprintf "%-+*A" 7 'P')) "'P'" +// test "test8498" (lazy(sprintf "%-+.5A" 'P')) "'P'" +// test "test8499" (lazy(sprintf "%-+.*A" 4 'P')) "'P'" +// test "test8500" (lazy(sprintf "%-+*.*A" 5 4 'P')) "'P'" +// test "test8501" (lazy(sprintf "%A" System.IO.FileShare.None)) "None" +// test "test8502" (lazy(sprintf "%5A" System.IO.FileShare.None)) "None" +// test "test8503" (lazy(sprintf "%1A" System.IO.FileShare.None)) "None" +// test "test8504" (lazy(sprintf "%*A" 7 System.IO.FileShare.None)) "None" +// test "test8505" (lazy(sprintf "%.5A" System.IO.FileShare.None)) "None" +// test "test8506" (lazy(sprintf "%.*A" 4 System.IO.FileShare.None)) "None" +// test "test8507" (lazy(sprintf "%*.*A" 5 4 System.IO.FileShare.None)) "None" +// test "test8508" (lazy(sprintf "%-A" System.IO.FileShare.None)) "None" +// test "test8509" (lazy(sprintf "%-5A" System.IO.FileShare.None)) "None" +// test "test8510" (lazy(sprintf "%-1A" System.IO.FileShare.None)) "None" +// test "test8511" (lazy(sprintf "%-*A" 7 System.IO.FileShare.None)) "None" +// test "test8512" (lazy(sprintf "%-.5A" System.IO.FileShare.None)) "None" +// test "test8513" (lazy(sprintf "%-.*A" 4 System.IO.FileShare.None)) "None" +// test "test8514" (lazy(sprintf "%-*.*A" 5 4 System.IO.FileShare.None)) "None" +// test "test8515" (lazy(sprintf "%+A" System.IO.FileShare.None)) "None" +// test "test8516" (lazy(sprintf "%+5A" System.IO.FileShare.None)) "None" +// test "test8517" (lazy(sprintf "%+1A" System.IO.FileShare.None)) "None" +// test "test8518" (lazy(sprintf "%+*A" 7 System.IO.FileShare.None)) "None" +// test "test8519" (lazy(sprintf "%+.5A" System.IO.FileShare.None)) "None" +// test "test8520" (lazy(sprintf "%+.*A" 4 System.IO.FileShare.None)) "None" +// test "test8521" (lazy(sprintf "%+*.*A" 5 4 System.IO.FileShare.None)) "None" +// test "test8522" (lazy(sprintf "%-+A" System.IO.FileShare.None)) "None" +// test "test8523" (lazy(sprintf "%-+5A" System.IO.FileShare.None)) "None" +// test "test8524" (lazy(sprintf "%-+1A" System.IO.FileShare.None)) "None" +// test "test8525" (lazy(sprintf "%-+*A" 7 System.IO.FileShare.None)) "None" +// test "test8526" (lazy(sprintf "%-+.5A" System.IO.FileShare.None)) "None" +// test "test8527" (lazy(sprintf "%-+.*A" 4 System.IO.FileShare.None)) "None" +// test "test8528" (lazy(sprintf "%-+*.*A" 5 4 System.IO.FileShare.None)) "None" +// test "test8529" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") "abc")) "abc!!!" +// test "test8530" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") 15)) "15!!!" +// test "test8531" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") -10)) "-10!!!" +// test "test8532" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") null)) "!!!" +// test "test8533" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") 'P')) "P!!!" +// test "test8534" (lazy(sprintf "%a" (fun _ s -> (string s) + "!!!") System.IO.FileShare.None)) "None!!!" +// test "test8535" (lazy(sprintf "%t" (fun _ -> "???"))) "???" +// test "test8536" (lazy(sprintf "A%dB" 0)) "A0B" +// test "test8537" (lazy(sprintf "A%dB%dC" 0 1)) "A0B1C" +// test "test8538" (lazy(sprintf "A%dB%dC%dD" 0 1 2)) "A0B1C2D" +// test "test8539" (lazy(sprintf "A%dB%dC%dD%dE" 0 1 2 3)) "A0B1C2D3E" +// test "test8540" (lazy(sprintf "A%dB%dC%dD%dE%dF" 0 1 2 3 4)) "A0B1C2D3E4F" +// test "test8541" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG" 0 1 2 3 4 5)) "A0B1C2D3E4F5G" +// test "test8542" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH" 0 1 2 3 4 5 6)) "A0B1C2D3E4F5G6H" +// test "test8543" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI" 0 1 2 3 4 5 6 7)) "A0B1C2D3E4F5G6H7I" +// test "test8544" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ" 0 1 2 3 4 5 6 7 8)) "A0B1C2D3E4F5G6H7I8J" +// test "test8545" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK" 0 1 2 3 4 5 6 7 8 9)) "A0B1C2D3E4F5G6H7I8J9K" +// test "test8546" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL" 0 1 2 3 4 5 6 7 8 9 10)) "A0B1C2D3E4F5G6H7I8J9K10L" +// test "test8547" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM" 0 1 2 3 4 5 6 7 8 9 10 11)) "A0B1C2D3E4F5G6H7I8J9K10L11M" +// test "test8548" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN" 0 1 2 3 4 5 6 7 8 9 10 11 12)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N" +// test "test8549" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO" 0 1 2 3 4 5 6 7 8 9 10 11 12 13)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O" +// test "test8550" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P" +// test "test8551" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q" +// test "test8552" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ%dR" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R" +// test "test8553" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ%dR%dS" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S" +// test "test8554" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ%dR%dS%dT" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T" +// test "test8555" (lazy(sprintf "A%dB%dC%dD%dE%dF%dG%dH%dI%dJ%dK%dL%dM%dN%dO%dP%dQ%dR%dS%dT%dU" 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T19U" +// test "test8556" (lazy(sprintf "A%aB" (fun _ v -> string v) 0)) "A0B" +// test "test8557" (lazy(sprintf "A%aB%aC" (fun _ v -> string v) 0 (fun _ v -> string v) 1)) "A0B1C" +// test "test8558" (lazy(sprintf "A%aB%aC%aD" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2)) "A0B1C2D" +// test "test8559" (lazy(sprintf "A%aB%aC%aD%aE" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3)) "A0B1C2D3E" +// test "test8560" (lazy(sprintf "A%aB%aC%aD%aE%aF" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4)) "A0B1C2D3E4F" +// test "test8561" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5)) "A0B1C2D3E4F5G" +// test "test8562" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6)) "A0B1C2D3E4F5G6H" +// test "test8563" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7)) "A0B1C2D3E4F5G6H7I" +// test "test8564" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8)) "A0B1C2D3E4F5G6H7I8J" +// test "test8565" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9)) "A0B1C2D3E4F5G6H7I8J9K" +// test "test8566" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10)) "A0B1C2D3E4F5G6H7I8J9K10L" +// test "test8567" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11)) "A0B1C2D3E4F5G6H7I8J9K10L11M" +// test "test8568" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N" +// test "test8569" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O" +// test "test8570" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P" +// test "test8571" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q" +// test "test8572" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ%aR" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15 (fun _ v -> string v) 16)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R" +// test "test8573" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ%aR%aS" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15 (fun _ v -> string v) 16 (fun _ v -> string v) 17)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S" +// test "test8574" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ%aR%aS%aT" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15 (fun _ v -> string v) 16 (fun _ v -> string v) 17 (fun _ v -> string v) 18)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T" +// test "test8575" (lazy(sprintf "A%aB%aC%aD%aE%aF%aG%aH%aI%aJ%aK%aL%aM%aN%aO%aP%aQ%aR%aS%aT%aU" (fun _ v -> string v) 0 (fun _ v -> string v) 1 (fun _ v -> string v) 2 (fun _ v -> string v) 3 (fun _ v -> string v) 4 (fun _ v -> string v) 5 (fun _ v -> string v) 6 (fun _ v -> string v) 7 (fun _ v -> string v) 8 (fun _ v -> string v) 9 (fun _ v -> string v) 10 (fun _ v -> string v) 11 (fun _ v -> string v) 12 (fun _ v -> string v) 13 (fun _ v -> string v) 14 (fun _ v -> string v) 15 (fun _ v -> string v) 16 (fun _ v -> string v) 17 (fun _ v -> string v) 18 (fun _ v -> string v) 19)) "A0B1C2D3E4F5G6H7I8J9K10L11M12N13O14P15Q16R17S18T19U" +// test "test8576" (lazy(sprintf "01-00%d01-01%a11-10%d11-11" 0 (fun _ v -> (string v) + "X") 1 10 )) "01-00001-011X11-101011-11" +// test "test8577" (lazy(sprintf "01-00%d01-01%a11-10%d11-11%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-011X11-101011-11ReadX_TAIL" +// test "test8578" (lazy(sprintf "01-00%d01-01%d01-02%a11-10%d11-11%d11-12" 0 1 (fun _ v -> (string v) + "X") 1 10 11 )) "01-00001-01101-021X11-101011-111111-12" +// test "test8579" (lazy(sprintf "01-00%d01-01%d01-02%a11-10%d11-11%d11-12%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-021X11-101011-111111-12ReadX_TAIL" +// test "test8580" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%a11-10%d11-11%d11-12%d11-13" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 )) "01-00001-01101-02201-031X11-101011-111111-121211-13" +// test "test8581" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%a11-10%d11-11%d11-12%d11-13%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-031X11-101011-111111-121211-13ReadX_TAIL" +// test "test8582" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%a11-10%d11-11%d11-12%d11-13%d11-14" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 )) "01-00001-01101-02201-03301-041X11-101011-111111-121211-131311-14" +// test "test8583" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%a11-10%d11-11%d11-12%d11-13%d11-14%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-041X11-101011-111111-121211-131311-14ReadX_TAIL" +// test "test8584" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 )) "01-00001-01101-02201-03301-04401-051X11-101011-111111-121211-131311-141411-15" +// test "test8585" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-051X11-101011-111111-121211-131311-141411-15ReadX_TAIL" +// test "test8586" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 )) "01-00001-01101-02201-03301-04401-05501-061X11-101011-111111-121211-131311-141411-151511-16" +// test "test8587" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-05501-061X11-101011-111111-121211-131311-141411-151511-16ReadX_TAIL" +// test "test8588" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 )) "01-00001-01101-02201-03301-04401-05501-06601-071X11-101011-111111-121211-131311-141411-151511-161611-17" +// test "test8589" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-05501-06601-071X11-101011-111111-121211-131311-141411-151511-161611-17ReadX_TAIL" +// test "test8590" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%d01-08%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%d11-18" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 )) "01-00001-01101-02201-03301-04401-05501-06601-07701-081X11-101011-111111-121211-131311-141411-151511-161611-171711-18" +// test "test8591" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%d01-08%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%d11-18%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-05501-06601-07701-081X11-101011-111111-121211-131311-141411-151511-161611-171711-18ReadX_TAIL" +// test "test8592" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%d01-08%d01-09%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%d11-18%d11-19" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 )) "01-00001-01101-02201-03301-04401-05501-06601-07701-08801-091X11-101011-111111-121211-131311-141411-151511-161611-171711-181811-19" +// test "test8593" (lazy(sprintf "01-00%d01-01%d01-02%d01-03%d01-04%d01-05%d01-06%d01-07%d01-08%d01-09%a11-10%d11-11%d11-12%d11-13%d11-14%d11-15%d11-16%d11-17%d11-18%d11-19%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "01-00001-01101-02201-03301-04401-05501-06601-07701-08801-091X11-101011-111111-121211-131311-141411-151511-161611-171711-181811-19ReadX_TAIL" +// test "test8594" (lazy(sprintf "02-00%d02-01%a12-10%d12-11%a22-20%d22-21" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 )) "02-00002-011X12-101012-112X22-202022-21" +// test "test8595" (lazy(sprintf "02-00%d02-01%a12-10%d12-11%a22-20%d22-21%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-011X12-101012-112X22-202022-21ReadX_TAIL" +// test "test8596" (lazy(sprintf "02-00%d02-01%d02-02%a12-10%d12-11%d12-12%a22-20%d22-21%d22-22" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 )) "02-00002-01102-021X12-101012-111112-122X22-202022-212122-22" +// test "test8597" (lazy(sprintf "02-00%d02-01%d02-02%a12-10%d12-11%d12-12%a22-20%d22-21%d22-22%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-021X12-101012-111112-122X22-202022-212122-22ReadX_TAIL" +// test "test8598" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%a12-10%d12-11%d12-12%d12-13%a22-20%d22-21%d22-22%d22-23" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 )) "02-00002-01102-02202-031X12-101012-111112-121212-132X22-202022-212122-222222-23" +// test "test8599" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%a12-10%d12-11%d12-12%d12-13%a22-20%d22-21%d22-22%d22-23%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-031X12-101012-111112-121212-132X22-202022-212122-222222-23ReadX_TAIL" +// test "test8600" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%a12-10%d12-11%d12-12%d12-13%d12-14%a22-20%d22-21%d22-22%d22-23%d22-24" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 )) "02-00002-01102-02202-03302-041X12-101012-111112-121212-131312-142X22-202022-212122-222222-232322-24" +// test "test8601" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%a12-10%d12-11%d12-12%d12-13%d12-14%a22-20%d22-21%d22-22%d22-23%d22-24%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-041X12-101012-111112-121212-131312-142X22-202022-212122-222222-232322-24ReadX_TAIL" +// test "test8602" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 )) "02-00002-01102-02202-03302-04402-051X12-101012-111112-121212-131312-141412-152X22-202022-212122-222222-232322-242422-25" +// test "test8603" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-051X12-101012-111112-121212-131312-141412-152X22-202022-212122-222222-232322-242422-25ReadX_TAIL" +// test "test8604" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 )) "02-00002-01102-02202-03302-04402-05502-061X12-101012-111112-121212-131312-141412-151512-162X22-202022-212122-222222-232322-242422-252522-26" +// test "test8605" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-05502-061X12-101012-111112-121212-131312-141412-151512-162X22-202022-212122-222222-232322-242422-252522-26ReadX_TAIL" +// test "test8606" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 )) "02-00002-01102-02202-03302-04402-05502-06602-071X12-101012-111112-121212-131312-141412-151512-161612-172X22-202022-212122-222222-232322-242422-252522-262622-27" +// test "test8607" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-05502-06602-071X12-101012-111112-121212-131312-141412-151512-161612-172X22-202022-212122-222222-232322-242422-252522-262622-27ReadX_TAIL" +// test "test8608" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%d02-08%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%d12-18%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%d22-28" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 )) "02-00002-01102-02202-03302-04402-05502-06602-07702-081X12-101012-111112-121212-131312-141412-151512-161612-171712-182X22-202022-212122-222222-232322-242422-252522-262622-272722-28" +// test "test8609" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%d02-08%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%d12-18%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%d22-28%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-05502-06602-07702-081X12-101012-111112-121212-131312-141412-151512-161612-171712-182X22-202022-212122-222222-232322-242422-252522-262622-272722-28ReadX_TAIL" +// test "test8610" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%d02-08%d02-09%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%d12-18%d12-19%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%d22-28%d22-29" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 )) "02-00002-01102-02202-03302-04402-05502-06602-07702-08802-091X12-101012-111112-121212-131312-141412-151512-161612-171712-181812-192X22-202022-212122-222222-232322-242422-252522-262622-272722-282822-29" +// test "test8611" (lazy(sprintf "02-00%d02-01%d02-02%d02-03%d02-04%d02-05%d02-06%d02-07%d02-08%d02-09%a12-10%d12-11%d12-12%d12-13%d12-14%d12-15%d12-16%d12-17%d12-18%d12-19%a22-20%d22-21%d22-22%d22-23%d22-24%d22-25%d22-26%d22-27%d22-28%d22-29%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "02-00002-01102-02202-03302-04402-05502-06602-07702-08802-091X12-101012-111112-121212-131312-141412-151512-161612-171712-181812-192X22-202022-212122-222222-232322-242422-252522-262622-272722-282822-29ReadX_TAIL" +// test "test8612" (lazy(sprintf "03-00%d03-01%a13-10%d13-11%a23-20%d23-21%a33-30%d33-31" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 )) "03-00003-011X13-101013-112X23-202023-213X33-303033-31" +// test "test8613" (lazy(sprintf "03-00%d03-01%a13-10%d13-11%a23-20%d23-21%a33-30%d33-31%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-011X13-101013-112X23-202023-213X33-303033-31ReadX_TAIL" +// test "test8614" (lazy(sprintf "03-00%d03-01%d03-02%a13-10%d13-11%d13-12%a23-20%d23-21%d23-22%a33-30%d33-31%d33-32" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 )) "03-00003-01103-021X13-101013-111113-122X23-202023-212123-223X33-303033-313133-32" +// test "test8615" (lazy(sprintf "03-00%d03-01%d03-02%a13-10%d13-11%d13-12%a23-20%d23-21%d23-22%a33-30%d33-31%d33-32%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-021X13-101013-111113-122X23-202023-212123-223X33-303033-313133-32ReadX_TAIL" +// test "test8616" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%a13-10%d13-11%d13-12%d13-13%a23-20%d23-21%d23-22%d23-23%a33-30%d33-31%d33-32%d33-33" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 )) "03-00003-01103-02203-031X13-101013-111113-121213-132X23-202023-212123-222223-233X33-303033-313133-323233-33" +// test "test8617" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%a13-10%d13-11%d13-12%d13-13%a23-20%d23-21%d23-22%d23-23%a33-30%d33-31%d33-32%d33-33%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-031X13-101013-111113-121213-132X23-202023-212123-222223-233X33-303033-313133-323233-33ReadX_TAIL" +// test "test8618" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%a13-10%d13-11%d13-12%d13-13%d13-14%a23-20%d23-21%d23-22%d23-23%d23-24%a33-30%d33-31%d33-32%d33-33%d33-34" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 )) "03-00003-01103-02203-03303-041X13-101013-111113-121213-131313-142X23-202023-212123-222223-232323-243X33-303033-313133-323233-333333-34" +// test "test8619" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%a13-10%d13-11%d13-12%d13-13%d13-14%a23-20%d23-21%d23-22%d23-23%d23-24%a33-30%d33-31%d33-32%d33-33%d33-34%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-041X13-101013-111113-121213-131313-142X23-202023-212123-222223-232323-243X33-303033-313133-323233-333333-34ReadX_TAIL" +// test "test8620" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 )) "03-00003-01103-02203-03303-04403-051X13-101013-111113-121213-131313-141413-152X23-202023-212123-222223-232323-242423-253X33-303033-313133-323233-333333-343433-35" +// test "test8621" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-051X13-101013-111113-121213-131313-141413-152X23-202023-212123-222223-232323-242423-253X33-303033-313133-323233-333333-343433-35ReadX_TAIL" +// test "test8622" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 )) "03-00003-01103-02203-03303-04403-05503-061X13-101013-111113-121213-131313-141413-151513-162X23-202023-212123-222223-232323-242423-252523-263X33-303033-313133-323233-333333-343433-353533-36" +// test "test8623" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-05503-061X13-101013-111113-121213-131313-141413-151513-162X23-202023-212123-222223-232323-242423-252523-263X33-303033-313133-323233-333333-343433-353533-36ReadX_TAIL" +// test "test8624" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 )) "03-00003-01103-02203-03303-04403-05503-06603-071X13-101013-111113-121213-131313-141413-151513-161613-172X23-202023-212123-222223-232323-242423-252523-262623-273X33-303033-313133-323233-333333-343433-353533-363633-37" +// test "test8625" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-05503-06603-071X13-101013-111113-121213-131313-141413-151513-161613-172X23-202023-212123-222223-232323-242423-252523-262623-273X33-303033-313133-323233-333333-343433-353533-363633-37ReadX_TAIL" +// test "test8626" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%d03-08%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%d13-18%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%d23-28%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%d33-38" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 )) "03-00003-01103-02203-03303-04403-05503-06603-07703-081X13-101013-111113-121213-131313-141413-151513-161613-171713-182X23-202023-212123-222223-232323-242423-252523-262623-272723-283X33-303033-313133-323233-333333-343433-353533-363633-373733-38" +// test "test8627" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%d03-08%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%d13-18%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%d23-28%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%d33-38%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-05503-06603-07703-081X13-101013-111113-121213-131313-141413-151513-161613-171713-182X23-202023-212123-222223-232323-242423-252523-262623-272723-283X33-303033-313133-323233-333333-343433-353533-363633-373733-38ReadX_TAIL" +// test "test8628" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%d03-08%d03-09%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%d13-18%d13-19%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%d23-28%d23-29%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%d33-38%d33-39" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 )) "03-00003-01103-02203-03303-04403-05503-06603-07703-08803-091X13-101013-111113-121213-131313-141413-151513-161613-171713-181813-192X23-202023-212123-222223-232323-242423-252523-262623-272723-282823-293X33-303033-313133-323233-333333-343433-353533-363633-373733-383833-39" +// test "test8629" (lazy(sprintf "03-00%d03-01%d03-02%d03-03%d03-04%d03-05%d03-06%d03-07%d03-08%d03-09%a13-10%d13-11%d13-12%d13-13%d13-14%d13-15%d13-16%d13-17%d13-18%d13-19%a23-20%d23-21%d23-22%d23-23%d23-24%d23-25%d23-26%d23-27%d23-28%d23-29%a33-30%d33-31%d33-32%d33-33%d33-34%d33-35%d33-36%d33-37%d33-38%d33-39%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "03-00003-01103-02203-03303-04403-05503-06603-07703-08803-091X13-101013-111113-121213-131313-141413-151513-161613-171713-181813-192X23-202023-212123-222223-232323-242423-252523-262623-272723-282823-293X33-303033-313133-323233-333333-343433-353533-363633-373733-383833-39ReadX_TAIL" +// test "test8630" (lazy(sprintf "04-00%d04-01%a14-10%d14-11%a24-20%d24-21%a34-30%d34-31%a44-40%d44-41" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 )) "04-00004-011X14-101014-112X24-202024-213X34-303034-314X44-404044-41" +// test "test8631" (lazy(sprintf "04-00%d04-01%a14-10%d14-11%a24-20%d24-21%a34-30%d34-31%a44-40%d44-41%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-011X14-101014-112X24-202024-213X34-303034-314X44-404044-41ReadX_TAIL" +// test "test8632" (lazy(sprintf "04-00%d04-01%d04-02%a14-10%d14-11%d14-12%a24-20%d24-21%d24-22%a34-30%d34-31%d34-32%a44-40%d44-41%d44-42" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 )) "04-00004-01104-021X14-101014-111114-122X24-202024-212124-223X34-303034-313134-324X44-404044-414144-42" +// test "test8633" (lazy(sprintf "04-00%d04-01%d04-02%a14-10%d14-11%d14-12%a24-20%d24-21%d24-22%a34-30%d34-31%d34-32%a44-40%d44-41%d44-42%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-021X14-101014-111114-122X24-202024-212124-223X34-303034-313134-324X44-404044-414144-42ReadX_TAIL" +// test "test8634" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%a14-10%d14-11%d14-12%d14-13%a24-20%d24-21%d24-22%d24-23%a34-30%d34-31%d34-32%d34-33%a44-40%d44-41%d44-42%d44-43" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 )) "04-00004-01104-02204-031X14-101014-111114-121214-132X24-202024-212124-222224-233X34-303034-313134-323234-334X44-404044-414144-424244-43" +// test "test8635" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%a14-10%d14-11%d14-12%d14-13%a24-20%d24-21%d24-22%d24-23%a34-30%d34-31%d34-32%d34-33%a44-40%d44-41%d44-42%d44-43%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-031X14-101014-111114-121214-132X24-202024-212124-222224-233X34-303034-313134-323234-334X44-404044-414144-424244-43ReadX_TAIL" +// test "test8636" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%a14-10%d14-11%d14-12%d14-13%d14-14%a24-20%d24-21%d24-22%d24-23%d24-24%a34-30%d34-31%d34-32%d34-33%d34-34%a44-40%d44-41%d44-42%d44-43%d44-44" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 )) "04-00004-01104-02204-03304-041X14-101014-111114-121214-131314-142X24-202024-212124-222224-232324-243X34-303034-313134-323234-333334-344X44-404044-414144-424244-434344-44" +// test "test8637" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%a14-10%d14-11%d14-12%d14-13%d14-14%a24-20%d24-21%d24-22%d24-23%d24-24%a34-30%d34-31%d34-32%d34-33%d34-34%a44-40%d44-41%d44-42%d44-43%d44-44%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-041X14-101014-111114-121214-131314-142X24-202024-212124-222224-232324-243X34-303034-313134-323234-333334-344X44-404044-414144-424244-434344-44ReadX_TAIL" +// test "test8638" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 )) "04-00004-01104-02204-03304-04404-051X14-101014-111114-121214-131314-141414-152X24-202024-212124-222224-232324-242424-253X34-303034-313134-323234-333334-343434-354X44-404044-414144-424244-434344-444444-45" +// test "test8639" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-051X14-101014-111114-121214-131314-141414-152X24-202024-212124-222224-232324-242424-253X34-303034-313134-323234-333334-343434-354X44-404044-414144-424244-434344-444444-45ReadX_TAIL" +// test "test8640" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 )) "04-00004-01104-02204-03304-04404-05504-061X14-101014-111114-121214-131314-141414-151514-162X24-202024-212124-222224-232324-242424-252524-263X34-303034-313134-323234-333334-343434-353534-364X44-404044-414144-424244-434344-444444-454544-46" +// test "test8641" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-05504-061X14-101014-111114-121214-131314-141414-151514-162X24-202024-212124-222224-232324-242424-252524-263X34-303034-313134-323234-333334-343434-353534-364X44-404044-414144-424244-434344-444444-454544-46ReadX_TAIL" +// test "test8642" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 )) "04-00004-01104-02204-03304-04404-05504-06604-071X14-101014-111114-121214-131314-141414-151514-161614-172X24-202024-212124-222224-232324-242424-252524-262624-273X34-303034-313134-323234-333334-343434-353534-363634-374X44-404044-414144-424244-434344-444444-454544-464644-47" +// test "test8643" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-05504-06604-071X14-101014-111114-121214-131314-141414-151514-161614-172X24-202024-212124-222224-232324-242424-252524-262624-273X34-303034-313134-323234-333334-343434-353534-363634-374X44-404044-414144-424244-434344-444444-454544-464644-47ReadX_TAIL" +// test "test8644" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%d04-08%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%d14-18%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%d24-28%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%d34-38%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%d44-48" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 )) "04-00004-01104-02204-03304-04404-05504-06604-07704-081X14-101014-111114-121214-131314-141414-151514-161614-171714-182X24-202024-212124-222224-232324-242424-252524-262624-272724-283X34-303034-313134-323234-333334-343434-353534-363634-373734-384X44-404044-414144-424244-434344-444444-454544-464644-474744-48" +// test "test8645" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%d04-08%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%d14-18%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%d24-28%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%d34-38%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%d44-48%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-05504-06604-07704-081X14-101014-111114-121214-131314-141414-151514-161614-171714-182X24-202024-212124-222224-232324-242424-252524-262624-272724-283X34-303034-313134-323234-333334-343434-353534-363634-373734-384X44-404044-414144-424244-434344-444444-454544-464644-474744-48ReadX_TAIL" +// test "test8646" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%d04-08%d04-09%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%d14-18%d14-19%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%d24-28%d24-29%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%d34-38%d34-39%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%d44-48%d44-49" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 )) "04-00004-01104-02204-03304-04404-05504-06604-07704-08804-091X14-101014-111114-121214-131314-141414-151514-161614-171714-181814-192X24-202024-212124-222224-232324-242424-252524-262624-272724-282824-293X34-303034-313134-323234-333334-343434-353534-363634-373734-383834-394X44-404044-414144-424244-434344-444444-454544-464644-474744-484844-49" +// test "test8647" (lazy(sprintf "04-00%d04-01%d04-02%d04-03%d04-04%d04-05%d04-06%d04-07%d04-08%d04-09%a14-10%d14-11%d14-12%d14-13%d14-14%d14-15%d14-16%d14-17%d14-18%d14-19%a24-20%d24-21%d24-22%d24-23%d24-24%d24-25%d24-26%d24-27%d24-28%d24-29%a34-30%d34-31%d34-32%d34-33%d34-34%d34-35%d34-36%d34-37%d34-38%d34-39%a44-40%d44-41%d44-42%d44-43%d44-44%d44-45%d44-46%d44-47%d44-48%d44-49%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "04-00004-01104-02204-03304-04404-05504-06604-07704-08804-091X14-101014-111114-121214-131314-141414-151514-161614-171714-181814-192X24-202024-212124-222224-232324-242424-252524-262624-272724-282824-293X34-303034-313134-323234-333334-343434-353534-363634-373734-383834-394X44-404044-414144-424244-434344-444444-454544-464644-474744-484844-49ReadX_TAIL" +// test "test8648" (lazy(sprintf "05-00%d05-01%a15-10%d15-11%a25-20%d25-21%a35-30%d35-31%a45-40%d45-41%a55-50%d55-51" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 )) "05-00005-011X15-101015-112X25-202025-213X35-303035-314X45-404045-415X55-505055-51" +// test "test8649" (lazy(sprintf "05-00%d05-01%a15-10%d15-11%a25-20%d25-21%a35-30%d35-31%a45-40%d45-41%a55-50%d55-51%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-011X15-101015-112X25-202025-213X35-303035-314X45-404045-415X55-505055-51ReadX_TAIL" +// test "test8650" (lazy(sprintf "05-00%d05-01%d05-02%a15-10%d15-11%d15-12%a25-20%d25-21%d25-22%a35-30%d35-31%d35-32%a45-40%d45-41%d45-42%a55-50%d55-51%d55-52" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 )) "05-00005-01105-021X15-101015-111115-122X25-202025-212125-223X35-303035-313135-324X45-404045-414145-425X55-505055-515155-52" +// test "test8651" (lazy(sprintf "05-00%d05-01%d05-02%a15-10%d15-11%d15-12%a25-20%d25-21%d25-22%a35-30%d35-31%d35-32%a45-40%d45-41%d45-42%a55-50%d55-51%d55-52%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-021X15-101015-111115-122X25-202025-212125-223X35-303035-313135-324X45-404045-414145-425X55-505055-515155-52ReadX_TAIL" +// test "test8652" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%a15-10%d15-11%d15-12%d15-13%a25-20%d25-21%d25-22%d25-23%a35-30%d35-31%d35-32%d35-33%a45-40%d45-41%d45-42%d45-43%a55-50%d55-51%d55-52%d55-53" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 )) "05-00005-01105-02205-031X15-101015-111115-121215-132X25-202025-212125-222225-233X35-303035-313135-323235-334X45-404045-414145-424245-435X55-505055-515155-525255-53" +// test "test8653" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%a15-10%d15-11%d15-12%d15-13%a25-20%d25-21%d25-22%d25-23%a35-30%d35-31%d35-32%d35-33%a45-40%d45-41%d45-42%d45-43%a55-50%d55-51%d55-52%d55-53%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-031X15-101015-111115-121215-132X25-202025-212125-222225-233X35-303035-313135-323235-334X45-404045-414145-424245-435X55-505055-515155-525255-53ReadX_TAIL" +// test "test8654" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%a15-10%d15-11%d15-12%d15-13%d15-14%a25-20%d25-21%d25-22%d25-23%d25-24%a35-30%d35-31%d35-32%d35-33%d35-34%a45-40%d45-41%d45-42%d45-43%d45-44%a55-50%d55-51%d55-52%d55-53%d55-54" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 )) "05-00005-01105-02205-03305-041X15-101015-111115-121215-131315-142X25-202025-212125-222225-232325-243X35-303035-313135-323235-333335-344X45-404045-414145-424245-434345-445X55-505055-515155-525255-535355-54" +// test "test8655" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%a15-10%d15-11%d15-12%d15-13%d15-14%a25-20%d25-21%d25-22%d25-23%d25-24%a35-30%d35-31%d35-32%d35-33%d35-34%a45-40%d45-41%d45-42%d45-43%d45-44%a55-50%d55-51%d55-52%d55-53%d55-54%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-041X15-101015-111115-121215-131315-142X25-202025-212125-222225-232325-243X35-303035-313135-323235-333335-344X45-404045-414145-424245-434345-445X55-505055-515155-525255-535355-54ReadX_TAIL" +// test "test8656" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 )) "05-00005-01105-02205-03305-04405-051X15-101015-111115-121215-131315-141415-152X25-202025-212125-222225-232325-242425-253X35-303035-313135-323235-333335-343435-354X45-404045-414145-424245-434345-444445-455X55-505055-515155-525255-535355-545455-55" +// test "test8657" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-051X15-101015-111115-121215-131315-141415-152X25-202025-212125-222225-232325-242425-253X35-303035-313135-323235-333335-343435-354X45-404045-414145-424245-434345-444445-455X55-505055-515155-525255-535355-545455-55ReadX_TAIL" +// test "test8658" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 )) "05-00005-01105-02205-03305-04405-05505-061X15-101015-111115-121215-131315-141415-151515-162X25-202025-212125-222225-232325-242425-252525-263X35-303035-313135-323235-333335-343435-353535-364X45-404045-414145-424245-434345-444445-454545-465X55-505055-515155-525255-535355-545455-555555-56" +// test "test8659" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-05505-061X15-101015-111115-121215-131315-141415-151515-162X25-202025-212125-222225-232325-242425-252525-263X35-303035-313135-323235-333335-343435-353535-364X45-404045-414145-424245-434345-444445-454545-465X55-505055-515155-525255-535355-545455-555555-56ReadX_TAIL" +// test "test8660" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 )) "05-00005-01105-02205-03305-04405-05505-06605-071X15-101015-111115-121215-131315-141415-151515-161615-172X25-202025-212125-222225-232325-242425-252525-262625-273X35-303035-313135-323235-333335-343435-353535-363635-374X45-404045-414145-424245-434345-444445-454545-464645-475X55-505055-515155-525255-535355-545455-555555-565655-57" +// test "test8661" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-05505-06605-071X15-101015-111115-121215-131315-141415-151515-161615-172X25-202025-212125-222225-232325-242425-252525-262625-273X35-303035-313135-323235-333335-343435-353535-363635-374X45-404045-414145-424245-434345-444445-454545-464645-475X55-505055-515155-525255-535355-545455-555555-565655-57ReadX_TAIL" +// test "test8662" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%d05-08%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%d15-18%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%d25-28%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%d35-38%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%d45-48%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%d55-58" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 )) "05-00005-01105-02205-03305-04405-05505-06605-07705-081X15-101015-111115-121215-131315-141415-151515-161615-171715-182X25-202025-212125-222225-232325-242425-252525-262625-272725-283X35-303035-313135-323235-333335-343435-353535-363635-373735-384X45-404045-414145-424245-434345-444445-454545-464645-474745-485X55-505055-515155-525255-535355-545455-555555-565655-575755-58" +// test "test8663" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%d05-08%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%d15-18%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%d25-28%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%d35-38%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%d45-48%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%d55-58%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-05505-06605-07705-081X15-101015-111115-121215-131315-141415-151515-161615-171715-182X25-202025-212125-222225-232325-242425-252525-262625-272725-283X35-303035-313135-323235-333335-343435-353535-363635-373735-384X45-404045-414145-424245-434345-444445-454545-464645-474745-485X55-505055-515155-525255-535355-545455-555555-565655-575755-58ReadX_TAIL" +// test "test8664" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%d05-08%d05-09%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%d15-18%d15-19%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%d25-28%d25-29%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%d35-38%d35-39%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%d45-48%d45-49%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%d55-58%d55-59" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 )) "05-00005-01105-02205-03305-04405-05505-06605-07705-08805-091X15-101015-111115-121215-131315-141415-151515-161615-171715-181815-192X25-202025-212125-222225-232325-242425-252525-262625-272725-282825-293X35-303035-313135-323235-333335-343435-353535-363635-373735-383835-394X45-404045-414145-424245-434345-444445-454545-464645-474745-484845-495X55-505055-515155-525255-535355-545455-555555-565655-575755-585855-59" +// test "test8665" (lazy(sprintf "05-00%d05-01%d05-02%d05-03%d05-04%d05-05%d05-06%d05-07%d05-08%d05-09%a15-10%d15-11%d15-12%d15-13%d15-14%d15-15%d15-16%d15-17%d15-18%d15-19%a25-20%d25-21%d25-22%d25-23%d25-24%d25-25%d25-26%d25-27%d25-28%d25-29%a35-30%d35-31%d35-32%d35-33%d35-34%d35-35%d35-36%d35-37%d35-38%d35-39%a45-40%d45-41%d45-42%d45-43%d45-44%d45-45%d45-46%d45-47%d45-48%d45-49%a55-50%d55-51%d55-52%d55-53%d55-54%d55-55%d55-56%d55-57%d55-58%d55-59%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "05-00005-01105-02205-03305-04405-05505-06605-07705-08805-091X15-101015-111115-121215-131315-141415-151515-161615-171715-181815-192X25-202025-212125-222225-232325-242425-252525-262625-272725-282825-293X35-303035-313135-323235-333335-343435-353535-363635-373735-383835-394X45-404045-414145-424245-434345-444445-454545-464645-474745-484845-495X55-505055-515155-525255-535355-545455-555555-565655-575755-585855-59ReadX_TAIL" +// test "test8666" (lazy(sprintf "06-00%d06-01%a16-10%d16-11%a26-20%d26-21%a36-30%d36-31%a46-40%d46-41%a56-50%d56-51%a66-60%d66-61" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 )) "06-00006-011X16-101016-112X26-202026-213X36-303036-314X46-404046-415X56-505056-516X66-606066-61" +// test "test8667" (lazy(sprintf "06-00%d06-01%a16-10%d16-11%a26-20%d26-21%a36-30%d36-31%a46-40%d46-41%a56-50%d56-51%a66-60%d66-61%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-011X16-101016-112X26-202026-213X36-303036-314X46-404046-415X56-505056-516X66-606066-61ReadX_TAIL" +// test "test8668" (lazy(sprintf "06-00%d06-01%d06-02%a16-10%d16-11%d16-12%a26-20%d26-21%d26-22%a36-30%d36-31%d36-32%a46-40%d46-41%d46-42%a56-50%d56-51%d56-52%a66-60%d66-61%d66-62" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 )) "06-00006-01106-021X16-101016-111116-122X26-202026-212126-223X36-303036-313136-324X46-404046-414146-425X56-505056-515156-526X66-606066-616166-62" +// test "test8669" (lazy(sprintf "06-00%d06-01%d06-02%a16-10%d16-11%d16-12%a26-20%d26-21%d26-22%a36-30%d36-31%d36-32%a46-40%d46-41%d46-42%a56-50%d56-51%d56-52%a66-60%d66-61%d66-62%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-021X16-101016-111116-122X26-202026-212126-223X36-303036-313136-324X46-404046-414146-425X56-505056-515156-526X66-606066-616166-62ReadX_TAIL" +// test "test8670" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%a16-10%d16-11%d16-12%d16-13%a26-20%d26-21%d26-22%d26-23%a36-30%d36-31%d36-32%d36-33%a46-40%d46-41%d46-42%d46-43%a56-50%d56-51%d56-52%d56-53%a66-60%d66-61%d66-62%d66-63" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 )) "06-00006-01106-02206-031X16-101016-111116-121216-132X26-202026-212126-222226-233X36-303036-313136-323236-334X46-404046-414146-424246-435X56-505056-515156-525256-536X66-606066-616166-626266-63" +// test "test8671" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%a16-10%d16-11%d16-12%d16-13%a26-20%d26-21%d26-22%d26-23%a36-30%d36-31%d36-32%d36-33%a46-40%d46-41%d46-42%d46-43%a56-50%d56-51%d56-52%d56-53%a66-60%d66-61%d66-62%d66-63%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-031X16-101016-111116-121216-132X26-202026-212126-222226-233X36-303036-313136-323236-334X46-404046-414146-424246-435X56-505056-515156-525256-536X66-606066-616166-626266-63ReadX_TAIL" +// test "test8672" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%a16-10%d16-11%d16-12%d16-13%d16-14%a26-20%d26-21%d26-22%d26-23%d26-24%a36-30%d36-31%d36-32%d36-33%d36-34%a46-40%d46-41%d46-42%d46-43%d46-44%a56-50%d56-51%d56-52%d56-53%d56-54%a66-60%d66-61%d66-62%d66-63%d66-64" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 )) "06-00006-01106-02206-03306-041X16-101016-111116-121216-131316-142X26-202026-212126-222226-232326-243X36-303036-313136-323236-333336-344X46-404046-414146-424246-434346-445X56-505056-515156-525256-535356-546X66-606066-616166-626266-636366-64" +// test "test8673" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%a16-10%d16-11%d16-12%d16-13%d16-14%a26-20%d26-21%d26-22%d26-23%d26-24%a36-30%d36-31%d36-32%d36-33%d36-34%a46-40%d46-41%d46-42%d46-43%d46-44%a56-50%d56-51%d56-52%d56-53%d56-54%a66-60%d66-61%d66-62%d66-63%d66-64%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-041X16-101016-111116-121216-131316-142X26-202026-212126-222226-232326-243X36-303036-313136-323236-333336-344X46-404046-414146-424246-434346-445X56-505056-515156-525256-535356-546X66-606066-616166-626266-636366-64ReadX_TAIL" +// test "test8674" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 )) "06-00006-01106-02206-03306-04406-051X16-101016-111116-121216-131316-141416-152X26-202026-212126-222226-232326-242426-253X36-303036-313136-323236-333336-343436-354X46-404046-414146-424246-434346-444446-455X56-505056-515156-525256-535356-545456-556X66-606066-616166-626266-636366-646466-65" +// test "test8675" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-051X16-101016-111116-121216-131316-141416-152X26-202026-212126-222226-232326-242426-253X36-303036-313136-323236-333336-343436-354X46-404046-414146-424246-434346-444446-455X56-505056-515156-525256-535356-545456-556X66-606066-616166-626266-636366-646466-65ReadX_TAIL" +// test "test8676" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 )) "06-00006-01106-02206-03306-04406-05506-061X16-101016-111116-121216-131316-141416-151516-162X26-202026-212126-222226-232326-242426-252526-263X36-303036-313136-323236-333336-343436-353536-364X46-404046-414146-424246-434346-444446-454546-465X56-505056-515156-525256-535356-545456-555556-566X66-606066-616166-626266-636366-646466-656566-66" +// test "test8677" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-05506-061X16-101016-111116-121216-131316-141416-151516-162X26-202026-212126-222226-232326-242426-252526-263X36-303036-313136-323236-333336-343436-353536-364X46-404046-414146-424246-434346-444446-454546-465X56-505056-515156-525256-535356-545456-555556-566X66-606066-616166-626266-636366-646466-656566-66ReadX_TAIL" +// test "test8678" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 )) "06-00006-01106-02206-03306-04406-05506-06606-071X16-101016-111116-121216-131316-141416-151516-161616-172X26-202026-212126-222226-232326-242426-252526-262626-273X36-303036-313136-323236-333336-343436-353536-363636-374X46-404046-414146-424246-434346-444446-454546-464646-475X56-505056-515156-525256-535356-545456-555556-565656-576X66-606066-616166-626266-636366-646466-656566-666666-67" +// test "test8679" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-05506-06606-071X16-101016-111116-121216-131316-141416-151516-161616-172X26-202026-212126-222226-232326-242426-252526-262626-273X36-303036-313136-323236-333336-343436-353536-363636-374X46-404046-414146-424246-434346-444446-454546-464646-475X56-505056-515156-525256-535356-545456-555556-565656-576X66-606066-616166-626266-636366-646466-656566-666666-67ReadX_TAIL" +// test "test8680" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%d06-08%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%d16-18%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%d26-28%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%d36-38%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%d46-48%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%d56-58%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%d66-68" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 )) "06-00006-01106-02206-03306-04406-05506-06606-07706-081X16-101016-111116-121216-131316-141416-151516-161616-171716-182X26-202026-212126-222226-232326-242426-252526-262626-272726-283X36-303036-313136-323236-333336-343436-353536-363636-373736-384X46-404046-414146-424246-434346-444446-454546-464646-474746-485X56-505056-515156-525256-535356-545456-555556-565656-575756-586X66-606066-616166-626266-636366-646466-656566-666666-676766-68" +// test "test8681" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%d06-08%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%d16-18%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%d26-28%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%d36-38%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%d46-48%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%d56-58%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%d66-68%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-05506-06606-07706-081X16-101016-111116-121216-131316-141416-151516-161616-171716-182X26-202026-212126-222226-232326-242426-252526-262626-272726-283X36-303036-313136-323236-333336-343436-353536-363636-373736-384X46-404046-414146-424246-434346-444446-454546-464646-474746-485X56-505056-515156-525256-535356-545456-555556-565656-575756-586X66-606066-616166-626266-636366-646466-656566-666666-676766-68ReadX_TAIL" +// test "test8682" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%d06-08%d06-09%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%d16-18%d16-19%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%d26-28%d26-29%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%d36-38%d36-39%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%d46-48%d46-49%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%d56-58%d56-59%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%d66-68%d66-69" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 )) "06-00006-01106-02206-03306-04406-05506-06606-07706-08806-091X16-101016-111116-121216-131316-141416-151516-161616-171716-181816-192X26-202026-212126-222226-232326-242426-252526-262626-272726-282826-293X36-303036-313136-323236-333336-343436-353536-363636-373736-383836-394X46-404046-414146-424246-434346-444446-454546-464646-474746-484846-495X56-505056-515156-525256-535356-545456-555556-565656-575756-585856-596X66-606066-616166-626266-636366-646466-656566-666666-676766-686866-69" +// test "test8683" (lazy(sprintf "06-00%d06-01%d06-02%d06-03%d06-04%d06-05%d06-06%d06-07%d06-08%d06-09%a16-10%d16-11%d16-12%d16-13%d16-14%d16-15%d16-16%d16-17%d16-18%d16-19%a26-20%d26-21%d26-22%d26-23%d26-24%d26-25%d26-26%d26-27%d26-28%d26-29%a36-30%d36-31%d36-32%d36-33%d36-34%d36-35%d36-36%d36-37%d36-38%d36-39%a46-40%d46-41%d46-42%d46-43%d46-44%d46-45%d46-46%d46-47%d46-48%d46-49%a56-50%d56-51%d56-52%d56-53%d56-54%d56-55%d56-56%d56-57%d56-58%d56-59%a66-60%d66-61%d66-62%d66-63%d66-64%d66-65%d66-66%d66-67%d66-68%d66-69%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "06-00006-01106-02206-03306-04406-05506-06606-07706-08806-091X16-101016-111116-121216-131316-141416-151516-161616-171716-181816-192X26-202026-212126-222226-232326-242426-252526-262626-272726-282826-293X36-303036-313136-323236-333336-343436-353536-363636-373736-383836-394X46-404046-414146-424246-434346-444446-454546-464646-474746-484846-495X56-505056-515156-525256-535356-545456-555556-565656-575756-585856-596X66-606066-616166-626266-636366-646466-656566-666666-676766-686866-69ReadX_TAIL" +// test "test8684" (lazy(sprintf "07-00%d07-01%a17-10%d17-11%a27-20%d27-21%a37-30%d37-31%a47-40%d47-41%a57-50%d57-51%a67-60%d67-61%a77-70%d77-71" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 )) "07-00007-011X17-101017-112X27-202027-213X37-303037-314X47-404047-415X57-505057-516X67-606067-617X77-707077-71" +// test "test8685" (lazy(sprintf "07-00%d07-01%a17-10%d17-11%a27-20%d27-21%a37-30%d37-31%a47-40%d47-41%a57-50%d57-51%a67-60%d67-61%a77-70%d77-71%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-011X17-101017-112X27-202027-213X37-303037-314X47-404047-415X57-505057-516X67-606067-617X77-707077-71ReadX_TAIL" +// test "test8686" (lazy(sprintf "07-00%d07-01%d07-02%a17-10%d17-11%d17-12%a27-20%d27-21%d27-22%a37-30%d37-31%d37-32%a47-40%d47-41%d47-42%a57-50%d57-51%d57-52%a67-60%d67-61%d67-62%a77-70%d77-71%d77-72" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 )) "07-00007-01107-021X17-101017-111117-122X27-202027-212127-223X37-303037-313137-324X47-404047-414147-425X57-505057-515157-526X67-606067-616167-627X77-707077-717177-72" +// test "test8687" (lazy(sprintf "07-00%d07-01%d07-02%a17-10%d17-11%d17-12%a27-20%d27-21%d27-22%a37-30%d37-31%d37-32%a47-40%d47-41%d47-42%a57-50%d57-51%d57-52%a67-60%d67-61%d67-62%a77-70%d77-71%d77-72%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-021X17-101017-111117-122X27-202027-212127-223X37-303037-313137-324X47-404047-414147-425X57-505057-515157-526X67-606067-616167-627X77-707077-717177-72ReadX_TAIL" +// test "test8688" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%a17-10%d17-11%d17-12%d17-13%a27-20%d27-21%d27-22%d27-23%a37-30%d37-31%d37-32%d37-33%a47-40%d47-41%d47-42%d47-43%a57-50%d57-51%d57-52%d57-53%a67-60%d67-61%d67-62%d67-63%a77-70%d77-71%d77-72%d77-73" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 )) "07-00007-01107-02207-031X17-101017-111117-121217-132X27-202027-212127-222227-233X37-303037-313137-323237-334X47-404047-414147-424247-435X57-505057-515157-525257-536X67-606067-616167-626267-637X77-707077-717177-727277-73" +// test "test8689" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%a17-10%d17-11%d17-12%d17-13%a27-20%d27-21%d27-22%d27-23%a37-30%d37-31%d37-32%d37-33%a47-40%d47-41%d47-42%d47-43%a57-50%d57-51%d57-52%d57-53%a67-60%d67-61%d67-62%d67-63%a77-70%d77-71%d77-72%d77-73%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-031X17-101017-111117-121217-132X27-202027-212127-222227-233X37-303037-313137-323237-334X47-404047-414147-424247-435X57-505057-515157-525257-536X67-606067-616167-626267-637X77-707077-717177-727277-73ReadX_TAIL" +// test "test8690" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%a17-10%d17-11%d17-12%d17-13%d17-14%a27-20%d27-21%d27-22%d27-23%d27-24%a37-30%d37-31%d37-32%d37-33%d37-34%a47-40%d47-41%d47-42%d47-43%d47-44%a57-50%d57-51%d57-52%d57-53%d57-54%a67-60%d67-61%d67-62%d67-63%d67-64%a77-70%d77-71%d77-72%d77-73%d77-74" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 )) "07-00007-01107-02207-03307-041X17-101017-111117-121217-131317-142X27-202027-212127-222227-232327-243X37-303037-313137-323237-333337-344X47-404047-414147-424247-434347-445X57-505057-515157-525257-535357-546X67-606067-616167-626267-636367-647X77-707077-717177-727277-737377-74" +// test "test8691" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%a17-10%d17-11%d17-12%d17-13%d17-14%a27-20%d27-21%d27-22%d27-23%d27-24%a37-30%d37-31%d37-32%d37-33%d37-34%a47-40%d47-41%d47-42%d47-43%d47-44%a57-50%d57-51%d57-52%d57-53%d57-54%a67-60%d67-61%d67-62%d67-63%d67-64%a77-70%d77-71%d77-72%d77-73%d77-74%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-041X17-101017-111117-121217-131317-142X27-202027-212127-222227-232327-243X37-303037-313137-323237-333337-344X47-404047-414147-424247-434347-445X57-505057-515157-525257-535357-546X67-606067-616167-626267-636367-647X77-707077-717177-727277-737377-74ReadX_TAIL" +// test "test8692" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 )) "07-00007-01107-02207-03307-04407-051X17-101017-111117-121217-131317-141417-152X27-202027-212127-222227-232327-242427-253X37-303037-313137-323237-333337-343437-354X47-404047-414147-424247-434347-444447-455X57-505057-515157-525257-535357-545457-556X67-606067-616167-626267-636367-646467-657X77-707077-717177-727277-737377-747477-75" +// test "test8693" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-051X17-101017-111117-121217-131317-141417-152X27-202027-212127-222227-232327-242427-253X37-303037-313137-323237-333337-343437-354X47-404047-414147-424247-434347-444447-455X57-505057-515157-525257-535357-545457-556X67-606067-616167-626267-636367-646467-657X77-707077-717177-727277-737377-747477-75ReadX_TAIL" +// test "test8694" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 )) "07-00007-01107-02207-03307-04407-05507-061X17-101017-111117-121217-131317-141417-151517-162X27-202027-212127-222227-232327-242427-252527-263X37-303037-313137-323237-333337-343437-353537-364X47-404047-414147-424247-434347-444447-454547-465X57-505057-515157-525257-535357-545457-555557-566X67-606067-616167-626267-636367-646467-656567-667X77-707077-717177-727277-737377-747477-757577-76" +// test "test8695" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-05507-061X17-101017-111117-121217-131317-141417-151517-162X27-202027-212127-222227-232327-242427-252527-263X37-303037-313137-323237-333337-343437-353537-364X47-404047-414147-424247-434347-444447-454547-465X57-505057-515157-525257-535357-545457-555557-566X67-606067-616167-626267-636367-646467-656567-667X77-707077-717177-727277-737377-747477-757577-76ReadX_TAIL" +// test "test8696" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 )) "07-00007-01107-02207-03307-04407-05507-06607-071X17-101017-111117-121217-131317-141417-151517-161617-172X27-202027-212127-222227-232327-242427-252527-262627-273X37-303037-313137-323237-333337-343437-353537-363637-374X47-404047-414147-424247-434347-444447-454547-464647-475X57-505057-515157-525257-535357-545457-555557-565657-576X67-606067-616167-626267-636367-646467-656567-666667-677X77-707077-717177-727277-737377-747477-757577-767677-77" +// test "test8697" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-05507-06607-071X17-101017-111117-121217-131317-141417-151517-161617-172X27-202027-212127-222227-232327-242427-252527-262627-273X37-303037-313137-323237-333337-343437-353537-363637-374X47-404047-414147-424247-434347-444447-454547-464647-475X57-505057-515157-525257-535357-545457-555557-565657-576X67-606067-616167-626267-636367-646467-656567-666667-677X77-707077-717177-727277-737377-747477-757577-767677-77ReadX_TAIL" +// test "test8698" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%d07-08%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%d17-18%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%d27-28%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%d37-38%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%d47-48%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%d57-58%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%d67-68%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%d77-78" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 )) "07-00007-01107-02207-03307-04407-05507-06607-07707-081X17-101017-111117-121217-131317-141417-151517-161617-171717-182X27-202027-212127-222227-232327-242427-252527-262627-272727-283X37-303037-313137-323237-333337-343437-353537-363637-373737-384X47-404047-414147-424247-434347-444447-454547-464647-474747-485X57-505057-515157-525257-535357-545457-555557-565657-575757-586X67-606067-616167-626267-636367-646467-656567-666667-676767-687X77-707077-717177-727277-737377-747477-757577-767677-777777-78" +// test "test8699" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%d07-08%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%d17-18%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%d27-28%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%d37-38%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%d47-48%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%d57-58%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%d67-68%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%d77-78%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-05507-06607-07707-081X17-101017-111117-121217-131317-141417-151517-161617-171717-182X27-202027-212127-222227-232327-242427-252527-262627-272727-283X37-303037-313137-323237-333337-343437-353537-363637-373737-384X47-404047-414147-424247-434347-444447-454547-464647-474747-485X57-505057-515157-525257-535357-545457-555557-565657-575757-586X67-606067-616167-626267-636367-646467-656567-666667-676767-687X77-707077-717177-727277-737377-747477-757577-767677-777777-78ReadX_TAIL" +// test "test8700" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%d07-08%d07-09%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%d17-18%d17-19%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%d27-28%d27-29%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%d37-38%d37-39%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%d47-48%d47-49%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%d57-58%d57-59%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%d67-68%d67-69%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%d77-78%d77-79" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 )) "07-00007-01107-02207-03307-04407-05507-06607-07707-08807-091X17-101017-111117-121217-131317-141417-151517-161617-171717-181817-192X27-202027-212127-222227-232327-242427-252527-262627-272727-282827-293X37-303037-313137-323237-333337-343437-353537-363637-373737-383837-394X47-404047-414147-424247-434347-444447-454547-464647-474747-484847-495X57-505057-515157-525257-535357-545457-555557-565657-575757-585857-596X67-606067-616167-626267-636367-646467-656567-666667-676767-686867-697X77-707077-717177-727277-737377-747477-757577-767677-777777-787877-79" +// test "test8701" (lazy(sprintf "07-00%d07-01%d07-02%d07-03%d07-04%d07-05%d07-06%d07-07%d07-08%d07-09%a17-10%d17-11%d17-12%d17-13%d17-14%d17-15%d17-16%d17-17%d17-18%d17-19%a27-20%d27-21%d27-22%d27-23%d27-24%d27-25%d27-26%d27-27%d27-28%d27-29%a37-30%d37-31%d37-32%d37-33%d37-34%d37-35%d37-36%d37-37%d37-38%d37-39%a47-40%d47-41%d47-42%d47-43%d47-44%d47-45%d47-46%d47-47%d47-48%d47-49%a57-50%d57-51%d57-52%d57-53%d57-54%d57-55%d57-56%d57-57%d57-58%d57-59%a67-60%d67-61%d67-62%d67-63%d67-64%d67-65%d67-66%d67-67%d67-68%d67-69%a77-70%d77-71%d77-72%d77-73%d77-74%d77-75%d77-76%d77-77%d77-78%d77-79%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "07-00007-01107-02207-03307-04407-05507-06607-07707-08807-091X17-101017-111117-121217-131317-141417-151517-161617-171717-181817-192X27-202027-212127-222227-232327-242427-252527-262627-272727-282827-293X37-303037-313137-323237-333337-343437-353537-363637-373737-383837-394X47-404047-414147-424247-434347-444447-454547-464647-474747-484847-495X57-505057-515157-525257-535357-545457-555557-565657-575757-585857-596X67-606067-616167-626267-636367-646467-656567-666667-676767-686867-697X77-707077-717177-727277-737377-747477-757577-767677-777777-787877-79ReadX_TAIL" +// test "test8702" (lazy(sprintf "08-00%d08-01%a18-10%d18-11%a28-20%d28-21%a38-30%d38-31%a48-40%d48-41%a58-50%d58-51%a68-60%d68-61%a78-70%d78-71%a88-80%d88-81" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") 8 80 )) "08-00008-011X18-101018-112X28-202028-213X38-303038-314X48-404048-415X58-505058-516X68-606068-617X78-707078-718X88-808088-81" +// test "test8703" (lazy(sprintf "08-00%d08-01%a18-10%d18-11%a28-20%d28-21%a38-30%d38-31%a48-40%d48-41%a58-50%d58-51%a68-60%d68-61%a78-70%d78-71%a88-80%d88-81%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") 8 80 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-011X18-101018-112X28-202028-213X38-303038-314X48-404048-415X58-505058-516X68-606068-617X78-707078-718X88-808088-81ReadX_TAIL" +// test "test8704" (lazy(sprintf "08-00%d08-01%d08-02%a18-10%d18-11%d18-12%a28-20%d28-21%d28-22%a38-30%d38-31%d38-32%a48-40%d48-41%d48-42%a58-50%d58-51%d58-52%a68-60%d68-61%d68-62%a78-70%d78-71%d78-72%a88-80%d88-81%d88-82" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") 8 80 81 )) "08-00008-01108-021X18-101018-111118-122X28-202028-212128-223X38-303038-313138-324X48-404048-414148-425X58-505058-515158-526X68-606068-616168-627X78-707078-717178-728X88-808088-818188-82" +// test "test8705" (lazy(sprintf "08-00%d08-01%d08-02%a18-10%d18-11%d18-12%a28-20%d28-21%d28-22%a38-30%d38-31%d38-32%a48-40%d48-41%d48-42%a58-50%d58-51%d58-52%a68-60%d68-61%d68-62%a78-70%d78-71%d78-72%a88-80%d88-81%d88-82%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") 8 80 81 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-021X18-101018-111118-122X28-202028-212128-223X38-303038-313138-324X48-404048-414148-425X58-505058-515158-526X68-606068-616168-627X78-707078-717178-728X88-808088-818188-82ReadX_TAIL" +// test "test8706" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%a18-10%d18-11%d18-12%d18-13%a28-20%d28-21%d28-22%d28-23%a38-30%d38-31%d38-32%d38-33%a48-40%d48-41%d48-42%d48-43%a58-50%d58-51%d58-52%d58-53%a68-60%d68-61%d68-62%d68-63%a78-70%d78-71%d78-72%d78-73%a88-80%d88-81%d88-82%d88-83" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") 8 80 81 82 )) "08-00008-01108-02208-031X18-101018-111118-121218-132X28-202028-212128-222228-233X38-303038-313138-323238-334X48-404048-414148-424248-435X58-505058-515158-525258-536X68-606068-616168-626268-637X78-707078-717178-727278-738X88-808088-818188-828288-83" +// test "test8707" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%a18-10%d18-11%d18-12%d18-13%a28-20%d28-21%d28-22%d28-23%a38-30%d38-31%d38-32%d38-33%a48-40%d48-41%d48-42%d48-43%a58-50%d58-51%d58-52%d58-53%a68-60%d68-61%d68-62%d68-63%a78-70%d78-71%d78-72%d78-73%a88-80%d88-81%d88-82%d88-83%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") 8 80 81 82 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-031X18-101018-111118-121218-132X28-202028-212128-222228-233X38-303038-313138-323238-334X48-404048-414148-424248-435X58-505058-515158-525258-536X68-606068-616168-626268-637X78-707078-717178-727278-738X88-808088-818188-828288-83ReadX_TAIL" +// test "test8708" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%a18-10%d18-11%d18-12%d18-13%d18-14%a28-20%d28-21%d28-22%d28-23%d28-24%a38-30%d38-31%d38-32%d38-33%d38-34%a48-40%d48-41%d48-42%d48-43%d48-44%a58-50%d58-51%d58-52%d58-53%d58-54%a68-60%d68-61%d68-62%d68-63%d68-64%a78-70%d78-71%d78-72%d78-73%d78-74%a88-80%d88-81%d88-82%d88-83%d88-84" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") 8 80 81 82 83 )) "08-00008-01108-02208-03308-041X18-101018-111118-121218-131318-142X28-202028-212128-222228-232328-243X38-303038-313138-323238-333338-344X48-404048-414148-424248-434348-445X58-505058-515158-525258-535358-546X68-606068-616168-626268-636368-647X78-707078-717178-727278-737378-748X88-808088-818188-828288-838388-84" +// test "test8709" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%a18-10%d18-11%d18-12%d18-13%d18-14%a28-20%d28-21%d28-22%d28-23%d28-24%a38-30%d38-31%d38-32%d38-33%d38-34%a48-40%d48-41%d48-42%d48-43%d48-44%a58-50%d58-51%d58-52%d58-53%d58-54%a68-60%d68-61%d68-62%d68-63%d68-64%a78-70%d78-71%d78-72%d78-73%d78-74%a88-80%d88-81%d88-82%d88-83%d88-84%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") 8 80 81 82 83 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-041X18-101018-111118-121218-131318-142X28-202028-212128-222228-232328-243X38-303038-313138-323238-333338-344X48-404048-414148-424248-434348-445X58-505058-515158-525258-535358-546X68-606068-616168-626268-636368-647X78-707078-717178-727278-737378-748X88-808088-818188-828288-838388-84ReadX_TAIL" +// test "test8710" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 )) "08-00008-01108-02208-03308-04408-051X18-101018-111118-121218-131318-141418-152X28-202028-212128-222228-232328-242428-253X38-303038-313138-323238-333338-343438-354X48-404048-414148-424248-434348-444448-455X58-505058-515158-525258-535358-545458-556X68-606068-616168-626268-636368-646468-657X78-707078-717178-727278-737378-747478-758X88-808088-818188-828288-838388-848488-85" +// test "test8711" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-051X18-101018-111118-121218-131318-141418-152X28-202028-212128-222228-232328-242428-253X38-303038-313138-323238-333338-343438-354X48-404048-414148-424248-434348-444448-455X58-505058-515158-525258-535358-545458-556X68-606068-616168-626268-636368-646468-657X78-707078-717178-727278-737378-747478-758X88-808088-818188-828288-838388-848488-85ReadX_TAIL" +// test "test8712" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 )) "08-00008-01108-02208-03308-04408-05508-061X18-101018-111118-121218-131318-141418-151518-162X28-202028-212128-222228-232328-242428-252528-263X38-303038-313138-323238-333338-343438-353538-364X48-404048-414148-424248-434348-444448-454548-465X58-505058-515158-525258-535358-545458-555558-566X68-606068-616168-626268-636368-646468-656568-667X78-707078-717178-727278-737378-747478-757578-768X88-808088-818188-828288-838388-848488-858588-86" +// test "test8713" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-05508-061X18-101018-111118-121218-131318-141418-151518-162X28-202028-212128-222228-232328-242428-252528-263X38-303038-313138-323238-333338-343438-353538-364X48-404048-414148-424248-434348-444448-454548-465X58-505058-515158-525258-535358-545458-555558-566X68-606068-616168-626268-636368-646468-656568-667X78-707078-717178-727278-737378-747478-757578-768X88-808088-818188-828288-838388-848488-858588-86ReadX_TAIL" +// test "test8714" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 )) "08-00008-01108-02208-03308-04408-05508-06608-071X18-101018-111118-121218-131318-141418-151518-161618-172X28-202028-212128-222228-232328-242428-252528-262628-273X38-303038-313138-323238-333338-343438-353538-363638-374X48-404048-414148-424248-434348-444448-454548-464648-475X58-505058-515158-525258-535358-545458-555558-565658-576X68-606068-616168-626268-636368-646468-656568-666668-677X78-707078-717178-727278-737378-747478-757578-767678-778X88-808088-818188-828288-838388-848488-858588-868688-87" +// test "test8715" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-05508-06608-071X18-101018-111118-121218-131318-141418-151518-161618-172X28-202028-212128-222228-232328-242428-252528-262628-273X38-303038-313138-323238-333338-343438-353538-363638-374X48-404048-414148-424248-434348-444448-454548-464648-475X58-505058-515158-525258-535358-545458-555558-565658-576X68-606068-616168-626268-636368-646468-656568-666668-677X78-707078-717178-727278-737378-747478-757578-767678-778X88-808088-818188-828288-838388-848488-858588-868688-87ReadX_TAIL" +// test "test8716" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%d08-08%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%d18-18%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%d28-28%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%d38-38%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%d48-48%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%d58-58%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%d68-68%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%d78-78%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%d88-88" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 )) "08-00008-01108-02208-03308-04408-05508-06608-07708-081X18-101018-111118-121218-131318-141418-151518-161618-171718-182X28-202028-212128-222228-232328-242428-252528-262628-272728-283X38-303038-313138-323238-333338-343438-353538-363638-373738-384X48-404048-414148-424248-434348-444448-454548-464648-474748-485X58-505058-515158-525258-535358-545458-555558-565658-575758-586X68-606068-616168-626268-636368-646468-656568-666668-676768-687X78-707078-717178-727278-737378-747478-757578-767678-777778-788X88-808088-818188-828288-838388-848488-858588-868688-878788-88" +// test "test8717" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%d08-08%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%d18-18%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%d28-28%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%d38-38%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%d48-48%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%d58-58%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%d68-68%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%d78-78%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%d88-88%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-05508-06608-07708-081X18-101018-111118-121218-131318-141418-151518-161618-171718-182X28-202028-212128-222228-232328-242428-252528-262628-272728-283X38-303038-313138-323238-333338-343438-353538-363638-373738-384X48-404048-414148-424248-434348-444448-454548-464648-474748-485X58-505058-515158-525258-535358-545458-555558-565658-575758-586X68-606068-616168-626268-636368-646468-656568-666668-676768-687X78-707078-717178-727278-737378-747478-757578-767678-777778-788X88-808088-818188-828288-838388-848488-858588-868688-878788-88ReadX_TAIL" +// test "test8718" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%d08-08%d08-09%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%d18-18%d18-19%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%d28-28%d28-29%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%d38-38%d38-39%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%d48-48%d48-49%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%d58-58%d58-59%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%d68-68%d68-69%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%d78-78%d78-79%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%d88-88%d88-89" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 88 )) "08-00008-01108-02208-03308-04408-05508-06608-07708-08808-091X18-101018-111118-121218-131318-141418-151518-161618-171718-181818-192X28-202028-212128-222228-232328-242428-252528-262628-272728-282828-293X38-303038-313138-323238-333338-343438-353538-363638-373738-383838-394X48-404048-414148-424248-434348-444448-454548-464648-474748-484848-495X58-505058-515158-525258-535358-545458-555558-565658-575758-585858-596X68-606068-616168-626268-636368-646468-656568-666668-676768-686868-697X78-707078-717178-727278-737378-747478-757578-767678-777778-787878-798X88-808088-818188-828288-838388-848488-858588-868688-878788-888888-89" +// test "test8719" (lazy(sprintf "08-00%d08-01%d08-02%d08-03%d08-04%d08-05%d08-06%d08-07%d08-08%d08-09%a18-10%d18-11%d18-12%d18-13%d18-14%d18-15%d18-16%d18-17%d18-18%d18-19%a28-20%d28-21%d28-22%d28-23%d28-24%d28-25%d28-26%d28-27%d28-28%d28-29%a38-30%d38-31%d38-32%d38-33%d38-34%d38-35%d38-36%d38-37%d38-38%d38-39%a48-40%d48-41%d48-42%d48-43%d48-44%d48-45%d48-46%d48-47%d48-48%d48-49%a58-50%d58-51%d58-52%d58-53%d58-54%d58-55%d58-56%d58-57%d58-58%d58-59%a68-60%d68-61%d68-62%d68-63%d68-64%d68-65%d68-66%d68-67%d68-68%d68-69%a78-70%d78-71%d78-72%d78-73%d78-74%d78-75%d78-76%d78-77%d78-78%d78-79%a88-80%d88-81%d88-82%d88-83%d88-84%d88-85%d88-86%d88-87%d88-88%d88-89%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 88 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "08-00008-01108-02208-03308-04408-05508-06608-07708-08808-091X18-101018-111118-121218-131318-141418-151518-161618-171718-181818-192X28-202028-212128-222228-232328-242428-252528-262628-272728-282828-293X38-303038-313138-323238-333338-343438-353538-363638-373738-383838-394X48-404048-414148-424248-434348-444448-454548-464648-474748-484848-495X58-505058-515158-525258-535358-545458-555558-565658-575758-585858-596X68-606068-616168-626268-636368-646468-656568-666668-676768-686868-697X78-707078-717178-727278-737378-747478-757578-767678-777778-787878-798X88-808088-818188-828288-838388-848488-858588-868688-878788-888888-89ReadX_TAIL" +// test "test8720" (lazy(sprintf "09-00%d09-01%a19-10%d19-11%a29-20%d29-21%a39-30%d39-31%a49-40%d49-41%a59-50%d59-51%a69-60%d69-61%a79-70%d79-71%a89-80%d89-81%a99-90%d99-91" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") 8 80 (fun _ v -> (string v) + "X") 9 90 )) "09-00009-011X19-101019-112X29-202029-213X39-303039-314X49-404049-415X59-505059-516X69-606069-617X79-707079-718X89-808089-819X99-909099-91" +// test "test8721" (lazy(sprintf "09-00%d09-01%a19-10%d19-11%a29-20%d29-21%a39-30%d39-31%a49-40%d49-41%a59-50%d59-51%a69-60%d69-61%a79-70%d79-71%a89-80%d89-81%a99-90%d99-91%a_TAIL" 0 (fun _ v -> (string v) + "X") 1 10 (fun _ v -> (string v) + "X") 2 20 (fun _ v -> (string v) + "X") 3 30 (fun _ v -> (string v) + "X") 4 40 (fun _ v -> (string v) + "X") 5 50 (fun _ v -> (string v) + "X") 6 60 (fun _ v -> (string v) + "X") 7 70 (fun _ v -> (string v) + "X") 8 80 (fun _ v -> (string v) + "X") 9 90 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-011X19-101019-112X29-202029-213X39-303039-314X49-404049-415X59-505059-516X69-606069-617X79-707079-718X89-808089-819X99-909099-91ReadX_TAIL" +// test "test8722" (lazy(sprintf "09-00%d09-01%d09-02%a19-10%d19-11%d19-12%a29-20%d29-21%d29-22%a39-30%d39-31%d39-32%a49-40%d49-41%d49-42%a59-50%d59-51%d59-52%a69-60%d69-61%d69-62%a79-70%d79-71%d79-72%a89-80%d89-81%d89-82%a99-90%d99-91%d99-92" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") 8 80 81 (fun _ v -> (string v) + "X") 9 90 91 )) "09-00009-01109-021X19-101019-111119-122X29-202029-212129-223X39-303039-313139-324X49-404049-414149-425X59-505059-515159-526X69-606069-616169-627X79-707079-717179-728X89-808089-818189-829X99-909099-919199-92" +// test "test8723" (lazy(sprintf "09-00%d09-01%d09-02%a19-10%d19-11%d19-12%a29-20%d29-21%d29-22%a39-30%d39-31%d39-32%a49-40%d49-41%d49-42%a59-50%d59-51%d59-52%a69-60%d69-61%d69-62%a79-70%d79-71%d79-72%a89-80%d89-81%d89-82%a99-90%d99-91%d99-92%a_TAIL" 0 1 (fun _ v -> (string v) + "X") 1 10 11 (fun _ v -> (string v) + "X") 2 20 21 (fun _ v -> (string v) + "X") 3 30 31 (fun _ v -> (string v) + "X") 4 40 41 (fun _ v -> (string v) + "X") 5 50 51 (fun _ v -> (string v) + "X") 6 60 61 (fun _ v -> (string v) + "X") 7 70 71 (fun _ v -> (string v) + "X") 8 80 81 (fun _ v -> (string v) + "X") 9 90 91 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-021X19-101019-111119-122X29-202029-212129-223X39-303039-313139-324X49-404049-414149-425X59-505059-515159-526X69-606069-616169-627X79-707079-717179-728X89-808089-818189-829X99-909099-919199-92ReadX_TAIL" +// test "test8724" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%a19-10%d19-11%d19-12%d19-13%a29-20%d29-21%d29-22%d29-23%a39-30%d39-31%d39-32%d39-33%a49-40%d49-41%d49-42%d49-43%a59-50%d59-51%d59-52%d59-53%a69-60%d69-61%d69-62%d69-63%a79-70%d79-71%d79-72%d79-73%a89-80%d89-81%d89-82%d89-83%a99-90%d99-91%d99-92%d99-93" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") 8 80 81 82 (fun _ v -> (string v) + "X") 9 90 91 92 )) "09-00009-01109-02209-031X19-101019-111119-121219-132X29-202029-212129-222229-233X39-303039-313139-323239-334X49-404049-414149-424249-435X59-505059-515159-525259-536X69-606069-616169-626269-637X79-707079-717179-727279-738X89-808089-818189-828289-839X99-909099-919199-929299-93" +// test "test8725" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%a19-10%d19-11%d19-12%d19-13%a29-20%d29-21%d29-22%d29-23%a39-30%d39-31%d39-32%d39-33%a49-40%d49-41%d49-42%d49-43%a59-50%d59-51%d59-52%d59-53%a69-60%d69-61%d69-62%d69-63%a79-70%d79-71%d79-72%d79-73%a89-80%d89-81%d89-82%d89-83%a99-90%d99-91%d99-92%d99-93%a_TAIL" 0 1 2 (fun _ v -> (string v) + "X") 1 10 11 12 (fun _ v -> (string v) + "X") 2 20 21 22 (fun _ v -> (string v) + "X") 3 30 31 32 (fun _ v -> (string v) + "X") 4 40 41 42 (fun _ v -> (string v) + "X") 5 50 51 52 (fun _ v -> (string v) + "X") 6 60 61 62 (fun _ v -> (string v) + "X") 7 70 71 72 (fun _ v -> (string v) + "X") 8 80 81 82 (fun _ v -> (string v) + "X") 9 90 91 92 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-031X19-101019-111119-121219-132X29-202029-212129-222229-233X39-303039-313139-323239-334X49-404049-414149-424249-435X59-505059-515159-525259-536X69-606069-616169-626269-637X79-707079-717179-727279-738X89-808089-818189-828289-839X99-909099-919199-929299-93ReadX_TAIL" +// test "test8726" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%a19-10%d19-11%d19-12%d19-13%d19-14%a29-20%d29-21%d29-22%d29-23%d29-24%a39-30%d39-31%d39-32%d39-33%d39-34%a49-40%d49-41%d49-42%d49-43%d49-44%a59-50%d59-51%d59-52%d59-53%d59-54%a69-60%d69-61%d69-62%d69-63%d69-64%a79-70%d79-71%d79-72%d79-73%d79-74%a89-80%d89-81%d89-82%d89-83%d89-84%a99-90%d99-91%d99-92%d99-93%d99-94" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") 8 80 81 82 83 (fun _ v -> (string v) + "X") 9 90 91 92 93 )) "09-00009-01109-02209-03309-041X19-101019-111119-121219-131319-142X29-202029-212129-222229-232329-243X39-303039-313139-323239-333339-344X49-404049-414149-424249-434349-445X59-505059-515159-525259-535359-546X69-606069-616169-626269-636369-647X79-707079-717179-727279-737379-748X89-808089-818189-828289-838389-849X99-909099-919199-929299-939399-94" +// test "test8727" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%a19-10%d19-11%d19-12%d19-13%d19-14%a29-20%d29-21%d29-22%d29-23%d29-24%a39-30%d39-31%d39-32%d39-33%d39-34%a49-40%d49-41%d49-42%d49-43%d49-44%a59-50%d59-51%d59-52%d59-53%d59-54%a69-60%d69-61%d69-62%d69-63%d69-64%a79-70%d79-71%d79-72%d79-73%d79-74%a89-80%d89-81%d89-82%d89-83%d89-84%a99-90%d99-91%d99-92%d99-93%d99-94%a_TAIL" 0 1 2 3 (fun _ v -> (string v) + "X") 1 10 11 12 13 (fun _ v -> (string v) + "X") 2 20 21 22 23 (fun _ v -> (string v) + "X") 3 30 31 32 33 (fun _ v -> (string v) + "X") 4 40 41 42 43 (fun _ v -> (string v) + "X") 5 50 51 52 53 (fun _ v -> (string v) + "X") 6 60 61 62 63 (fun _ v -> (string v) + "X") 7 70 71 72 73 (fun _ v -> (string v) + "X") 8 80 81 82 83 (fun _ v -> (string v) + "X") 9 90 91 92 93 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-041X19-101019-111119-121219-131319-142X29-202029-212129-222229-232329-243X39-303039-313139-323239-333339-344X49-404049-414149-424249-434349-445X59-505059-515159-525259-535359-546X69-606069-616169-626269-636369-647X79-707079-717179-727279-737379-748X89-808089-818189-828289-838389-849X99-909099-919199-929299-939399-94ReadX_TAIL" +// test "test8728" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 )) "09-00009-01109-02209-03309-04409-051X19-101019-111119-121219-131319-141419-152X29-202029-212129-222229-232329-242429-253X39-303039-313139-323239-333339-343439-354X49-404049-414149-424249-434349-444449-455X59-505059-515159-525259-535359-545459-556X69-606069-616169-626269-636369-646469-657X79-707079-717179-727279-737379-747479-758X89-808089-818189-828289-838389-848489-859X99-909099-919199-929299-939399-949499-95" +// test "test8729" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%a_TAIL" 0 1 2 3 4 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-051X19-101019-111119-121219-131319-141419-152X29-202029-212129-222229-232329-242429-253X39-303039-313139-323239-333339-343439-354X49-404049-414149-424249-434349-444449-455X59-505059-515159-525259-535359-545459-556X69-606069-616169-626269-636369-646469-657X79-707079-717179-727279-737379-747479-758X89-808089-818189-828289-838389-848489-859X99-909099-919199-929299-939399-949499-95ReadX_TAIL" +// test "test8730" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 )) "09-00009-01109-02209-03309-04409-05509-061X19-101019-111119-121219-131319-141419-151519-162X29-202029-212129-222229-232329-242429-252529-263X39-303039-313139-323239-333339-343439-353539-364X49-404049-414149-424249-434349-444449-454549-465X59-505059-515159-525259-535359-545459-555559-566X69-606069-616169-626269-636369-646469-656569-667X79-707079-717179-727279-737379-747479-757579-768X89-808089-818189-828289-838389-848489-858589-869X99-909099-919199-929299-939399-949499-959599-96" +// test "test8731" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%a_TAIL" 0 1 2 3 4 5 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-05509-061X19-101019-111119-121219-131319-141419-151519-162X29-202029-212129-222229-232329-242429-252529-263X39-303039-313139-323239-333339-343439-353539-364X49-404049-414149-424249-434349-444449-454549-465X59-505059-515159-525259-535359-545459-555559-566X69-606069-616169-626269-636369-646469-656569-667X79-707079-717179-727279-737379-747479-757579-768X89-808089-818189-828289-838389-848489-858589-869X99-909099-919199-929299-939399-949499-959599-96ReadX_TAIL" +// test "test8732" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 )) "09-00009-01109-02209-03309-04409-05509-06609-071X19-101019-111119-121219-131319-141419-151519-161619-172X29-202029-212129-222229-232329-242429-252529-262629-273X39-303039-313139-323239-333339-343439-353539-363639-374X49-404049-414149-424249-434349-444449-454549-464649-475X59-505059-515159-525259-535359-545459-555559-565659-576X69-606069-616169-626269-636369-646469-656569-666669-677X79-707079-717179-727279-737379-747479-757579-767679-778X89-808089-818189-828289-838389-848489-858589-868689-879X99-909099-919199-929299-939399-949499-959599-969699-97" +// test "test8733" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%a_TAIL" 0 1 2 3 4 5 6 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-05509-06609-071X19-101019-111119-121219-131319-141419-151519-161619-172X29-202029-212129-222229-232329-242429-252529-262629-273X39-303039-313139-323239-333339-343439-353539-363639-374X49-404049-414149-424249-434349-444449-454549-464649-475X59-505059-515159-525259-535359-545459-555559-565659-576X69-606069-616169-626269-636369-646469-656569-666669-677X79-707079-717179-727279-737379-747479-757579-767679-778X89-808089-818189-828289-838389-848489-858589-868689-879X99-909099-919199-929299-939399-949499-959599-969699-97ReadX_TAIL" +// test "test8734" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%d09-08%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%d19-18%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%d29-28%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%d39-38%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%d49-48%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%d59-58%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%d69-68%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%d79-78%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%d89-88%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%d99-98" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 97 )) "09-00009-01109-02209-03309-04409-05509-06609-07709-081X19-101019-111119-121219-131319-141419-151519-161619-171719-182X29-202029-212129-222229-232329-242429-252529-262629-272729-283X39-303039-313139-323239-333339-343439-353539-363639-373739-384X49-404049-414149-424249-434349-444449-454549-464649-474749-485X59-505059-515159-525259-535359-545459-555559-565659-575759-586X69-606069-616169-626269-636369-646469-656569-666669-676769-687X79-707079-717179-727279-737379-747479-757579-767679-777779-788X89-808089-818189-828289-838389-848489-858589-868689-878789-889X99-909099-919199-929299-939399-949499-959599-969699-979799-98" +// test "test8735" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%d09-08%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%d19-18%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%d29-28%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%d39-38%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%d49-48%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%d59-58%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%d69-68%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%d79-78%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%d89-88%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%d99-98%a_TAIL" 0 1 2 3 4 5 6 7 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 97 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-05509-06609-07709-081X19-101019-111119-121219-131319-141419-151519-161619-171719-182X29-202029-212129-222229-232329-242429-252529-262629-272729-283X39-303039-313139-323239-333339-343439-353539-363639-373739-384X49-404049-414149-424249-434349-444449-454549-464649-474749-485X59-505059-515159-525259-535359-545459-555559-565659-575759-586X69-606069-616169-626269-636369-646469-656569-666669-676769-687X79-707079-717179-727279-737379-747479-757579-767679-777779-788X89-808089-818189-828289-838389-848489-858589-868689-878789-889X99-909099-919199-929299-939399-949499-959599-969699-979799-98ReadX_TAIL" +// test "test8736" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%d09-08%d09-09%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%d19-18%d19-19%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%d29-28%d29-29%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%d39-38%d39-39%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%d49-48%d49-49%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%d59-58%d59-59%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%d69-68%d69-69%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%d79-78%d79-79%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%d89-88%d89-89%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%d99-98%d99-99" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 88 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 97 98 )) "09-00009-01109-02209-03309-04409-05509-06609-07709-08809-091X19-101019-111119-121219-131319-141419-151519-161619-171719-181819-192X29-202029-212129-222229-232329-242429-252529-262629-272729-282829-293X39-303039-313139-323239-333339-343439-353539-363639-373739-383839-394X49-404049-414149-424249-434349-444449-454549-464649-474749-484849-495X59-505059-515159-525259-535359-545459-555559-565659-575759-585859-596X69-606069-616169-626269-636369-646469-656569-666669-676769-686869-697X79-707079-717179-727279-737379-747479-757579-767679-777779-787879-798X89-808089-818189-828289-838389-848489-858589-868689-878789-888889-899X99-909099-919199-929299-939399-949499-959599-969699-979799-989899-99" +// test "test8737" (lazy(sprintf "09-00%d09-01%d09-02%d09-03%d09-04%d09-05%d09-06%d09-07%d09-08%d09-09%a19-10%d19-11%d19-12%d19-13%d19-14%d19-15%d19-16%d19-17%d19-18%d19-19%a29-20%d29-21%d29-22%d29-23%d29-24%d29-25%d29-26%d29-27%d29-28%d29-29%a39-30%d39-31%d39-32%d39-33%d39-34%d39-35%d39-36%d39-37%d39-38%d39-39%a49-40%d49-41%d49-42%d49-43%d49-44%d49-45%d49-46%d49-47%d49-48%d49-49%a59-50%d59-51%d59-52%d59-53%d59-54%d59-55%d59-56%d59-57%d59-58%d59-59%a69-60%d69-61%d69-62%d69-63%d69-64%d69-65%d69-66%d69-67%d69-68%d69-69%a79-70%d79-71%d79-72%d79-73%d79-74%d79-75%d79-76%d79-77%d79-78%d79-79%a89-80%d89-81%d89-82%d89-83%d89-84%d89-85%d89-86%d89-87%d89-88%d89-89%a99-90%d99-91%d99-92%d99-93%d99-94%d99-95%d99-96%d99-97%d99-98%d99-99%a_TAIL" 0 1 2 3 4 5 6 7 8 (fun _ v -> (string v) + "X") 1 10 11 12 13 14 15 16 17 18 (fun _ v -> (string v) + "X") 2 20 21 22 23 24 25 26 27 28 (fun _ v -> (string v) + "X") 3 30 31 32 33 34 35 36 37 38 (fun _ v -> (string v) + "X") 4 40 41 42 43 44 45 46 47 48 (fun _ v -> (string v) + "X") 5 50 51 52 53 54 55 56 57 58 (fun _ v -> (string v) + "X") 6 60 61 62 63 64 65 66 67 68 (fun _ v -> (string v) + "X") 7 70 71 72 73 74 75 76 77 78 (fun _ v -> (string v) + "X") 8 80 81 82 83 84 85 86 87 88 (fun _ v -> (string v) + "X") 9 90 91 92 93 94 95 96 97 98 (fun _ v -> (string v) + "X") System.IO.FileShare.Read )) "09-00009-01109-02209-03309-04409-05509-06609-07709-08809-091X19-101019-111119-121219-131319-141419-151519-161619-171719-181819-192X29-202029-212129-222229-232329-242429-252529-262629-272729-282829-293X39-303039-313139-323239-333339-343439-353539-363639-373739-383839-394X49-404049-414149-424249-434349-444449-454549-464649-474749-484849-495X59-505059-515159-525259-535359-545459-555559-565659-575759-585859-596X69-606069-616169-626269-636369-646469-656569-666669-676769-686869-697X79-707079-717179-727279-737379-747479-757579-767679-777779-787879-798X89-808089-818189-828289-838389-848489-858589-868689-878789-888889-899X99-909099-919199-929299-939399-949499-959599-969699-979799-989899-99ReadX_TAIL" #endif func0() func1000() @@ -9294,7 +9295,7 @@ func4000() func5000() func6000() func7000() -func8000() +//func8000() #endif #if TESTS_AS_APP diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index c4792c3cf1c..f8eb5281129 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -193,6 +193,9 @@ module CoreTests = [] let ``printf-FSC_BASIC`` () = singleTestBuildAndRun "core/printf" FSC_BASIC + [] + let ``printf-interpolated-FSC_BASIC`` () = singleTestBuildAndRun "core/printf-interpolated" FSC_BASIC + [] let ``tlr-FSC_BASIC`` () = singleTestBuildAndRun "core/tlr" FSC_BASIC From f9684c9d36a7c7e1a884415eaede70d6abb70996 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 1 May 2020 18:12:39 +0100 Subject: [PATCH 31/87] update baselines --- tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs | 2 +- tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index 2815d8594cc..17cb980c3ee 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -2236,6 +2236,7 @@ Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: S Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String Value Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String get_Value() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String) +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String,System.Object[]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToError[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLine[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) @@ -2246,7 +2247,6 @@ Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringBuilder[T](System.Text. Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThenFail[T,TResult](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[System.String,TResult], Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) -Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriterThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,TResult], System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index cb77401ddfc..f74a5236ed4 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -2236,6 +2236,7 @@ Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: S Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String Value Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String get_Value() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String) +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String,System.Object[]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToError[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLine[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) @@ -2246,7 +2247,6 @@ Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringBuilder[T](System.Text. Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThenFail[T,TResult](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[System.String,TResult], Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) -Microsoft.FSharp.Core.PrintfModule: T InterpolatedPrintFormatToStringThen[T](Microsoft.FSharp.Core.PrintfFormat`4[T,Microsoft.FSharp.Core.Unit,System.String,System.String]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriterThen[TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,TResult], System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,TResult]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormat[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) From 8e10cd9a5c396a7048965bccff70117b24c9b5f8 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 1 May 2020 23:48:26 +0100 Subject: [PATCH 32/87] fix baselines --- tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs | 7 ++++++- tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index 17cb980c3ee..1143debb2b3 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -2236,7 +2236,12 @@ Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: S Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String Value Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String get_Value() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String) -Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String,System.Object[]) +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] Captures +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] get_Captures() +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: Void .ctor(System.String, System.Object[]) +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Object[] Captures +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Object[] get_Captures() +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String, System.Object[]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToError[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLine[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index f74a5236ed4..0c0d2dd4e0a 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -2236,7 +2236,12 @@ Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: S Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String Value Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String get_Value() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String) -Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String,System.Object[]) +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] Captures +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] get_Captures() +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: Void .ctor(System.String, System.Object[]) +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Object[] Captures +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Object[] get_Captures() +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String, System.Object[]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToError[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLine[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) From 6d9a1acadf0ef17b81aa6034feb01a43a80cefbf Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 1 May 2020 23:52:35 +0100 Subject: [PATCH 33/87] add Experimental attributes --- src/fsharp/FSharp.Core/printf.fsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/fsharp/FSharp.Core/printf.fsi b/src/fsharp/FSharp.Core/printf.fsi index 6e56342ea66..1c66d74b9db 100644 --- a/src/fsharp/FSharp.Core/printf.fsi +++ b/src/fsharp/FSharp.Core/printf.fsi @@ -25,11 +25,13 @@ type PrintfFormat<'Printer,'State,'Residue,'Result> = /// The input string. /// The captured expressions in an interpolation string. /// The PrintfFormat containing the formatted result. + [] new : value:string * captures: obj[] -> PrintfFormat<'Printer,'State,'Residue,'Result> /// The raw text of the format string. member Value : string + [] member Captures: obj[] /// Type of a formatting expression. @@ -51,6 +53,7 @@ type PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> = /// The input string. /// The captured expressions in an interpolation string. /// The created format string. + [] new: value:string * captures: obj[] -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> /// Type of a formatting expression. From 49eb291736f684aceff38cc8addbb12e4af03486 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 2 May 2020 21:16:13 +0800 Subject: [PATCH 34/87] update string interp negative tests --- .../Compiler/Language/StringInterpolation.fs | 49 ++++++++++++------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 9f2317a8f3b..fc0ecdee4d2 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -87,8 +87,11 @@ check \"xvcewweh5\" $\"\"\"this is {1}\"\"\" \"this is 1\" check \"xvcewweh6\" $\"\"\"this i\s {1}\"\"\" \"this i\s 1\" +// check nested string with %s +check \"xvcewweh7\" $\"\"\"x = %s{\"1\"}\"\"\" \"x = 1\" + // multiline -check \"xvcewweh6\" +check \"xvcewweh8\" $\"\"\"this is {1+1}\"\"\" \"\"\"this @@ -478,9 +481,6 @@ check "vcewweh22f" $"x = %s{s}" "x = sixsix" // check %A of string check "vcewweh22g" $"x = %A{s}" "x = \"sixsix\"" -// check nested string with %s -check "vcewweh22l" $"x = %s{"1"}" "x = 1" - check "vcewweh20" $"x = %A{1}" "x = 1" """ @@ -549,12 +549,10 @@ let x4 = $"one %d" // naked percent in interpolated let x5 = $"one %A" // naked percent in interpolated let x6 = $"one %P" // interpolation hole marker in interploation let x7 = $"one %P()" // interpolation hole marker in interploation -let x8 = $"one %P(){"gotcha"}" // interpolation hole marker in interploation +let x8 = $"one %P(){1}" // interpolation hole marker in interploation let x9 = $"one %f" // naked percent in interpolated let xa = $"one %d{3:N}" // mix of formats -let xb = $"{%5d{1:N3}}" // inner error that looks like format specifiers let xc = $"5%6" // bad F# format specifier -let xd = $"%A{}" // empty expression let xe = $"%A{{1}}" // fake expression (delimiters escaped) """ CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] @@ -576,20 +574,37 @@ but here has type "Invalid interpolated string. The '%P' specifier may not be used explicitly."); (FSharpErrorSeverity.Error, 3361, (8, 10, 8, 21), "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); - (FSharpErrorSeverity.Error, 3361, (9, 10, 9, 31), - "Invalid interpolated string. The '%P' specifier may not be used explicitly."); + (FSharpErrorSeverity.Error, 3361, (9, 10, 9, 24), + "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); (FSharpErrorSeverity.Error, 3354, (10, 10, 10, 19), "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); (FSharpErrorSeverity.Error, 3354, (11, 10, 11, 24), "Invalid interpolated string. .NET-style format specifiers such as '{x,3}' or '{x:N5}' may not be mixed with '%' format specifiers.") - (FSharpErrorSeverity.Error, 1156, (12, 13, 12, 14), - "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger).") - (FSharpErrorSeverity.Error, 741, (13, 9, 13, 14), - "Unable to parse format string 'Bad precision in format specifier'") - (FSharpErrorSeverity.Error, 10, (14, 14, 14, 15), - "Unexpected interpolated string (final part) in interaction") - (FSharpErrorSeverity.Error, 741, (15, 10, 15, 19), - "Unable to parse format string 'Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.'") + (FSharpErrorSeverity.Error, 3354, (12, 10, 12, 16), + "Invalid interpolated string. Bad precision in format specifier") + (FSharpErrorSeverity.Error, 3354, (13, 10, 13, 20), + "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.") + |] + + let code = """ +let xb = $"{%5d{1:N3}}" // inner error that looks like format specifiers +""" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 1156, (2, 14, 2, 16), + "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger)."); + (FSharpErrorSeverity.Error, 10, (2, 18, 2, 19), + "Unexpected symbol ':' in expression. Expected '}' or other token."); + (FSharpErrorSeverity.Error, 604, (2, 16, 2, 17), "Unmatched '{'") + |] + + let code = """ +let xd = $"%A{}" // empty expression +""" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 10, (2, 15, 2, 17), + "Unexpected interpolated string (final part) in binding") |] [] From 49ba592afad7e87692706be7f43ff13d7b330a30 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 2 May 2020 22:39:15 +0800 Subject: [PATCH 35/87] stringinterp test: add PrintFormat tests --- .../Compiler/Language/StringInterpolation.fs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index fc0ecdee4d2..7062b691f4a 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -323,6 +323,28 @@ check "fwejwflpej17" (fmt_de $"abc {30000,-10:N} def {40000:N} hij") "abc 30.000 """ + [] + let ``String interpolation to PrintFormat`` () = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + """ +open System.Text +open Printf +let check msg a b = + if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + +check "fwejwflpej1" (sprintf $"") "" +check "fwejwflpej2" (sprintf $"abc") "abc" +check "fwejwflpej3" (sprintf $"abc{1}") "abc1" + +let sb = StringBuilder() +bprintf sb $"{0}" +bprintf sb $"abc" +bprintf sb $"abc{1}" +check "fwejwflpej4" (sb.ToString()) "0abcabc1" + + """ + + [] let ``String interpolation using .NET Formats`` () = CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] From 7f396177996a7d63e1ce650423993f07facd8239 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Sat, 2 May 2020 23:33:52 +0800 Subject: [PATCH 36/87] printf: fix empty interpolation string evaluates to null in printf env --- src/fsharp/FSharp.Core/printf.fs | 111 ++++++++++++++++--------------- 1 file changed, 57 insertions(+), 54 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index ab0c4e39e8e..d01eb4d55e3 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -287,60 +287,63 @@ module internal PrintfImpl = member env.RunSteps (args: obj[], steps: Step[]) = let mutable argIndex = 0 - for step in steps do - match step with - | Step (prefix, conv1) -> - env.WriteSkipEmpty(prefix) - env.Write(&argIndex, args, conv1) - - | StepString prefix -> - env.WriteSkipEmpty(prefix) - - | StepLittleT(prefix) -> - env.WriteSkipEmpty prefix - let farg = args.[argIndex] - argIndex <- argIndex + 1 - let f = farg :?> ('State -> 'Residue) - env.WriteT(f env.State) - - | StepLittleA(prefix) -> - env.WriteSkipEmpty prefix - let farg = args.[argIndex] - argIndex <- argIndex + 1 - let arg = args.[argIndex] - argIndex <- argIndex + 1 - let f = farg :?> ('State -> obj -> 'Residue) - env.WriteT(f env.State arg) - - | StepStar1(prefix, conv) -> - env.WriteSkipEmpty prefix - let star1 = args.[argIndex] :?> int - argIndex <- argIndex + 1 - let arg1 = args.[argIndex] - argIndex <- argIndex + 1 - env.Write (conv arg1 star1) - - | StepPercentStar1(prefix) -> - //let _star1 = args.[argIndex] :?> int - argIndex <- argIndex + 1 - env.WriteSkipEmpty prefix - env.Write("%") - - | StepStar2(prefix, conv) -> - env.WriteSkipEmpty prefix - let star1 = args.[argIndex] :?> int - argIndex <- argIndex + 1 - let star2 = args.[argIndex] :?> int - argIndex <- argIndex + 1 - let arg1 = args.[argIndex] - argIndex <- argIndex + 1 - env.Write (conv arg1 star1 star2) - - | StepPercentStar2(prefix) -> - env.WriteSkipEmpty prefix - //let _star1 = args.[argIndex] :?> int - argIndex <- argIndex + 2 - env.Write("%") + match steps with + | [| StepString one |] -> env.Write(one) + | _ -> + for step in steps do + match step with + | Step (prefix, conv1) -> + env.WriteSkipEmpty(prefix) + env.Write(&argIndex, args, conv1) + + | StepString prefix -> + env.WriteSkipEmpty(prefix) + + | StepLittleT(prefix) -> + env.WriteSkipEmpty prefix + let farg = args.[argIndex] + argIndex <- argIndex + 1 + let f = farg :?> ('State -> 'Residue) + env.WriteT(f env.State) + + | StepLittleA(prefix) -> + env.WriteSkipEmpty prefix + let farg = args.[argIndex] + argIndex <- argIndex + 1 + let arg = args.[argIndex] + argIndex <- argIndex + 1 + let f = farg :?> ('State -> obj -> 'Residue) + env.WriteT(f env.State arg) + + | StepStar1(prefix, conv) -> + env.WriteSkipEmpty prefix + let star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let arg1 = args.[argIndex] + argIndex <- argIndex + 1 + env.Write (conv arg1 star1) + + | StepPercentStar1(prefix) -> + //let _star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + env.WriteSkipEmpty prefix + env.Write("%") + + | StepStar2(prefix, conv) -> + env.WriteSkipEmpty prefix + let star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let star2 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let arg1 = args.[argIndex] + argIndex <- argIndex + 1 + env.Write (conv arg1 star1 star2) + + | StepPercentStar2(prefix) -> + env.WriteSkipEmpty prefix + //let _star1 = args.[argIndex] :?> int + argIndex <- argIndex + 2 + env.Write("%") env.Finish() From 5f01922bfbd4074be0076c5aeb0b067237477e08 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 4 May 2020 14:03:51 +0100 Subject: [PATCH 37/87] enable test corectly --- tests/fsharp/tests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index f8eb5281129..b802a58e8b7 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -194,7 +194,7 @@ module CoreTests = let ``printf-FSC_BASIC`` () = singleTestBuildAndRun "core/printf" FSC_BASIC [] - let ``printf-interpolated-FSC_BASIC`` () = singleTestBuildAndRun "core/printf-interpolated" FSC_BASIC + let ``printf-interpolated-FSC_BASIC`` () = singleTestBuildAndRunVersion "core/printf-interpolated" FSC_BASIC "preview" [] let ``tlr-FSC_BASIC`` () = singleTestBuildAndRun "core/tlr" FSC_BASIC From 66a1c3572b7a3e2f2def8fd15ade197a7c9e7612 Mon Sep 17 00:00:00 2001 From: Yatao Li Date: Mon, 4 May 2020 21:07:52 +0800 Subject: [PATCH 38/87] Revert "printf: fix empty interpolation string evaluates to null in printf env" This reverts commit 7f396177996a7d63e1ce650423993f07facd8239. --- src/fsharp/FSharp.Core/printf.fs | 111 +++++++++++++++---------------- 1 file changed, 54 insertions(+), 57 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index d01eb4d55e3..ab0c4e39e8e 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -287,63 +287,60 @@ module internal PrintfImpl = member env.RunSteps (args: obj[], steps: Step[]) = let mutable argIndex = 0 - match steps with - | [| StepString one |] -> env.Write(one) - | _ -> - for step in steps do - match step with - | Step (prefix, conv1) -> - env.WriteSkipEmpty(prefix) - env.Write(&argIndex, args, conv1) - - | StepString prefix -> - env.WriteSkipEmpty(prefix) - - | StepLittleT(prefix) -> - env.WriteSkipEmpty prefix - let farg = args.[argIndex] - argIndex <- argIndex + 1 - let f = farg :?> ('State -> 'Residue) - env.WriteT(f env.State) - - | StepLittleA(prefix) -> - env.WriteSkipEmpty prefix - let farg = args.[argIndex] - argIndex <- argIndex + 1 - let arg = args.[argIndex] - argIndex <- argIndex + 1 - let f = farg :?> ('State -> obj -> 'Residue) - env.WriteT(f env.State arg) - - | StepStar1(prefix, conv) -> - env.WriteSkipEmpty prefix - let star1 = args.[argIndex] :?> int - argIndex <- argIndex + 1 - let arg1 = args.[argIndex] - argIndex <- argIndex + 1 - env.Write (conv arg1 star1) - - | StepPercentStar1(prefix) -> - //let _star1 = args.[argIndex] :?> int - argIndex <- argIndex + 1 - env.WriteSkipEmpty prefix - env.Write("%") - - | StepStar2(prefix, conv) -> - env.WriteSkipEmpty prefix - let star1 = args.[argIndex] :?> int - argIndex <- argIndex + 1 - let star2 = args.[argIndex] :?> int - argIndex <- argIndex + 1 - let arg1 = args.[argIndex] - argIndex <- argIndex + 1 - env.Write (conv arg1 star1 star2) - - | StepPercentStar2(prefix) -> - env.WriteSkipEmpty prefix - //let _star1 = args.[argIndex] :?> int - argIndex <- argIndex + 2 - env.Write("%") + for step in steps do + match step with + | Step (prefix, conv1) -> + env.WriteSkipEmpty(prefix) + env.Write(&argIndex, args, conv1) + + | StepString prefix -> + env.WriteSkipEmpty(prefix) + + | StepLittleT(prefix) -> + env.WriteSkipEmpty prefix + let farg = args.[argIndex] + argIndex <- argIndex + 1 + let f = farg :?> ('State -> 'Residue) + env.WriteT(f env.State) + + | StepLittleA(prefix) -> + env.WriteSkipEmpty prefix + let farg = args.[argIndex] + argIndex <- argIndex + 1 + let arg = args.[argIndex] + argIndex <- argIndex + 1 + let f = farg :?> ('State -> obj -> 'Residue) + env.WriteT(f env.State arg) + + | StepStar1(prefix, conv) -> + env.WriteSkipEmpty prefix + let star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let arg1 = args.[argIndex] + argIndex <- argIndex + 1 + env.Write (conv arg1 star1) + + | StepPercentStar1(prefix) -> + //let _star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + env.WriteSkipEmpty prefix + env.Write("%") + + | StepStar2(prefix, conv) -> + env.WriteSkipEmpty prefix + let star1 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let star2 = args.[argIndex] :?> int + argIndex <- argIndex + 1 + let arg1 = args.[argIndex] + argIndex <- argIndex + 1 + env.Write (conv arg1 star1 star2) + + | StepPercentStar2(prefix) -> + env.WriteSkipEmpty prefix + //let _star1 = args.[argIndex] :?> int + argIndex <- argIndex + 2 + env.Write("%") env.Finish() From ba0e2ce054d7a5fe6c47f232940c276f2ada8145 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 4 May 2020 16:17:24 +0100 Subject: [PATCH 39/87] simplify codegen for interpolated strings --- src/fsharp/CheckFormatStrings.fs | 29 ++--- src/fsharp/CheckFormatStrings.fsi | 2 +- src/fsharp/FSharp.Core/printf.fs | 109 +++++++++++------- src/fsharp/FSharp.Core/printf.fsi | 13 ++- src/fsharp/TcGlobals.fs | 3 - src/fsharp/TypeChecker.fs | 96 ++++++++------- src/fsharp/TypedTreeOps.fs | 3 - src/fsharp/TypedTreeOps.fsi | 2 - .../SurfaceArea.coreclr.fs | 12 +- .../SurfaceArea.net40.fs | 12 +- .../fsharp/core/printf-interpolated/test.fsx | 20 +++- 11 files changed, 174 insertions(+), 127 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index c87655e77bf..3d3ef336d05 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -48,7 +48,7 @@ let newInfo () = addZeros = false precision = false} -let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormattableString (context: FormatStringCheckContext option) fmt bty cty = +let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormattableString (context: FormatStringCheckContext option) fmt printerArgTy printerResidueTy = // 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) = @@ -78,6 +78,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta let dotnetFormatString = StringBuilder() let appendToDotnetFormatString (s: string) = dotnetFormatString.Append(s) |> ignore let mutable dotnetFormatStringInterpolationHoleCount = 0 + let percentATys = ResizeArray<_>() let rec parseLoop acc (i, relLine, relCol) = if i >= len then @@ -327,13 +328,15 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta | Some '+' -> collectSpecifierLocation relLine relCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) + let xty = NewInferenceType () + percentATys.Add(xty) + parseLoop ((posi, xty) :: acc) (i, relLine, relCol+1) | Some n -> raise (Failure (FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), n.ToString()))) | 'a' -> checkOtherFlags ch let xty = NewInferenceType () - let fty = bty --> (xty --> cty) + let fty = printerArgTy --> (xty --> printerResidueTy) collectSpecifierLocation relLine relCol 2 let i = skipPossibleInterpolationHole (i+1) parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i, relLine, relCol+1) @@ -342,7 +345,7 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta checkOtherFlags ch collectSpecifierLocation relLine relCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, bty --> cty) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, printerArgTy --> printerResidueTy) :: acc) (i, relLine, relCol+1) | c -> raise (Failure (FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c))) @@ -354,18 +357,18 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta parseLoop acc (i+1, relLine, relCol+1) let results = parseLoop [] (0, 0, m.StartColumn) - results, Seq.toList specifierLocations, dotnetFormatString.ToString() + results, Seq.toList specifierLocations, dotnetFormatString.ToString(), percentATys.ToArray() -let ParseFormatString m g isInterpolated isFormattableString formatStringCheckContext fmt bty cty dty = - let argtys, specifierLocations, dotnetFormatString = parseFormatStringInternal m g isInterpolated isFormattableString formatStringCheckContext fmt bty cty - let aty = List.foldBack (-->) argtys dty - let ety = mkRefTupledTy g argtys - (argtys, aty, ety), specifierLocations, dotnetFormatString +let ParseFormatString m g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy printerResultTy = + let argTys, specifierLocations, dotnetFormatString, percentATys = parseFormatStringInternal m g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy + let printerTy = List.foldBack (-->) argTys printerResultTy + let printerTupleTy = mkRefTupledTy g argTys + argTys, printerTy, printerTupleTy, percentATys, specifierLocations, dotnetFormatString -let TryCountFormatStringArguments m g isInterpolated fmt bty cty = +let TryCountFormatStringArguments m g isInterpolated fmt printerArgTy printerResidueTy = try - let argtys, _specifierLocations, _dotnetFormatString = parseFormatStringInternal m g isInterpolated false None fmt bty cty - Some argtys.Length + let argTys, _specifierLocations, _dotnetFormatString, _percentATys = parseFormatStringInternal m g isInterpolated false None fmt printerArgTy printerResidueTy + Some argTys.Length with _ -> None diff --git a/src/fsharp/CheckFormatStrings.fsi b/src/fsharp/CheckFormatStrings.fsi index 20788d6674c..cbb798873b6 100644 --- a/src/fsharp/CheckFormatStrings.fsi +++ b/src/fsharp/CheckFormatStrings.fsi @@ -13,6 +13,6 @@ open FSharp.Compiler.Range open FSharp.Compiler.TypedTree open FSharp.Compiler.TcGlobals -val ParseFormatString : m: range -> g: TcGlobals -> isInterpolated: bool -> isFormattableString: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> (TType list * TType * TType) * (range * int) list * string +val ParseFormatString : m: range -> g: TcGlobals -> isInterpolated: bool -> isFormattableString: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> TType list * TType * TType * TType[] * (range * int) list * string val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> isInterpolated: bool -> fmt:string -> bty:TType -> cty:TType -> int option diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index ab0c4e39e8e..43a685e7a9b 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -2,21 +2,34 @@ namespace Microsoft.FSharp.Core -type PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value:string, captures: obj[]) = +open System +open System.Collections.Concurrent +open System.Globalization +open System.Reflection + +open Microsoft.FSharp.Core +open Microsoft.FSharp.Core.Operators +open Microsoft.FSharp.Collections + +open LanguagePrimitives.IntrinsicOperators + +type PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value:string, captures: obj[], captureTys: Type[]) = - new (value) = new PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value, null) + new (value) = new PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value, null, null) + + member _.Value = value - member x.Value = value + member _.Captures = captures - member x.Captures = captures + member _.CaptureTypes = captureTys - override __.ToString() = value + override _.ToString() = value -type PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple>(value:string, captures) = +type PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple>(value:string, captures, captureTys: Type[]) = - inherit PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value, captures) + inherit PrintfFormat<'Printer, 'State, 'Residue, 'Result>(value, captures, captureTys) - new (value) = new PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple>(value, null) + new (value) = new PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple>(value, null, null) type Format<'Printer, 'State, 'Residue, 'Result> = PrintfFormat<'Printer, 'State, 'Residue, 'Result> @@ -45,15 +58,6 @@ module internal PrintfImpl = /// with just one reflection call /// 2. we can make combinable parts independent from particular printf implementation. Thus final result can be cached and shared. /// i.e when first call to printf "%s %s" will trigger creation of the specialization. Subsequent calls will pick existing specialization - open System - - open System.Collections.Concurrent - open System.Globalization - open System.Reflection - open Microsoft.FSharp.Core - open Microsoft.FSharp.Core.Operators - open Microsoft.FSharp.Collections - open LanguagePrimitives.IntrinsicOperators [] type FormatFlags = @@ -223,7 +227,8 @@ module internal PrintfImpl = [] /// Represents one step in the execution of a format string type Step = - | Step of prefix: string * conv1: (obj -> string) + | StepWithArg of prefix: string * conv1: (obj -> string) + | StepWithTypedArg of prefix: string * conv1: (obj -> Type -> string) | StepString of prefix: string | StepLittleT of prefix: string | StepLittleA of prefix: string @@ -236,7 +241,10 @@ module internal PrintfImpl = let mutable count = 0 for step in steps do match step with - | Step (prefix, _conv1) -> + | StepWithArg (prefix, _conv) -> + if not (String.IsNullOrEmpty prefix) then count <- count + 1 + count <- count + 1 + | StepWithTypedArg (prefix, _conv) -> if not (String.IsNullOrEmpty prefix) then count <- count + 1 count <- count + 1 | StepString prefix -> @@ -279,19 +287,25 @@ module internal PrintfImpl = if not (String.IsNullOrEmpty s) then env.Write s - member inline env.Write(argIndex: byref, args: obj[], conv: obj -> string) = - let arg = args.[argIndex] - argIndex <- argIndex + 1 - env.Write(conv arg) - - member env.RunSteps (args: obj[], steps: Step[]) = + member env.RunSteps (args: obj[], argTys: Type[], steps: Step[]) = let mutable argIndex = 0 + let mutable tyIndex = 0 for step in steps do match step with - | Step (prefix, conv1) -> + | StepWithArg (prefix, conv) -> env.WriteSkipEmpty(prefix) - env.Write(&argIndex, args, conv1) + let arg = args.[argIndex] + argIndex <- argIndex + 1 + env.Write(conv arg) + + | StepWithTypedArg (prefix, conv) -> + env.WriteSkipEmpty(prefix) + let arg = args.[argIndex] + let argTy = argTys.[tyIndex] + argIndex <- argIndex + 1 + tyIndex <- tyIndex + 1 + env.Write(conv arg argTy) | StepString prefix -> env.WriteSkipEmpty(prefix) @@ -366,7 +380,7 @@ module internal PrintfImpl = let allSteps = finalizeSteps steps (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> let (args, env) = prev() - env.RunSteps(finalizeArgs args, allSteps) + env.RunSteps(finalizeArgs args, null, allSteps) ) static member CaptureFinal1<'A>(steps) = @@ -375,7 +389,7 @@ module internal PrintfImpl = (fun (arg1: 'A) -> let (args, env) = prev() let finalArgs = box arg1 :: args - env.RunSteps(finalizeArgs finalArgs, allSteps) + env.RunSteps(finalizeArgs finalArgs, null, allSteps) ) ) @@ -385,7 +399,7 @@ module internal PrintfImpl = (fun (arg1: 'A) (arg2: 'B) -> let (args, env) = prev() let finalArgs = box arg2 :: box arg1 :: args - env.RunSteps(finalizeArgs finalArgs, allSteps) + env.RunSteps(finalizeArgs finalArgs, null, allSteps) ) ) @@ -395,7 +409,7 @@ module internal PrintfImpl = (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> let (args, env) = prev() let finalArgs = box arg3 :: box arg2 :: box arg1 :: args - env.RunSteps(finalizeArgs finalArgs, allSteps) + env.RunSteps(finalizeArgs finalArgs, null, allSteps) ) ) @@ -985,7 +999,9 @@ module internal PrintfImpl = | false, '%' -> StepPercentStar1 prefix | true, '%' -> StepPercentStar2 prefix | _ -> - let argTy = (match argTys with null -> typeof | _ -> argTys.[argTys.Length - 1]) + // For curried interpolated string format processing, the static types of the '%A' arguments + // are provided via the argument typed extracted from the curried function. They are known on first phase. + let argTy = match argTys with null -> typeof | _ -> argTys.[argTys.Length - 1] let conv = getValueConverter argTy spec if isTwoStar then let convFunc = conv.FuncObj :?> (obj -> int -> int -> string) @@ -994,10 +1010,25 @@ module internal PrintfImpl = let convFunc = conv.FuncObj :?> (obj -> int -> string) StepStar1 (prefix, convFunc) else - let argTy = (match argTys with null -> typeof | _ -> argTys.[0]) - let conv = getValueConverter argTy spec - let convFunc = conv.FuncObj :?> (obj -> string) - Step (prefix, convFunc) + // For interpolated string format processing, the static types of the '%A' arguments + // are provided via CaptureTypes and are only known on second phase. + match argTys with + | null when spec.TypeChar = 'A' -> + let convFunc arg argTy = + let mi = mi_GenericToString.MakeGenericMethod [| argTy |] + let f = mi.Invoke(null, [| box spec |]) :?> ValueConverter + let f2 = f.FuncObj :?> (obj -> string) + f2 arg + + StepWithTypedArg (prefix, convFunc) + + | _ -> + // For curried interpolated string format processing, the static types of the '%A' arguments + // are provided via the argument typed extracted from the curried function. They are known on first phase. + let argTy = match argTys with null -> typeof | _ -> argTys.[0] + let conv = getValueConverter argTy spec + let convFunc = conv.FuncObj :?> (obj -> string) + StepWithArg (prefix, convFunc) let parseSpec i = let flags, i = FormatString.parseFlags fmt (i + 1) @@ -1165,7 +1196,7 @@ module internal PrintfImpl = inherit PrintfEnv(()) let mutable c = null - override __.Finish() : string = c + override __.Finish() : string = if isNull c then "" else c override __.Write(s: string) = if isNull c then c <- s else c <- c + s override __.WriteT s = if isNull c then c <- s else c <- c + s @@ -1214,7 +1245,7 @@ module Printf = // The ksprintf $"...%d{3}...." path, running the steps straight away to produce a string let steps = cacheItem.Steps let env = envf cacheItem.BlockCount :> PrintfEnv<_,_,_> - let res = env.RunSteps(captures, steps) + let res = env.RunSteps(captures, format.CaptureTypes, steps) unbox res // prove 'T = 'Result //continuation res @@ -1236,7 +1267,7 @@ module Printf = // The sprintf $"...%d{3}...." path, running the steps straight away to produce a string let steps = cacheItem.Steps let env = StringPrintfEnv cacheItem.BlockCount - let res = env.RunSteps(captures, steps) + let res = env.RunSteps(captures, format.CaptureTypes, steps) unbox res // proves 'T = string [] diff --git a/src/fsharp/FSharp.Core/printf.fsi b/src/fsharp/FSharp.Core/printf.fsi index 1c66d74b9db..3cbb4845d49 100644 --- a/src/fsharp/FSharp.Core/printf.fsi +++ b/src/fsharp/FSharp.Core/printf.fsi @@ -23,10 +23,11 @@ type PrintfFormat<'Printer,'State,'Residue,'Result> = /// Construct a format string /// The input string. - /// The captured expressions in an interpolation string. + /// The captured expressions in an interpolated string. + /// The types of expressions for %A holes in interpolated string. /// The PrintfFormat containing the formatted result. [] - new : value:string * captures: obj[] -> PrintfFormat<'Printer,'State,'Residue,'Result> + new : value:string * captures: obj[] * captureTys: Type[] -> PrintfFormat<'Printer,'State,'Residue,'Result> /// The raw text of the format string. member Value : string @@ -34,6 +35,9 @@ type PrintfFormat<'Printer,'State,'Residue,'Result> = [] member Captures: obj[] + [] + member CaptureTypes: System.Type[] + /// Type of a formatting expression. /// Function type generated by printf. /// Type argument passed to %a formatters @@ -51,10 +55,11 @@ type PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> = /// Construct a format string /// The input string. - /// The captured expressions in an interpolation string. + /// The captured expressions in an interpolated string. + /// The types of expressions for %A holes in interpolated string. /// The created format string. [] - new: value:string * captures: obj[] -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> + new: value:string * captures: obj[] * captureTys: Type[] -> PrintfFormat<'Printer,'State,'Residue,'Result,'Tuple> /// Type of a formatting expression. /// Function type generated by printf. diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index 418308d67b5..9beb8f7ffd2 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -712,7 +712,6 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d let v_seq_singleton_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "singleton" , None , Some "Singleton" , [vara], ([[varaTy]], mkSeqTy varaTy)) let v_seq_empty_info = makeIntrinsicValRef(fslib_MFSeqModule_nleref, "empty" , None , Some "Empty" , [vara], ([], mkSeqTy varaTy)) let v_new_format_info = makeIntrinsicValRef(fslib_MFCore_nleref, ".ctor" , Some "PrintfFormat`5", None , [vara;varb;varc;vard;vare], ([[v_string_ty]], mkPrintfFormatTy varaTy varbTy varcTy vardTy vareTy)) - let v_new_format_ext_info = makeIntrinsicValRef(fslib_MFCore_nleref, ".ctor" , Some "PrintfFormat`5", None , [vara;varb;varc;vard;vare], ([[v_string_ty; mkArrayType 1 v_obj_ty]], mkPrintfFormatTy varaTy varbTy varcTy vardTy vareTy)) let v_sprintf_info = makeIntrinsicValRef(fslib_MFExtraTopLevelOperators_nleref, "sprintf" , None , Some "PrintFormatToStringThen", [vara], ([[mk_format4_ty varaTy v_unit_ty v_string_ty v_string_ty]], varaTy)) let v_lazy_force_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Force" , Some "Lazy`1" , None , [vara], ([[mkLazyTy varaTy]; []], varaTy)) let v_lazy_create_info = makeIntrinsicValRef(fslib_MFLazyExtensions_nleref, "Create" , Some "Lazy`1" , None , [vara], ([[v_unit_ty --> varaTy]], mkLazyTy varaTy)) @@ -1382,7 +1381,6 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val seq_empty_vref = ValRefForIntrinsic v_seq_empty_info member val new_format_vref = ValRefForIntrinsic v_new_format_info member val sprintf_vref = ValRefForIntrinsic v_sprintf_info - member val new_format_ext_vref = ValRefForIntrinsic v_new_format_ext_info member val unbox_vref = ValRefForIntrinsic v_unbox_info member val unbox_fast_vref = ValRefForIntrinsic v_unbox_fast_info member val istype_vref = ValRefForIntrinsic v_istype_info @@ -1410,7 +1408,6 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member __.seq_empty_info = v_seq_empty_info member __.sprintf_info = v_sprintf_info member __.new_format_info = v_new_format_info - member __.new_format_ext_info = v_new_format_ext_info member __.unbox_info = v_unbox_info member __.get_generic_comparer_info = v_get_generic_comparer_info member __.get_generic_er_equality_comparer_info = v_get_generic_er_equality_comparer_info diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index be040490c81..df4e51be00f 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7109,7 +7109,7 @@ and TcFormatStringExpr cenv overallTy env m tpenv (fmtString: string) = let formatStringCheckContext = match cenv.tcSink.CurrentSink with None -> None | Some sink -> sink.FormatStringCheckContext let normalizedString = (fmtString.Replace("\r\n", "\n").Replace("\r", "\n")) - let (_argtys, atyRequired, etyRequired), specifierLocations, _dotnetFormatString = + let _argTys, atyRequired, etyRequired, _percentATys, specifierLocations, _dotnetFormatString = try CheckFormatStrings.ParseFormatString m g false false formatStringCheckContext normalizedString bty cty dty with Failure errString -> error (Error(FSComp.SR.tcUnableToParseFormatString errString, m)) @@ -7149,6 +7149,12 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice List.filter (fun minfo -> minfo.NumArgs = [3]) with + | [ctorInfo] -> ctorInfo + | _ -> languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m + let stringKind = // If this is an interpolated string then try to force the result to be a string if (AddCxTypeEqualsTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy g.string_ty) then @@ -7157,7 +7163,11 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice None match createMethodOpt with - | Some createMethod -> Choice2Of3 createMethod - | None -> Choice1Of3 () + | Some createMethod -> Choice2Of2 createMethod + | None -> languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m // ... or if that fails then may be a PrintfFormat by a type-directed rule.... elif not (isObjTy g overallTy) && AddCxTypeMustSubsumeTypeUndoIfFailed env.DisplayEnv cenv.css m overallTy formatTy then // And if that succeeds, the printerTy and printerResultTy must be the same (there are no curried arguments) UnifyTypes cenv env m printerTy printerResultTy - match GetIntrinsicConstructorInfosOfType cenv.infoReader m formatTy |> List.filter (fun minfo -> minfo.NumArgs = [2]) with - | [ctorInfo] -> Choice3Of3 ctorInfo - | _ -> Choice1Of3 () + Choice1Of2 (false, newFormatMethod) else // this should fail and produce an error UnifyTypes cenv env m overallTy g.string_ty - Choice1Of3 () + Choice1Of2 (true, newFormatMethod) - let isFormattableString = (match stringKind with Choice2Of3 _ -> true | _ -> false) + let isFormattableString = (match stringKind with Choice2Of2 _ -> true | _ -> false) // The format string used for checking in CheckFormatStrings. This replaces interpolation holes with %P let printfFormatString = @@ -7216,49 +7223,58 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice String.concat "" // Parse the format string to work out the phantom types and check for absence of '%' specifiers in FormattableString - let (argtys, printerTyRequired, printerTupleTyRequired), _specifierLocations, dotnetFormatString = + let argTys, _printerTy, printerTupleTyRequired, percentATys, _specifierLocations, dotnetFormatString = try CheckFormatStrings.ParseFormatString m g true isFormattableString None printfFormatString printerArgTy printerResidueTy printerResultTy with Failure errString -> error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) // Check the expressions filling the holes - if argtys.Length <> synFillExprs.Length then + if argTys.Length <> synFillExprs.Length then error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) match stringKind with - // The case for $"..." used as type string - | Choice1Of3 () -> + // The case for $"..." used as type string and $"...%d{x}..." used as type PrintfFormat - create a PrintfFormat that captures + // is arguments + | Choice1Of2 (isString, newFormatMethod) -> - UnifyTypes cenv env m printerTy printerTyRequired UnifyTypes cenv env m printerTupleTy printerTupleTyRequired - let fmtExpr = mkCallNewFormat g m printerTy printerArgTy printerResidueTy printerResultTy printerTupleTy (mkString g m printfFormatString) - // Type check the expressions filling the holes - let flexes = argtys |> List.map (fun _ -> false) - let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs + let flexes = argTys |> List.map (fun _ -> false) + let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argTys synFillExprs + + let fillExprsBoxed = (argTys, fillExprs) ||> List.map2 (mkCallBox g m) - // Check the library support is available in the referenced FSharp.Core - if cenv.g.new_format_ext_vref.TryDeref.IsNone then - languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m + let argsExpr = mkArray (g.obj_ty, fillExprsBoxed, m) + let percentATysExpr = + if percentATys.Length = 0 then + mkNull m (mkArrayType g g.system_Type_ty) + else + let tyExprs = percentATys |> Array.map (mkCallTypeOf g m) |> Array.toList + mkArray (g.system_Type_ty, tyExprs, m) - // Make the call to isprintf - mkCall_sprintf g m printerTy fmtExpr fillExprs, tpenv + let fmtExpr = MakeMethInfoCall cenv.amap m newFormatMethod [] [mkString g m printfFormatString; argsExpr; percentATysExpr] + + if isString then + // Make the call to sprintf + mkCall_sprintf g m printerTy fmtExpr [], tpenv + else + fmtExpr, tpenv // The case for $"..." used as type FormattableString or IFormattable - | Choice2Of3 createMethInfo -> + | Choice2Of2 createFormattableStringMethod -> // Type check the expressions filling the holes - let flexes = argtys |> List.map (fun _ -> false) - let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs + let flexes = argTys |> List.map (fun _ -> false) + let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argTys synFillExprs - let fillExprsBoxed = (argtys, fillExprs) ||> List.map2 (mkCallBox g m) + let fillExprsBoxed = (argTys, fillExprs) ||> List.map2 (mkCallBox g m) let dotnetFormatStringExpr = mkString g m dotnetFormatString let argsExpr = mkArray (g.obj_ty, fillExprsBoxed, m) // FormattableString are *always* turned into FormattableStringFactory.Create calls, boxing each argument - let createExpr, _ = BuildPossiblyConditionalMethodCall cenv env NeverMutates m false createMethInfo NormalValUse [] [dotnetFormatStringExpr; argsExpr] [] + let createExpr, _ = BuildPossiblyConditionalMethodCall cenv env NeverMutates m false createFormattableStringMethod NormalValUse [] [dotnetFormatStringExpr; argsExpr] [] let resultExpr = if typeEquiv g overallTy g.system_IFormattable_ty then @@ -7267,28 +7283,6 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice - - UnifyTypes cenv env m printerTupleTy printerTupleTyRequired - - // Type check the expressions filling the holes - let flexes = argtys |> List.map (fun _ -> false) - let fillExprs, tpenv = TcExprs cenv env m tpenv flexes argtys synFillExprs - - let fillExprsBoxed = (argtys, fillExprs) ||> List.map2 (mkCallBox g m) - - let argsExpr = mkArray (g.obj_ty, fillExprsBoxed, m) - - // Check the library support is available in the referenced FSharp.Core - if cenv.g.new_format_ext_vref.TryDeref.IsNone then - languageFeatureNotSupportedInLibraryError cenv.g.langVersion LanguageFeature.StringInterpolation m - - let fmtExpr = MakeMethInfoCall cenv.amap m ctor [] [mkString g m printfFormatString; argsExpr] - - fmtExpr, tpenv - //------------------------------------------------------------------------- // TcConstExpr //------------------------------------------------------------------------- diff --git a/src/fsharp/TypedTreeOps.fs b/src/fsharp/TypedTreeOps.fs index c628cb6a3b8..e1ae875fd97 100644 --- a/src/fsharp/TypedTreeOps.fs +++ b/src/fsharp/TypedTreeOps.fs @@ -6917,9 +6917,6 @@ let mkCallNewDecimal (g: TcGlobals) m (e1, e2, e3, e4, e5) = mkApps g (typedExpr let mkCallNewFormat (g: TcGlobals) m aty bty cty dty ety e1 = mkApps g (typedExprForIntrinsic g m g.new_format_info, [[aty;bty;cty;dty;ety]], [ e1 ], m) -let mkCallNewFormatWithExprs (g: TcGlobals) m aty bty cty dty ety fmtStringExpr captureExprs = - mkApps g (typedExprForIntrinsic g m g.new_format_ext_info, [[aty;bty;cty;dty;ety]], [fmtStringExpr;captureExprs ], m) - let TryEliminateDesugaredConstants g m c = match c with | Const.Decimal d -> diff --git a/src/fsharp/TypedTreeOps.fsi b/src/fsharp/TypedTreeOps.fsi index 222a97eb40e..fa41d748586 100755 --- a/src/fsharp/TypedTreeOps.fsi +++ b/src/fsharp/TypedTreeOps.fsi @@ -1775,8 +1775,6 @@ val mkInvalidCastExnNewobj: TcGlobals -> ILInstr val mkCallNewFormat: TcGlobals -> range -> TType -> TType -> TType -> TType -> TType -> formatStringExpr: Expr -> Expr -val mkCallNewFormatWithExprs: TcGlobals -> range -> TType -> TType -> TType -> TType -> TType -> formatStringExpr: Expr -> captureExprs: Expr -> Expr - val mkCallUnbox : TcGlobals -> range -> TType -> Expr -> Expr val mkCallGetGenericComparer : TcGlobals -> range -> Expr diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs index 1143debb2b3..36b87b13bb6 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.coreclr.fs @@ -2232,16 +2232,20 @@ Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.S Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.String Value Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.String get_Value() Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: Void .ctor(System.String) +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: Void .ctor(System.String, System.Object[], System.Type[]) +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] Captures +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] get_Captures() +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Type[] CaptureTypes +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Type[] get_CaptureTypes() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String ToString() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String Value Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String get_Value() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String) -Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] Captures -Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] get_Captures() -Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: Void .ctor(System.String, System.Object[]) +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String, System.Object[], System.Type[]) Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Object[] Captures Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Object[] get_Captures() -Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String, System.Object[]) +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Type[] CaptureTypes +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Type[] get_CaptureTypes() Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToError[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLine[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs index 0c0d2dd4e0a..468b1218a0b 100644 --- a/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs +++ b/tests/FSharp.Core.UnitTests/SurfaceArea.net40.fs @@ -2232,16 +2232,20 @@ Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.S Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.String Value Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.String get_Value() Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: Void .ctor(System.String) +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: Void .ctor(System.String, System.Object[], System.Type[]) +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] Captures +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] get_Captures() +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Type[] CaptureTypes +Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Type[] get_CaptureTypes() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String ToString() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String Value Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.String get_Value() Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String) -Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] Captures -Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: System.Object[] get_Captures() -Microsoft.FSharp.Core.PrintfFormat`4[TPrinter,TState,TResidue,TResult]: Void .ctor(System.String, System.Object[]) +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String, System.Object[], System.Type[]) Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Object[] Captures Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Object[] get_Captures() -Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: Void .ctor(System.String, System.Object[]) +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Type[] CaptureTypes +Microsoft.FSharp.Core.PrintfFormat`5[TPrinter,TState,TResidue,TResult,TTuple]: System.Type[] get_CaptureTypes() Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToError[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLineToTextWriter[T](System.IO.TextWriter, Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) Microsoft.FSharp.Core.PrintfModule: T PrintFormatLine[T](Microsoft.FSharp.Core.PrintfFormat`4[T,System.IO.TextWriter,Microsoft.FSharp.Core.Unit,Microsoft.FSharp.Core.Unit]) diff --git a/tests/fsharp/core/printf-interpolated/test.fsx b/tests/fsharp/core/printf-interpolated/test.fsx index baf5989741d..cc259438492 100644 --- a/tests/fsharp/core/printf-interpolated/test.fsx +++ b/tests/fsharp/core/printf-interpolated/test.fsx @@ -31,6 +31,9 @@ let verify actual expected = test expected actual expected let adjust1 obj n1 = unbox ((unbox obj) n1) +let _ = test "cewoui20" (lazy(sprintf $"")) "" +let _ = test "cewoui21" (lazy(sprintf $"abc")) "abc" +let _ = test "cewoui22" (lazy(sprintf $"%d{3}")) "3" let _ = test "cewoui2a" (lazy(sprintf $"%o{0}")) "0" let _ = test "cewoui2b" (lazy(sprintf $"%o{0}")) "0" let _ = test "cewoui2c" (lazy(sprintf $"%o{5}")) "5" @@ -230,9 +233,20 @@ let _ = test "test8440" (lazy(sprintf $"""%-+1A{-10}""")) "-10" test "test8442" (lazy(sprintf $"""%-+.5A{-10}""")) "-10" - // NOTE: %A in $"..." does *not* pick up the static type, which makes a difference for 'None' - test "test8445a" (lazy(sprintf "%A" (None: int option))) "None" - test "test8445b" (lazy(sprintf $"""%A{(None: int option)}""")) "" + // Check the static type matters for %A holes + test "test8445b1" (lazy(sprintf $"""%A{(Unchecked.defaultof)}""")) "None" + test "test8445b2" (lazy(sprintf $"""%A{box (None: int option)}""")) "" + test "test8445b3" (lazy(sprintf $"""%A{(None: int option)}""")) "None" + test "test8445b4" (lazy(sprintf $"""%A{(None: string option)}""")) "None" + test "test8445b5" (lazy(sprintf $"""%A{(None: obj option)}""")) "None" + test "test8445b6" (lazy($"""%A{(Unchecked.defaultof)}""")) "None" + test "test8445b7a" (lazy($"""{null}""")) "" + test "test8445b7b" (lazy($"""%O{null}""")) "" + test "test8445b8" (lazy($"""%A{null}""")) "" + test "test8445b9" (lazy($"""%A{box (None: int option)}""")) "" + test "test8445b10" (lazy($"""%A{(None: int option)}""")) "None" + test "test8445b11" (lazy($"""%A{(None: string option)}""")) "None" + test "test8445b12" (lazy($"""%A{(None: obj option)}""")) "None" test "test8445" (lazy(sprintf $"""%A{null}""")) "" test "test8446" (lazy(sprintf $"""%5A{null}""")) "" From bf29c6e99fcbdf31aad990d08bcf560d25667da5 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 3 Jun 2020 17:23:32 +0100 Subject: [PATCH 40/87] fix build --- tests/fsharp/Compiler/Language/StringInterpolation.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 7062b691f4a..a4364c8e556 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -4,7 +4,7 @@ namespace FSharp.Compiler.UnitTests open NUnit.Framework open FSharp.Compiler.SourceCodeServices -open FSharp.Compiler.UnitTests.Utilities +open FSharp.TestHelpers [] module StringInterpolationTests = From fe5f2a29e0ee9739e1c3af0d15b729c42c52eac1 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 17 Jun 2020 12:47:05 +0100 Subject: [PATCH 41/87] fix build --- tests/fsharp/Compiler/Language/StringInterpolation.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index a4364c8e556..974956f9722 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -4,7 +4,7 @@ namespace FSharp.Compiler.UnitTests open NUnit.Framework open FSharp.Compiler.SourceCodeServices -open FSharp.TestHelpers +open FSharp.Test.Utilities [] module StringInterpolationTests = From e04ac6dbbca42a959bcedfb6816ded67a666f7bc Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Sat, 27 Jun 2020 12:13:50 -0700 Subject: [PATCH 42/87] Merge master to feature/string-interp (#9580) * Update dependencies from https://github.com/dotnet/arcade build 20200626.2 (#9577) Microsoft.DotNet.Arcade.Sdk From Version 1.0.0-beta.20302.3 -> To Version 1.0.0-beta.20326.2 Co-authored-by: dotnet-maestro[bot] * Improve perf for String.filter up to 3x (#9509) * Improve perf for String.filter 2-2.5x * Cleanup: remove "foo" etc in tests * Add tests for new execution path for LOH in String.filter * Change test string * String map performance improvement (#9470) * Simplify and improve perf of String.length * Improve performance of String.map * Revert "Simplify and improve perf of String.length" * Resolves https://github.com/dotnet/fsharp/pull/9470#discussion_r441293049 * Lingering space * Change `String` to use `new` to clarify use of ctor * Add some better tests for String.map, add side-effect test * Add tests to ensure the mapping function is called a deterministically amount of times * Fix typo * Remove "foo" from String.map tests * Perf: String.replicate from O(n) to O(log(n)), up to 12x speed improvement (#9512) * Turn String.replicate from O(n) into O(log(n)) * Cleanup String.replicate tests by removing usages of "foo" * String.replicate: add tests for missing cases, and for the new O(log(n)) cut-off points * Improve String.replicate algorithm further * Add tests for String.replicate covering all lines/branches of algo * Fix accidental comment Co-authored-by: dotnet-maestro[bot] <42748379+dotnet-maestro[bot]@users.noreply.github.com> Co-authored-by: dotnet-maestro[bot] Co-authored-by: Abel Braaksma --- eng/Version.Details.xml | 4 +- eng/common/sdl/packages.config | 2 +- eng/common/templates/job/execute-sdl.yml | 2 +- global.json | 2 +- src/fsharp/FSharp.Core/string.fs | 73 ++++++++++++++--- .../StringModule.fs | 82 ++++++++++++++++--- 6 files changed, 137 insertions(+), 28 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 231cca712e2..44065c7c818 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -3,9 +3,9 @@ - + https://github.com/dotnet/arcade - 9b71be0663493cd0e111b55536a2e1eeb272f54c + ed69753a3ffbdaa08365252c710d57a64d17f859 diff --git a/eng/common/sdl/packages.config b/eng/common/sdl/packages.config index 256ffbfb93a..968b39bef5f 100644 --- a/eng/common/sdl/packages.config +++ b/eng/common/sdl/packages.config @@ -1,4 +1,4 @@ - + diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml index 52e2ff021d7..bf09d2511c6 100644 --- a/eng/common/templates/job/execute-sdl.yml +++ b/eng/common/templates/job/execute-sdl.yml @@ -65,7 +65,7 @@ jobs: continueOnError: ${{ parameters.sdlContinueOnError }} - ${{ if eq(parameters.overrideParameters, '') }}: - powershell: eng/common/sdl/execute-all-sdl-tools.ps1 - -GuardianPackageName Microsoft.Guardian.Cli.0.7.2 + -GuardianPackageName Microsoft.Guardian.Cli.win10-x64.0.20.1 -NugetPackageDirectory $(Build.SourcesDirectory)\.packages -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) ${{ parameters.additionalParameters }} diff --git a/global.json b/global.json index 146979f9b93..7afbdb52d28 100644 --- a/global.json +++ b/global.json @@ -9,7 +9,7 @@ } }, "msbuild-sdks": { - "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.20302.3", + "Microsoft.DotNet.Arcade.Sdk": "1.0.0-beta.20326.2", "Microsoft.DotNet.Helix.Sdk": "2.0.0-beta.19069.2" } } diff --git a/src/fsharp/FSharp.Core/string.fs b/src/fsharp/FSharp.Core/string.fs index 7e4b65f1a08..d009615e6aa 100644 --- a/src/fsharp/FSharp.Core/string.fs +++ b/src/fsharp/FSharp.Core/string.fs @@ -12,6 +12,11 @@ namespace Microsoft.FSharp.Core [] [] module String = + [] + /// LOH threshold is calculated from FSharp.Compiler.AbstractIL.Internal.Library.LOH_SIZE_THRESHOLD_BYTES, + /// and is equal to 80_000 / sizeof + let LOH_CHAR_THRESHOLD = 40_000 + [] let length (str:string) = if isNull str then 0 else str.Length @@ -37,9 +42,13 @@ namespace Microsoft.FSharp.Core if String.IsNullOrEmpty str then String.Empty else - let res = StringBuilder str.Length - str |> iter (fun c -> res.Append(mapping c) |> ignore) - res.ToString() + let result = str.ToCharArray() + let mutable i = 0 + for c in result do + result.[i] <- mapping c + i <- i + 1 + + new String(result) [] let mapi (mapping: int -> char -> char) (str:string) = @@ -53,13 +62,30 @@ namespace Microsoft.FSharp.Core [] let filter (predicate: char -> bool) (str:string) = - if String.IsNullOrEmpty str then + let len = length str + + if len = 0 then String.Empty - else - let res = StringBuilder str.Length + + elif len > LOH_CHAR_THRESHOLD then + // By using SB here, which is twice slower than the optimized path, we prevent LOH allocations + // and 'stop the world' collections if the filtering results in smaller strings. + // We also don't pre-allocate SB here, to allow for less mem pressure when filter result is small. + let res = StringBuilder() str |> iter (fun c -> if predicate c then res.Append c |> ignore) res.ToString() + else + // Must do it this way, since array.fs is not yet in scope, but this is safe + let target = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked len + let mutable i = 0 + for c in str do + if predicate c then + target.[i] <- c + i <- i + 1 + + String(target, 0, i) + [] let collect (mapping: char -> string) (str:string) = if String.IsNullOrEmpty str then @@ -81,13 +107,38 @@ namespace Microsoft.FSharp.Core let replicate (count:int) (str:string) = if count < 0 then invalidArgInputMustBeNonNegative "count" count - if String.IsNullOrEmpty str then + let len = length str + if len = 0 || count = 0 then String.Empty + + elif len = 1 then + new String(str.[0], count) + + elif count <= 4 then + match count with + | 1 -> str + | 2 -> String.Concat(str, str) + | 3 -> String.Concat(str, str, str) + | _ -> String.Concat(str, str, str, str) + else - let res = StringBuilder(count * str.Length) - for i = 0 to count - 1 do - res.Append str |> ignore - res.ToString() + // Using the primitive, because array.fs is not yet in scope. It's safe: both len and count are positive. + let target = Microsoft.FSharp.Primitives.Basics.Array.zeroCreateUnchecked (len * count) + let source = str.ToCharArray() + + // O(log(n)) performance loop: + // Copy first string, then keep copying what we already copied + // (i.e., doubling it) until we reach or pass the halfway point + Array.Copy(source, 0, target, 0, len) + let mutable i = len + while i * 2 < target.Length do + Array.Copy(target, 0, target, i, i) + i <- i * 2 + + // finally, copy the remain half, or less-then half + Array.Copy(target, 0, target, i, target.Length - i) + new String(target) + [] let forall predicate (str:string) = diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/StringModule.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/StringModule.fs index a72ab64e236..6c1b1360585 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/StringModule.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/Microsoft.FSharp.Collections/StringModule.fs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. namespace FSharp.Core.UnitTests.Collections @@ -71,11 +71,35 @@ type StringModule() = [] member this.Map() = - let e1 = String.map (fun c -> c) "foo" - Assert.AreEqual("foo", e1) + let e1 = String.map id "xyz" + Assert.AreEqual("xyz", e1) - let e2 = String.map (fun c -> c) null - Assert.AreEqual("", e2) + let e2 = String.map (fun c -> c + char 1) "abcde" + Assert.AreEqual("bcdef", e2) + + let e3 = String.map (fun c -> c) null + Assert.AreEqual("", e3) + + let e4 = String.map (fun c -> c) String.Empty + Assert.AreEqual("", e4) + + let e5 = String.map (fun _ -> 'B') "A" + Assert.AreEqual("B", e5) + + let e6 = String.map (fun _ -> failwith "should not raise") null + Assert.AreEqual("", e6) + + // this tests makes sure mapping function is not called too many times + let mutable x = 0 + let e7 = String.map (fun _ -> if x > 2 then failwith "should not raise" else x <- x + 1; 'x') "abc" + Assert.AreEqual(x, 3) + Assert.AreEqual(e7, "xxx") + + // side-effect and "order of operation" test + let mutable x = 0 + let e8 = String.map (fun c -> x <- x + 1; c + char x) "abcde" + Assert.AreEqual(x, 5) + Assert.AreEqual(e8, "bdfhj") [] member this.MapI() = @@ -87,18 +111,25 @@ type StringModule() = [] member this.Filter() = - let e1 = String.filter (fun c -> true) "foo" - Assert.AreEqual("foo", e1) + let e1 = String.filter (fun c -> true) "Taradiddle" + Assert.AreEqual("Taradiddle", e1) let e2 = String.filter (fun c -> true) null Assert.AreEqual("", e2) - let e3 = String.filter (fun c -> c <> 'o') "foo bar" - Assert.AreEqual("f bar", e3) + let e3 = String.filter Char.IsUpper "How Vexingly Quick Daft Zebras Jump!" + Assert.AreEqual("HVQDZJ", e3) let e4 = String.filter (fun c -> c <> 'o') "" Assert.AreEqual("", e4) + let e5 = String.filter (fun c -> c > 'B' ) "ABRACADABRA" + Assert.AreEqual("RCDR", e5) + + // LOH test with 55k string, which is 110k bytes + let e5 = String.filter (fun c -> c > 'B' ) (String.replicate 5_000 "ABRACADABRA") + Assert.AreEqual(String.replicate 5_000 "RCDR", e5) + [] member this.Collect() = let e1 = String.collect (fun c -> "a"+string c) "foo" @@ -125,15 +156,42 @@ type StringModule() = [] member this.Replicate() = - let e1 = String.replicate 0 "foo" + let e1 = String.replicate 0 "Snickersnee" Assert.AreEqual("", e1) - let e2 = String.replicate 2 "foo" - Assert.AreEqual("foofoo", e2) + let e2 = String.replicate 2 "Collywobbles, " + Assert.AreEqual("Collywobbles, Collywobbles, ", e2) let e3 = String.replicate 2 null Assert.AreEqual("", e3) + let e4 = String.replicate 300_000 "" + Assert.AreEqual("", e4) + + let e5 = String.replicate 23 "天地玄黃,宇宙洪荒。" + Assert.AreEqual(230 , e5.Length) + Assert.AreEqual("天地玄黃,宇宙洪荒。天地玄黃,宇宙洪荒。", e5.Substring(0, 20)) + + // This tests the cut-off point for the O(log(n)) algorithm with a prime number + let e6 = String.replicate 84673 "!!!" + Assert.AreEqual(84673 * 3, e6.Length) + + // This tests the cut-off point for the O(log(n)) algorithm with a 2^x number + let e7 = String.replicate 1024 "!!!" + Assert.AreEqual(1024 * 3, e7.Length) + + let e8 = String.replicate 1 "What a wonderful world" + Assert.AreEqual("What a wonderful world", e8) + + let e9 = String.replicate 3 "أضعت طريقي! أضعت طريقي" // means: I'm lost + Assert.AreEqual("أضعت طريقي! أضعت طريقيأضعت طريقي! أضعت طريقيأضعت طريقي! أضعت طريقي", e9) + + let e10 = String.replicate 4 "㏖ ㏗ ℵ " + Assert.AreEqual("㏖ ㏗ ℵ ㏖ ㏗ ℵ ㏖ ㏗ ℵ ㏖ ㏗ ℵ ", e10) + + let e11 = String.replicate 5 "5" + Assert.AreEqual("55555", e11) + CheckThrowsArgumentException(fun () -> String.replicate -1 "foo" |> ignore) [] From 0bfde327687ab010d6d9a0b0b88a16786adb4266 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Sun, 28 Jun 2020 18:24:17 -0700 Subject: [PATCH 43/87] Re enable tests for operators: OperatorsModule1.fs and OperatorsModule2.fs (#9516) (#9589) * Re-enabling tests from OperatorsModule1/2.fs (compile errors) * Fix compile errors in OperatorsModule1/2.fs, fix tests. Note tanh test comment. * Fix `tanh` test, ensure stable result on x86 vs x64 runtimes * Stop using exception AssertionException, so that test window shows useful info * Whitespace cleanup and redundant code removal * Cleanup spelling etc * Re-enabling int, int16, int32, int64, nativeint, incr, nullArg etc tests * Special-case floating-point assertion messages for higher precision output * Fix/update/add tests (some still failing) * Separate Checked tests, add & fix others, differentiate framework/bitness for some tests * Add branch for .NET Native (ignore cos test) * Resorting to comparing floats with a delta using Assert.AreNearEqual * Add some more tests Co-authored-by: Abel Braaksma --- .../FSharp.Core.UnitTests.fsproj | 19 +- .../FSharp.Core/OperatorsModule1.fs | 802 +++++---------- .../FSharp.Core/OperatorsModule2.fs | 909 +++++++++++------- .../FSharp.Core/OperatorsModuleChecked.fs | 307 ++++++ tests/FSharp.Core.UnitTests/LibraryTestFx.fs | 2 + .../NUnitFrameworkShims.fs | 55 +- 6 files changed, 1197 insertions(+), 897 deletions(-) create mode 100644 tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModuleChecked.fs diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj index 90b1436e398..0706e76eef5 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj +++ b/tests/FSharp.Core.UnitTests/FSharp.Core.UnitTests.fsproj @@ -33,6 +33,13 @@ + + + + + + + @@ -59,10 +66,6 @@ - - - - @@ -87,9 +90,7 @@ - - - + @@ -106,4 +107,8 @@ + + + + diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModule1.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModule1.fs index 5aacff670ab..e81ddf29c01 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModule1.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModule1.fs @@ -3,360 +3,57 @@ // Various tests for the: // Microsoft.FSharp.Core.Operators module -namespace SystematicUnitTests.FSharp_Core.Microsoft_FSharp_Core +namespace FSharp.Core.UnitTests.Operators open System -open SystematicUnitTests.LibraryTestFx +open FSharp.Core.UnitTests.LibraryTestFx open NUnit.Framework -open Microsoft.FSharp.Core.Operators.Checked [] type OperatorsModule1() = [] - member this.Checkedbyte() = - // int type - let intByte = Operators.Checked.byte 100 - Assert.AreEqual(intByte,(byte)100) - - // char type - let charByte = Operators.Checked.byte '0' - Assert.AreEqual(charByte,(byte)48) - - // boundary value - let boundByte = Operators.Checked.byte 255.0 - Assert.AreEqual(boundByte, (byte)255) - - // overflow exception - try - let overflowByte = Operators.Checked.byte 256.0 - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - - - - - [] - member this.Checkedchar() = - - // number - let numberChar = Operators.Checked.char 48 - Assert.AreEqual(numberChar,'0') - - // letter - let letterChar = Operators.Checked.char 65 - Assert.AreEqual(letterChar,'A') - - // boundary value - let boundchar = Operators.Checked.char 126 - Assert.AreEqual(boundchar, '~') - - // overflow exception - try - let overflowchar = Operators.Checked.char (System.Int64.MaxValue+(int64)2) - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - - [] - member this.CheckedInt() = - - // char - let charInt = Operators.Checked.int '0' - Assert.AreEqual(charInt,48) - - // float - let floatInt = Operators.Checked.int 10.0 - Assert.AreEqual(floatInt,10) - - - // boundary value - let boundInt = Operators.Checked.int 32767.0 - Assert.AreEqual(boundInt, (int)32767) - - // overflow exception - try - let overflowint = Operators.Checked.int 2147483648.0 - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - - () - - [] - member this.CheckedInt16() = - - // char - let charInt16 = Operators.Checked.int16 '0' - Assert.AreEqual(charInt16,(int16)48) - - // float - let floatInt16 = Operators.Checked.int16 10.0 - Assert.AreEqual(floatInt16,(int16)10) - - // boundary value - let boundInt16 = Operators.Checked.int16 32767.0 - Assert.AreEqual(boundInt16, (int16)32767) - - // overflow exception - try - let overflowint16 = Operators.Checked.int16 32768.0 - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - () - - [] - member this.CheckedInt32() = - - // char - let charInt32 = Operators.Checked.int32 '0' - Assert.AreEqual(charInt32,(int32)48) - - // float - let floatInt32 = Operators.Checked.int32 10.0 - Assert.AreEqual(floatInt32,(int32)10) - - // boundary value - let boundInt32 = Operators.Checked.int32 2147483647.0 - Assert.AreEqual(boundInt32, (int32)2147483647) - - // overflow exception - try - let overflowint32 = Operators.Checked.int32 2147483648.0 - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - - () - - [] - member this.CheckedInt64() = - - // char - let charInt64 = Operators.Checked.int64 '0' - Assert.AreEqual(charInt64,(int64)48) - - // float - let floatInt64 = Operators.Checked.int64 10.0 - Assert.AreEqual(floatInt64,(int64)10) - - // boundary value - let boundInt64 = Operators.Checked.int64 9223372036854775807I - let a = 9223372036854775807L - Assert.AreEqual(boundInt64, 9223372036854775807L) - - // overflow exception - try - let overflowint64 = Operators.Checked.int64 (System.Double.MaxValue+2.0) - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - () - - [] - member this.CheckedNativeint() = - - // char - let charnativeint = Operators.Checked.nativeint '0' - Assert.AreEqual(charnativeint,(nativeint)48) - - // float - let floatnativeint = Operators.Checked.nativeint 10.0 - Assert.AreEqual(floatnativeint,(nativeint)10) - - // boundary value - let boundnativeint = Operators.Checked.nativeint 32767.0 - Assert.AreEqual(boundnativeint, (nativeint)32767) - - // overflow exception - try - let overflownativeint = Operators.Checked.nativeint 2147483648.0 - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - () - - [] - member this.Checkedsbyte() = - - // char - let charsbyte = Operators.Checked.sbyte '0' - Assert.AreEqual(charsbyte,(sbyte)48) - - // float - let floatsbyte = Operators.Checked.sbyte -10.0 - Assert.AreEqual(floatsbyte,(sbyte)(-10)) - - // boundary value - let boundsbyte = Operators.Checked.sbyte -127.0 - Assert.AreEqual(boundsbyte, (sbyte)(-127)) - - // overflow exception - try - let overflowsbyte = Operators.Checked.sbyte -256.0 - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - - () - - [] - member this.Checkeduint16() = + member _.KeyValue() = - // char - let charuint16 = Operators.Checked.uint16 '0' - Assert.AreEqual(charuint16,(uint16)48) - - // float - let floatuint16 = Operators.Checked.uint16 10.0 - Assert.AreEqual(floatuint16,(uint16)(10)) - - // boundary value - let bounduint16 = Operators.Checked.uint16 65535.0 - Assert.AreEqual(bounduint16, (uint16)(65535)) - - // overflow exception - try - let overflowuint16 = Operators.Checked.uint16 65536.0 - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - () - - [] - member this.Checkeduint32() = - - // char - let charuint32 = Operators.Checked.uint32 '0' - Assert.AreEqual(charuint32,(uint32)48) - - // float - let floatuint32 = Operators.Checked.uint32 10.0 - Assert.AreEqual(floatuint32,(uint32)(10)) - - // boundary value - let bounduint32 = Operators.Checked.uint32 429496729.0 - Assert.AreEqual(bounduint32, (uint32)(429496729)) - - - // overflow exception - try - let overflowuint32 = Operators.Checked.uint32 uint32.MaxValue+1u - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - () - - [] - member this.Checkeduint64() = - - // char - let charuint64 = Operators.Checked.uint64 '0' - Assert.AreEqual(charuint64,(uint64)48) - - // float - let floatuint64 = Operators.Checked.uint64 10.0 - Assert.AreEqual(floatuint64,(uint64)(10)) - - // boundary value - let bounduint64 = Operators.Checked.uint64 429496729.0 - Assert.AreEqual(bounduint64, (uint64)(429496729)) - - // overflow exception - try - let overflowuint64 = Operators.Checked.uint64 System.UInt64.MaxValue+1UL - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - () - - [] - member this.Checkedunativeint() = - - // char - let charunativeint = Operators.Checked.unativeint '0' - Assert.AreEqual(charunativeint,(unativeint)48) - - // float - let floatunativeint = Operators.Checked.unativeint 10.0 - Assert.AreEqual(floatunativeint,(unativeint)10) - - // boundary value - let boundunativeint = Operators.Checked.unativeint 65353.0 - Assert.AreEqual(boundunativeint, (unativeint)65353) - - // overflow exception - try - let overflowuint64 = Operators.Checked.uint64 System.UInt64.MaxValue+1UL - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") - () - - [] - member this.KeyValue() = - - let funcKeyValue x = match x with | Operators.KeyValue(a) -> a // string int let stringint = funcKeyValue ( new System.Collections.Generic.KeyValuePair("string",1)) - Assert.AreEqual(stringint,("string",1)) + Assert.AreEqual(("string",1), stringint) // float char let floatchar = funcKeyValue ( new System.Collections.Generic.KeyValuePair(1.0,'a')) - Assert.AreEqual(floatchar,(1.0,'a')) + Assert.AreEqual((1.0,'a'), floatchar) // null let nullresult = funcKeyValue ( new System.Collections.Generic.KeyValuePair(null,' ')) let (nullstring:string,blankchar:char) = nullresult CheckThrowsNullRefException(fun () -> nullstring.ToString() |> ignore) - - - () - + [] - member this.OptimizedRangesGetArraySlice() = + member _.OptimizedRangesGetArraySlice() = - let param1 = Some(1) let param2 = Some(2) // int let intslice = Operators.OperatorIntrinsics.GetArraySlice [|1;2;3;4;5;6|] param1 param2 - Assert.AreEqual(intslice,[|2;3|]) + Assert.AreEqual([|2;3|], intslice) // string let stringslice = Operators.OperatorIntrinsics.GetArraySlice [|"1";"2";"3"|] param1 param2 - Assert.AreEqual(stringslice,[|"2";"3"|]) + Assert.AreEqual([|"2";"3"|], stringslice) // null let stringslice = Operators.OperatorIntrinsics.GetArraySlice [|null;null;null|] param1 param2 - Assert.AreEqual(stringslice,[|null;null|]) - - () - + Assert.AreEqual([|null;null|], stringslice) + [] - member this.OptimizedRangesGetArraySlice2D() = + member _.OptimizedRangesGetArraySlice2D() = - let param1D1 = Some(0) let param1D2 = Some(1) let param2D1 = Some(0) @@ -366,36 +63,32 @@ type OperatorsModule1() = let intArray2D = Array2D.init 2 3 (fun i j -> i*100+j) let intslice = Operators.OperatorIntrinsics.GetArraySlice2D intArray2D param1D1 param1D2 param2D1 param2D2 - Assert.AreEqual(intslice.[1,1],101) + Assert.AreEqual(101, intslice.[1,1]) // string let stringArray2D = Array2D.init 2 3 (fun i j -> (i*100+j).ToString()) let stringslice = Operators.OperatorIntrinsics.GetArraySlice2D stringArray2D param1D1 param1D2 param2D1 param2D2 - Assert.AreEqual(stringslice.[1,1],(101).ToString()) + Assert.AreEqual((101).ToString(), stringslice.[1,1]) // null let nullArray2D = Array2D.init 2 3 (fun i j -> null) let nullslice = Operators.OperatorIntrinsics.GetArraySlice2D nullArray2D param1D1 param1D2 param2D1 param2D2 - Assert.AreEqual(nullslice.[1,1],null) - - () - + Assert.AreEqual(null, nullslice.[1,1]) + [] - member this.OptimizedRangesGetStringSlice() = + member _.OptimizedRangesGetStringSlice() = let param1 = Some(4) let param2 = Some(6) // string let stringslice = Operators.OperatorIntrinsics.GetStringSlice "abcdefg" param1 param2 - Assert.AreEqual(stringslice,"efg") + Assert.AreEqual("efg", stringslice) // null CheckThrowsNullRefException(fun () -> Operators.OperatorIntrinsics.GetStringSlice null param1 param2 |> ignore) - () - - + [] - member this.OptimizedRangesSetArraySlice() = + member _.OptimizedRangesSetArraySlice() = let param1 = Some(1) let param2 = Some(2) @@ -403,23 +96,22 @@ type OperatorsModule1() = let intArray1 = [|1;2;3|] let intArray2 = [|4;5;6|] Operators.OperatorIntrinsics.SetArraySlice intArray1 param1 param2 intArray2 - Assert.AreEqual(intArray1,[|1;4;5|]) + Assert.AreEqual([|1;4;5|], intArray1) // string let stringArray1 = [|"1";"2";"3"|] let stringArray2 = [|"4";"5";"6"|] Operators.OperatorIntrinsics.SetArraySlice stringArray1 param1 param2 stringArray2 - Assert.AreEqual(stringArray1,[|"1";"4";"5"|]) + Assert.AreEqual([|"1";"4";"5"|], stringArray1) // null let nullArray1 = [|null;null;null|] let nullArray2 = [|null;null;null|] Operators.OperatorIntrinsics.SetArraySlice nullArray1 param1 param2 nullArray2 CheckThrowsNullRefException(fun () -> nullArray1.[0].ToString() |> ignore) - () - + [] - member this.OptimizedRangesSetArraySlice2D() = + member _.OptimizedRangesSetArraySlice2D() = let param1D1 = Some(0) let param1D2 = Some(1) let param2D1 = Some(0) @@ -429,43 +121,40 @@ type OperatorsModule1() = let intArray1 = Array2D.init 2 3 (fun i j -> i*10+j) let intArray2 = Array2D.init 2 3 (fun i j -> i*100+j) Operators.OperatorIntrinsics.SetArraySlice2D intArray1 param1D1 param1D2 param2D1 param2D2 intArray2 - Assert.AreEqual(intArray1.[1,1],101) + Assert.AreEqual(101, intArray1.[1,1]) // string let stringArray2D1 = Array2D.init 2 3 (fun i j -> (i*10+j).ToString()) let stringArray2D2 = Array2D.init 2 3 (fun i j -> (i*100+j).ToString()) Operators.OperatorIntrinsics.SetArraySlice2D stringArray2D1 param1D1 param1D2 param2D1 param2D2 stringArray2D2 - Assert.AreEqual(stringArray2D1.[1,1],(101).ToString()) + Assert.AreEqual((101).ToString(), stringArray2D1.[1,1]) // null let nullArray2D1 = Array2D.init 2 3 (fun i j -> null) let nullArray2D2 = Array2D.init 2 3 (fun i j -> null) Operators.OperatorIntrinsics.SetArraySlice2D nullArray2D1 param1D1 param1D2 param2D1 param2D2 nullArray2D2 CheckThrowsNullRefException(fun () -> nullArray2D1.[0,0].ToString() |> ignore) - () - + [] - member this.OptimizedRangesSetArraySlice3D() = + member _.OptimizedRangesSetArraySlice3D() = let intArray1 = Array3D.init 2 3 4 (fun i j k -> i*10+j) let intArray2 = Array3D.init 2 3 4 (fun i j k -> i*100+j) Operators.OperatorIntrinsics.SetArraySlice3D intArray1 (Some 0) (Some 1) (Some 0) (Some 1) (Some 0) (Some 1) intArray2 - Assert.AreEqual(intArray1.[1,1,1],101) - () + Assert.AreEqual(101, intArray1.[1,1,1]) [] - member this.OptimizedRangesSetArraySlice4D() = + member _.OptimizedRangesSetArraySlice4D() = let intArray1 = Array4D.init 2 3 4 5 (fun i j k l -> i*10+j) let intArray2 = Array4D.init 2 3 4 5 (fun i j k l -> i*100+j) - Operators.OperatorIntrinsics.SetArraySlice4D intArray1 (Some 0) (Some 1) (Some 0) (Some 1) (Some 0) (Some 1) intArray2 - Assert.AreEqual(intArray1.[1,1,1,1],101) - () - + Operators.OperatorIntrinsics.SetArraySlice4D intArray1 (Some 0) (Some 1) (Some 0) (Some 1) (Some 0) (Some 1) (Some 0) (Some 1) intArray2 + Assert.AreEqual(101, intArray1.[1,1,1,1]) + [] - member this.Uncheckeddefaultof () = + member _.Uncheckeddefaultof () = // int let intdefault = Operators.Unchecked.defaultof - Assert.AreEqual(intdefault, 0) + Assert.AreEqual(0, intdefault) // string let stringdefault = Operators.Unchecked.defaultof @@ -473,433 +162,419 @@ type OperatorsModule1() = // null let structdefault = Operators.Unchecked.defaultof - Assert.AreEqual( structdefault.Day,1) - - () - + Assert.AreEqual(1, structdefault.Day) + [] - member this.abs () = + member _.abs () = // int let intabs = Operators.abs (-7) - Assert.AreEqual(intabs, 7) + Assert.AreEqual(7, intabs) - // float + // float let floatabs = Operators.abs (-100.0) - Assert.AreEqual(floatabs, 100.0) + Assert.AreEqual(100.0, floatabs) // decimal let decimalabs = Operators.abs (-1000M) - Assert.AreEqual(decimalabs, 1000M) - - () - + Assert.AreEqual(1000M, decimalabs) + [] - member this.acos () = + member _.acos () = // min value let minacos = Operators.acos (0.0) - Assert.AreEqual(minacos, 1.5707963267948966) + Assert.AreNearEqual(1.5707963267948966, minacos) // normal value let normalacos = Operators.acos (0.3) - Assert.AreEqual(normalacos, 1.2661036727794992) + Assert.AreNearEqual(1.2661036727794992, normalacos) // max value let maxacos = Operators.acos (1.0) - Assert.AreEqual(maxacos, 0.0) - () - + Assert.AreEqual(0.0, maxacos) + [] - member this.asin () = + member _.asin () = // min value let minasin = Operators.asin (0.0) - Assert.AreEqual(minasin, 0) + Assert.AreEqual(0.0, minasin) // normal value let normalasin = Operators.asin (0.5) - Assert.AreEqual(normalasin, 0.52359877559829893) + Assert.AreNearEqual(0.52359877559829893, normalasin) // max value let maxasin = Operators.asin (1.0) - Assert.AreEqual(maxasin, 1.5707963267948966) - () - - - + Assert.AreNearEqual(1.5707963267948966, maxasin) + [] - member this.atan () = + member _.atan () = // min value let minatan = Operators.atan (0.0) - Assert.AreEqual(minatan, 0) + Assert.AreNearEqual(0.0, minatan) // normal value let normalatan = Operators.atan (1.0) - Assert.AreEqual(normalatan, 0.78539816339744828) + Assert.AreNearEqual(0.78539816339744828, normalatan) // biggish value let maxatan = Operators.atan (infinity) - Assert.AreEqual(maxatan, 1.5707963267948966) - () - + Assert.AreNearEqual(1.5707963267948966, maxatan) + [] - member this.atan2 () = + member _.atan2 () = // min value let minatan2 = Operators.atan2 (0.0) (1.0) - Assert.AreEqual(minatan2, 0) + Assert.AreNearEqual(0.0, minatan2) // normal value let normalatan2 = Operators.atan2 (1.0) (1.0) - Assert.AreEqual(normalatan2, 0.78539816339744828) + Assert.AreNearEqual(0.78539816339744828, normalatan2) // biggish value let maxatan2 = Operators.atan2 (1.0) (0.0) - Assert.AreEqual(maxatan2, 1.5707963267948966) - () - + Assert.AreNearEqual(1.5707963267948966, maxatan2) + [] - member this.box () = + member _.box () = // int value let intbox = Operators.box 1 - Assert.AreEqual(intbox, 1) + Assert.AreEqual(1, intbox) // string value let stringlbox = Operators.box "string" - Assert.AreEqual(stringlbox, "string") + Assert.AreEqual("string", stringlbox) // null value let nullbox = Operators.box null CheckThrowsNullRefException(fun () -> nullbox.ToString() |> ignore) - () - + [] - member this.byte() = - // int type + member _.byte() = + // int type let intByte = Operators.byte 100 - Assert.AreEqual(intByte,(byte)100) + Assert.AreEqual(100uy, intByte) - // char type + // char type let charByte = Operators.byte '0' - Assert.AreEqual(charByte,(byte)48) + Assert.AreEqual(48uy, charByte) // boundary value let boundByte = Operators.byte 255.0 - Assert.AreEqual(boundByte, (byte)255) + Assert.AreEqual(255uy, boundByte) - // overflow exception - try - let overflowbyte = Operators.byte (System.Int64.MaxValue*(int64)2) - Assert.Fail("Expectt overflow exception but not.") - with - | :? System.OverflowException -> () - | _ -> Assert.Fail("Expectt overflow exception but not.") + // Overflow + let result = Operators.byte Int64.MaxValue + Assert.AreEqual(Byte.MaxValue, result) + + // Overflow + let result = Operators.byte Int64.MinValue + Assert.AreEqual(0uy, result) + + // Overflow + let result = Operators.byte Double.MinValue + Assert.AreEqual(0uy, result) + + // Overflow + let result = Operators.byte Double.MaxValue + Assert.AreEqual(0uy, result) + + // Overflow + let result = Operators.byte (Int64.MaxValue * 8L) + Assert.AreEqual(248uy, result) // bit-complement + + // Overflow + let result = 255uy + 5uy + Assert.AreEqual(4uy, result) + + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.byte Decimal.MinValue |> ignore) [] - member this.ceil() = - // min value + member _.ceil() = + // min value let minceil = Operators.ceil 0.1 - Assert.AreEqual(minceil,1.0) + Assert.AreEqual(1.0, minceil) - // normal value + // normal value let normalceil = Operators.ceil 100.0 - Assert.AreEqual(normalceil,100.0) + Assert.AreEqual(100.0, normalceil) // max value let maxceil = Operators.ceil 1.7E+308 - Assert.AreEqual(maxceil, 1.7E+308) + Assert.AreEqual(1.7E+308, maxceil) [] - member this.char() = - // int type + member _.char() = + // int type let intchar = Operators.char 48 - Assert.AreEqual(intchar,'0') + Assert.AreEqual('0', intchar) - // string type + // string type let stringchar = Operators.char " " - Assert.AreEqual(stringchar, ' ') + Assert.AreEqual(' ', stringchar) [] - member this.compare() = - // int type + member _.compare() = + // int type let intcompare = Operators.compare 100 101 - Assert.AreEqual(intcompare,-1) + Assert.AreEqual(-1, intcompare) - // char type + // char type let charcompare = Operators.compare '0' '1' - Assert.AreEqual(charcompare,-1) + Assert.AreEqual(-1, charcompare) // null value let boundcompare = Operators.compare null null - Assert.AreEqual(boundcompare, 0) - - + Assert.AreEqual(0, boundcompare) + [] - member this.cos () = + member _.cos () = // min value let mincos = Operators.cos (0.0) - Assert.AreEqual(mincos, 1) + Assert.AreEqual(1.0, mincos) // normal value let normalcos = Operators.cos (1.0) - Assert.AreEqual(normalcos, 0.54030230586813977) + Assert.AreNearEqual(0.54030230586813977, normalcos) // biggish value let maxcos = Operators.cos (1.57) - Assert.AreEqual(maxcos, 0.00079632671073326335) - () - + Assert.AreNearEqual(0.00079632671073326335, maxcos) + [] - member this.cosh () = - + member _.cosh () = + // min value let mincosh = Operators.cosh (0.0) - Assert.AreEqual(mincosh, 1.0) + Assert.AreEqual(1.0, mincosh) // normal value let normalcosh = Operators.cosh (1.0) - Assert.AreEqual(normalcosh, 1.5430806348152437) + Assert.AreNearEqual(1.5430806348152437, normalcosh) // biggish value let maxcosh = Operators.cosh (1.57) - Assert.AreEqual(maxcosh, 2.5073466880660993) - - - () - - - + Assert.AreNearEqual(2.5073466880660993, maxcosh) + [] - member this.decimal () = + member _.decimal () = // int value let mindecimal = Operators.decimal (1) - Assert.AreEqual(mindecimal, 1) + Assert.AreEqual(1M, mindecimal) // float value let maxdecimal = Operators.decimal (1.0) - Assert.AreEqual(maxdecimal, 1) - () - + Assert.AreEqual(1M, maxdecimal) + [] - member this.decr() = - // zero + member _.decr() = + // zero let zeroref = ref 0 Operators.decr zeroref - Assert.AreEqual(zeroref,(ref -1)) + Assert.AreEqual((ref -1), zeroref) // big number let bigref = ref 32767 Operators.decr bigref - Assert.AreEqual(bigref,(ref 32766)) + Assert.AreEqual((ref 32766), bigref) // normal value let normalref = ref 100 Operators.decr (normalref) - Assert.AreEqual(normalref,(ref 99)) + Assert.AreEqual((ref 99), normalref) [] - member this.defaultArg() = - // zero + member _.defaultArg() = + // zero let zeroOption = Some(0) let intdefaultArg = Operators.defaultArg zeroOption 2 - Assert.AreEqual(intdefaultArg,0) + Assert.AreEqual(0, intdefaultArg) // big number let bigOption = Some(32767) let bigdefaultArg = Operators.defaultArg bigOption 32766 - Assert.AreEqual(bigdefaultArg,32767) + Assert.AreEqual(32767, bigdefaultArg) // normal value let normalOption = Some(100) let normalfaultArg = Operators.defaultArg normalOption 100 - Assert.AreEqual(normalfaultArg, 100) + Assert.AreEqual(100, normalfaultArg) [] - member this.double() = - // int type - let intdouble = Operators.double 100 - Assert.AreEqual(intdouble,100.0) + member _.double() = + // int type + let intdouble = Operators.float 100 + Assert.AreEqual(100.0, intdouble) - // char type - let chardouble = Operators.double '0' - Assert.AreEqual(chardouble,48) - () - + // char type + let chardouble = Operators.float '0' + Assert.AreEqual(48.0, chardouble) + [] - member this.enum() = - // zero + member _.enum() = + // zero let intarg : int32 = 0 let intenum = Operators.enum intarg - Assert.AreEqual(intenum,System.ConsoleColor.Black) + Assert.AreEqual(System.ConsoleColor.Black, intenum) // big number let bigarg : int32 = 15 let charenum = Operators.enum bigarg - Assert.AreEqual(charenum,System.ConsoleColor.White) + Assert.AreEqual(System.ConsoleColor.White, charenum) // normal value let normalarg : int32 = 9 let boundenum = Operators.enum normalarg - Assert.AreEqual(boundenum, System.ConsoleColor.Blue) + Assert.AreEqual(System.ConsoleColor.Blue, boundenum) #if IGNORED [] - member this.exit() = - // zero - try + member _.exit() = + // zero + try let intexit = Operators.exit 1 - () + with | _ -> () - //Assert.AreEqual(intexit,-1) + //Assert.AreEqual(-1, intexit) // big number let charexit = Operators.exit 32767 - //Assert.AreEqual(charexit,-1) + //Assert.AreEqual(-1, charexit) // normal value let boundexit = Operators.exit 100 - Assert.AreEqual(boundexit, 0) + Assert.AreEqual(0, boundexit) #endif [] - member this.exp() = - // zero + member _.exp() = + // zero let zeroexp = Operators.exp 0.0 - Assert.AreEqual(zeroexp,1.0) + Assert.AreEqual(1.0, zeroexp) // big number let bigexp = Operators.exp 32767.0 - Assert.AreEqual(bigexp,infinity) + Assert.AreEqual(infinity, bigexp) // normal value let normalexp = Operators.exp 100.0 - Assert.AreEqual(normalexp, 2.6881171418161356E+43) + Assert.AreEqual(2.6881171418161356E+43, normalexp) [] - member this.failwith() = - try + member _.failwith() = + try let _ = Operators.failwith "failwith" Assert.Fail("Expect fail but not.") - () + with | Failure("failwith") -> () |_ -> Assert.Fail("Throw unexpected exception") - - + [] - member this.float() = - // int type + member _.float() = + // int type let intfloat = Operators.float 100 - Assert.AreEqual(intfloat,(float)100) + Assert.AreEqual((float)100, intfloat) - // char type + // char type let charfloat = Operators.float '0' - Assert.AreEqual(charfloat,(float)48) - - () - - + Assert.AreEqual((float)48, charfloat) + [] - member this.float32() = - // int type + member _.float32() = + // int type let intfloat32 = Operators.float32 100 - Assert.AreEqual(intfloat32,(float32)100) + Assert.AreEqual((float32)100, intfloat32) - // char type + // char type let charfloat32 = Operators.float32 '0' - Assert.AreEqual(charfloat32,(float32)48) - - () - - + Assert.AreEqual((float32)48, charfloat32) + [] - member this.floor() = - // float type - let intfloor = Operators.floor 100.0 - Assert.AreEqual(intfloor,100) + member _.floor() = + // float type + let intfloor = Operators.floor 100.9 + Assert.AreEqual(100.0, intfloor) - // float32 type - let charfloor = Operators.floor ((float32)100.0) - Assert.AreEqual(charfloor,100) + // float32 type + let charfloor = Operators.floor ((float32)100.9) + Assert.AreEqual(100.0f, charfloor) [] - member this.fst() = - // int type + member _.fst() = + // int type let intfst = Operators.fst (100,101) - Assert.AreEqual(intfst,100) + Assert.AreEqual(100, intfst) - // char type + // char type let charfst = Operators.fst ('0','1') - Assert.AreEqual(charfst,'0') + Assert.AreEqual('0', charfst) // null value let boundfst = Operators.fst (null,null) - Assert.AreEqual(boundfst, null) + Assert.AreEqual(null, boundfst) [] - member this.hash() = - // int type + member _.hash() = + // int type (stable between JIT versions) let inthash = Operators.hash 100 - Assert.AreEqual(inthash,100) + Assert.AreEqual(100, inthash) - // char type + // char type (stable between JIT versions) let charhash = Operators.hash '0' - Assert.AreEqual(charhash,3145776) + Assert.AreEqual(3145776, charhash) - // string value - let boundhash = Operators.hash "A" - Assert.AreEqual(boundhash, -842352673) + // string value (test disabled, each JIT and each x86 vs x64 creates a different hash here) + //let boundhash = Operators.hash "A" + //Assert.AreEqual(-842352673, boundhash) [] - member this.id() = - // int type + member _.id() = + // int type let intid = Operators.id 100 - Assert.AreEqual(intid,100) + Assert.AreEqual(100, intid) - // char type + // char type let charid = Operators.id '0' - Assert.AreEqual(charid,'0') + Assert.AreEqual('0', charid) // string value let boundid = Operators.id "A" - Assert.AreEqual(boundid, "A") - - + Assert.AreEqual("A", boundid) + [] - member this.ignore() = - // value type + member _.ignore() = + // value type let result = Operators.ignore 10 - Assert.AreEqual(result,null) + Assert.AreEqual(null, result) // reference type let result = Operators.ignore "A" - Assert.AreEqual(result,null) - - () + Assert.AreEqual(null, result) -#if IGNORED - [] - member this.incr() = - // legit value + [] + member _.incr() = + // legit value let result = ref 10 Operators.incr result - Assert.AreEqual(!result,11) + Assert.AreEqual(11, !result) - // overflow + // Overflow. let result = ref (Operators.Checked.int System.Int32.MaxValue) - CheckThrowsOverflowException(fun() -> Operators.incr result |> ignore) - - () -#endif + Operators.incr result + Assert.AreEqual(System.Int32.MinValue, !result) [] - member this.infinity() = + member _.infinity() = let inf = Operators.infinity let result = inf > System.Double.MaxValue @@ -907,21 +582,18 @@ type OperatorsModule1() = // arithmetic operation let result = infinity + 3.0 - Assert.AreEqual(result,infinity) + Assert.AreEqual(Double.PositiveInfinity, result) let result = infinity - 3.0 - Assert.AreEqual(result,infinity) + Assert.AreEqual(Double.PositiveInfinity, result) let result = infinity * 3.0 - Assert.AreEqual(result,infinity) + Assert.AreEqual(Double.PositiveInfinity, result) let result = infinity / 3.0 - Assert.AreEqual(result,infinity) + Assert.AreEqual(Double.PositiveInfinity, result) let result = infinity / 3.0 - Assert.AreEqual(result,infinity) - - - () - + Assert.AreEqual(Double.PositiveInfinity, result) + [] - member this.infinityf() = + member _.infinityf() = let inf = Operators.infinityf let result = inf > System.Single.MaxValue @@ -929,16 +601,12 @@ type OperatorsModule1() = // arithmetic operation let result = infinityf + 3.0f - Assert.AreEqual(result,infinity) + Assert.AreEqual(Single.PositiveInfinity, result) let result = infinityf - 3.0f - Assert.AreEqual(result,infinity) + Assert.AreEqual(Single.PositiveInfinity, result) let result = infinityf * 3.0f - Assert.AreEqual(result,infinity) + Assert.AreEqual(Single.PositiveInfinity, result) let result = infinityf / 3.0f - Assert.AreEqual(result,infinity) + Assert.AreEqual(Single.PositiveInfinity, result) let result = infinityf / 3.0f - Assert.AreEqual(result,infinityf) - - () - - \ No newline at end of file + Assert.AreEqual(Single.PositiveInfinity, result) diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModule2.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModule2.fs index 8ac0ebfd9c6..da874ebe118 100644 --- a/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModule2.fs +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModule2.fs @@ -3,138 +3,194 @@ // Various tests for the: // Microsoft.FSharp.Core.Operators module -namespace SystematicUnitTests.FSharp_Core.Microsoft_FSharp_Core +namespace FSharp.Core.UnitTests.Operators open System -open SystematicUnitTests.LibraryTestFx +open FSharp.Core.UnitTests.LibraryTestFx open NUnit.Framework -open Microsoft.FSharp.Core.Operators.Checked [] type OperatorsModule2() = -#if IGNORED - [] - member this.int() = - // int + [] + member _.int() = + // int let result = Operators.int 10 - Assert.AreEqual(result,10) + Assert.AreEqual(10, result) // string let result = Operators.int "10" - Assert.AreEqual(result,10) + Assert.AreEqual(10, result) // double let result = Operators.int 10.0 - Assert.AreEqual(result,10) + Assert.AreEqual(10, result) // negative let result = Operators.int -10 - Assert.AreEqual(result,-10) + Assert.AreEqual(-10, result) // zero let result = Operators.int 0 - Assert.AreEqual(result,0) + Assert.AreEqual(0, result) - // overflow - CheckThrowsOverflowException(fun() -> Operators.int System.Double.MaxValue |>ignore) + // Overflow + let result = Operators.int Double.MaxValue + Assert.AreEqual(Int32.MinValue, result) - () -#endif + // Overflow + let result = Operators.int Double.MinValue + Assert.AreEqual(Int32.MinValue, result) + + // Overflow + let result = Operators.int Int64.MaxValue + Assert.AreEqual(-1, result) + + // Overflow + let result = Operators.int Int64.MinValue + Assert.AreEqual(0, result) + + // Overflow + let result = Int32.MaxValue + 1 + Assert.AreEqual(Int32.MinValue, result) -#if IGNORED - [] - member this.int16() = - // int + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.int Decimal.MinValue |> ignore) + + [] + member _.int16() = + // int let result = Operators.int16 10 - Assert.AreEqual(result,10) + Assert.AreEqual(10s, result) // double let result = Operators.int16 10.0 - Assert.AreEqual(result,10) + Assert.AreEqual(10s, result) // negative let result = Operators.int16 -10 - Assert.AreEqual(result,-10) + Assert.AreEqual(-10s, result) // zero let result = Operators.int16 0 - Assert.AreEqual(result,0) + Assert.AreEqual(0s, result) // string let result = Operators.int16 "10" - Assert.AreEqual(result,10) + Assert.AreEqual(10s, result) - // overflow - CheckThrowsOverflowException(fun() -> Operators.int16 System.Double.MaxValue |>ignore) - () -#endif - -#if IGNORED - [] - member this.int32() = - // int + // Overflow + let result = Operators.int16 Double.MaxValue + Assert.AreEqual(0s, result) + + // Overflow + let result = Operators.int16 Double.MinValue + Assert.AreEqual(0s, result) + + let result = Operators.int16 Int64.MaxValue + Assert.AreEqual(-1s, result) + + // Overflow + let result = Operators.int16 Int64.MinValue + Assert.AreEqual(0s, result) + + // Overflow + let result = Int16.MaxValue + 1s + Assert.AreEqual(Int16.MinValue, result) + + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.int16 Decimal.MinValue |> ignore) + + [] + member _.int32() = + // int let result = Operators.int32 10 - Assert.AreEqual(result,10) + Assert.AreEqual(10, result) // double let result = Operators.int32 10.0 - Assert.AreEqual(result,10) + Assert.AreEqual(10, result) // negative let result = Operators.int32 -10 - Assert.AreEqual(result,-10) + Assert.AreEqual(-10, result) // zero let result = Operators.int32 0 - Assert.AreEqual(result,0) + Assert.AreEqual(0, result) // string let result = Operators.int32 "10" - Assert.AreEqual(result,10) + Assert.AreEqual(10, result) - // overflow - CheckThrowsOverflowException(fun() -> Operators.int32 System.Double.MaxValue |>ignore) - () -#endif - -#if IGNORED - [] - member this.int64() = - // int + // Overflow + let result = Operators.int32 Double.MaxValue + Assert.AreEqual(Int32.MinValue, result) + + // Overflow + let result = Operators.int32 Double.MinValue + Assert.AreEqual(Int32.MinValue, result) + + // Overflow + let result = Operators.int32 Int64.MaxValue + Assert.AreEqual(-1, result) + + // Overflow + let result = Operators.int32 Int64.MinValue + Assert.AreEqual(0, result) + + // Overflow + let result = Int32.MaxValue + 5 + Assert.AreEqual(Int32.MinValue + 4, result) + + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.int32 Decimal.MinValue |> ignore) + + [] + member _.int64() = + // int let result = Operators.int64 10 - Assert.AreEqual(result,10) + Assert.AreEqual(10L, result) // double let result = Operators.int64 10.0 - Assert.AreEqual(result,10) + Assert.AreEqual(10L, result) // negative let result = Operators.int64 -10 - Assert.AreEqual(result,-10) + Assert.AreEqual(-10L, result) // zero let result = Operators.int64 0 - Assert.AreEqual(result,0) + Assert.AreEqual(0L, result) // string let result = Operators.int64 "10" - Assert.AreEqual(result,10) + Assert.AreEqual(10L, result) - // overflow - CheckThrowsOverflowException(fun() -> Operators.int64 System.Double.MaxValue |>ignore) - () -#endif + // Overflow. + let result = Operators.int64 Double.MaxValue + Assert.AreEqual(Int64.MinValue, result) + + // Overflow + let result = Operators.int64 Double.MinValue + Assert.AreEqual(Int64.MinValue, result) + + // Overflow + let result = Operators.int64 UInt64.MaxValue + Assert.AreEqual(-1L, result) + + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.int64 Decimal.MinValue |> ignore) + + [] + member _.invalidArg() = + CheckThrowsArgumentException(fun() -> Operators.invalidArg "A" "B" |>ignore ) -// [] -// member this.invalidArg() = -// CheckThrowsArgumentException(fun() -> Operators.invalidArg "A" "B" |>ignore ) -// -// () [] - member this.lock() = - // lock + member _.lock() = + // lock printfn "test8 started" let syncRoot = System.Object() let k = ref 0 @@ -142,542 +198,753 @@ type OperatorsModule2() = System.Threading.Thread.Sleep(1) !k ) } let arr = Async.RunSynchronously (Async.Parallel(Seq.map comp [1..50])) - Assert.AreEqual((Array.sort compare arr; arr), [|1..50|]) + Assert.AreEqual([|1..50|], Array.sort arr) // without lock let syncRoot = System.Object() let k = ref 0 let comp _ = async { do incr k - do! System.Threading.Thread.AsyncSleep(10) + do! Async.Sleep (10) return !k } let arr = Async.RunSynchronously (Async.Parallel(Seq.map comp [1..100])) - Assert.AreNotEqual ((Array.sort compare arr; arr) , [|1..100|]) - - () + Assert.AreNotEqual ([|1..100|], Array.sort arr) [] - member this.log() = + member _.log() = // double let result = Operators.log 10.0 - Assert.AreEqual(result.ToString(),"2.30258509299405") + Assert.AreEqual(2.3025850929940459, result) // negative let result = Operators.log -10.0 - Assert.AreEqual(result.ToString(),System.Double.NaN.ToString()) + Assert.AreEqual(Double.NaN, result) // zero let result = Operators.log 0.0 - Assert.AreEqual(result,-infinity) - - () + Assert.AreEqual(Double.NegativeInfinity , result) [] - member this.log10() = + member _.log10() = // double let result = Operators.log10 10.0 - Assert.AreEqual(result,1) + Assert.AreEqual(1.0, result) // negative let result = Operators.log10 -10.0 - Assert.AreEqual(result.ToString(),System.Double.NaN.ToString()) + Assert.AreEqual(System.Double.NaN, result) // zero let result = Operators.log10 0.0 - Assert.AreEqual(result,-infinity) - - () + Assert.AreEqual(Double.NegativeInfinity, result) [] - member this.max() = + member _.max() = // value type let result = Operators.max 10 8 - Assert.AreEqual(result,10) + Assert.AreEqual(10, result) // negative let result = Operators.max -10.0 -8.0 - Assert.AreEqual(result,-8.0) + Assert.AreEqual(-8.0, result) // zero let result = Operators.max 0 0 - Assert.AreEqual(result,0) + Assert.AreEqual(0, result) // reference type let result = Operators.max "A" "ABC" - Assert.AreEqual(result,"ABC") - - // overflow - CheckThrowsOverflowException(fun() -> Operators.max 10 System.Int32.MaxValue+1 |>ignore) - - () + Assert.AreEqual("ABC", result) [] - member this.min() = + member _.min() = // value type let result = Operators.min 10 8 - Assert.AreEqual(result,8) + Assert.AreEqual(8, result) // negative let result = Operators.min -10.0 -8.0 - Assert.AreEqual(result,-10.0) + Assert.AreEqual(-10.0, result) // zero let result = Operators.min 0 0 - Assert.AreEqual(result,0) + Assert.AreEqual(0, result) // reference type let result = Operators.min "A" "ABC" - Assert.AreEqual(result,"A") - - // overflow - CheckThrowsOverflowException(fun() -> Operators.min 10 System.Int32.MinValue - 1 |>ignore) - - () + Assert.AreEqual("A", result) [] - member this.nan() = + member _.nan() = // value type - let result = Operators.nan - Assert.AreEqual(result.ToString(),System.Double.NaN.ToString()) - - () + let result = Operators.nan + Assert.AreEqual(System.Double.NaN, nan) [] - member this.nanf() = + member _.nanf() = // value type - let result = Operators.nanf - Assert.AreEqual(result,System.Single.NaN) + let result = Operators.nanf + Assert.AreEqual(System.Single.NaN, result) - () - -#if IGNORED - [] - member this.nativeint() = - // int + [] + member _.nativeint() = + // int let result = Operators.nativeint 10 - Assert.AreEqual(result,10n) + Assert.AreEqual(10n, result) // double let result = Operators.nativeint 10.0 - Assert.AreEqual(result,10n) + Assert.AreEqual(10n, result) // int64 let result = Operators.nativeint 10L - Assert.AreEqual(result,10n) + Assert.AreEqual(10n, result) // negative let result = Operators.nativeint -10 - Assert.AreEqual(result,-10n) + Assert.AreEqual(-10n, result) // zero let result = Operators.nativeint 0 - Assert.AreEqual(result,0n) - - // overflow - CheckThrowsOverflowException(fun() -> Operators.nativeint System.Double.MaxValue |>ignore) - - () -#endif + Assert.AreEqual(0n, result) + + // Overflow Double.MaxValue is equal on 32 bits and 64 bits runtimes + let result = Operators.nativeint Double.MaxValue + if Info.isX86Runtime then + Assert.AreEqual(-2147483648n, result) + else + // Cannot use -9223372036854775808, compiler doesn't allow it, see https://github.com/dotnet/fsharp/issues/9524 + Assert.AreEqual(-9223372036854775807n - 1n, result) + + // Overflow (depends on pointer size) + let result = Operators.nativeint Double.MinValue + if Info.isX86Runtime then + Assert.AreEqual(-2147483648n, result) + else + // Cannot use -9223372036854775808, compiler doesn't allow it, see https://github.com/dotnet/fsharp/issues/9524 + Assert.AreEqual(-9223372036854775807n - 1n, result) + + // Overflow (depends on pointer size) + let result = Operators.nativeint Int64.MinValue + if Info.isX86Runtime then + Assert.AreEqual(0n, result) + else + // Cannot use -9223372036854775808, compiler doesn't allow it, see https://github.com/dotnet/fsharp/issues/9524 + Assert.AreEqual(-9223372036854775807n - 1n, result) + + // Overflow (depends on pointer size) + if Info.isX86Runtime then + let result = nativeint Int32.MaxValue + 5n + Assert.AreEqual(-2147483644n, result) + else + let result = nativeint Int64.MaxValue + 5n + Assert.AreEqual(-9223372036854775804n, result) + + + // Overflow (depends on pointer size) + let result = Operators.nativeint System.Double.MaxValue + if Info.isX86Runtime then + Assert.AreEqual(-2147483648n, result) + else + // Cannot express this as a literal, see https://github.com/dotnet/fsharp/issues/9524 + Assert.AreEqual("-9223372036854775808", string result) + + let result = Operators.nativeint System.Double.MinValue + if Info.isX86Runtime then + Assert.AreEqual(-2147483648n, result) + else + // Cannot express this as a literal, see https://github.com/dotnet/fsharp/issues/9524 + Assert.AreEqual("-9223372036854775808", string result) [] - member this.not() = + member _.not() = let result = Operators.not true Assert.IsFalse(result) let result = Operators.not false - Assert.IsTrue(result) - - () + Assert.IsTrue(result) -// [] -// member this.nullArg() = -// CheckThrowsArgumentNullException(fun() -> Operators.nullArg "A" |> ignore) -// -// () + [] + member _.nullArg() = + CheckThrowsArgumentNullException(fun() -> Operators.nullArg "A" |> ignore) + [] - member this.pown() = - // int + member _.pown() = + // int let result = Operators.pown 10 2 - Assert.AreEqual(result,100) + Assert.AreEqual(100, result) // double let result = Operators.pown 10.0 2 - Assert.AreEqual(result,100) + Assert.AreEqual(100.0, result) // int64 let result = Operators.pown 10L 2 - Assert.AreEqual(result,100) + Assert.AreEqual(100L, result) // decimal let result = Operators.pown 10M 2 - Assert.AreEqual(result,100) + Assert.AreEqual(100M, result) // negative let result = Operators.pown -10 2 - Assert.AreEqual(result,100) + Assert.AreEqual(100, result) // zero let result = Operators.pown 0 2 - Assert.AreEqual(result,0) + Assert.AreEqual(0, result) // overflow let result = Operators.pown System.Double.MaxValue System.Int32.MaxValue - Assert.AreEqual(result,infinity) + Assert.AreEqual(Double.PositiveInfinity, result) CheckThrowsOverflowException(fun() -> Operators.pown System.Int32.MaxValue System.Int32.MaxValue |>ignore) - () - [] - member this.raise() = + member _.raise() = CheckThrowsArgumentException(fun()-> Operators.raise <| new ArgumentException("Invalid Argument ") |> ignore) - - () [] - member this.ref() = + member _.ref() = // value type - let result = Operators.ref 0 - let funInt (x:int) = - result := !result + x - () - Array.iter funInt [|1..10|] + let result = Operators.ref 0 + let funInt (x:int) = + result := !result + x + () + Array.iter funInt [|1..10|] Assert.AreEqual(!result,55) // reference type let result = Operators.ref "" let funStr (x : string) = - result := (!result) + x + result := (!result) + x () Array.iter funStr [|"A";"B";"C";"D"|] Assert.AreEqual(!result,"ABCD") - () - [] - member this.reraise() = - // double + member _.reraise() = + // nothing to reraise should not trigger exception try () with | _ -> Operators.reraise() - () - [] - member this.round() = + member _.round() = // double let result = Operators.round 10.0 - Assert.AreEqual(result,10) + Assert.AreEqual(10.0, result) + + // double + let result = Operators.round 0.6640367702678489 + Assert.AreEqual(1.0, result) + + // double + let result = Operators.round 0.6640367702678489e4 + Assert.AreEqual(6640.0, result) + + // double, show half-to-even + let result = Operators.round 0.6640500000e4 + Assert.AreEqual(6640.0, result) + + // double, show half-to-even + let result = Operators.round 0.6639500000e4 + Assert.AreEqual(6640.0, result) + + // double, show half-to-even + let result = Operators.round 0.6641500000e4 + Assert.AreEqual(6642.0, result) + + // double, show rounding up if anything follows '5' + let result = Operators.round 0.66405000001e4 + Assert.AreEqual(6641.0, result) // decimal let result = Operators.round 10M - Assert.AreEqual(result,10) + Assert.AreEqual(10M, result) + + // decimal, show half-to-even + let result = Operators.round 1233.5M + Assert.AreEqual(1234M, result) + + // decimal, show half-to-even + let result = Operators.round 1234.5M + Assert.AreEqual(1234M, result) + + // decimal, show half-to-even + let result = Operators.round 1235.5M + Assert.AreEqual(1236M, result) + + // decimal, show rounding up if anything follows '5' + let result = Operators.round 1234.500000000001M + Assert.AreEqual(1235M, result) + + // decimal, round up + let result = Operators.round 1234.6M + Assert.AreEqual(1235M, result) - () - [] - member this.sbyte() = - // int + member _.sbyte() = + // int let result = Operators.sbyte 10 - Assert.AreEqual(result,10) + Assert.AreEqual(10y, result) // double let result = Operators.sbyte 10.0 - Assert.AreEqual(result,10) + Assert.AreEqual(10y, result) // negative let result = Operators.sbyte -10 - Assert.AreEqual(result,-10) + Assert.AreEqual(-10y, result) // zero let result = Operators.sbyte 0 - Assert.AreEqual(result,0) + Assert.AreEqual(0y, result) + + // Overflow + let result = Operators.sbyte Int64.MaxValue + Assert.AreEqual(-1y, result) + + // Overflow + let result = Operators.sbyte Int64.MinValue + Assert.AreEqual(0y, result) + + // Overflow + let result = Operators.sbyte Double.MinValue + Assert.AreEqual(0y, result) + + // Overflow + let result = Operators.sbyte Double.MaxValue + Assert.AreEqual(0y, result) + + // Overflow + let result = Operators.sbyte (Int64.MaxValue * 8L) + Assert.AreEqual(-8y, result) // bit-complement + + // Overflow + let result = 127y + 1y + Assert.AreEqual(-128y, result) + + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.sbyte Decimal.MinValue |> ignore) - () - [] - member this.sign() = - // int + member _.sign() = + // int let result = Operators.sign 10 - Assert.AreEqual(result,1) + Assert.AreEqual(1, result) + // negative int + let result = Operators.sign -10 + Assert.AreEqual(-1, result) + + // zero int + let result = Operators.sign 0 + Assert.AreEqual(0, result) + // double let result = Operators.sign 10.0 - Assert.AreEqual(result,1) + Assert.AreEqual(1, result) - // negative - let result = Operators.sign -10 - Assert.AreEqual(result,-1) + // double max + let result = Operators.sign Double.MaxValue + Assert.AreEqual(1, result) - // zero - let result = Operators.sign 0 - Assert.AreEqual(result,0) + // double min + let result = Operators.sign Double.MinValue + Assert.AreEqual(-1, result) + + // double epsilon positive + let result = Operators.sign Double.Epsilon + Assert.AreEqual(1, result) + + // double epsilon negative + let result = Operators.sign (-Double.Epsilon) + Assert.AreEqual(-1, result) + + // double inf + let result = Operators.sign Double.PositiveInfinity + Assert.AreEqual(1, result) + + // double -inf + let result = Operators.sign Double.NegativeInfinity + Assert.AreEqual(-1, result) + + // float32 + let result = Operators.sign 10.0f + Assert.AreEqual(1, result) + + // float32 max + let result = Operators.sign Single.MaxValue + Assert.AreEqual(1, result) + + // float32 min + let result = Operators.sign Single.MinValue + Assert.AreEqual(-1, result) + + // float32 epsilon positive + let result = Operators.sign Single.Epsilon + Assert.AreEqual(1, result) + + // float32 epsilon negative + let result = Operators.sign (-Single.Epsilon) + Assert.AreEqual(-1, result) + + // float32 inf + let result = Operators.sign Single.PositiveInfinity + Assert.AreEqual(1, result) + + // float32 -inf + let result = Operators.sign Single.NegativeInfinity + Assert.AreEqual(-1, result) + + // double nan + CheckThrowsArithmeticException(fun () -> Operators.sign Double.NaN |> ignore) + + // float32 nan + CheckThrowsArithmeticException(fun () -> Operators.sign Single.NaN |> ignore) - () - [] - member this.sin() = + member _.sin() = let result = Operators.sin 0.5 - Assert.AreEqual(result.ToString(),"0.479425538604203") + Assert.AreNearEqual(0.479425538604203, result) + + let result = Operators.sin Double.NaN + Assert.AreEqual(Double.NaN, result) + + let result = Operators.sin Double.PositiveInfinity + Assert.AreEqual(Double.NaN, result) + + let result = Operators.sin Double.NegativeInfinity + Assert.AreEqual(Double.NaN, result) - () - [] - member this.single() = - // int - let result = Operators.single 10 - Assert.AreEqual(result,10) + member _.single() = + // int + let result = Operators.float32 10 + Assert.AreEqual(10f, result) // double - let result = Operators.single 10.0 - Assert.AreEqual(result,10) + let result = Operators.float32 10.0 + Assert.AreEqual(10f, result) // string - let result = Operators.single "10" - Assert.AreEqual(result,10) - - () - + let result = Operators.float32 "10" + Assert.AreEqual(10f, result) + [] - member this.sinh() = + member _.sinh() = let result = Operators.sinh 1.0 - Assert.AreEqual(result.ToString(),"1.1752011936438") + Assert.AreNearEqual(1.1752011936438014, result) - () - + let result = Operators.sinh 0.0 + Assert.AreNearEqual(0.0, result) + + let result = Operators.sinh Double.PositiveInfinity + Assert.AreNearEqual(Double.PositiveInfinity, result) + + let result = Operators.sinh Double.NegativeInfinity + Assert.AreNearEqual(Double.NegativeInfinity, result) + + let result = Operators.sinh Double.NaN + Assert.AreNearEqual(Double.NaN, result) + [] - member this.sizeof() = - // value type + member _.sizeof() = + // value type let result = Operators.sizeof - Assert.AreEqual(result,4) + Assert.AreEqual(4, result) - // System.Int64 + // System.Int64 let result = Operators.sizeof - Assert.AreEqual(result,8) + Assert.AreEqual(8, result) - // reference type + // reference type should have the same size as the IntPtr let result = Operators.sizeof - Assert.AreEqual(result,4) + Assert.AreEqual(IntPtr.Size, result) - // null + // null should have the same size as the IntPtr let result = Operators.sizeof - Assert.AreEqual(result,4) + Assert.AreEqual(IntPtr.Size, result) - () - [] - member this.snd() = - // value type + member _.snd() = + // value type let result = Operators.snd ("ABC",100) - Assert.AreEqual(result,100) + Assert.AreEqual(100, result) - // reference type + // reference type let result = Operators.snd (100,"ABC") - Assert.AreEqual(result,"ABC") + Assert.AreEqual("ABC", result) - // null + // null let result = Operators.snd (100,null) - Assert.AreEqual(result,null) + Assert.AreEqual(null, result) - () - [] - member this.sqrt() = - // double + member _.sqrt() = + // double let result = Operators.sqrt 100.0 - Assert.AreEqual(result,10) + Assert.AreEqual(10.0, result) + + let result = Operators.sqrt -2.0 + Assert.AreEqual(Double.NaN, result) - () - [] - member this.stderr() = - let result = Operators.stderr - Assert.AreEqual(result.WriteLine("go"),null) + member _.stderr() = + let result = Operators.stderr + Assert.AreEqual(null, result.WriteLine("go")) - () - [] - member this.stdin() = - let result = Operators.stdin - Assert.AreEqual(result.Dispose(),null) + member _.stdin() = + let result = Operators.stdin + Assert.AreEqual(null, result.Dispose()) - () - [] - member this.stdout() = - let result = Operators.stdout - Assert.AreEqual(result.WriteLine("go"),null) + member _.stdout() = + let result = Operators.stdout + Assert.AreEqual(null, result.WriteLine("go")) - () - [] - member this.string() = + member _.string() = // value type let result = Operators.string 100 - Assert.AreEqual(result,"100") + Assert.AreEqual("100", result) // reference type let result = Operators.string "ABC" - Assert.AreEqual(result,"ABC") + Assert.AreEqual("ABC", result) - // unit - CheckThrowsNullRefException(fun () -> Operators.string null |>ignore) - - () - [] - member this.tan() = + member _.tan() = // double let result = Operators.tan 1.0 - Assert.AreEqual(result.ToString(),"1.5574077246549") + Assert.AreNearEqual(1.5574077246549023, result) - () - [] - member this.tanh() = - // double + member _.tanh() = + // The x86 runtime uses 64 bit precision, whereas the x64 runtime uses SSE instructions with 80 bit precision + // details can be found here: https://github.com/dotnet/fsharp/issues/9522 let result = Operators.tanh 0.8 - Assert.AreEqual(result,0.664036770267849) + Assert.AreNearEqual(0.66403677026784902, result) + + let result = Operators.tanh 19.06154 + Assert.AreNearEqual(1.0, result) // can be 0.99999999999999989 + + let result = tanh 0.0 + Assert.AreEqual(0.0, result) + + let result = tanh infinity + Assert.AreEqual(1.0, result) + + let result = tanh -infinity + Assert.AreEqual(-1.0, result) - () - [] - member this.truncate() = + member _.truncate() = // double let result = Operators.truncate 10.101 - Assert.AreEqual(result,10) + Assert.AreEqual(10.0, result) // decimal let result = Operators.truncate 10.101M - Assert.AreEqual(result,10M) + Assert.AreEqual(10M, result) // zero let result = Operators.truncate 0.101 - Assert.AreEqual(result,0) + Assert.AreEqual(0.0, result) - () - [] - member this.typedefof() = + member _.typedefof() = // value type let result = Operators.typedefof - Assert.AreEqual(result.FullName,"System.Int32") + Assert.AreEqual("System.Int32", result.FullName) // reference type let result = Operators.typedefof - Assert.AreEqual(result.FullName,"System.String") + Assert.AreEqual("System.String", result.FullName) // unit let result = Operators.typedefof - Assert.AreEqual(result.FullName,"Microsoft.FSharp.Core.Unit") + Assert.AreEqual("Microsoft.FSharp.Core.Unit", result.FullName) - () - [] - member this.typeof() = + member _.typeof() = // value type let result = Operators.typeof - Assert.AreEqual(result.FullName,"System.Int32") + Assert.AreEqual("System.Int32", result.FullName) // reference type let result = Operators.typeof - Assert.AreEqual(result.FullName,"System.String") + Assert.AreEqual("System.String", result.FullName) // unit let result = Operators.typeof - Assert.AreEqual(result.FullName,"Microsoft.FSharp.Core.Unit") + Assert.AreEqual("Microsoft.FSharp.Core.Unit", result.FullName) - () - [] - member this.uint16() = - // int + member _.uint16() = + // int let result = Operators.uint16 100 - Assert.AreEqual(result,100us) + Assert.AreEqual(100us, result) // double let result = Operators.uint16 (100.0:double) - Assert.AreEqual(result,100us) + Assert.AreEqual(100us, result) // decimal let result = Operators.uint16 100M - Assert.AreEqual(result,100us) + Assert.AreEqual(100us, result) + + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.uint16 Decimal.MinValue |> ignore) - () - [] - member this.uint32() = + member _.uint32() = // int let result = Operators.uint32 100 - Assert.AreEqual(result,100ul) + Assert.AreEqual(100u, result) // double let result = Operators.uint32 (100.0:double) - Assert.AreEqual(result,100ul) + Assert.AreEqual(100u, result) // decimal let result = Operators.uint32 100M - Assert.AreEqual(result,100ul) + Assert.AreEqual(100u, result) - () - + // Overflow + let result = Operators.uint32 Double.MaxValue + Assert.AreEqual(0u, result) + + // Overflow + let result = Operators.uint32 Double.MinValue + Assert.AreEqual(0u, result) + + // Overflow + let result = Operators.uint32 Int64.MaxValue + Assert.AreEqual(UInt32.MaxValue, result) + + // Overflow + let result = Operators.uint32 Int64.MinValue + Assert.AreEqual(0u, result) + + // Overflow + let result = UInt32.MaxValue + 5u + Assert.AreEqual(4u, result) + + // both 'u' and 'ul' are valid numeric suffixes for UInt32 + let result = 42u + 42ul + Assert.AreEqual(84u, result) + Assert.AreEqual(84ul, result) + + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.uint32 Decimal.MinValue |> ignore) + [] - member this.uint64() = + member _.uint64() = // int let result = Operators.uint64 100 - Assert.AreEqual(result,100UL) + Assert.AreEqual(100UL, result) // double - let result = Operators.uint64 (100.0:double) - Assert.AreEqual(result,100UL) + let result = Operators.uint64 100.0 + Assert.AreEqual(100UL, result) // decimal let result = Operators.uint64 100M - Assert.AreEqual(result,100UL) - - () - + Assert.AreEqual(100UL, result) + + // Overflow + let result = Operators.uint64 Double.MaxValue + Assert.AreEqual(0UL, result) + + // Overflow + let result = Operators.uint64 Double.MinValue + Assert.AreEqual(9223372036854775808UL, result) // surprising, but true, 2^63 + 1 + + // Overflow + let result = Operators.uint64 Int64.MinValue + Assert.AreEqual(9223372036854775808UL, result) + + // Overflow + let result = Operators.uint64 SByte.MinValue + Assert.AreEqual(UInt64.MaxValue - 127UL, result) + + // Overflow + let result = UInt64.MaxValue + 5UL + Assert.AreEqual(4UL, result) + + // OverflowException, from decimal is always checked + CheckThrowsOverflowException(fun() -> Operators.uint64 Decimal.MinValue |> ignore) + [] - member this.unativeint() = + member _.unativeint() = // int let result = Operators.unativeint 100 - Assert.AreEqual(result,100un) + let x: unativeint = 12un + Assert.AreEqual(100un, result) // double - let result = Operators.unativeint (100.0:double) - Assert.AreEqual(result,100un) - - () - + let result = Operators.unativeint 100.0 + Assert.AreEqual(100un, result) + + // Overflow Double.MaxValue is equal on 32 bits and 64 bits runtimes + let result = Operators.unativeint Double.MaxValue + Assert.AreEqual(0un, result) + + // Overflow (depends on pointer size) + let result = Operators.unativeint Double.MinValue + if Info.isX86Runtime then + Assert.AreEqual(0un, result) + else + Assert.AreEqual(9223372036854775808un, result) // surprising, but true, 2^63 + 1 + + // Overflow (depends on pointer size) + let result = Operators.unativeint Int64.MinValue + if Info.isX86Runtime then + Assert.AreEqual(0un, result) + else + Assert.AreEqual(9223372036854775808un, result) + + // Overflow (depends on pointer size) + let result = 0un - 1un + if Info.isX86Runtime then + Assert.AreEqual(4294967295un, result) + else + Assert.AreEqual(18446744073709551615un, result) + [] - member this.unbox() = + member _.unbox() = // value type let oint = box 100 let result = Operators.unbox oint - Assert.AreEqual(result,100) + Assert.AreEqual(100, result) // reference type let ostr = box "ABC" let result = Operators.unbox ostr - Assert.AreEqual(result,"ABC") + Assert.AreEqual("ABC", result) - // null + // null let onull = box null let result = Operators.unbox onull - Assert.AreEqual(result,null) - - () - + Assert.AreEqual(null, result) + + // None == null + let onone = box None + let result = Operators.unbox onone + Assert.AreEqual(None, result) + Assert.AreEqual(null, result) + [] - member this.using() = + member _.using() = let sr = new System.IO.StringReader("ABCD") Assert.AreEqual(sr.ReadToEnd(),"ABCD") - let result = Operators.using sr (fun x -> x.ToString()) + let _ = Operators.using sr (fun x -> x.ToString()) CheckThrowsObjectDisposedException(fun () -> sr.ReadToEnd() |> ignore) - - () \ No newline at end of file diff --git a/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModuleChecked.fs b/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModuleChecked.fs new file mode 100644 index 00000000000..12e33a6b69a --- /dev/null +++ b/tests/FSharp.Core.UnitTests/FSharp.Core/OperatorsModuleChecked.fs @@ -0,0 +1,307 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +// Various tests for the Checked module + +namespace FSharp.Core.UnitTests.Operators + +open System +open FSharp.Core.UnitTests.LibraryTestFx +open NUnit.Framework +open Microsoft.FSharp.Core.Operators.Checked + +[] +type OperatorsModuleChecked() = + + [] + member _.Checkedbyte() = + // int type + let intByte = Operators.Checked.byte 100 + Assert.AreEqual(100uy, intByte) + + // char type + let charByte = Operators.Checked.byte '0' + Assert.AreEqual(48uy, charByte) + + // boundary value + let boundByte = Operators.Checked.byte 255.0 + Assert.AreEqual(255uy, boundByte) + + // overflow exception + CheckThrowsOverflowException(fun () -> Operators.Checked.byte 256 |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> 255uy + 1uy |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> 0uy - 1uy |> ignore) + + [] + member _.Checkedchar() = + + // number + let numberChar = Operators.Checked.char 48 + Assert.AreEqual('0', numberChar) + + // letter + let letterChar = Operators.Checked.char 65 + Assert.AreEqual('A', letterChar) + + // boundary value + let boundchar = Operators.Checked.char 126 + Assert.AreEqual('~', boundchar) + + // overflow exception + CheckThrowsOverflowException(fun () -> Operators.Checked.char (int64 Char.MaxValue + 1L) |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> '\uFFFF' + '\u0001' |> ignore) + + + [] + member _.CheckedInt() = + + // char + let charInt = Operators.Checked.int '0' + Assert.AreEqual(48, charInt) + + // float + let floatInt = Operators.Checked.int 10.0 + Assert.AreEqual(10, floatInt) + + // boundary value + let boundInt = Operators.Checked.int 32767.0 + Assert.AreEqual(32767, boundInt) + + // overflow exception + CheckThrowsOverflowException(fun() -> Operators.Checked.int 2147483648.0 |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> Int32.MaxValue + 1 |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> Int32.MinValue - 1 |> ignore) + + [] + member _.CheckedInt16() = + + // char + let charInt16 = Operators.Checked.int16 '0' + Assert.AreEqual(48s, charInt16) + + // float + let floatInt16 = Operators.Checked.int16 10.0 + Assert.AreEqual(10s, floatInt16) + + // boundary value + let boundInt16 = Operators.Checked.int16 32767.0 + Assert.AreEqual(32767s, boundInt16) + + // overflow exception + CheckThrowsOverflowException(fun () -> Operators.Checked.int16 32768.0 |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> Int16.MaxValue + 1s |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> Int16.MinValue - 1s |> ignore) + + [] + member _.CheckedInt32() = + + // char + let charInt32 = Operators.Checked.int32 '0' + Assert.AreEqual(48, charInt32) + + // float + let floatInt32 = Operators.Checked.int32 10.0 + Assert.AreEqual(10, floatInt32) + + // boundary value + let boundInt32 = Operators.Checked.int32 2147483647.0 + Assert.AreEqual(2147483647, boundInt32) + + // overflow exception + CheckThrowsOverflowException(fun () -> Operators.Checked.int32 2147483648.0 |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> Int32.MaxValue + 1 |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> Int32.MinValue - 1 |> ignore) + + [] + member _.CheckedInt64() = + + // char + let charInt64 = Operators.Checked.int64 '0' + Assert.AreEqual(48L, charInt64) + + // float + let floatInt64 = Operators.Checked.int64 10.0 + Assert.AreEqual(10L, floatInt64) + + // boundary value + let boundInt64 = Operators.Checked.int64 9223372036854775807I + let _ = 9223372036854775807L + Assert.AreEqual(9223372036854775807L, boundInt64) + + // boundary value + let boundInt64 = Operators.Checked.int64 -9223372036854775808I + let _ = -9223372036854775808L + Assert.AreEqual(-9223372036854775808L, boundInt64) + + // overflow exception + CheckThrowsOverflowException(fun() -> Operators.Checked.int64 (float Int64.MaxValue + 1.0) |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> Int64.MaxValue + 1L |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> Int64.MinValue - 1L |> ignore) + + [] + member _.CheckedNativeint() = + + // char + let charnativeint = Operators.Checked.nativeint '0' + Assert.AreEqual(48n, charnativeint) + + // float + let floatnativeint = Operators.Checked.nativeint 10.0 + Assert.AreEqual(10n, floatnativeint) + + // boundary value + let boundnativeint = Operators.Checked.nativeint 32767.0 + Assert.AreEqual(32767n, boundnativeint) + + // overflow exception (depends on pointer size) + CheckThrowsOverflowException(fun() -> + if Info.isX86Runtime then + Operators.Checked.nativeint 2147483648.0 |> ignore + else + Operators.Checked.nativeint 9223372036854775808.0 |> ignore) + + + [] + member _.Checkedsbyte() = + + // char + let charsbyte = Operators.Checked.sbyte '0' + Assert.AreEqual(48y, charsbyte) + + // float + let floatsbyte = Operators.Checked.sbyte -10.0 + Assert.AreEqual(-10y, floatsbyte) + + // boundary value + let boundsbyte = Operators.Checked.sbyte -127.0 + Assert.AreEqual(-127y, boundsbyte) + + // overflow exception + CheckThrowsOverflowException(fun () -> Operators.Checked.sbyte -256 |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> SByte.MaxValue + 1y |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> SByte.MinValue - 1y |> ignore) + + [] + member _.Checkeduint16() = + + // char + let charuint16 = Operators.Checked.uint16 '0' + Assert.AreEqual(48us, charuint16) + + // float + let floatuint16 = Operators.Checked.uint16 10.0 + Assert.AreEqual(10us, floatuint16) + + // boundary value + let bounduint16 = Operators.Checked.uint16 65535.0 + Assert.AreEqual(65535us, bounduint16) + + CheckThrowsOverflowException(fun() -> Operators.Checked.uint16 65536.0 |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> UInt16.MaxValue + 1us |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> UInt16.MinValue - 1us |> ignore) + + [] + member _.Checkeduint32() = + + // char + let charuint32 = Operators.Checked.uint32 '0' + Assert.AreEqual(48u, charuint32) + + // float + let floatuint32 = Operators.Checked.uint32 10.0 + Assert.AreEqual(10u, floatuint32) + + // boundary value + let bounduint32 = Operators.Checked.uint32 429496729.0 + Assert.AreEqual(429496729u, bounduint32) + + // overflow exception + CheckThrowsOverflowException(fun () -> Operators.Checked.uint32(float UInt32.MaxValue + 1.0) |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> UInt32.MaxValue + 1u |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> UInt32.MinValue - 1u |> ignore) + + [] + member _.Checkeduint64() = + + // char + let charuint64 = Operators.Checked.uint64 '0' + Assert.AreEqual(48UL, charuint64) + + // float + let floatuint64 = Operators.Checked.uint64 10.0 + Assert.AreEqual(10UL, floatuint64) + + // boundary value + let bounduint64 = Operators.Checked.uint64 429496729.0 + Assert.AreEqual(429496729UL, bounduint64) + + // overflow exception + CheckThrowsOverflowException(fun () -> Operators.Checked.uint64 (float System.UInt64.MaxValue + 1.0) |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> UInt64.MaxValue + 1UL |> ignore) + + // overflow exception + CheckThrowsOverflowException(fun () -> UInt64.MinValue - 1UL |> ignore) + + [] + member _.Checkedunativeint() = + + // char + let charunativeint = Operators.Checked.unativeint '0' + Assert.AreEqual(48un, charunativeint) + + // float + let floatunativeint = Operators.Checked.unativeint 10.0 + Assert.AreEqual(10un, floatunativeint) + + // boundary value (dependent on pointer size) + if Info.isX86Runtime then + let boundunativeint = Operators.Checked.unativeint 4294967295.0 + Assert.AreEqual(4294967295un, boundunativeint) + else + let boundnativeint = Operators.Checked.unativeint 1.84467440737095505E+19 // 64 bit max value cannot be expressed exactly as double + Assert.AreEqual(18446744073709549568un, boundnativeint) + + // overflow exception (depends on pointer size) + CheckThrowsOverflowException(fun () -> + if Info.isX86Runtime then + Operators.Checked.unativeint (float UInt32.MaxValue + 1.0) |> ignore + else + Operators.Checked.unativeint (float UInt64.MaxValue + 1.0) |> ignore + ) + + diff --git a/tests/FSharp.Core.UnitTests/LibraryTestFx.fs b/tests/FSharp.Core.UnitTests/LibraryTestFx.fs index f17daba432e..48c12638010 100644 --- a/tests/FSharp.Core.UnitTests/LibraryTestFx.fs +++ b/tests/FSharp.Core.UnitTests/LibraryTestFx.fs @@ -37,6 +37,7 @@ let private CheckThrowsExn2<'a when 'a :> exn> s (f : unit -> unit) = // attribute to flag these exception's usage as a bug. let CheckThrowsNullRefException f = CheckThrowsExn f let CheckThrowsIndexOutRangException f = CheckThrowsExn f +let CheckThrowsObjectDisposedException f = CheckThrowsExn f // Legit exceptions let CheckThrowsNotSupportedException f = CheckThrowsExn f @@ -49,6 +50,7 @@ let CheckThrowsDivideByZeroException f = CheckThrowsExn let CheckThrowsOverflowException f = CheckThrowsExn f let CheckThrowsInvalidOperationExn f = CheckThrowsExn f let CheckThrowsFormatException f = CheckThrowsExn f +let CheckThrowsArithmeticException f = CheckThrowsExn f // Verifies two sequences are equal (same length, equiv elements) let VerifySeqsEqual (seq1 : seq<'T>) (seq2 : seq<'T>) = diff --git a/tests/FSharp.Core.UnitTests/NUnitFrameworkShims.fs b/tests/FSharp.Core.UnitTests/NUnitFrameworkShims.fs index 8b0eec34fed..b91a8bc88e8 100644 --- a/tests/FSharp.Core.UnitTests/NUnitFrameworkShims.fs +++ b/tests/FSharp.Core.UnitTests/NUnitFrameworkShims.fs @@ -5,6 +5,8 @@ namespace NUnit.Framework open System open System.Collections.Generic open System.Linq +open System.Runtime.InteropServices + #if XUNIT open Xunit @@ -39,7 +41,27 @@ type IgnoreAttribute (_comment:string) = // Alias NUnit and XUnit Assert as LocalAssert type TestFrameworkAssert = Assert -exception AssertionException of string +module Info = + /// Use this to distinguish cases where output is deterministically different between x86 runtime or x64 runtime, + /// for instance w.r.t. floating point arithmetic. For more info, see https://github.com/dotnet/roslyn/issues/7333 + let isX86Runtime = sizeof = 4 + + /// Use this to distinguish cases where output is deterministically different between x86 runtime or x64 runtime, + /// for instance w.r.t. floating point arithmetic. For more info, see https://github.com/dotnet/roslyn/issues/7333 + let isX64Runtime = sizeof = 8 + + let framework = RuntimeInformation.FrameworkDescription + + /// Whether a test is run inside a .NET Core Runtime + let isNetCore = framework.StartsWith(".NET Core") + + /// Whether a test is run using a .NET Framework Runtime + let isNetFramework = framework.StartsWith(".NET Framework") + + /// Whether a test is run after being compiled to .NET Native + let isNetNative = framework.StartsWith(".NET Native") + + module private Impl = open FsCheck.Arb @@ -86,10 +108,28 @@ module private Impl = | _ -> Object.Equals(expected, actual) + /// Special treatment of float and float32 to get a somewhat meaningful error message + /// (otherwise, the missing precision leads to different values that are close together looking the same) + let floatStr (flt1: obj) (flt2: obj) = + match flt1, flt2 with + | :? float as flt1, (:? float as flt2) -> + flt1.ToString("R"), flt2.ToString("R") + + | :? float32 as flt1, (:? float32 as flt2) -> + flt1.ToString("R"), flt2.ToString("R") + + | _ -> flt1.ToString(), flt2.ToString() + + type Assert = + static member AreEqual(expected : obj, actual : obj, message : string) = + if not (Impl.equals expected actual) then - let message = sprintf "%s: Expected %A but got %A" message expected actual + let message = + let (exp, act) = Impl.floatStr expected actual + sprintf "%s: Expected %s but got %s" message exp act + AssertionException message |> raise static member AreNotEqual(expected : obj, actual : obj, message : string) = @@ -99,6 +139,17 @@ type Assert = static member AreEqual(expected : obj, actual : obj) = Assert.AreEqual(expected, actual, "Assertion") + /// Use this to compare floats within a delta of 1e-15, useful for discrepancies + /// between 80-bit (dotnet, RyuJIT) and 64-bit (x86, Legacy JIT) floating point calculations + static member AreNearEqual(expected: float, actual: float) = + let delta = 1.0e-15 + let message = + let ((e, a)) = Impl.floatStr expected actual + sprintf "Are near equal: expected %s, but got %s (with delta: %f)" e a delta + + global.NUnit.Framework.Assert.AreEqual(expected, actual, 1.0, message) + Assert.AreEqual(Math.Round(expected, 15), Math.Round(actual, 15), message) + static member AreNotEqual(expected : obj, actual : obj) = Assert.AreNotEqual(expected, actual, "Assertion") static member IsNull(o : obj) = Assert.AreEqual(null, o) From 504d5377bcbcfa628dc3afd4fb6b9416f3c31b15 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Tue, 30 Jun 2020 09:52:30 -0700 Subject: [PATCH 44/87] Moved fsharpqa/Libraries/Core/Unchecked test cases to NUnit (#9576) (#9599) Co-authored-by: Thorsten Reichert --- .../Core/Unchecked/DefaultOfTests.fs | 63 +++++++++++++++++++ tests/fsharp/FSharpSuite.Tests.fsproj | 1 + .../Libraries/Core/Unchecked/DefaultOf01.fs | 51 --------------- .../Libraries/Core/Unchecked/DefaultOf02.fs | 17 ----- .../Source/Libraries/Core/Unchecked/env.lst | 2 - 5 files changed, 64 insertions(+), 70 deletions(-) create mode 100644 tests/fsharp/Compiler/Libraries/Core/Unchecked/DefaultOfTests.fs delete mode 100644 tests/fsharpqa/Source/Libraries/Core/Unchecked/DefaultOf01.fs delete mode 100644 tests/fsharpqa/Source/Libraries/Core/Unchecked/DefaultOf02.fs delete mode 100644 tests/fsharpqa/Source/Libraries/Core/Unchecked/env.lst diff --git a/tests/fsharp/Compiler/Libraries/Core/Unchecked/DefaultOfTests.fs b/tests/fsharp/Compiler/Libraries/Core/Unchecked/DefaultOfTests.fs new file mode 100644 index 00000000000..7c732e479ad --- /dev/null +++ b/tests/fsharp/Compiler/Libraries/Core/Unchecked/DefaultOfTests.fs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests + +open NUnit.Framework + +[] +module ``DefaultOf Tests`` = + + type DUType = + | A + | B of int + | C of DUType * DUType + + type RecordType = { A : int; B : string; C : DUType } + + type ClassType = string + + type InterfaceType = + abstract DoStuff : unit -> unit + + type EnumType = + | A = 1 + | B = 2 + | C = 4 + + type StructType = struct + val m_ivalue : int + val m_svalue : string + member this.IValue = this.m_ivalue + member this.SValue = this.m_svalue + end + + [] + let `` Unchecked defaultof reference types``() = + Assert.areEqual Unchecked.defaultof null + Assert.areEqual (box Unchecked.defaultof) null + Assert.areEqual (box Unchecked.defaultof) null + Assert.areEqual (box Unchecked.defaultof) null + + [] + let ``Unchecked defaultof stack types``() = + Assert.areEqual Unchecked.defaultof 0 + Assert.areEqual Unchecked.defaultof 0.0 + Assert.areEqual Unchecked.defaultof (enum 0) + Assert.areEqual Unchecked.defaultof.IValue 0 + Assert.areEqual Unchecked.defaultof.SValue null + + type R = { x : int; y : string } + type U = | A of int | B of string + type S = struct val mutable x : int end + type C() = class end + + [] + let ``Unchecked defaultof and equality``() = + // FSharp1.0:5417 - Unchecked.defaultof<_> on records/unions can cause structural equality check to throw + // Check that Unchecked.defaultof<_> works correctly on various types, mostly structs/unions/records + + Assert.areEqual Unchecked.defaultof Unchecked.defaultof + Assert.areEqual Unchecked.defaultof Unchecked.defaultof + Assert.areEqual Unchecked.defaultof Unchecked.defaultof + Assert.areEqual Unchecked.defaultof Unchecked.defaultof + Assert.areEqual Unchecked.defaultof Unchecked.defaultof \ No newline at end of file diff --git a/tests/fsharp/FSharpSuite.Tests.fsproj b/tests/fsharp/FSharpSuite.Tests.fsproj index 4f9cb099cd1..77cf64d2a31 100644 --- a/tests/fsharp/FSharpSuite.Tests.fsproj +++ b/tests/fsharp/FSharpSuite.Tests.fsproj @@ -76,6 +76,7 @@ + diff --git a/tests/fsharpqa/Source/Libraries/Core/Unchecked/DefaultOf01.fs b/tests/fsharpqa/Source/Libraries/Core/Unchecked/DefaultOf01.fs deleted file mode 100644 index 196448fc4bf..00000000000 --- a/tests/fsharpqa/Source/Libraries/Core/Unchecked/DefaultOf01.fs +++ /dev/null @@ -1,51 +0,0 @@ -// #Regression #Libraries #Unchecked -#light - -// 1760, Implement Unchecked.defaultof<_> (delete LanguagePrimitives.DefaultValueUnchecked) - -// Test the 'defaultof<_>' function - -// Reference types --------------------------- -type DUType = - | A - | B of int - | C of DUType * DUType - -type RecordType = { A : int; B : string; C : DUType } - -type ClassType = string - -type InterfaceType = - abstract DoStuff : unit -> unit - -// Stack types ------------------------------- -type EnumType = - | A = 1 - | B = 2 - | C = 4 - -type StructType = struct - val m_ivalue : int - val m_svalue : string - member this.IValue = this.m_ivalue - member this.SValue = this.m_svalue -end - -// Test reference types -if Unchecked.defaultof <> null then exit 1 -// This behaivor for DU, Records, and Interfaces is bey design (need to box to get null) -if box(Unchecked.defaultof) <> null then exit 1 -if box(Unchecked.defaultof) <> null then exit 1 -if box(Unchecked.defaultof) <> null then exit 1 - -let p = Unchecked.defaultof - -// Test stack types -if Unchecked.defaultof <> 0 then exit 1 -if Unchecked.defaultof <> 0.0 then exit 1 -if Unchecked.defaultof <> enum 0 then exit 1 - -if (Unchecked.defaultof).IValue <> 0 then exit 1 -if (Unchecked.defaultof).SValue <> null then exit 1 - -exit 0 diff --git a/tests/fsharpqa/Source/Libraries/Core/Unchecked/DefaultOf02.fs b/tests/fsharpqa/Source/Libraries/Core/Unchecked/DefaultOf02.fs deleted file mode 100644 index 00414dd8cc8..00000000000 --- a/tests/fsharpqa/Source/Libraries/Core/Unchecked/DefaultOf02.fs +++ /dev/null @@ -1,17 +0,0 @@ -// #Regression #Libraries #Unchecked -#light - -// FSharp1.0:5417 - Unchecked.defaultof<_> on records/unions can cause structural equality check to throw -// Check that Unchecked.defaultof<_> works correctly on various types, mostly structs/unions/records - -type R = { x : int; y : string } -type U = | A of int | B of string -type S = struct val mutable x : int end -type C() = class end - -let shouldBeTrue = - Unchecked.defaultof = Unchecked.defaultof // Records as null - && Unchecked.defaultof = Unchecked.defaultof // Unions as null - && Unchecked.defaultof = Unchecked.defaultof // Structs as null - && Unchecked.defaultof = Unchecked.defaultof // Classes as null - && Unchecked.defaultof = Unchecked.defaultof diff --git a/tests/fsharpqa/Source/Libraries/Core/Unchecked/env.lst b/tests/fsharpqa/Source/Libraries/Core/Unchecked/env.lst deleted file mode 100644 index b5cefff3c2e..00000000000 --- a/tests/fsharpqa/Source/Libraries/Core/Unchecked/env.lst +++ /dev/null @@ -1,2 +0,0 @@ - SOURCE=DefaultOf01.fs # DefaultOf01 - SOURCE=DefaultOf02.fs # DefaultOf02 \ No newline at end of file From edd276cbe2471174f59315d0150a9d3a1ccb50ef Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Fri, 3 Jul 2020 06:42:19 -0700 Subject: [PATCH 45/87] Moved fsharpqa/Libraries/Core/Unchecked test cases to NUnit (#9576) (#9604) Co-authored-by: Thorsten Reichert From 5e3a85c83c2bf38bfc832603de776bea076be292 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Fri, 3 Jul 2020 10:51:18 -0700 Subject: [PATCH 46/87] Merge master to feature/string-interp (#9615) * Moved fsharpqa/Libraries/Core/Unchecked test cases to NUnit (#9576) * Moved fsharpqa/Libraries/Core/Reflectiontest cases to NUnit (#9611) * Migrated PreComputedTupleConstructor01.fs test case * Migrated PreComputedTupleConstructor02.fs test case * Migrated DU.fs and Record.fs test cases * Allow notebook to discover location of shared framework (#9596) Co-authored-by: Thorsten Reichert Co-authored-by: Kevin Ransom (msft) Co-authored-by: Phillip Carter --- .../FSharp.DependencyManager.Utilities.fs | 4 +-- .../PreComputedTupleConstructorTests.fs | 28 +++++++++++++++++++ .../Libraries/Core/Reflection/SprintfTests.fs | 28 +++++++++++++++++++ tests/fsharp/FSharpSuite.Tests.fsproj | 2 ++ .../Source/Libraries/Core/Reflection/DU.fs | 10 ------- .../PreComputeTupleConstructor01.fs | 12 -------- .../PreComputeTupleConstructor02.fs | 12 -------- .../Libraries/Core/Reflection/Record.fs | 10 ------- .../Source/Libraries/Core/Reflection/env.lst | 4 --- 9 files changed, 60 insertions(+), 50 deletions(-) create mode 100644 tests/fsharp/Compiler/Libraries/Core/Reflection/PreComputedTupleConstructorTests.fs create mode 100644 tests/fsharp/Compiler/Libraries/Core/Reflection/SprintfTests.fs delete mode 100644 tests/fsharpqa/Source/Libraries/Core/Reflection/DU.fs delete mode 100644 tests/fsharpqa/Source/Libraries/Core/Reflection/PreComputeTupleConstructor01.fs delete mode 100644 tests/fsharpqa/Source/Libraries/Core/Reflection/PreComputeTupleConstructor02.fs delete mode 100644 tests/fsharpqa/Source/Libraries/Core/Reflection/Record.fs delete mode 100644 tests/fsharpqa/Source/Libraries/Core/Reflection/env.lst diff --git a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs index d6aad41fe5d..e16dacc71f6 100644 --- a/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs +++ b/src/fsharp/FSharp.DependencyManager.Nuget/FSharp.DependencyManager.Utilities.fs @@ -126,12 +126,12 @@ module internal Utilities = | value when not (String.IsNullOrEmpty(value)) -> Some value // Value set externally | _ -> - // Probe for netsdk install + // Probe for netsdk install, dotnet. and dotnet.exe is a constant offset from the location of System.Int32 let dotnetLocation = let dotnetApp = let platform = Environment.OSVersion.Platform if platform = PlatformID.Unix then "dotnet" else "dotnet.exe" - let assemblyLocation = typeof.GetTypeInfo().Assembly.Location + let assemblyLocation = typeof.GetTypeInfo().Assembly.Location Path.Combine(assemblyLocation, "../../..", dotnetApp) if File.Exists(dotnetLocation) then diff --git a/tests/fsharp/Compiler/Libraries/Core/Reflection/PreComputedTupleConstructorTests.fs b/tests/fsharp/Compiler/Libraries/Core/Reflection/PreComputedTupleConstructorTests.fs new file mode 100644 index 00000000000..cd24ea20106 --- /dev/null +++ b/tests/fsharp/Compiler/Libraries/Core/Reflection/PreComputedTupleConstructorTests.fs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests + +open NUnit.Framework + +[] +module ``PreComputedTupleConstructor Tests`` = + + [] + let ``PreComputedTupleConstructor of int and string``() = + // Regression test for FSHARP1.0:5113 + // MT DCR: Reflection.FSharpValue.PreComputeTupleConstructor fails when executed for NetFx 2.0 by a Dev10 compiler + + let testDelegate = TestDelegate (fun () -> + Reflection.FSharpValue.PreComputeTupleConstructor(typeof) [| box 12; box "text" |] |> ignore) + + Assert.DoesNotThrow testDelegate |> ignore + + [] + let ``PreComputedTupleConstructor with wrong order of arguments``() = + // Regression test for FSHARP1.0:5113 + // MT DCR: Reflection.FSharpValue.PreComputeTupleConstructor fails when executed for NetFx 2.0 by a Dev10 compiler + + let testDelegate = TestDelegate (fun () -> + Reflection.FSharpValue.PreComputeTupleConstructor(typeof) [| box "text"; box 12; |] |> ignore) + + Assert.Throws testDelegate |> ignore \ No newline at end of file diff --git a/tests/fsharp/Compiler/Libraries/Core/Reflection/SprintfTests.fs b/tests/fsharp/Compiler/Libraries/Core/Reflection/SprintfTests.fs new file mode 100644 index 00000000000..67ea1baa1bb --- /dev/null +++ b/tests/fsharp/Compiler/Libraries/Core/Reflection/SprintfTests.fs @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests + +open NUnit.Framework + +[] +module ``Sprintf Tests`` = + + type MyR = {c:int;b:int;a:int} + + [] + let ``Sprintf %A of record type``() = + // Regression test for FSHARP1.0:5113 + + let s1 = sprintf "%A" {a=1;b=2;c=3} + let s2 = sprintf "%A" {c=3;b=2;a=1} + + Assert.areEqual s1 s2 + + type MyT = MyC of int * string * bool + + [] + let ``Sprintf %A of discriminated union type``() = + // Regression test for FSHARP1.0:5113 + + let DU = MyC (1,"2",true) + Assert.areEqual "MyC (1, \"2\", true)" (sprintf "%A" DU) \ No newline at end of file diff --git a/tests/fsharp/FSharpSuite.Tests.fsproj b/tests/fsharp/FSharpSuite.Tests.fsproj index 77cf64d2a31..8d27921a5e6 100644 --- a/tests/fsharp/FSharpSuite.Tests.fsproj +++ b/tests/fsharp/FSharpSuite.Tests.fsproj @@ -76,6 +76,8 @@ + + diff --git a/tests/fsharpqa/Source/Libraries/Core/Reflection/DU.fs b/tests/fsharpqa/Source/Libraries/Core/Reflection/DU.fs deleted file mode 100644 index 74f709d760e..00000000000 --- a/tests/fsharpqa/Source/Libraries/Core/Reflection/DU.fs +++ /dev/null @@ -1,10 +0,0 @@ -// #Regression #Libraries #Reflection -// Regression test for FSHARP1.0:5113 - -type MyT = MyC of int * string * bool - -let DU = MyC (1,"2",true) - -printfn "%A" DU - -(if (sprintf "%A" DU) = "MyC (1, \"2\", true)" then 0 else 1) |> exit diff --git a/tests/fsharpqa/Source/Libraries/Core/Reflection/PreComputeTupleConstructor01.fs b/tests/fsharpqa/Source/Libraries/Core/Reflection/PreComputeTupleConstructor01.fs deleted file mode 100644 index 0b179241cec..00000000000 --- a/tests/fsharpqa/Source/Libraries/Core/Reflection/PreComputeTupleConstructor01.fs +++ /dev/null @@ -1,12 +0,0 @@ -// #Regression #Libraries #Reflection -// Regression test for FSHARP1.0:5113 -// MT DCR: Reflection.FSharpValue.PreComputeTupleConstructor fails when executed for NetFx 2.0 by a Dev10 compiler - -let test1 = try - Reflection.FSharpValue.PreComputeTupleConstructor(typeof) [| box 12; box "text" |] |> ignore - true - with - | _ -> false - - -(if test1 then 0 else 1) |> exit diff --git a/tests/fsharpqa/Source/Libraries/Core/Reflection/PreComputeTupleConstructor02.fs b/tests/fsharpqa/Source/Libraries/Core/Reflection/PreComputeTupleConstructor02.fs deleted file mode 100644 index 50b1d7bad63..00000000000 --- a/tests/fsharpqa/Source/Libraries/Core/Reflection/PreComputeTupleConstructor02.fs +++ /dev/null @@ -1,12 +0,0 @@ -// #Regression #Libraries #Reflection -// Regression test for FSHARP1.0:5113 -// MT DCR: Reflection.FSharpValue.PreComputeTupleConstructor fails when executed for NetFx 2.0 by a Dev10 compiler - -let test1 = try - Reflection.FSharpValue.PreComputeTupleConstructor(typeof) [| box "text"; box 12; |] |> ignore - false - with - | _ -> true // yes, we expect the call above to throw since the types are swapped! - - -(if test1 then 0 else 1) |> exit diff --git a/tests/fsharpqa/Source/Libraries/Core/Reflection/Record.fs b/tests/fsharpqa/Source/Libraries/Core/Reflection/Record.fs deleted file mode 100644 index 8c780033a77..00000000000 --- a/tests/fsharpqa/Source/Libraries/Core/Reflection/Record.fs +++ /dev/null @@ -1,10 +0,0 @@ -// #Regression #Libraries #Reflection -// Regression test for FSHARP1.0:5113 - -type MyR = {c:int;b:int;a:int} - -let s1 = sprintf "%A" {a=1;b=2;c=3} -let s2 = sprintf "%A" {c=3;b=2;a=1} - -(if s1 = s2 then 0 else 1) |> exit - diff --git a/tests/fsharpqa/Source/Libraries/Core/Reflection/env.lst b/tests/fsharpqa/Source/Libraries/Core/Reflection/env.lst deleted file mode 100644 index bf3361d68e3..00000000000 --- a/tests/fsharpqa/Source/Libraries/Core/Reflection/env.lst +++ /dev/null @@ -1,4 +0,0 @@ - SOURCE=DU.fs # DU.fs - SOURCE=PreComputeTupleConstructor01.fs # PreComputeTupleConstructor01.fs - SOURCE=PreComputeTupleConstructor02.fs # PreComputeTupleConstructor02.fs - SOURCE=Record.fs # Record.fs From 97313827e930f02e6a68fea3e6031d31f30de0a9 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Sat, 4 Jul 2020 10:34:27 -0700 Subject: [PATCH 47/87] Merge master to feature/string-interp (#9619) * Moved fsharpqa/Libraries/Core/Unchecked test cases to NUnit (#9576) * Moved fsharpqa/Libraries/Core/Reflectiontest cases to NUnit (#9611) * Migrated PreComputedTupleConstructor01.fs test case * Migrated PreComputedTupleConstructor02.fs test case * Migrated DU.fs and Record.fs test cases * Allow notebook to discover location of shared framework (#9596) Co-authored-by: Thorsten Reichert Co-authored-by: Kevin Ransom (msft) From 068198fcae6c8f290b6155583f456fa881dbfcd4 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Tue, 7 Jul 2020 14:26:48 -0700 Subject: [PATCH 48/87] Text tweeks --- src/fsharp/FSComp.txt | 2 +- src/fsharp/LanguageFeatures.fs | 1 + src/fsharp/xlf/FSComp.txt.cs.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.de.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.es.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.fr.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.it.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.ja.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.ko.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.pl.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.ru.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.tr.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 60 +++++++++++++++++++++++++++ src/fsharp/xlf/FSStrings.cs.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.de.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.es.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.fr.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.it.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.ja.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.ko.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.pl.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.pt-BR.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.ru.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.tr.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.zh-Hans.xlf | 20 +++++++++ src/fsharp/xlf/FSStrings.zh-Hant.xlf | 20 +++++++++ 28 files changed, 1042 insertions(+), 1 deletion(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 2c19bb6acce..f6f93e709f7 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1528,4 +1528,4 @@ featureWitnessPassing,"witness passing for trait constraints in F# quotations" 3353,fsiInvalidDirective,"Invalid directive '#%s %s'" 3360,typrelInterfaceWithConcreteAndVariable,"'%s' cannot implement the interface '%s' with the two instantiations '%s' and '%s' because they may unify." 3361,typrelInterfaceWithConcreteAndVariableObjectExpression,"You cannot implement the interface '%s' with the two instantiations '%s' and '%s' because they may unify." -featureInterfacesWithMultipleGenericInstantiation,"interfaces with multiple generic instantiation" +featureInterfacesWithMultipleGenericInstantiation,"interfaces with multiple generic instantiation" \ No newline at end of file diff --git a/src/fsharp/LanguageFeatures.fs b/src/fsharp/LanguageFeatures.fs index cdaf6b59f7d..8c8330ea4d1 100644 --- a/src/fsharp/LanguageFeatures.fs +++ b/src/fsharp/LanguageFeatures.fs @@ -143,6 +143,7 @@ type LanguageVersion (specifiedVersionAsString) = | LanguageFeature.DefaultInterfaceMemberConsumption -> FSComp.SR.featureDefaultInterfaceMemberConsumption() | LanguageFeature.WitnessPassing -> FSComp.SR.featureWitnessPassing() | LanguageFeature.InterfacesWithMultipleGenericInstantiation -> FSComp.SR.featureInterfacesWithMultipleGenericInstantiation() + | LanguageFeature.StringInterpolation -> FSComp.SR.featureStringInterpolation() /// Get a version string associated with the given feature. member _.GetFeatureVersionString feature = diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 85af396cf5c..a21228ea386 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Dostupná přetížení:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} – {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. Stream nezačíná zdrojem s hodnotou null a není ve formátu .RES. @@ -232,6 +277,16 @@ Atributy nejde použít pro rozšíření typů. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use! se nedá kombinovat s and!. @@ -242,6 +297,11 @@ Konstrukt let! ... and! ... se dá použít jen v případě, že tvůrce výpočetních výrazů definuje buď metodu {0}, nebo vhodné metody MergeSource a Bind. + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index ba71a1e94f4..1f5408225ee 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Verfügbare Überladungen:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. Der Stream beginnt nicht mit einer NULL-Ressource und ist nicht im RES-Format. @@ -232,6 +277,16 @@ Attribute können nicht auf Typerweiterungen angewendet werden. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! "use!" darf nicht mit "and!" kombiniert werden. @@ -242,6 +297,11 @@ Das Konstrukt "let! ... and! ..." kann nur verwendet werden, wenn der Berechnungsausdrucks-Generator entweder eine {0}-Methode oder geeignete MergeSource- und Bind-Methoden definiert. + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index 6a8d27fdd3d..3b7d89e217a 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Sobrecargas disponibles:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. El flujo no comienza con un recurso nulo ni está en formato ".RES". @@ -232,6 +277,16 @@ Los atributos no se pueden aplicar a las extensiones de tipo. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! No se puede combinar use! con and! @@ -242,6 +297,11 @@ La construcción "let! ... and! ..." solo se puede usar si el generador de expresiones de cálculo define un método "{0}" o bien los métodos "MergeSource" y "Bind" adecuados. + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index cc158d8cf4e..9f789df22f5 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Surcharges disponibles :\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. Le flux ne commence pas par une ressource null et n'est pas au format '.RES'. @@ -232,6 +277,16 @@ Impossible d'appliquer des attributs aux extensions de type. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use! ne peut pas être combiné avec and! @@ -242,6 +297,11 @@ La construction 'let! ... and! ...' peut uniquement être utilisée si le générateur d'expressions de calcul définit une méthode '{0}' ou les méthodes 'MergeSource' et 'Bind' appropriées + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 28a352f71a5..08a952c93d4 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Overload disponibili:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. Il flusso non inizia con una risorsa Null e non è in formato '.RES'. @@ -232,6 +277,16 @@ Gli attributi non possono essere applicati a estensioni di tipo. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! Non è possibile combinare use! con and! @@ -242,6 +297,11 @@ È possibile usare il costrutto 'let! ... and! ...' solo se il generatore di espressioni di calcolo definisce un metodo '{0}' o metodi 'MergeSource' e 'Bind' appropriati + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 53d07ef597a..3a8851c514b 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} 使用可能なオーバーロード:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. ストリームは null リソースでは始まらず、'RES' 形式でもありません。 @@ -232,6 +277,16 @@ 属性を型拡張に適用することはできません。 + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use! を and! と組み合わせて使用することはできません @@ -242,6 +297,11 @@ 'let! ... and! ...' コンストラクトは、コンピュテーション式ビルダーが '{0}' メソッドまたは適切な 'MergeSource' および 'Bind' メソッドのいずれかを定義している場合にのみ使用できます + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index cd857ba2135..45152342c98 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} 사용 가능한 오버로드:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. 스트림은 null 리소스로 시작되지 않으며 '.RES' 형식이 아닙니다. @@ -232,6 +277,16 @@ 형식 확장에 특성을 적용할 수 없습니다. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use!는 and!와 함께 사용할 수 없습니다. @@ -242,6 +297,11 @@ 'let! ... and! ...' 구문은 계산 식 작성기에서 '{0}' 메서드 또는 적절한 'MergeSource' 및 'Bind' 메서드를 정의한 경우에만 사용할 수 있습니다. + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 8b9d38f36a1..2a6f60b04af 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Dostępne przeciążenia:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} — {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. Strumień nie zaczyna się od zasobu o wartości null i nie jest w formacie „.RES”. @@ -232,6 +277,16 @@ Atrybutów nie można stosować do rozszerzeń typu. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! Elementu use! nie można łączyć z elementem and! @@ -242,6 +297,11 @@ Konstrukcji „let! ... and! ...” można użyć tylko wtedy, gdy konstruktor wyrażeń obliczeniowych definiuje metodę „{0}” lub odpowiednie metody „MergeSource” i „Bind” + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 4dd0ec18ee8..217fa520c15 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Sobrecargas disponíveis:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. Stream não começa com um recurso nulo e não está no formato '.RES'. @@ -232,6 +277,16 @@ Os atributos não podem ser aplicados às extensões de tipo. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use! não pode ser combinado com and! @@ -242,6 +297,11 @@ O constructo 'let! ... and! ...' só pode ser usado se o construtor de expressões de computação definir um método '{0}' ou um método 'MergeSource' ou 'Bind' apropriado + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index 56b8c542b09..92749ca8e05 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Доступные перегрузки:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. Поток не начинается с нулевого ресурса и не соответствует формату ".RES". @@ -232,6 +277,16 @@ Атрибуты не могут быть применены к расширениям типа. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use! запрещено сочетать с and! @@ -242,6 +297,11 @@ Конструкцию "let! ... and! ..." можно использовать только в том случае, если построитель выражений с вычислениями определяет либо метод "{0}", либо соответствующие методы "MergeSource" и "Bind" + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index dc434b9351f..d14480996e5 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} Kullanılabilir aşırı yüklemeler:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. Akış null kaynakla başlamıyor ve '.RES' biçiminde değil. @@ -232,6 +277,16 @@ Öznitelikler tür uzantılarına uygulanamaz. + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use!, and! ile birleştirilemez @@ -242,6 +297,11 @@ 'let! ... and! ...' yapısı, yalnızca hesaplama ifadesi oluşturucu bir '{0}' metodunu ya da uygun 'MergeSource' ve 'Bind' metotlarını tanımlarsa kullanılabilir + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 3dddbf576b1..684f538e86f 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} 可用重载:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. 流应以空资源开头并且应采用 .RES 格式。 @@ -232,6 +277,16 @@ 属性不可应用于类型扩展。 + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use! 不得与 and! 结合使用 @@ -242,6 +297,11 @@ 仅当计算表达式生成器定义了 "{0}" 方法或适当的 "MergeSource" 和 "Bind" 方法时,才可以使用 "let! ... and! ..." 构造 + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index cff0c650647..69feb5de782 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -12,6 +12,11 @@ Feature '{0}' is not supported by target runtime. + + Feature '{0}' requires the F# library for language version {1} or greater. + Feature '{0}' requires the F# library for language version {1} or greater. + + Available overloads:\n{0} 可用的多載:\n{0} @@ -137,6 +142,11 @@ single underscore pattern + + string interpolation + string interpolation + + wild card in for loop wild card in for loop @@ -147,6 +157,26 @@ witness passing for trait constraints in F# quotations + + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. + + + + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. + + + + The '%P' specifier may not be used explicitly. + The '%P' specifier may not be used explicitly. + + + + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. + + - {0} - {0} @@ -162,6 +192,21 @@ Invalid directive '#{0} {1}' + + a byte string may not be interpolated + a byte string may not be interpolated + + + + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. + + + + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. + + Stream does not begin with a null resource and is not in '.RES' format. 資料流未以 null 資源開頭,並且未使用 '.RES' 格式。 @@ -232,6 +277,16 @@ 屬性無法套用到類型延伸模組。 + + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' + + + + Invalid alignment in interpolated string + Invalid alignment in interpolated string + + use! may not be combined with and! use! 不可與 and! 合併 @@ -242,6 +297,11 @@ 只有在計算運算式產生器定義 '{0}' 方法或正確的 'MergeSource' 和 'Bind' 方法時,才可使用 'let! ... and! ...' 建構 + + Invalid interpolated string. {0} + Invalid interpolated string. {0} + + Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSStrings.cs.xlf b/src/fsharp/xlf/FSStrings.cs.xlf index fc719cd81a5..dc79e7e1234 100644 --- a/src/fsharp/xlf/FSStrings.cs.xlf +++ b/src/fsharp/xlf/FSStrings.cs.xlf @@ -7,6 +7,26 @@ symbol ..^ + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . Viz taky {0}. diff --git a/src/fsharp/xlf/FSStrings.de.xlf b/src/fsharp/xlf/FSStrings.de.xlf index c9bf6de009b..47a0a129698 100644 --- a/src/fsharp/xlf/FSStrings.de.xlf +++ b/src/fsharp/xlf/FSStrings.de.xlf @@ -7,6 +7,26 @@ Symbol "..^" + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . Siehe auch "{0}". diff --git a/src/fsharp/xlf/FSStrings.es.xlf b/src/fsharp/xlf/FSStrings.es.xlf index 21f3c4ab7ad..be6a28471d1 100644 --- a/src/fsharp/xlf/FSStrings.es.xlf +++ b/src/fsharp/xlf/FSStrings.es.xlf @@ -7,6 +7,26 @@ símbolo "..^" + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . Vea también {0}. diff --git a/src/fsharp/xlf/FSStrings.fr.xlf b/src/fsharp/xlf/FSStrings.fr.xlf index 0b3644d7313..62128872410 100644 --- a/src/fsharp/xlf/FSStrings.fr.xlf +++ b/src/fsharp/xlf/FSStrings.fr.xlf @@ -7,6 +7,26 @@ symbole '..^' + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . Voir aussi {0}. diff --git a/src/fsharp/xlf/FSStrings.it.xlf b/src/fsharp/xlf/FSStrings.it.xlf index 9715ec16824..c8ada1cb297 100644 --- a/src/fsharp/xlf/FSStrings.it.xlf +++ b/src/fsharp/xlf/FSStrings.it.xlf @@ -7,6 +7,26 @@ simbolo '..^' + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . Vedere anche {0}. diff --git a/src/fsharp/xlf/FSStrings.ja.xlf b/src/fsharp/xlf/FSStrings.ja.xlf index e055cf262f6..f01f224d636 100644 --- a/src/fsharp/xlf/FSStrings.ja.xlf +++ b/src/fsharp/xlf/FSStrings.ja.xlf @@ -7,6 +7,26 @@ シンボル '..^' + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. 。{0} も参照してください。 diff --git a/src/fsharp/xlf/FSStrings.ko.xlf b/src/fsharp/xlf/FSStrings.ko.xlf index 62147543f85..17372265a93 100644 --- a/src/fsharp/xlf/FSStrings.ko.xlf +++ b/src/fsharp/xlf/FSStrings.ko.xlf @@ -7,6 +7,26 @@ 기호 '..^' + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. {0}도 참조하세요. diff --git a/src/fsharp/xlf/FSStrings.pl.xlf b/src/fsharp/xlf/FSStrings.pl.xlf index 0feea19067e..decd387ed69 100644 --- a/src/fsharp/xlf/FSStrings.pl.xlf +++ b/src/fsharp/xlf/FSStrings.pl.xlf @@ -7,6 +7,26 @@ symbol „..^” + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . Zobacz też {0}. diff --git a/src/fsharp/xlf/FSStrings.pt-BR.xlf b/src/fsharp/xlf/FSStrings.pt-BR.xlf index dc96593ddff..272d0a24545 100644 --- a/src/fsharp/xlf/FSStrings.pt-BR.xlf +++ b/src/fsharp/xlf/FSStrings.pt-BR.xlf @@ -7,6 +7,26 @@ símbolo '..^' + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . Consulte também {0}. diff --git a/src/fsharp/xlf/FSStrings.ru.xlf b/src/fsharp/xlf/FSStrings.ru.xlf index c55e25658ea..2e83330f130 100644 --- a/src/fsharp/xlf/FSStrings.ru.xlf +++ b/src/fsharp/xlf/FSStrings.ru.xlf @@ -7,6 +7,26 @@ символ "..^" + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . См. также {0}. diff --git a/src/fsharp/xlf/FSStrings.tr.xlf b/src/fsharp/xlf/FSStrings.tr.xlf index 702a7a35169..7ad1316f1ec 100644 --- a/src/fsharp/xlf/FSStrings.tr.xlf +++ b/src/fsharp/xlf/FSStrings.tr.xlf @@ -7,6 +7,26 @@ '..^' sembolü + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. . Ayrıca bkz. {0}. diff --git a/src/fsharp/xlf/FSStrings.zh-Hans.xlf b/src/fsharp/xlf/FSStrings.zh-Hans.xlf index 80f7968de17..a2aab5530b3 100644 --- a/src/fsharp/xlf/FSStrings.zh-Hans.xlf +++ b/src/fsharp/xlf/FSStrings.zh-Hans.xlf @@ -7,6 +7,26 @@ 符号 "..^" + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. 。请参见 {0}。 diff --git a/src/fsharp/xlf/FSStrings.zh-Hant.xlf b/src/fsharp/xlf/FSStrings.zh-Hant.xlf index 215554d12ea..8d3f3a5cea5 100644 --- a/src/fsharp/xlf/FSStrings.zh-Hant.xlf +++ b/src/fsharp/xlf/FSStrings.zh-Hant.xlf @@ -7,6 +7,26 @@ 符號 '..^' + + interpolated string + interpolated string + + + + interpolated string (first part) + interpolated string (first part) + + + + interpolated string (final part) + interpolated string (final part) + + + + interpolated string (part) + interpolated string (part) + + . See also {0}. 。請參閱 {0}。 From 7108b5bae61a798be85821eac0420ab5bcc99d26 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Wed, 8 Jul 2020 11:20:01 -0700 Subject: [PATCH 49/87] don't auto-resolve types from System.Runtime.WindowsRuntime (#9644) (#9648) Co-authored-by: Brett V. Forsgren --- src/fsharp/DotNetFrameworkDependencies.fs | 19 ++++++++++++++++--- .../CompletionTests.fs | 11 +++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/fsharp/DotNetFrameworkDependencies.fs b/src/fsharp/DotNetFrameworkDependencies.fs index 06e23bb55e2..0fcb0e6d9b8 100644 --- a/src/fsharp/DotNetFrameworkDependencies.fs +++ b/src/fsharp/DotNetFrameworkDependencies.fs @@ -259,10 +259,23 @@ module internal FSharp.Compiler.DotNetFrameworkDependencies if not (assemblies.ContainsKey(referenceName)) then try if File.Exists(path) then - // System.Private.CoreLib doesn't load with reflection - if referenceName = "System.Private.CoreLib" then + match referenceName with + | "System.Runtime.WindowsRuntime" + | "System.Runtime.WindowsRuntime.UI.Xaml" -> + // The Windows compatibility pack included in the runtime contains a reference to + // System.Runtime.WindowsRuntime, but to properly use that type the runtime also needs a + // reference to the Windows.md meta-package, which isn't referenced by default. To avoid + // a bug where types from `Windows, Version=255.255.255.255` can't be found we're going to + // not default include this assembly. It can still be manually referenced if it's needed + // via the System.Runtime.WindowsRuntime NuGet package. + // + // In the future this branch can be removed because WinRT support is being removed from the + // .NET 5 SDK (https://github.com/dotnet/runtime/pull/36715) + () + | "System.Private.CoreLib" -> + // System.Private.CoreLib doesn't load with reflection assemblies.Add(referenceName, path) - else + | _ -> try let asm = System.Reflection.Assembly.LoadFrom(path) assemblies.Add(referenceName, path) diff --git a/tests/FSharp.Compiler.Private.Scripting.UnitTests/CompletionTests.fs b/tests/FSharp.Compiler.Private.Scripting.UnitTests/CompletionTests.fs index cd7da3c7d3a..03935ee77f0 100644 --- a/tests/FSharp.Compiler.Private.Scripting.UnitTests/CompletionTests.fs +++ b/tests/FSharp.Compiler.Private.Scripting.UnitTests/CompletionTests.fs @@ -31,6 +31,17 @@ type CompletionTests() = Assert.AreEqual(1, matchingCompletions.Length) } |> Async.StartAsTask :> Task + [] + member _.``Completions from types that try to pull in Windows runtime extensions``() = + async { + use script = new FSharpScript() + script.Eval("open System") |> ignoreValue + script.Eval("let t = TimeSpan.FromHours(1.0)") |> ignoreValue + let! completions = script.GetCompletionItems("t.", 1, 2) + let matchingCompletions = completions |> Array.filter (fun d -> d.Name = "TotalHours") + Assert.AreEqual(1, matchingCompletions.Length) + } |> Async.StartAsTask :> Task + [] member _.``Static member completions``() = async { From c032ce11410e28b64b5914e31fc79426c157798b Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Fri, 10 Jul 2020 12:11:43 -0700 Subject: [PATCH 50/87] yeet (#9657) (#9661) yeet Co-authored-by: Phillip Carter --- src/fsharp/PostInferenceChecks.fs | 43 ------------------------------- 1 file changed, 43 deletions(-) diff --git a/src/fsharp/PostInferenceChecks.fs b/src/fsharp/PostInferenceChecks.fs index b684871ec52..1bd7e324dfd 100644 --- a/src/fsharp/PostInferenceChecks.fs +++ b/src/fsharp/PostInferenceChecks.fs @@ -706,49 +706,6 @@ let compareTypesWithRegardToTypeVariablesAndMeasures g amap m typ1 typ2 = FeasiblyEqual else NotEqual - -//let CheckMultipleInterfaceInstantiations cenv (typ:TType) (interfaces:TType list) isObjectExpression m = -// let keyf ty = assert isAppTy cenv.g ty; (tcrefOfAppTy cenv.g ty).Stamp -// if not(cenv.g.langVersion.SupportsFeature LanguageFeature.InterfacesWithMultipleGenericInstantiation) then -// let table = interfaces |> MultiMap.initBy keyf -// let firstInterfaceWithMultipleGenericInstantiations = -// interfaces |> List.tryPick (fun typ1 -> -// table |> MultiMap.find (keyf typ1) |> List.tryPick (fun typ2 -> -// if // same nominal type -// tyconRefEq cenv.g (tcrefOfAppTy cenv.g typ1) (tcrefOfAppTy cenv.g typ2) && -// // different instantiations -// not (typeEquivAux EraseNone cenv.g typ1 typ2) -// then Some (typ1, typ2) -// else None)) -// match firstInterfaceWithMultipleGenericInstantiations with -// | None -> () -// | Some (typ1, typ2) -> -// let typ1Str = NicePrint.minimalStringOfType cenv.denv typ1 -// let typ2Str = NicePrint.minimalStringOfType cenv.denv typ2 -// errorR(Error(FSComp.SR.chkMultipleGenericInterfaceInstantiations(typ1Str, typ2Str), m)) -// else -// let groups = interfaces |> List.groupBy keyf -// let errors = seq { -// for (_, items) in groups do -// for i1 in 0 .. items.Length - 1 do -// for i2 in i1 + 1 .. items.Length - 1 do -// let typ1 = items.[i1] -// let typ2 = items.[i2] -// match compareTypesWithRegardToTypeVariablesAndMeasures cenv.g cenv.amap m typ1 typ2 with -// | ExactlyEqual -> () // exact duplicates are checked in another place -// | FeasiblyEqual -> -// let typ1Str = NicePrint.minimalStringOfType cenv.denv typ1 -// let typ2Str = NicePrint.minimalStringOfType cenv.denv typ2 -// if isObjectExpression then -// yield (Error(FSComp.SR.typrelInterfaceWithConcreteAndVariableObjectExpression(tcRef1.DisplayNameWithStaticParametersAndUnderscoreTypars, typ1Str, typ2Str),m)) -// else -// let typStr = NicePrint.minimalStringOfType cenv.denv typ -// yield (Error(FSComp.SR.typrelInterfaceWithConcreteAndVariable(typStr, tcRef1.DisplayNameWithStaticParametersAndUnderscoreTypars, typ1Str, typ2Str),m)) -// | NotEqual -> () -// } -// match Seq.tryHead errors with -// | None -> () -// | Some e -> errorR(e) let CheckMultipleInterfaceInstantiations cenv (typ:TType) (interfaces:TType list) isObjectExpression m = let keyf ty = assert isAppTy cenv.g ty; (tcrefOfAppTy cenv.g ty).Stamp From da3cba5c7977fa425653b8bd7dc3b604e316fb34 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Fri, 10 Jul 2020 15:15:41 -0700 Subject: [PATCH 51/87] yeet (#9657) (#9670) yeet Co-authored-by: Phillip Carter From cb608ae5d15b49a39dd13695f15d5ca7a2db4622 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 22 Jul 2020 15:46:16 +0100 Subject: [PATCH 52/87] fix up tokenizer tests --- tests/service/TokenizerTests.fs | 88 +++++++++++++++---- .../UnitTests/VisualFSharp.UnitTests.fsproj | 5 +- 2 files changed, 77 insertions(+), 16 deletions(-) diff --git a/tests/service/TokenizerTests.fs b/tests/service/TokenizerTests.fs index c8b024a67ca..6a7c2bce31d 100644 --- a/tests/service/TokenizerTests.fs +++ b/tests/service/TokenizerTests.fs @@ -64,26 +64,84 @@ let ``Tokenizer test 2``() = let tokenizedLines = tokenizeLines [| "// Tests tokenizing string interpolation" - "let hello = $\"Hello world {1+1} = {2}\" " |] + "let hello0 = $\"\"" + "let hello1 = $\"Hello world\" " + "let hello2 = $\"Hello world {1+1} = {2}\" " + "let hello0v = @$\"\"" + "let hello1v = @$\"Hello world\" " + "let hello2v = @$\"Hello world {1+1} = {2}\" " + "let hello1t = @$\"\"\"abc\"\"\"" + "let hello2t = @$\"\"\"Hello world {1+1} = {2}\"\"\" " |] let actual = [ for lineNo, lineToks in tokenizedLines do yield lineNo, [ for str, info in lineToks do yield info.TokenName, str ] ] let expected = - [(0, - [("LINE_COMMENT", "//"); ("LINE_COMMENT", " "); ("LINE_COMMENT", "Tests"); - ("LINE_COMMENT", " "); ("LINE_COMMENT", "tokenizing"); ("LINE_COMMENT", " "); - ("LINE_COMMENT", "string"); ("LINE_COMMENT", " "); - ("LINE_COMMENT", "interpolation")]); - (1, - [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello"); ("WHITESPACE", " "); - ("EQUALS", "="); ("WHITESPACE", " "); ("STRING_TEXT", "$\""); - ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); - ("STRING_TEXT", " "); ("STRING", "{"); ("STRING_TEXT", "1"); - ("STRING_TEXT", "+"); ("STRING_TEXT", "1"); ("STRING_TEXT", "}"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("STRING", "{"); ("STRING_TEXT", "2"); ("STRING_TEXT", "}"); ("STRING", "\""); - ("STRING_TEXT", " ")])] + [(0, + [("LINE_COMMENT", "//"); ("LINE_COMMENT", " "); ("LINE_COMMENT", "Tests"); + ("LINE_COMMENT", " "); ("LINE_COMMENT", "tokenizing"); ("LINE_COMMENT", " "); + ("LINE_COMMENT", "string"); ("LINE_COMMENT", " "); + ("LINE_COMMENT", "interpolation")]); + (1, + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello0"); ("WHITESPACE", " "); + ("EQUALS", "="); ("WHITESPACE", " "); ("STRING_TEXT", "$\""); + ("INTERP_STRING_BEGIN_END", "\"")]); + (2, + [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello1"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); + ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")]); + (3, + [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello2"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); + ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "1"); + ("STRING_TEXT", "+"); ("STRING_TEXT", "1"); ("STRING_TEXT", "}"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "2"); ("STRING_TEXT", "}"); + ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")]); + (4, + [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello0v"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + ("INTERP_STRING_BEGIN_END", "\"")]); + (5, + [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello1v"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); + ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")]); + (6, + [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello2v"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); + ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "1"); + ("STRING_TEXT", "+"); ("STRING_TEXT", "1"); ("STRING_TEXT", "}"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "2"); ("STRING_TEXT", "}"); + ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")]); + (7, + [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello1t"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + ("INTERP_STRING_BEGIN_END", "\""); ("INTERP_STRING_BEGIN_END", "\""); + ("STRING_TEXT", "abc"); ("INTERP_STRING_BEGIN_END", "\""); + ("INTERP_STRING_BEGIN_END", "\""); ("INTERP_STRING_BEGIN_END", "\"")]); + (8, + [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello2t"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + ("INTERP_STRING_BEGIN_END", "\""); ("INTERP_STRING_BEGIN_END", "\""); + ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); + ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "1"); + ("STRING_TEXT", "+"); ("STRING_TEXT", "1"); ("STRING_TEXT", "}"); + ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); + ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "2"); ("STRING_TEXT", "}"); + ("INTERP_STRING_BEGIN_END", "\""); ("INTERP_STRING_BEGIN_END", "\""); + ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")])] if actual <> expected then printfn "actual = %A" actual diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 3692a280de6..06b705cf855 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -74,7 +74,10 @@ CompilerService\ProjectAnalysisTests.fs - printerResidueTy) :: acc) (i, relLine, relCol+1) - | c -> raise (Failure (FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c))) + | c -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c) | '\n' -> appendToDotnetFormatString fmt.[i..i] diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 974956f9722..789a24868f0 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -14,7 +14,7 @@ module StringInterpolationTests = CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] """ let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + if a = b then printfn "test case '%s' succeeded" msg else failwithf "test case '%s' failed, expected %A, got %A" msg b a check "vcewweh1" $"this is 2" "this is 2" @@ -27,7 +27,9 @@ check "vcewweh4" $"this is {1} + {1+1}" "this is 1 + 2" check "vcewweh5" $"this is {1}" "this is 1" check "vcewweh6" $"123{456}789{012}345" "12345678912345" + check "vcewweh7" $"this is {1} {2} {3} {4} {5} {6} {7}" "this is 1 2 3 4 5 6 7" + check "vcewweh8" $"this is {7} {6} {5} {4} {3} {2} {1}" "this is 7 6 5 4 3 2 1" check "vcewweh9" $"{1} diff --git a/tests/fsharp/core/printf/test.fsx b/tests/fsharp/core/printf/test.fsx index 41e11250cb2..af5673500f3 100644 --- a/tests/fsharp/core/printf/test.fsx +++ b/tests/fsharp/core/printf/test.fsx @@ -9295,7 +9295,7 @@ func4000() func5000() func6000() func7000() -//func8000() +func8000() #endif #if TESTS_AS_APP From e2f80b33b35ff09c84ccbe1256856418662f9131 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 22 Jul 2020 20:57:55 +0100 Subject: [PATCH 57/87] add various testing --- src/fsharp/service/ServiceLexing.fs | 10 +- .../Compiler/Language/StringInterpolation.fs | 219 +++++++++--------- tests/service/TokenizerTests.fs | 85 ++++--- 3 files changed, 151 insertions(+), 163 deletions(-) diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index edc40b13d91..ecb624f3106 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -308,12 +308,10 @@ module internal TokenClassifications = | LINE_COMMENT _ -> (FSharpTokenColorKind.Comment, FSharpTokenCharKind.LineComment, FSharpTokenTriggerClass.None) - | STRING_TEXT _ -> - (FSharpTokenColorKind.String, FSharpTokenCharKind.String, FSharpTokenTriggerClass.None) - | KEYWORD_STRING _ -> (FSharpTokenColorKind.Keyword, FSharpTokenCharKind.Keyword, FSharpTokenTriggerClass.None) + | STRING_TEXT _ | INTERP_STRING_BEGIN_END _ | INTERP_STRING_BEGIN_PART _ | INTERP_STRING_PART _ @@ -372,6 +370,12 @@ module internal LexerStateEncoding = | STRING_TEXT s | EOF s -> s + // These all indicate the production of a string part or string after a sequence + // of STRING_TEXT + | INTERP_STRING_BEGIN_PART _ + | INTERP_STRING_PART _ + | INTERP_STRING_BEGIN_END _ + | INTERP_STRING_END _ | BYTEARRAY _ | STRING _ -> LexCont.Token(prevLexcont.LexerIfdefStack) diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 789a24868f0..04709703f97 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -9,91 +9,89 @@ open FSharp.Test.Utilities [] module StringInterpolationTests = - [] - let ``Basic string interpolation`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] - """ + let SimpleCheckTest text = + CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] (""" let check msg a b = if a = b then printfn "test case '%s' succeeded" msg else failwithf "test case '%s' failed, expected %A, got %A" msg b a -check "vcewweh1" $"this is 2" "this is 2" +""" + text) + + [] + let ``Basic string interpolation`` () = + SimpleCheckTest + """ +check "basic-string-interp1" $"this is 2" "this is 2" -check "vcewweh2" $"this is {1} + 1 = 2" "this is 1 + 1 = 2" +check "basic-string-interp2" $"this is {1} + 1 = 2" "this is 1 + 1 = 2" -check "vcewweh3" $"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" +check "basic-string-interp3" $"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" -check "vcewweh4" $"this is {1} + {1+1}" "this is 1 + 2" +check "basic-string-interp4" $"this is {1} + {1+1}" "this is 1 + 2" -check "vcewweh5" $"this is {1}" "this is 1" +check "basic-string-interp5" $"this is {1}" "this is 1" -check "vcewweh6" $"123{456}789{012}345" "12345678912345" +check "basic-string-interp6" $"123{456}789{012}345" "12345678912345" -check "vcewweh7" $"this is {1} {2} {3} {4} {5} {6} {7}" "this is 1 2 3 4 5 6 7" +check "basic-string-interp7" $"this is {1} {2} {3} {4} {5} {6} {7}" "this is 1 2 3 4 5 6 7" -check "vcewweh8" $"this is {7} {6} {5} {4} {3} {2} {1}" "this is 7 6 5 4 3 2 1" +check "basic-string-interp8" $"this is {7} {6} {5} {4} {3} {2} {1}" "this is 7 6 5 4 3 2 1" -check "vcewweh9" $"{1} +check "basic-string-interp9" $"{1} {2}" "1 2" """ [] let ``Basic string interpolation verbatim strings`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - -check "xvcewweh1" @$"this is 2" "this is 2" +check "basic-string-interp-triple1" @$"this is 2" "this is 2" -check "xvcewweh2" @$"this is {1} + 1 = 2" "this is 1 + 1 = 2" +check "basic-string-interp-verbatim2" @$"this is {1} + 1 = 2" "this is 1 + 1 = 2" -check "xvcewweh3" @$"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" +check "basic-string-interp-verbatim3" @$"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" -check "xvcewweh4" @$"this is {1} + {1+1}" "this is 1 + 2" +check "basic-string-interp-verbatim4" @$"this is {1} + {1+1}" "this is 1 + 2" -check "xvcewweh5" @$"this is {1}" "this is 1" +check "basic-string-interp-verbatim5" @$"this is {1}" "this is 1" -check "xvcewweh6" @$"this i\s {1}" "this i\s 1" +check "basic-string-interp-verbatim6" @$"this i\s {1}" "this i\s 1" -check "xvcewweh1b" $@"this is 2" "this is 2" +check "basic-string-interp-verbatim1b" $@"this is 2" "this is 2" -check "xvcewweh2b" $@"this is {1} + 1 = 2" "this is 1 + 1 = 2" +check "basic-string-interp-verbatim2b" $@"this is {1} + 1 = 2" "this is 1 + 1 = 2" -check "xvcewweh3b" $@"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" +check "basic-string-interp-verbatim3b" $@"this is {1} + {1+1} = 3" "this is 1 + 2 = 3" -check "xvcewweh4b" $@"this is {1} + {1+1}" "this is 1 + 2" +check "basic-string-interp-verbatim4b" $@"this is {1} + {1+1}" "this is 1 + 2" -check "xvcewweh5b" $@"this is {1}" "this is 1" +check "basic-string-interp-verbatim5b" $@"this is {1}" "this is 1" -check "xvcewweh6b" $@"this i\s {1}" "this i\s 1" +check "basic-string-interp-verbatim6b" $@"this i\s {1}" "this i\s 1" """ [] let ``Basic string interpolation triple quote strings`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest " -let check msg a b = - if a = b then printfn \"%s succeeded\" msg else failwithf \"%s failed, expected %A, got %A\" msg b a - -check \"xvcewweh1\" $\"\"\"this is 2\"\"\" \"this is 2\" +check \"basic-string-interp-triple1\" $\"\"\"this is 2\"\"\" \"this is 2\" -check \"xvcewweh2\" $\"\"\"this is {1} + 1 = 2\"\"\" \"this is 1 + 1 = 2\" +check \"basic-string-interp-triple2\" $\"\"\"this is {1} + 1 = 2\"\"\" \"this is 1 + 1 = 2\" -check \"xvcewweh3\" $\"\"\"this is {1} + {1+1} = 3\"\"\" \"this is 1 + 2 = 3\" +check \"basic-string-interp-triple3\" $\"\"\"this is {1} + {1+1} = 3\"\"\" \"this is 1 + 2 = 3\" -check \"xvcewweh4\" $\"\"\"this is {1} + {1+1}\"\"\" \"this is 1 + 2\" +check \"basic-string-interp-triple4\" $\"\"\"this is {1} + {1+1}\"\"\" \"this is 1 + 2\" -check \"xvcewweh5\" $\"\"\"this is {1}\"\"\" \"this is 1\" +check \"basic-string-interp-triple5\" $\"\"\"this is {1}\"\"\" \"this is 1\" -check \"xvcewweh6\" $\"\"\"this i\s {1}\"\"\" \"this i\s 1\" +check \"basic-string-interp-triple6\" $\"\"\"this i\s {1}\"\"\" \"this i\s 1\" // check nested string with %s -check \"xvcewweh7\" $\"\"\"x = %s{\"1\"}\"\"\" \"x = 1\" +check \"basic-string-interp-triple7\" $\"\"\"x = %s{\"1\"}\"\"\" \"x = 1\" // multiline -check \"xvcewweh8\" +check \"basic-string-interp-triple8\" $\"\"\"this is {1+1}\"\"\" \"\"\"this @@ -102,12 +100,10 @@ is 2\"\"\" [] let ``String interpolation using atomic expression forms`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ let x = 12 let s = "sixsix" -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a check "vcewwei4" $"this is %d{1} + {1+1+3} = 6" "this is 1 + 5 = 6" @@ -127,59 +123,59 @@ check "vcewwei8" $"abc{4%3}def" "abc1def" [] let ``String interpolation using nested control flow expressions`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - let x = 12 let s = "sixsix" // Check let expression -check "vcewwej7" $"abc {let x = 3 in x + x} def" "abc 6 def" +check "string-interp-nested7" $"abc {let x = 3 in x + x} def" "abc 6 def" // Check if expression (parenthesized) -check "vcewwej9" $"abc{(if true then 3 else 4)}def" "abc3def" +check "string-interp-nested9" $"abc{(if true then 3 else 4)}def" "abc3def" // Check if-then-else expression (un-parenthesized) -check "vcewwej10" $"abc{if true then 3 else 4}def" "abc3def" +check "string-interp-nested10" $"abc{if true then 3 else 4}def" "abc3def" // Check two if-then-else expression (un-parenthesized) -check "vcewwej11" $"abc{if true then 3 else 4}def{if false then 3 else 4}xyz" "abc3def4xyz" +check "string-interp-nested11" $"abc{if true then 3 else 4}def{if false then 3 else 4}xyz" "abc3def4xyz" // Check two if-then-else expression (un-parenthesized, first split) -check "vcewwej12" $"abc{if true then 3 +check "string-interp-nested12" + $"abc{if true then 3 else 4}def{if false then 3 else 4}xyz" "abc3def4xyz" // Check two if-then-else expression (un-parenthesized, second split) -check "vcewwej13" $"abc{if true then 3 else 4}def{if false then 3 +check "string-interp-nested13" + $"abc{if true then 3 else 4}def{if false then 3 else 4}xyz" "abc3def4xyz" // Check two if-then-else expression (un-parenthesized, both split) -check "vcewwej14" $"abc{if true then 3 +check "string-interp-nested14" + $"abc{if true then 3 else 4}def{if false then 3 else 4}xyz" "abc3def4xyz" // Check if-then expression (un-parenthesized) -check "vcewwej15" $"abc{if true then ()}def" "abcdef" +check "string-interp-nested15" $"abc{if true then ()}def" "abcdef" // Check two if-then expression (un-parenthesized) -check "vcewwej16" $"abc{if true then ()}def{if true then ()}xyz" "abcdefxyz" +check "string-interp-nested16" $"abc{if true then ()}def{if true then ()}xyz" "abcdefxyz" // Check multi-line let with parentheses -check "fahweelvlo" +check "string-interp-nested17" $"abc {(let x = 3 x + x)} def" "abc 6 def" // Check multi-line let without parentheses -check "fahweelvlo3" +check "string-interp-nested18" $"abc {let x = 3 x + x} def" "abc 6 def" // Check multi-line let without parentheses (two) -check "fahweelvlo4" +check "string-interp-nested19" $"abc {let x = 3 x + x} def {let x = 3 x + x} xyz" @@ -194,11 +190,8 @@ check "vcewweh17" $"abc{while false do ()}def" "abcdef" [] let ``String interpolation using nested string`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest " -let check msg a b = - if a = b then printfn \"%s succeeded\" msg else failwithf \"%s failed, expected %A, got %A\" msg b a - // check nested string check \"vcewweh22m1\" $\"\"\"x = {\"1\"} \"\"\" \"x = 1 \" @@ -213,10 +206,8 @@ do [] let ``Triple quote string interpolation using nested string`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest " -let check msg a b = - if a = b then printfn \"%s succeeded\" msg else failwithf \"%s failed, expected %A, got %A\" msg b a do let itvar=\"i\" let iterfrom=\"0\" @@ -235,10 +226,8 @@ for(i=0;i<100;++i) { [] let ``Mixed quote string interpolation using nested string`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest " -let check msg a b = - if a = b then printfn \"%s succeeded\" msg else failwithf \"%s failed, expected %A, got %A\" msg b a check \"vcewweh22n1\" $\"\"\" @@ -265,12 +254,10 @@ check \"vcewweh22n2\" [] let ``String interpolation to FormattableString`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ open System open System.Globalization -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a let fmt (x: FormattableString) = x.ToString() let fmt_us (x: FormattableString) = x.ToString(CultureInfo("en-US")) @@ -296,12 +283,10 @@ check "fwejwflpej17" (fmt_de $"abc {30000,-10:N} def {40000:N} hij") "abc 30.000 [] let ``String interpolation to IFormattable`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ open System open System.Globalization -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a let fmt (x: IFormattable) = x.ToString() let fmt_us (x: IFormattable) = x.ToString("", CultureInfo("en-US")) @@ -327,12 +312,10 @@ check "fwejwflpej17" (fmt_de $"abc {30000,-10:N} def {40000:N} hij") "abc 30.000 [] let ``String interpolation to PrintFormat`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ open System.Text open Printf -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a check "fwejwflpej1" (sprintf $"") "" check "fwejwflpej2" (sprintf $"abc") "abc" @@ -349,11 +332,8 @@ check "fwejwflpej4" (sb.ToString()) "0abcabc1" [] let ``String interpolation using .NET Formats`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - check "vcewweh221q" $"abc {1}" "abc 1" check "vcewweh222w" $"abc {1:N3}" "abc 1.000" check "vcewweh223e" $"abc {1,10}" "abc 1" @@ -367,10 +347,8 @@ check "vcewweh225u" $"abc %-5d{1}" "abc 1 " [] let ``String interpolation of null`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a check "vcewweh221q1" $"{null}" "" check "vcewweh221q2" $"%s{null}" "" @@ -380,11 +358,8 @@ check "vcewweh221q4" $"abc %s{null} def" "abc def" [] let ``String interpolation of basic types`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - check "vcewweh221q11" $"{1y}" "1" check "vcewweh221q12" $"{1uy}" "1" check "vcewweh221q13" $"{1s}" "1" @@ -418,11 +393,8 @@ check "vcewweh221q1" $"%f{1.0}" "1.000000" """ [] let ``String interpolation using escaped braces`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - check "vcewweh226i" $"{{" "{" check "vcewweh226o" $"{{{{" "{{" check "vcewweh226p" $"{{{1}}}" "{1}" @@ -434,11 +406,8 @@ check "vcewweh229f" "}}" "}}" [] let ``String interpolation using verbatim strings`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - check "vcewweh226i" $"{{" "{" check "vcewweh226o" $"{{{{" "{{" check "vcewweh226p" $"{{{1}}}" "{1}" @@ -451,14 +420,11 @@ check "vcewweh229f" "}}" "}}" [] let ``String interpolation using record data`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ type R = { X : int } type R2 = { X : int ; Y: int } -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - // Check record expression (parenthesized) check "vcewweh18" $"abc{({contents=1}.contents)}def" "abc1def" @@ -476,11 +442,8 @@ check "vcewweh21" $"abc{{X=1; Y=2}}def" "abc{X=1; Y=2}def" [] let ``String interpolation using printf formats`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - let x = 12 let s = "sixsix" @@ -512,11 +475,8 @@ check "vcewweh20" $"x = %A{1}" "x = 1" [] let ``String interpolation using list and array data`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + SimpleCheckTest """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a - // check unannotated of list check "vcewweh22i" $"x = {[0..100]} " "x = [0; 1; 2; ... ] " @@ -541,12 +501,43 @@ check "vcewweh22k" $"x = %0A{[|0..100|]} " "x = [|0; 1; 2; 3; 4; 5; 6; 7; 8; 9; [] - let ``String interpolation using anonymous records`` () = - CompilerAssert.CompileExeAndRunWithOptions [| "--langversion:preview" |] + let ``Quotations of interpolation`` () = + SimpleCheckTest + """ +check "vcewweh1" + ((<@ $"this {1} is 2" @>).ToString().Replace("\r","").Replace("\n","").Replace("\"","")) + "Call (None, PrintFormatToString, [NewObject (PrintfFormat`5, Value (this %P() is 2), NewArray (Object, Call (None, Box, [Value (1)])), Value ())])" """ -let check msg a b = - if a = b then printfn "%s succeeded" msg else failwithf "%s failed, expected %A, got %A" msg b a + [] + let ``Quotations in interpolation`` () = + SimpleCheckTest + """ +check "check-quotation1" $"this {<@ 1 @>} is 2" "this Value (1) is 2" + """ + + [] + let ``Object expression in interpolation`` () = + SimpleCheckTest + """ +check "check-object-expression-in-interpolation1" + (let s = "AA" in $"this { {new System.Object() with member x.ToString() = s } } is 2") + "this AA is 2" + """ + + [] + let ``Exception handling in interpolation`` () = + SimpleCheckTest + """ +check "check-object-expression-in-interpolation1" + (let s = "AA" in $"this { try failwith s with _ -> s } is 2") + "this AA is 2" + """ + + [] + let ``String interpolation using anonymous records`` () = + SimpleCheckTest + """ // Check anonymous record expression (parenthesized) check "vcewweh23" $"abc{({| A=1 |})}def" "abc{ A = 1 }def" diff --git a/tests/service/TokenizerTests.fs b/tests/service/TokenizerTests.fs index 6a7c2bce31d..5f14a0b8aa5 100644 --- a/tests/service/TokenizerTests.fs +++ b/tests/service/TokenizerTests.fs @@ -87,61 +87,54 @@ let ``Tokenizer test 2``() = ("EQUALS", "="); ("WHITESPACE", " "); ("STRING_TEXT", "$\""); ("INTERP_STRING_BEGIN_END", "\"")]); (2, - [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello1"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello1"); ("WHITESPACE", " "); + ("EQUALS", "="); ("WHITESPACE", " "); ("STRING_TEXT", "$\""); ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); - ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")]); + ("INTERP_STRING_BEGIN_END", "\""); ("WHITESPACE", " ")]); (3, - [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello2"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello2"); ("WHITESPACE", " "); + ("EQUALS", "="); ("WHITESPACE", " "); ("STRING_TEXT", "$\""); ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); - ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "1"); - ("STRING_TEXT", "+"); ("STRING_TEXT", "1"); ("STRING_TEXT", "}"); + ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("INT32", "1"); + ("PLUS_MINUS_OP", "+"); ("INT32", "1"); ("STRING_TEXT", "}"); ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "2"); ("STRING_TEXT", "}"); - ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")]); + ("INTERP_STRING_PART", "{"); ("INT32", "2"); ("STRING_TEXT", "}"); + ("INTERP_STRING_END", "\""); ("WHITESPACE", " ")]); (4, - [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello0v"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); - ("INTERP_STRING_BEGIN_END", "\"")]); + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello0v"); + ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); + ("STRING_TEXT", "@$\""); ("INTERP_STRING_BEGIN_END", "\"")]); (5, - [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello1v"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); - ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); - ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")]); + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello1v"); + ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); + ("STRING_TEXT", "@$\""); ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); + ("STRING_TEXT", "world"); ("INTERP_STRING_BEGIN_END", "\""); + ("WHITESPACE", " ")]); (6, - [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello2v"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); - ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); - ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "1"); - ("STRING_TEXT", "+"); ("STRING_TEXT", "1"); ("STRING_TEXT", "}"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "2"); ("STRING_TEXT", "}"); - ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")]); + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello2v"); + ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); + ("STRING_TEXT", "@$\""); ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); + ("STRING_TEXT", "world"); ("STRING_TEXT", " "); + ("INTERP_STRING_BEGIN_PART", "{"); ("INT32", "1"); ("PLUS_MINUS_OP", "+"); + ("INT32", "1"); ("STRING_TEXT", "}"); ("STRING_TEXT", " "); + ("STRING_TEXT", "="); ("STRING_TEXT", " "); ("INTERP_STRING_PART", "{"); + ("INT32", "2"); ("STRING_TEXT", "}"); ("INTERP_STRING_END", "\""); + ("WHITESPACE", " ")]); (7, - [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello1t"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); - ("INTERP_STRING_BEGIN_END", "\""); ("INTERP_STRING_BEGIN_END", "\""); - ("STRING_TEXT", "abc"); ("INTERP_STRING_BEGIN_END", "\""); - ("INTERP_STRING_BEGIN_END", "\""); ("INTERP_STRING_BEGIN_END", "\"")]); + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello1t"); + ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); + ("STRING_TEXT", "@$\""); ("STRING_TEXT", "\"\""); ("STRING_TEXT", "abc"); + ("STRING_TEXT", "\"\""); ("INTERP_STRING_BEGIN_END", "\"")]); (8, - [("STRING_TEXT", "let"); ("STRING_TEXT", " "); ("STRING_TEXT", "hello2t"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("STRING_TEXT", "@"); ("STRING_TEXT", "$"); ("INTERP_STRING_BEGIN_END", "\""); - ("INTERP_STRING_BEGIN_END", "\""); ("INTERP_STRING_BEGIN_END", "\""); - ("STRING_TEXT", "Hello"); ("STRING_TEXT", " "); ("STRING_TEXT", "world"); - ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "1"); - ("STRING_TEXT", "+"); ("STRING_TEXT", "1"); ("STRING_TEXT", "}"); - ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); - ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "2"); ("STRING_TEXT", "}"); - ("INTERP_STRING_BEGIN_END", "\""); ("INTERP_STRING_BEGIN_END", "\""); - ("INTERP_STRING_BEGIN_END", "\""); ("STRING_TEXT", " ")])] + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello2t"); + ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); + ("STRING_TEXT", "@$\""); ("STRING_TEXT", "\"\""); ("STRING_TEXT", "Hello"); + ("STRING_TEXT", " "); ("STRING_TEXT", "world"); ("STRING_TEXT", " "); + ("INTERP_STRING_BEGIN_PART", "{"); ("INT32", "1"); ("PLUS_MINUS_OP", "+"); + ("INT32", "1"); ("STRING_TEXT", "}"); ("STRING_TEXT", " "); + ("STRING_TEXT", "="); ("STRING_TEXT", " "); ("INTERP_STRING_PART", "{"); + ("INT32", "2"); ("STRING_TEXT", "}"); ("STRING_TEXT", "\"\""); + ("INTERP_STRING_END", "\""); ("WHITESPACE", " ")])] if actual <> expected then printfn "actual = %A" actual From 0f615362c3c084456a87ccbb7fbc04e1fa538df9 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Jul 2020 14:40:37 +0100 Subject: [PATCH 58/87] correct continuations for interpolated strings --- src/fsharp/CompileOps.fs | 2 +- src/fsharp/FSComp.txt | 24 +- src/fsharp/ParseHelpers.fs | 42 +- src/fsharp/fsi/fsi.fs | 2 +- src/fsharp/lex.fsl | 396 +++++++++++------- src/fsharp/lexhelp.fs | 8 +- src/fsharp/lexhelp.fsi | 12 +- src/fsharp/pars.fsy | 32 +- src/fsharp/pplex.fsl | 2 +- src/fsharp/service/FSharpCheckerResults.fs | 2 +- src/fsharp/service/ServiceLexing.fs | 280 +++++++++---- src/fsharp/xlf/FSComp.txt.cs.xlf | 10 + src/fsharp/xlf/FSComp.txt.de.xlf | 10 + src/fsharp/xlf/FSComp.txt.es.xlf | 10 + src/fsharp/xlf/FSComp.txt.fr.xlf | 10 + src/fsharp/xlf/FSComp.txt.it.xlf | 10 + src/fsharp/xlf/FSComp.txt.ja.xlf | 10 + src/fsharp/xlf/FSComp.txt.ko.xlf | 10 + src/fsharp/xlf/FSComp.txt.pl.xlf | 10 + src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 10 + src/fsharp/xlf/FSComp.txt.ru.xlf | 10 + src/fsharp/xlf/FSComp.txt.tr.xlf | 10 + src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 10 + src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 10 + .../HashIfExpression.fs | 14 +- .../Compiler/Language/StringInterpolation.fs | 76 ++-- tests/service/TokenizerTests.fs | 40 +- 27 files changed, 727 insertions(+), 335 deletions(-) diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 9996d33d415..2ec8e8938c7 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -3543,7 +3543,7 @@ let ParseOneInputLexbuf (tcConfig: TcConfig, lexResourceManager, conditionalComp try let skip = true in (* don't report whitespace from lexer *) let lightSyntaxStatus = LightSyntaxStatus (tcConfig.ComputeLightSyntaxInitialStatus filename, true) - let lexargs = mkLexargs (filename, conditionalCompilationDefines@tcConfig.conditionalCompilationDefines, lightSyntaxStatus, lexResourceManager, [], errorLogger, tcConfig.pathMap) + let lexargs = mkLexargs (conditionalCompilationDefines@tcConfig.conditionalCompilationDefines, lightSyntaxStatus, lexResourceManager, [], errorLogger, tcConfig.pathMap) let shortFilename = SanitizeFileName filename tcConfig.implicitIncludeDir let input = Lexhelp.usingLexbufForParsing (lexbuf, filename) (fun lexbuf -> diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index f6f93e709f7..f01e866759f 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1493,8 +1493,6 @@ notAFunctionButMaybeDeclaration,"This value is not a function and cannot be appl 3351,chkFeatureNotRuntimeSupported,"Feature '%s' is not supported by target runtime." 3352,typrelInterfaceMemberNoMostSpecificImplementation,"Interface member '%s' does not have a most specific implementation." 3353,chkFeatureNotSupportedInLibrary,"Feature '%s' requires the F# library for language version %s or greater." -3354,tcUnableToParseInterpolatedString,"Invalid interpolated string. %s" -3360,lexByteStringMayNotBeInterpolated,"a byte string may not be interpolated" useSdkRefs,"Use reference assemblies for .NET framework references when available (Enabled by default)." fSharpBannerVersion,"%s for F# %s" optsLangVersion,"Display the allowed values for language version, specify language version such as 'latest' or 'preview'" @@ -1516,16 +1514,20 @@ featureAndBang,"applicative computation expressions" featureNullableOptionalInterop,"nullable optional interop" featureDefaultInterfaceMemberConsumption,"default interface member consumption" featureStringInterpolation,"string interpolation" -3361,tcInterpolationMixedWithPercent,"Mismatch in interpolated string. Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'" -forFormatInvalidForInterpolated,"Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'." -forFormatInvalidForInterpolated2,".NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%%' format specifiers." -forFormatInvalidForInterpolated3,"The '%%P' specifier may not be used explicitly." -forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable or type FormattableString may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." -3362,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" -3363,lexSingleQuoteInSingleQuote,"Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal." -3364,lexTripleQuoteInTripleQuote,"Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression." featureWitnessPassing,"witness passing for trait constraints in F# quotations" 3353,fsiInvalidDirective,"Invalid directive '#%s %s'" 3360,typrelInterfaceWithConcreteAndVariable,"'%s' cannot implement the interface '%s' with the two instantiations '%s' and '%s' because they may unify." 3361,typrelInterfaceWithConcreteAndVariableObjectExpression,"You cannot implement the interface '%s' with the two instantiations '%s' and '%s' because they may unify." -featureInterfacesWithMultipleGenericInstantiation,"interfaces with multiple generic instantiation" \ No newline at end of file +featureInterfacesWithMultipleGenericInstantiation,"interfaces with multiple generic instantiation" +forFormatInvalidForInterpolated,"Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'." +forFormatInvalidForInterpolated2,".NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%%' format specifiers." +forFormatInvalidForInterpolated3,"The '%%P' specifier may not be used explicitly." +forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable or type FormattableString may not use '%%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used." +3371,tcInterpolationMixedWithPercent,"Mismatch in interpolated string. Interpolated strings may not use '%%' format specifiers unless each is given an expression, e.g. '%%d{{1+1}}'" +3372,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" +3373,lexSingleQuoteInSingleQuote,"Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal." +3374,lexTripleQuoteInTripleQuote,"Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression." +3375,parsNoEndOfInterpString,"Incomplete interpolated string" +3376,tcUnableToParseInterpolatedString,"Invalid interpolated string. %s" +3377,lexByteStringMayNotBeInterpolated,"a byte string may not be interpolated" +3375,parsNoEndOfInterpStringFill,"Incomplete interpolated string expression fill" diff --git a/src/fsharp/ParseHelpers.fs b/src/fsharp/ParseHelpers.fs index 969ce6fdc9d..963c3d3d6c4 100644 --- a/src/fsharp/ParseHelpers.fs +++ b/src/fsharp/ParseHelpers.fs @@ -116,6 +116,7 @@ type LexerIfdefStackEntry = | IfDefIf | IfDefElse +/// Represents the active #if/#else blocks type LexerIfdefStackEntries = (LexerIfdefStackEntry * range) list type LexerIfdefStack = LexerIfdefStackEntries @@ -124,12 +125,8 @@ type LexerIfdefStack = LexerIfdefStackEntries /// it reaches end of line or eof. The options are to continue with 'token' function /// or to continue with 'skip' function. type LexerEndlineContinuation = - | Token of LexerIfdefStackEntries - | Skip of LexerIfdefStackEntries * int * range: range - member x.LexerIfdefStack = - match x with - | LexerEndlineContinuation.Token ifd - | LexerEndlineContinuation.Skip(ifd, _, _) -> ifd + | Token + | Skip of int * range: range type LexerIfdefExpression = | IfdefAnd of LexerIfdefExpression*LexerIfdefExpression @@ -163,6 +160,10 @@ type LexerStringKind = static member InterpolatedStringFirst = { IsByteString = false; IsInterpolated = true; IsInterpolatedFirst=true } static member InterpolatedStringPart = { IsByteString = false; IsInterpolated = true; IsInterpolatedFirst=false } +/// Represents the degree of nesting of '{..}' and the style of the string to continue afterwards, in an interpolation fill. +/// Nesting counters and styles of outer interpolating strings are pushed on this stack. +type LexerInterpolatedStringNesting = (int * LexerStringStyle * range) list + /// The parser defines a number of tokens for whitespace and /// comments eliminated by the lexer. These carry a specification of /// a continuation for the lexer for continued processing after we've dealt with @@ -170,14 +171,14 @@ type LexerStringKind = [] [] type LexerWhitespaceContinuation = - | Token of ifdef: LexerIfdefStackEntries - | IfDefSkip of ifdef: LexerIfdefStackEntries * int * range: range - | String of ifdef: LexerIfdefStackEntries * style: LexerStringStyle * kind: LexerStringKind * range: range - | Comment of ifdef: LexerIfdefStackEntries * int * range: range - | SingleLineComment of ifdef: LexerIfdefStackEntries * int * range: range - | StringInComment of ifdef: LexerIfdefStackEntries * style: LexerStringStyle * int * range: range - | MLOnly of ifdef: LexerIfdefStackEntries * range: range - | EndLine of LexerEndlineContinuation + | Token of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting + | IfDefSkip of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * int * range: range + | String of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * style: LexerStringStyle * kind: LexerStringKind * range: range + | Comment of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * int * range: range + | SingleLineComment of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * int * range: range + | StringInComment of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * style: LexerStringStyle * int * range: range + | MLOnly of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * range: range + | EndLine of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * LexerEndlineContinuation member x.LexerIfdefStack = match x with @@ -187,8 +188,19 @@ type LexerWhitespaceContinuation = | LexCont.Comment (ifdef=ifd) | LexCont.SingleLineComment (ifdef=ifd) | LexCont.StringInComment (ifdef=ifd) + | LexCont.EndLine (ifdef=ifd) | LexCont.MLOnly (ifdef=ifd) -> ifd - | LexCont.EndLine endl -> endl.LexerIfdefStack + + member x.LexerInterpStringNesting = + match x with + | LexCont.Token (nesting=nesting) + | LexCont.IfDefSkip (nesting=nesting) + | LexCont.String (nesting=nesting) + | LexCont.Comment (nesting=nesting) + | LexCont.SingleLineComment (nesting=nesting) + | LexCont.StringInComment (nesting=nesting) + | LexCont.EndLine (nesting=nesting) + | LexCont.MLOnly (nesting=nesting) -> nesting and LexCont = LexerWhitespaceContinuation diff --git a/src/fsharp/fsi/fsi.fs b/src/fsharp/fsi/fsi.fs index d43bd29b318..6a5c9297af3 100644 --- a/src/fsharp/fsi/fsi.fs +++ b/src/fsharp/fsi/fsi.fs @@ -1976,7 +1976,7 @@ type internal FsiStdinLexerProvider Lexhelp.resetLexbufPos sourceFileName lexbuf let skip = true // don't report whitespace from lexer let defines = "INTERACTIVE"::tcConfigB.conditionalCompilationDefines - let lexargs = mkLexargs (sourceFileName,defines, interactiveInputLightSyntaxStatus, lexResourceManager, [], errorLogger, PathMap.empty) + let lexargs = mkLexargs (defines, interactiveInputLightSyntaxStatus, lexResourceManager, [], errorLogger, PathMap.empty) let tokenizer = LexFilter.LexFilter(interactiveInputLightSyntaxStatus, tcConfigB.compilingFslib, Lexer.token lexargs skip, lexbuf) tokenizer diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index b21883f1942..16f90ca7f99 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -519,119 +519,137 @@ rule token args skip = parse | _ -> fail args lexbuf (FSComp.SR.lexThisUnicodeOnlyInStringLiterals()) (CHAR (char 0)) } | "(*IF-FSHARP" - { if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + { if not skip then COMMENT (LexCont.Token (args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | "(*F#" - { if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + { if not skip then COMMENT (LexCont.Token (args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | "ENDIF-FSHARP*)" - { if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + { if not skip then COMMENT (LexCont.Token (args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | "F#*)" - { if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + { if not skip then COMMENT (LexCont.Token (args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | "(*)" { LPAREN_STAR_RPAREN } | "(*" { let m = lexbuf.LexemeRange - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,1,m))) else comment (1,m,args) skip lexbuf } + if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, 1, m)) + else comment (1,m,args) skip lexbuf } | "(*IF-CAML*)" | "(*IF-OCAML*)" { let m = lexbuf.LexemeRange - if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } + if not skip then COMMENT (LexCont.MLOnly(args.ifdefStack, args.stringNest, m)) + else mlOnly m args skip lexbuf } | '"' { let buf, fin, m = startString args lexbuf // Single quote in triple quote ok, others disallowed - match args.interpolatedStringNesting with - | (_, LexerStringStyle.TripleQuote) :: _ -> () + match args.stringNest with + | (_, LexerStringStyle.TripleQuote, _) :: _ -> () | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) | [] -> () - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, LexerStringKind.String, m))) else singleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, LexerStringKind.String, m)) + else singleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } | '$' '"' '"' '"' { let buf, fin, m = startString args lexbuf // Single quote in triple quote ok, others disallowed - match args.interpolatedStringNesting with + match args.stringNest with | _ :: _ -> errorR(Error(FSComp.SR.lexTripleQuoteInTripleQuote(), lexbuf.LexemeRange)) | [] -> () - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, LexerStringKind.InterpolatedStringFirst, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, LexerStringKind.InterpolatedStringFirst, m)) + else tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | '$' '"' { let buf,fin,m = startString args lexbuf // Single quote in triple quote ok, others disallowed - match args.interpolatedStringNesting with - | (_, LexerStringStyle.TripleQuote) :: _ -> () + match args.stringNest with + | (_, LexerStringStyle.TripleQuote, _) :: _ -> () | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) | _ -> () - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, LexerStringKind.InterpolatedStringFirst, m))) else singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, LexerStringKind.InterpolatedStringFirst, m)) + else singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | '"' '"' '"' { let buf, fin, m = startString args lexbuf // Single quote in triple quote ok, others disallowed - match args.interpolatedStringNesting with + match args.stringNest with | _ :: _ -> errorR(Error(FSComp.SR.lexTripleQuoteInTripleQuote(), lexbuf.LexemeRange)) | _ -> () - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, LexerStringKind.String, m))) else tripleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, LexerStringKind.String, m)) + else tripleQuoteString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } | '@' '"' { let buf, fin, m = startString args lexbuf // Single quote in triple quote ok, others disallowed - match args.interpolatedStringNesting with - | (_, LexerStringStyle.TripleQuote) :: _ -> () + match args.stringNest with + | (_, LexerStringStyle.TripleQuote, _) :: _ -> () | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) | _ -> () - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, LexerStringKind.String, m))) else verbatimString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, LexerStringKind.String, m)) + else verbatimString (buf, fin, m, LexerStringKind.String, args) skip lexbuf } | ("$@" | "@$") '"' { let buf, fin, m = startString args lexbuf // Single quote in triple quote ok, others disallowed - match args.interpolatedStringNesting with - | (_, LexerStringStyle.TripleQuote) :: _ -> () + match args.stringNest with + | (_, LexerStringStyle.TripleQuote, _) :: _ -> () | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) | _ -> () - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, LexerStringKind.InterpolatedStringFirst, m))) else verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, LexerStringKind.InterpolatedStringFirst, m)) + else verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringFirst, args) skip lexbuf } | truewhite+ { if skip then token args skip lexbuf - else WHITESPACE (LexCont.Token args.ifdefStack) } + else WHITESPACE (LexCont.Token(args.ifdefStack, args.stringNest)) } | offwhite+ { if args.lightSyntaxStatus.Status then errorR(Error(FSComp.SR.lexTabsNotAllowed(), lexbuf.LexemeRange)) - if not skip then (WHITESPACE (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + if not skip then WHITESPACE (LexCont.Token(args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | "////" op_char* { // 4+ slash are 1-line comments, online 3 slash are XmlDoc let m = lexbuf.LexemeRange - if not skip then (LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack,1,m))) else singleLineComment (None,1,m,args) skip lexbuf } + if not skip then LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack, args.stringNest, 1, m)) + else singleLineComment (None,1,m,args) skip lexbuf } | "///" op_char* { // Match exactly 3 slash, 4+ slash caught by preceding rule let m = lexbuf.LexemeRange let doc = lexemeTrimLeft lexbuf 3 let sb = (new StringBuilder(100)).Append(doc) - if not skip then (LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack,1,m))) else singleLineComment (Some sb,1,m,args) skip lexbuf } + if not skip then LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack, args.stringNest, 1, m)) + else singleLineComment (Some sb,1,m,args) skip lexbuf } | "//" op_char* { // Need to read all operator symbols too, otherwise it might be parsed by a rule below let m = lexbuf.LexemeRange - if not skip then (LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack,1,m))) else singleLineComment (None,1,m,args) skip lexbuf } + if not skip then LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack, args.stringNest, 1, m)) + else singleLineComment (None,1,m,args) skip lexbuf } | newline - { newline lexbuf; if not skip then (WHITESPACE (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + { newline lexbuf + if not skip then WHITESPACE (LexCont.Token(args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | '`' '`' ([^'`' '\n' '\r' '\t'] | '`' [^'`''\n' '\r' '\t']) + '`' '`' { Keywords.IdentifierToken args lexbuf (lexemeTrimBoth lexbuf 2 2) } @@ -683,7 +701,7 @@ rule token args skip = parse else // add a newline when we don't apply a directive since we consumed a newline getting here newline lexbuf - (HASH_LINE (LexCont.Token args.ifdefStack)) } + HASH_LINE (LexCont.Token (args.ifdefStack, args.stringNest)) } | "<@" { checkExprOp lexbuf; LQUOTE ("<@ @>", false) } @@ -771,10 +789,10 @@ rule token args skip = parse | "{" { - match args.interpolatedStringNesting with + match args.stringNest with | [] -> () - | (counter, style) :: rest -> - args.interpolatedStringNesting <- (counter + 1, style) :: rest + | (counter, style, m) :: rest -> + args.stringNest <- (counter + 1, style, m) :: rest LBRACE } @@ -784,19 +802,19 @@ rule token args skip = parse { // We encounter a '}' in the expression token stream. First check if we're in an interpolated string expression // and continue the string if necessary - match args.interpolatedStringNesting with - | (1, style) :: rest -> - args.interpolatedStringNesting <- rest + match args.stringNest with + | (1, style, _) :: rest -> + args.stringNest <- rest let buf, fin, m = startString args lexbuf if not skip then - (STRING_TEXT (LexCont.String(args.ifdefStack, style, LexerStringKind.InterpolatedStringPart, m))) + STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, style, LexerStringKind.InterpolatedStringPart, m)) else match style with | LexerStringStyle.Verbatim -> verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.SingleQuote -> singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.TripleQuote -> tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf - | (counter, style) :: rest -> - args.interpolatedStringNesting <- (counter - 1, style) :: rest + | (counter, style, m) :: rest -> + args.stringNest <- (counter - 1, style, m) :: rest RBRACE | _ -> RBRACE @@ -837,7 +855,8 @@ rule token args skip = parse | "#!" op_char* { // Treat shebangs like regular comments, but they are only allowed at the start of a file let m = lexbuf.LexemeRange - let tok = shouldStartFile args lexbuf m (0,FSComp.SR.lexHashBangMustBeFirstInFile()) (LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack,1,m))) + let tok = LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack, args.stringNest, 1, m)) + let tok = shouldStartFile args lexbuf m (0,FSComp.SR.lexHashBangMustBeFirstInFile()) tok if not skip then tok else singleLineComment (None,1,m,args) skip lexbuf } | "#light" anywhite* @@ -846,12 +865,14 @@ rule token args skip = parse warning(Error((0,"#light should only occur as the first non-comment text in an F# source file"), lexbuf.LexemeRange)) // TODO unreachable error above, I think? - brianmcn args.lightSyntaxStatus.Status <- true - if not skip then (HASH_LIGHT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + if not skip then HASH_LIGHT (LexCont.Token(args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | ("#indent" | "#light") anywhite+ "\"off\"" { args.lightSyntaxStatus.Status <- false mlCompatWarning (FSComp.SR.lexIndentOffForML()) lexbuf.LexemeRange - if not skip then (HASH_LIGHT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + if not skip then HASH_LIGHT (LexCont.Token (args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | anywhite* "#if" anywhite+ anystring { let m = lexbuf.LexemeRange @@ -862,8 +883,15 @@ rule token args skip = parse // Get the token; make sure it starts at zero position & return let cont, f = - ( if isTrue then (LexCont.EndLine(LexerEndlineContinuation.Token(args.ifdefStack)), endline (LexerEndlineContinuation.Token args.ifdefStack) args skip) - else (LexCont.EndLine(LexerEndlineContinuation.Skip(args.ifdefStack,0,m)), endline (LexerEndlineContinuation.Skip(args.ifdefStack,0,m)) args skip) ) + if isTrue then + let cont = LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Token) + let f = endline LexerEndlineContinuation.Token args skip + cont, f + else + let cont = LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Skip(0, m)) + let f = endline (LexerEndlineContinuation.Skip(0, m)) args skip + cont, f + let tok = shouldStartLine args lexbuf m (FSComp.SR.lexHashIfMustBeFirst()) (HASH_IF(m,lexed,cont)) if not skip then tok else f lexbuf } @@ -875,9 +903,9 @@ rule token args skip = parse | (IfDefIf,_) :: rest -> let m = lexbuf.LexemeRange args.ifdefStack <- (IfDefElse,m) :: rest - let tok = HASH_ELSE(m,lexed, LexCont.EndLine(LexerEndlineContinuation.Skip(args.ifdefStack,0,m))) + let tok = HASH_ELSE(m, lexed, LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Skip(0, m))) let tok = shouldStartLine args lexbuf m (FSComp.SR.lexHashElseMustBeFirst()) tok - if not skip then tok else endline (LexerEndlineContinuation.Skip(args.ifdefStack,0,m)) args skip lexbuf } + if not skip then tok else endline (LexerEndlineContinuation.Skip(0, m)) args skip lexbuf } | anywhite* "#endif" anywhite* ("//" [^'\n''\r']*)? { let lexed = (lexeme lexbuf) @@ -886,12 +914,13 @@ rule token args skip = parse | []-> LEX_FAILURE (FSComp.SR.lexHashEndingNoMatchingIf()) | _ :: rest -> args.ifdefStack <- rest - let tok = HASH_ENDIF(m,lexed,LexCont.EndLine(LexerEndlineContinuation.Token(args.ifdefStack))) + let tok = HASH_ENDIF(m,lexed,LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Token)) let tok = shouldStartLine args lexbuf m (FSComp.SR.lexHashEndifMustBeFirst()) tok - if not skip then tok else endline (LexerEndlineContinuation.Token(args.ifdefStack)) args skip lexbuf } + if not skip then tok else endline LexerEndlineContinuation.Token args skip lexbuf } | "#if" - { let tok = fail args lexbuf (FSComp.SR.lexHashIfMustHaveIdent()) (WHITESPACE (LexCont.Token args.ifdefStack)) + { let tok = WHITESPACE (LexCont.Token (args.ifdefStack, args.stringNest)) + let tok = fail args lexbuf (FSComp.SR.lexHashIfMustHaveIdent()) tok if not skip then tok else token args skip lexbuf } | surrogateChar surrogateChar @@ -900,7 +929,7 @@ rule token args skip = parse { unexpectedChar lexbuf } | eof - { EOF (LexCont.Token args.ifdefStack) } + { EOF (LexCont.Token(args.ifdefStack, args.stringNest)) } // Skips INACTIVE code until if finds #else / #endif matching with the #if or #else @@ -910,10 +939,11 @@ and ifdefSkip n m args skip = parse // If #if is the first thing on the line then increase depth, otherwise skip, because it is invalid (e.g. "(**) #if ...") if (m.StartColumn <> 0) then - if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, n, m))) else ifdefSkip n m args skip lexbuf + if not skip then INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, args.stringNest, n, m)) + else ifdefSkip n m args skip lexbuf else - let tok = INACTIVECODE(LexCont.EndLine(LexerEndlineContinuation.Skip(args.ifdefStack,n+1,m))) - if not skip then tok else endline (LexerEndlineContinuation.Skip(args.ifdefStack,n+1,m)) args skip lexbuf } + let tok = INACTIVECODE(LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Skip(n+1, m))) + if not skip then tok else endline (LexerEndlineContinuation.Skip(n+1, m)) args skip lexbuf } | anywhite* "#else" anywhite* ("//" [^'\n''\r']*)? { let lexed = (lexeme lexbuf) @@ -921,7 +951,8 @@ and ifdefSkip n m args skip = parse // If #else is the first thing on the line then process it, otherwise ignore, because it is invalid (e.g. "(**) #else ...") if (m.StartColumn <> 0) then - if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, n, m))) else ifdefSkip n m args skip lexbuf + if not skip then INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, args.stringNest, n, m)) + else ifdefSkip n m args skip lexbuf elif n = 0 then match args.ifdefStack with | []-> LEX_FAILURE (FSComp.SR.lexHashElseNoMatchingIf()) @@ -929,9 +960,11 @@ and ifdefSkip n m args skip = parse | (IfDefIf,_) :: rest -> let m = lexbuf.LexemeRange args.ifdefStack <- (IfDefElse,m) :: rest - if not skip then (HASH_ELSE(m,lexed,LexCont.EndLine(LexerEndlineContinuation.Token(args.ifdefStack)))) else endline (LexerEndlineContinuation.Token(args.ifdefStack)) args skip lexbuf + if not skip then HASH_ELSE(m,lexed,LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Token)) + else endline LexerEndlineContinuation.Token args skip lexbuf else - if not skip then (INACTIVECODE(LexCont.EndLine(LexerEndlineContinuation.Skip(args.ifdefStack, n, m)))) else endline (LexerEndlineContinuation.Skip(args.ifdefStack, n, m)) args skip lexbuf } + if not skip then INACTIVECODE(LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Skip(n, m))) + else endline (LexerEndlineContinuation.Skip(n, m)) args skip lexbuf } | anywhite* "#endif" anywhite* ("//" [^'\n''\r']*)? { let lexed = lexeme lexbuf @@ -939,17 +972,19 @@ and ifdefSkip n m args skip = parse // If #endif is the first thing on the line then process it, otherwise ignore, because it is invalid (e.g. "(**) #endif ...") if (m.StartColumn <> 0) then - if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, n, m))) else ifdefSkip n m args skip lexbuf + if not skip then INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, args.stringNest, n, m)) + else ifdefSkip n m args skip lexbuf elif n = 0 then match args.ifdefStack with | [] -> LEX_FAILURE (FSComp.SR.lexHashEndingNoMatchingIf()) | _ :: rest -> args.ifdefStack <- rest - if not skip then (HASH_ENDIF(m,lexed,LexCont.EndLine(LexerEndlineContinuation.Token(args.ifdefStack)))) else endline (LexerEndlineContinuation.Token(args.ifdefStack)) args skip lexbuf + if not skip then HASH_ENDIF(m,lexed,LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Token)) + else endline LexerEndlineContinuation.Token args skip lexbuf else - let tok = INACTIVECODE(LexCont.EndLine(LexerEndlineContinuation.Skip(args.ifdefStack,n-1,m))) + let tok = INACTIVECODE(LexCont.EndLine(args.ifdefStack, args.stringNest, LexerEndlineContinuation.Skip(n-1, m))) let tok = shouldStartLine args lexbuf m (FSComp.SR.lexWrongNestedHashEndif()) tok - if not skip then tok else endline (LexerEndlineContinuation.Skip(args.ifdefStack,(n-1),m)) args skip lexbuf } + if not skip then tok else endline (LexerEndlineContinuation.Skip(n-1, m)) args skip lexbuf } | newline { newline lexbuf; ifdefSkip n m args skip lexbuf } @@ -962,10 +997,11 @@ and ifdefSkip n m args skip = parse | _ { // This tries to be nice and get tokens as 'words' because VS uses this when selecting stuff - if not skip then (INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, n, m))) else ifdefSkip n m args skip lexbuf } + if not skip then INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, args.stringNest, n, m)) + else ifdefSkip n m args skip lexbuf } | eof - { EOF (LexCont.IfDefSkip(args.ifdefStack, n, m)) } + { EOF (LexCont.IfDefSkip(args.ifdefStack, args.stringNest, n, m)) } // Called after lexing #if IDENT/#else/#endif - this checks whether there is nothing except end of line // or end of file and then calls the lexing function specified by 'cont' - either token or ifdefSkip @@ -973,63 +1009,78 @@ and endline cont args skip = parse | newline { newline lexbuf match cont with - | LexerEndlineContinuation.Token(ifdefStack) -> if not skip then (WHITESPACE(LexCont.Token ifdefStack)) else token args skip lexbuf - | LexerEndlineContinuation.Skip(ifdefStack, n, m) -> if not skip then (INACTIVECODE (LexCont.IfDefSkip(ifdefStack,n,m))) else ifdefSkip n m args skip lexbuf + | LexerEndlineContinuation.Token -> + if not skip then WHITESPACE(LexCont.Token (args.ifdefStack, args.stringNest)) + else token args skip lexbuf + + | LexerEndlineContinuation.Skip(n, m) -> + if not skip then INACTIVECODE (LexCont.IfDefSkip(args.ifdefStack, args.stringNest, n, m)) + else ifdefSkip n m args skip lexbuf } | eof { match cont with - | LexerEndlineContinuation.Token(ifdefStack) -> (EOF(LexCont.Token ifdefStack)) - | LexerEndlineContinuation.Skip(ifdefStack, n, m) -> (EOF(LexCont.IfDefSkip(ifdefStack,n,m))) + | LexerEndlineContinuation.Token -> + EOF(LexCont.Token(args.ifdefStack, args.stringNest)) + | LexerEndlineContinuation.Skip(n, m) -> + EOF(LexCont.IfDefSkip(args.ifdefStack, args.stringNest, n, m)) } | [^'\r' '\n']+ | _ - { let tok = fail args lexbuf (FSComp.SR.pplexExpectedSingleLineComment()) (WHITESPACE (LexCont.Token args.ifdefStack)) + { let tok = WHITESPACE (LexCont.Token (args.ifdefStack, args.stringNest)) + let tok = fail args lexbuf (FSComp.SR.pplexExpectedSingleLineComment()) tok if not skip then tok else token args skip lexbuf } and singleQuoteString sargs skip = parse | '\\' newline anywhite* { let (_buf, _fin, m, kind, args) = sargs newline lexbuf - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | escape_char { let (buf, _fin, m, kind, args) = sargs addByteChar buf (escape (lexeme lexbuf).[1]) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | trigraph { let (buf, _fin, m, kind, args) = sargs let s = lexeme lexbuf addByteChar buf (trigraph s.[1] s.[2] s.[3]) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | hexGraphShort { let (buf, _fin, m, kind, args) = sargs addUnicodeChar buf (int (hexGraphShort (lexemeTrimLeft lexbuf 2))) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | unicodeGraphShort { let (buf, _fin, m, kind, args) = sargs addUnicodeChar buf (int (unicodeGraphShort (lexemeTrimLeft lexbuf 2))) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | unicodeGraphLong { let (buf, _fin, m, kind, args) = sargs let hexChars = lexemeTrimLeft lexbuf 2 - let result () = if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf + let tok = + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf match unicodeGraphLong hexChars with | Invalid -> - fail args lexbuf (FSComp.SR.lexInvalidUnicodeLiteral hexChars) (result ()) + fail args lexbuf (FSComp.SR.lexInvalidUnicodeLiteral hexChars) tok | SingleChar(c) -> addUnicodeChar buf (int c) - result () + tok | SurrogatePair(hi, lo) -> addUnicodeChar buf (int hi) addUnicodeChar buf (int lo) - result () } + tok } | '"' { let (buf, fin, _m, kind, _args) = sargs @@ -1043,56 +1094,64 @@ and singleQuoteString sargs skip = parse { let (buf, _fin, m, kind, args) = sargs let s = lexeme lexbuf addUnicodeString buf (if kind.IsInterpolated then s.[0..0] else s) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- (1, LexerStringStyle.SingleQuote) :: args.interpolatedStringNesting + args.stringNest <- (1, LexerStringStyle.SingleQuote, m) :: args.stringNest fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | newline { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | ident { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | integer | xinteger { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | anywhite + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } | eof { let (_buf, _fin, m, kind, args) = sargs - EOF (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m)) } + EOF (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) } | surrogateChar surrogateChar // surrogate code points always come in pairs | _ { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.SingleQuote, kind, m))) else singleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf } and verbatimString sargs skip = parse | '"' '"' { let (buf, _fin, m, kind, args) = sargs addByteChar buf '\"' - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf } | '"' { let (buf, fin, _m, kind, _args) = sargs @@ -1106,49 +1165,56 @@ and verbatimString sargs skip = parse { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf } | ("{{" | "}}") { let (buf, _fin, m, kind, args) = sargs let s = lexeme lexbuf addUnicodeString buf (if kind.IsInterpolated then s.[0..0] else s) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf } | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- (1, LexerStringStyle.Verbatim) :: args.interpolatedStringNesting + args.stringNest <- (1, LexerStringStyle.Verbatim, m) :: args.stringNest fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf } | ident { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf } | integer | xinteger { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf } | anywhite + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf } | eof { let (_buf, _fin, m, kind, args) = sargs - EOF (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m)) } + EOF (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) } | surrogateChar surrogateChar // surrogate code points always come in pairs | _ { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.Verbatim, kind, m))) else verbatimString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf } and tripleQuoteString sargs skip = parse | '"' '"' '"' @@ -1159,50 +1225,57 @@ and tripleQuoteString sargs skip = parse { let (buf, _fin, m, kind, args) = sargs newline lexbuf addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) + else tripleQuoteString sargs skip lexbuf } // The rest is to break into pieces to allow double-click-on-word and other such things | ident { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) + else tripleQuoteString sargs skip lexbuf } | integer | xinteger { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) + else tripleQuoteString sargs skip lexbuf } | anywhite + { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) + else tripleQuoteString sargs skip lexbuf } | ("{{" | "}}") { let (buf, _fin, m, kind, args) = sargs let s = lexeme lexbuf addUnicodeString buf (if kind.IsInterpolated then s.[0..0] else s) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) + else tripleQuoteString sargs skip lexbuf } | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.interpolatedStringNesting <- (1, LexerStringStyle.TripleQuote) :: args.interpolatedStringNesting + args.stringNest <- (1, LexerStringStyle.TripleQuote, m) :: args.stringNest fin.Finish buf kind true else addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) + else tripleQuoteString sargs skip lexbuf } | eof { let (_buf, _fin, m, kind, args) = sargs - EOF (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m)) } + EOF (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) } | surrogateChar surrogateChar // surrogate code points always come in pairs | _ { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) - if not skip then (STRING_TEXT (LexCont.String(args.ifdefStack, LexerStringStyle.TripleQuote, kind, m))) else tripleQuoteString sargs skip lexbuf } + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) + else tripleQuoteString sargs skip lexbuf } // Parsing single-line comment - we need to split it into words for Visual Studio IDE and singleLineComment cargs skip = parse @@ -1211,78 +1284,95 @@ and singleLineComment cargs skip = parse trySaveXmlDoc lexbuf buff newline lexbuf // Saves the documentation (if we're collecting any) into a buffer-local variable. - if not skip then (LINE_COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + if not skip then LINE_COMMENT (LexCont.Token(args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | eof { let _, _n, _m, args = cargs // NOTE: it is legal to end a file with this comment, so we'll return EOF as a token - EOF (LexCont.Token args.ifdefStack) } + EOF (LexCont.Token(args.ifdefStack, args.stringNest)) } | [^ ' ' '\n' '\r' ]+ | anywhite+ { let buff, n, m, args = cargs // Append the current token to the XML documentation if we're collecting it tryAppendXmlDoc buff (lexeme lexbuf) - if not skip then (LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack, n, m))) else singleLineComment (buff,n, m, args) skip lexbuf } + if not skip then LINE_COMMENT (LexCont.SingleLineComment(args.ifdefStack, args.stringNest, n, m)) + else singleLineComment (buff, n, m, args) skip lexbuf } | surrogateChar surrogateChar | _ { let _, _n, _m, args = cargs - if not skip then (LINE_COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + if not skip then LINE_COMMENT (LexCont.Token(args.ifdefStack, args.stringNest)) + else token args skip lexbuf } and comment cargs skip = parse | char { let n, m, args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } + if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) + else comment (n, m, args) skip lexbuf } | '"' { let n, m, args = cargs - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } + if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, n, m)) + else stringInComment n m args skip lexbuf } | '"' '"' '"' { let n, m, args = cargs - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } + if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, n, m)) + else tripleQuoteStringInComment n m args skip lexbuf } | '@' '"' { let n, m, args = cargs - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } + if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, n, m)) + else verbatimStringInComment n m args skip lexbuf } | "(*)" { let n, m, args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment cargs skip lexbuf } + if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) + else comment cargs skip lexbuf } | '(' '*' { let n, m, args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n+1, m))) else comment (n+1,m,args) skip lexbuf } + if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n+1, m)) + else comment (n+1,m,args) skip lexbuf } | newline { let n, m, args = cargs newline lexbuf - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment cargs skip lexbuf } + if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) + else comment cargs skip lexbuf } | "*)" { let n, m, args = cargs - if n > 1 then if not skip then (COMMENT (LexCont.Comment(args.ifdefStack,n-1,m))) else comment (n-1,m,args) skip lexbuf - else if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + if n > 1 then + if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n-1, m)) + else comment (n-1,m,args) skip lexbuf + else + if not skip then COMMENT (LexCont.Token(args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | anywhite+ | [^ '\'' '(' '*' '\n' '\r' '"' ')' '@' ' ' '\t' ]+ { let n, m, args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment cargs skip lexbuf } + if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) + else comment cargs skip lexbuf } | eof { let n, m, args = cargs - EOF (LexCont.Comment(args.ifdefStack, n, m)) } + EOF (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) } | surrogateChar surrogateChar | _ { let n, m, args = cargs - if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } + if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) + else comment (n, m, args) skip lexbuf } and stringInComment n m args skip = parse // Follow string lexing, skipping tokens until it finishes | '\\' newline anywhite* { newline lexbuf - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } + if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, n, m)) + else stringInComment n m args skip lexbuf } | escape_char | trigraph @@ -1293,69 +1383,82 @@ and stringInComment n m args skip = parse | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } + { if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, n, m)) + else stringInComment n m args skip lexbuf } | '"' - { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } + { if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) + else comment (n, m, args) skip lexbuf } | newline { newline lexbuf - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } + if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, n, m)) + else stringInComment n m args skip lexbuf } | eof - { EOF (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m)) } + { EOF (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.SingleQuote, n, m))) else stringInComment n m args skip lexbuf } + { if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, n, m)) + else stringInComment n m args skip lexbuf } and verbatimStringInComment n m args skip = parse // Follow verbatimString lexing, in short, skip double-quotes and other chars until we hit a single quote | '"' '"' - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, n, m)) + else verbatimStringInComment n m args skip lexbuf } | '"' - { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } + { if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) + else comment (n, m, args) skip lexbuf } | ident | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, n, m)) + else verbatimStringInComment n m args skip lexbuf } | newline { newline lexbuf - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } + if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, n, m)) + else verbatimStringInComment n m args skip lexbuf } | eof - { EOF (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m)) } + { EOF (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.Verbatim, n, m))) else verbatimStringInComment n m args skip lexbuf } + { if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, n, m)) + else verbatimStringInComment n m args skip lexbuf } and tripleQuoteStringInComment n m args skip = parse // Follow tripleQuoteString lexing | '"' '"' '"' - { if not skip then (COMMENT (LexCont.Comment(args.ifdefStack, n, m))) else comment (n, m, args) skip lexbuf } + { if not skip then COMMENT (LexCont.Comment(args.ifdefStack, args.stringNest, n, m)) + else comment (n, m, args) skip lexbuf } | ident | integer | xinteger | anywhite + - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } + { if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, n, m)) + else tripleQuoteStringInComment n m args skip lexbuf } | newline { newline lexbuf - if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } + if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, n, m)) + else tripleQuoteStringInComment n m args skip lexbuf } | eof - { EOF (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m)) } + { EOF (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, n, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.StringInComment(args.ifdefStack, LexerStringStyle.TripleQuote, n, m))) else tripleQuoteStringInComment n m args skip lexbuf } + { if not skip then COMMENT (LexCont.StringInComment(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, n, m)) + else tripleQuoteStringInComment n m args skip lexbuf } and mlOnly m args skip = parse @@ -1363,23 +1466,30 @@ and mlOnly m args skip = parse { let buf = ByteBuffer.Create 100 let m2 = lexbuf.LexemeRange let _ = singleQuoteString (buf, LexerStringFinisher.Default, m2, LexerStringKind.String, args) skip lexbuf - if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } + if not skip then COMMENT (LexCont.MLOnly(args.ifdefStack, args.stringNest, m)) + else mlOnly m args skip lexbuf } | newline - { newline lexbuf; if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } + { newline lexbuf + if not skip then COMMENT (LexCont.MLOnly(args.ifdefStack, args.stringNest, m)) + else mlOnly m args skip lexbuf } | "(*ENDIF-CAML*)" - { if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + { if not skip then COMMENT (LexCont.Token(args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | "(*ENDIF-OCAML*)" - { if not skip then (COMMENT (LexCont.Token args.ifdefStack)) else token args skip lexbuf } + { if not skip then COMMENT (LexCont.Token(args.ifdefStack, args.stringNest)) + else token args skip lexbuf } | [^ '(' '"' '\n' '\r' ]+ - { if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } + { if not skip then COMMENT (LexCont.MLOnly(args.ifdefStack, args.stringNest, m)) + else mlOnly m args skip lexbuf } | eof - { EOF (LexCont.MLOnly(args.ifdefStack, m)) } + { EOF (LexCont.MLOnly(args.ifdefStack, args.stringNest, m)) } | surrogateChar surrogateChar | _ - { if not skip then (COMMENT (LexCont.MLOnly(args.ifdefStack, m))) else mlOnly m args skip lexbuf } + { if not skip then COMMENT (LexCont.MLOnly(args.ifdefStack, args.stringNest, m)) + else mlOnly m args skip lexbuf } diff --git a/src/fsharp/lexhelp.fs b/src/fsharp/lexhelp.fs index 155edc91654..4e724c08194 100644 --- a/src/fsharp/lexhelp.fs +++ b/src/fsharp/lexhelp.fs @@ -49,14 +49,14 @@ type LexResourceManager(?capacity: int) = res /// Lexer parameters -type lexargs = +type LexArgs = { defines: string list mutable ifdefStack: LexerIfdefStack resourceManager: LexResourceManager lightSyntaxStatus : LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool - mutable interpolatedStringNesting: (int* LexerStringStyle) list + mutable stringNest: LexerInterpolatedStringNesting pathMap: PathMap } /// possible results of lexing a long Unicode escape sequence in a string literal, e.g. "\U0001F47D", @@ -66,7 +66,7 @@ type LongUnicodeLexResult = | SingleChar of uint16 | Invalid -let mkLexargs (_filename, defines, lightSyntaxStatus, resourceManager, ifdefStack, errorLogger, pathMap:PathMap) = +let mkLexargs (defines, lightSyntaxStatus, resourceManager, ifdefStack, errorLogger, pathMap:PathMap) = { defines = defines ifdefStack= ifdefStack @@ -74,7 +74,7 @@ let mkLexargs (_filename, defines, lightSyntaxStatus, resourceManager, ifdefStac resourceManager=resourceManager errorLogger=errorLogger applyLineDirectives=true - interpolatedStringNesting = [] + stringNest = [] pathMap=pathMap } diff --git a/src/fsharp/lexhelp.fsi b/src/fsharp/lexhelp.fsi index 388deeac57b..ece37441bdf 100644 --- a/src/fsharp/lexhelp.fsi +++ b/src/fsharp/lexhelp.fsi @@ -26,16 +26,14 @@ type LightSyntaxStatus = type LexResourceManager = new: ?capacity: int -> LexResourceManager -type lexargs = +type LexArgs = { defines: string list mutable ifdefStack: LexerIfdefStack resourceManager: LexResourceManager lightSyntaxStatus: LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool - /// The degree of nesting of '{..}' and the style of the string to continue afterwards, in an interpolation fill. - /// Nesting counters and styles of outer interpolating strings are pushed on this stack. - mutable interpolatedStringNesting: (int* LexerStringStyle) list + mutable stringNest: LexerInterpolatedStringNesting pathMap: PathMap } type LongUnicodeLexResult = @@ -45,7 +43,7 @@ type LongUnicodeLexResult = val resetLexbufPos: string -> UnicodeLexing.Lexbuf -> unit -val mkLexargs: 'a * string list * LightSyntaxStatus * LexResourceManager * LexerIfdefStack * ErrorLogger * PathMap -> lexargs +val mkLexargs: string list * LightSyntaxStatus * LexResourceManager * LexerIfdefStack * ErrorLogger * PathMap -> LexArgs val reusingLexbufForParsing: UnicodeLexing.Lexbuf -> (unit -> 'a) -> 'a @@ -92,9 +90,9 @@ exception IndentationProblem of string * Range.range module Keywords = - val KeywordOrIdentifierToken: lexargs -> UnicodeLexing.Lexbuf -> string -> token + val KeywordOrIdentifierToken: LexArgs -> UnicodeLexing.Lexbuf -> string -> token - val IdentifierToken: lexargs -> UnicodeLexing.Lexbuf -> string -> token + val IdentifierToken: LexArgs -> UnicodeLexing.Lexbuf -> string -> token val DoesIdentifierNeedQuotation: string -> bool diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index e40b87ad4fa..a18976f24d1 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -93,22 +93,26 @@ let raiseParseErrorAt m s = let checkEndOfFileError t = match t with - | LexCont.IfDefSkip(_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInHashIf()) - | LexCont.String (_, LexerStringStyle.SingleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInString()) - | LexCont.String (_, LexerStringStyle.TripleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteString()) - | LexCont.String (_, LexerStringStyle.Verbatim, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimString()) - | LexCont.Comment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) - | LexCont.SingleLineComment (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) - | LexCont.StringInComment (_, LexerStringStyle.SingleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInStringInComment()) - | LexCont.StringInComment (_, LexerStringStyle.Verbatim, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimStringInComment()) - | LexCont.StringInComment (_, LexerStringStyle.TripleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteStringInComment()) - | LexCont.MLOnly (_, m) -> reportParseErrorAt m (FSComp.SR.parsEofInIfOcaml()) - | LexCont.EndLine(LexerEndlineContinuation.Skip(_, _, m)) -> reportParseErrorAt m (FSComp.SR.parsEofInDirective()) - | LexCont.EndLine(LexerEndlineContinuation.Token(stack)) - | LexCont.Token(stack) -> - match stack with + | LexCont.IfDefSkip(_, _, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInHashIf()) + | LexCont.String (_, _, LexerStringStyle.SingleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInString()) + | LexCont.String (_, _, LexerStringStyle.TripleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteString()) + | LexCont.String (_, _, LexerStringStyle.Verbatim, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimString()) + | LexCont.Comment (_, _, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) + | LexCont.SingleLineComment (_, _, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) + | LexCont.StringInComment (_, _, LexerStringStyle.SingleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInStringInComment()) + | LexCont.StringInComment (_, _, LexerStringStyle.Verbatim, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimStringInComment()) + | LexCont.StringInComment (_, _, LexerStringStyle.TripleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteStringInComment()) + | LexCont.MLOnly (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInIfOcaml()) + | LexCont.EndLine(_, _, LexerEndlineContinuation.Skip(_, m)) -> reportParseErrorAt m (FSComp.SR.parsEofInDirective()) + | LexCont.EndLine(endifs, nesting, LexerEndlineContinuation.Token) + | LexCont.Token(endifs, nesting) -> + match endifs with | [] -> () | (_, m) :: _ -> reportParseErrorAt m (FSComp.SR.parsNoHashEndIfFound()) + match nesting with + | [] -> () + | (1, _, m) :: _ -> reportParseErrorAt m (FSComp.SR.parsNoEndOfInterpStringFill()) + | (_, _, m) :: _ -> reportParseErrorAt m (FSComp.SR.parsNoEndOfInterpString()) type BindingSet = BindingSetPreAttrs of range * bool * bool * (SynAttributes -> SynAccess option -> SynAttributes * SynBinding list) * range diff --git a/src/fsharp/pplex.fsl b/src/fsharp/pplex.fsl index fd984c41b45..d4227d28733 100644 --- a/src/fsharp/pplex.fsl +++ b/src/fsharp/pplex.fsl @@ -15,7 +15,7 @@ open Internal.Utilities.Text.Lexing let lexeme (lexbuf : UnicodeLexing.Lexbuf) = UnicodeLexing.Lexbuf.LexemeString lexbuf -let fail (args : lexargs) (lexbuf:UnicodeLexing.Lexbuf) e = +let fail (args : LexArgs) (lexbuf:UnicodeLexing.Lexbuf) e = let m = lexbuf.LexemeRange args.errorLogger.ErrorR(Error(e,m)) PPParser.EOF diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index 31263f4e282..9df21f8919a 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -1446,7 +1446,7 @@ module internal ParseAndCheckFile = // When analyzing files using ParseOneFile, i.e. for the use of editing clients, we do not apply line directives. // TODO(pathmap): expose PathMap on the service API, and thread it through here - let lexargs = mkLexargs(fileName, defines, lightSyntaxStatus, lexResourceManager, [], errHandler.ErrorLogger, PathMap.empty) + let lexargs = mkLexargs(defines, lightSyntaxStatus, lexResourceManager, [], errHandler.ErrorLogger, PathMap.empty) let lexargs = { lexargs with applyLineDirectives = false } let tokenizer = LexFilter.LexFilter(lightSyntaxStatus, options.CompilingFsLib, Lexer.token lexargs true, lexbuf) diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index ecb624f3106..b751c45fd77 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -325,9 +325,13 @@ module internal TokenClassifications = module internal TestExpose = let TokenInfo tok = TokenClassifications.tokenInfo tok - //---------------------------------------------------------------------------- - // Lexer states encoded to/from integers - //-------------------------------------------------------------------------- +/// Lexer states are encoded to/from integers. Typically one lexer state is +/// keep at the end of each line in an IDE service. IDE services are sometimes highly limited in the +/// memory they can use and this per-line state can be a significant cost if it associates with +/// many allocated objects. +/// +/// The encoding is lossy so some incremental lexing scenarios such as deeply nested #if +/// or accurate error messages from lexing for mismtached #if are not supported. [] type FSharpTokenizerLexState = { PosBits: int64 @@ -377,12 +381,12 @@ module internal LexerStateEncoding = | INTERP_STRING_BEGIN_END _ | INTERP_STRING_END _ | BYTEARRAY _ - | STRING _ -> LexCont.Token(prevLexcont.LexerIfdefStack) + | STRING _ -> LexCont.Token(prevLexcont.LexerIfdefStack, prevLexcont.LexerInterpStringNesting) | _ -> prevLexcont // Note that this will discard all lexcont state, including the ifdefStack. - let revertToDefaultLexCont = LexCont.Token [] + let revertToDefaultLexCont = LexCont.Token ([], []) let lexstateNumBits = 4 let ncommentsNumBits = 4 @@ -390,12 +394,14 @@ module internal LexerStateEncoding = let ifdefstackCountNumBits = 8 let ifdefstackNumBits = 24 // 0 means if, 1 means else let stringKindBits = 3 + let nestingBits = 12 let _ = assert (lexstateNumBits + ncommentsNumBits + hardwhiteNumBits + ifdefstackCountNumBits + ifdefstackNumBits - + stringKindBits <= 64) + + stringKindBits + + nestingBits <= 64) let lexstateStart = 0 let ncommentsStart = lexstateNumBits @@ -403,6 +409,7 @@ module internal LexerStateEncoding = let ifdefstackCountStart = lexstateNumBits+ncommentsNumBits+hardwhiteNumBits let ifdefstackStart = lexstateNumBits+ncommentsNumBits+hardwhiteNumBits+ifdefstackCountNumBits let stringKindStart = lexstateNumBits+ncommentsNumBits+hardwhiteNumBits+ifdefstackCountNumBits+ifdefstackNumBits + let nestingStart = lexstateNumBits+ncommentsNumBits+hardwhiteNumBits+ifdefstackCountNumBits+ifdefstackNumBits+stringKindBits let lexstateMask = Bits.mask64 lexstateStart lexstateNumBits let ncommentsMask = Bits.mask64 ncommentsStart ncommentsNumBits @@ -410,6 +417,7 @@ module internal LexerStateEncoding = let ifdefstackCountMask = Bits.mask64 ifdefstackCountStart ifdefstackCountNumBits let ifdefstackMask = Bits.mask64 ifdefstackStart ifdefstackNumBits let stringKindMask = Bits.mask64 stringKindStart stringKindBits + let nestingMask = Bits.mask64 nestingStart nestingBits let bitOfBool b = if b then 1 else 0 let boolOfBit n = (n = 1L) @@ -420,7 +428,20 @@ module internal LexerStateEncoding = let inline lexStateOfColorState (state: FSharpTokenizerColorState) = (int64 state <<< lexstateStart) &&& lexstateMask - let encodeLexCont (colorState: FSharpTokenizerColorState, numComments, b: pos, ifdefStack, light, stringKind: LexerStringKind) = + let encodeStringStyle kind = + match kind with + | LexerStringStyle.Verbatim -> 1 + | LexerStringStyle.TripleQuote -> 2 + | LexerStringStyle.SingleQuote -> 3 + + let decodeStringStyle kind = + match kind with + | 0 -> LexerStringStyle.SingleQuote + | 1 -> LexerStringStyle.Verbatim + | 2 -> LexerStringStyle.TripleQuote + | _ -> assert false; LexerStringStyle.SingleQuote + + let encodeLexCont (colorState: FSharpTokenizerColorState, numComments, b: pos, ifdefStack, light, stringKind: LexerStringKind, nesting) = let mutable ifdefStackCount = 0 let mutable ifdefStackBits = 0 for ifOrElse in ifdefStack do @@ -435,6 +456,22 @@ module internal LexerStateEncoding = (if stringKind.IsInterpolated then 0b010 else 0) ||| (if stringKind.IsInterpolatedFirst then 0b001 else 0) + let nestingValue = + let tag1, i1, kind1, rest = + match nesting with + | [] -> false, 0, 0, [] + | (i1, kind1, _)::rest -> true, i1, encodeStringStyle kind1, rest + let tag2, i2, kind2 = + match rest with + | [] -> false, 0, 0 + | (i2, kind2, _)::_ -> true, i2, encodeStringStyle kind2 + (if tag1 then 0b100000000000 else 0) ||| + (if tag2 then 0b010000000000 else 0) ||| + ((i1 <<< 7) &&& 0b001110000000) ||| + ((i2 <<< 4) &&& 0b000001110000) ||| + ((kind1 <<< 2) &&& 0b000000001100) ||| + ((kind2 <<< 0) &&& 0b000000000011) + let bits = lexStateOfColorState colorState ||| ((numComments <<< ncommentsStart) &&& ncommentsMask) @@ -442,6 +479,7 @@ module internal LexerStateEncoding = ||| ((int64 ifdefStackCount <<< ifdefstackCountStart) &&& ifdefstackCountMask) ||| ((int64 ifdefStackBits <<< ifdefstackStart) &&& ifdefstackMask) ||| ((int64 stringKindValue <<< stringKindStart) &&& stringKindMask) + ||| ((int64 nestingValue <<< nestingStart) &&& nestingMask) { PosBits = b.Encoding OtherBits = bits } @@ -472,73 +510,151 @@ module internal LexerStateEncoding = let hardwhite = boolOfBit ((bits &&& hardwhitePosMask) >>> hardwhitePosStart) - (colorState, ncomments, pos, ifDefs, hardwhite, stringKind) - - let encodeLexInt lightSyntaxStatus (lexcont: LexerWhitespaceContinuation) = - let tag, n1, p1, ifd, stringKind = - match lexcont with - | LexCont.Token ifd -> FSharpTokenizerColorState.Token, 0L, pos0, ifd, LexerStringKind.String - | LexCont.IfDefSkip (ifd, n, m) -> FSharpTokenizerColorState.IfDefSkip, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.EndLine(LexerEndlineContinuation.Skip(ifd, n, m)) -> FSharpTokenizerColorState.EndLineThenSkip, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.EndLine(LexerEndlineContinuation.Token ifd) -> FSharpTokenizerColorState.EndLineThenToken, 0L, pos0, ifd, LexerStringKind.String - | LexCont.String (ifd, LexerStringStyle.SingleQuote, kind, m) -> FSharpTokenizerColorState.String, 0L, m.Start, ifd, kind - | LexCont.String (ifd, LexerStringStyle.Verbatim, kind, m) -> FSharpTokenizerColorState.VerbatimString, 0L, m.Start, ifd, kind - | LexCont.String (ifd, LexerStringStyle.TripleQuote, kind, m) -> FSharpTokenizerColorState.TripleQuoteString, 0L, m.Start, ifd, kind - | LexCont.Comment (ifd, n, m) -> FSharpTokenizerColorState.Comment, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.SingleLineComment (ifd, n, m) -> FSharpTokenizerColorState.SingleLineComment, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.StringInComment (ifd, LexerStringStyle.SingleQuote, n, m) -> FSharpTokenizerColorState.StringInComment, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.StringInComment (ifd, LexerStringStyle.Verbatim, n, m) -> FSharpTokenizerColorState.VerbatimStringInComment, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.StringInComment (ifd, LexerStringStyle.TripleQuote, n, m) -> FSharpTokenizerColorState.TripleQuoteStringInComment, int64 n, m.Start, ifd, LexerStringKind.String - | LexCont.MLOnly (ifd, m) -> FSharpTokenizerColorState.CamlOnly, 0L, m.Start, ifd, LexerStringKind.String - encodeLexCont (tag, n1, p1, ifd, lightSyntaxStatus, stringKind) - + let nestingValue = int32 ((bits &&& nestingMask) >>> nestingStart) + let nesting : LexerInterpolatedStringNesting = + let tag1 = ((nestingValue &&& 0b100000000000) = 0b100000000000) + let tag2 = ((nestingValue &&& 0b010000000000) = 0b010000000000) + let i1 = ((nestingValue &&& 0b001110000000) >>> 7) + let i2 = ((nestingValue &&& 0b000001110000) >>> 4) + let kind1 = ((nestingValue &&& 0b000000001100) >>> 2) + let kind2 = ((nestingValue &&& 0b000000000011) >>> 0) + [ if tag1 then + i1, decodeStringStyle kind1, range0 + if tag2 then + i2, decodeStringStyle kind2, range0 + ] + + (colorState, ncomments, pos, ifDefs, hardwhite, stringKind, nesting) + + let encodeLexInt lightStatus (lexcont: LexerWhitespaceContinuation) = + match lexcont with + | LexCont.Token (ifdefs, nesting) -> + encodeLexCont (FSharpTokenizerColorState.Token, 0L, pos0, ifdefs, lightStatus, LexerStringKind.String, nesting) + | LexCont.IfDefSkip (ifdefs, nesting, n, m) -> + encodeLexCont (FSharpTokenizerColorState.IfDefSkip, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) + | LexCont.EndLine(ifdefs, nesting, econt) -> + match econt with + | LexerEndlineContinuation.Skip(n, m) -> + encodeLexCont (FSharpTokenizerColorState.EndLineThenSkip, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) + | LexerEndlineContinuation.Token -> + encodeLexCont (FSharpTokenizerColorState.EndLineThenToken, 0L, pos0, ifdefs, lightStatus, LexerStringKind.String, nesting) + | LexCont.String (ifdefs, nesting, style, kind, m) -> + let state = + match style with + | LexerStringStyle.SingleQuote -> FSharpTokenizerColorState.String + | LexerStringStyle.Verbatim -> FSharpTokenizerColorState.VerbatimString + | LexerStringStyle.TripleQuote -> FSharpTokenizerColorState.TripleQuoteString + encodeLexCont (state, 0L, m.Start, ifdefs, lightStatus, kind, nesting) + | LexCont.Comment (ifdefs, nesting, n, m) -> + encodeLexCont (FSharpTokenizerColorState.Comment, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) + | LexCont.SingleLineComment (ifdefs, nesting, n, m) -> + encodeLexCont (FSharpTokenizerColorState.SingleLineComment, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) + | LexCont.StringInComment (ifdefs, nesting, style, n, m) -> + let state = + match style with + | LexerStringStyle.SingleQuote -> FSharpTokenizerColorState.StringInComment + | LexerStringStyle.Verbatim -> FSharpTokenizerColorState.VerbatimStringInComment + | LexerStringStyle.TripleQuote -> FSharpTokenizerColorState.TripleQuoteStringInComment + encodeLexCont (state, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) + | LexCont.MLOnly (ifdefs, nesting, m) -> + encodeLexCont (FSharpTokenizerColorState.CamlOnly, 0L, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) + let decodeLexInt (state: FSharpTokenizerLexState) = - let tag, n1, p1, ifd, lightSyntaxStatusInitial, stringKind = decodeLexCont state + let tag, n1, p1, ifdefs, lightSyntaxStatusInitial, stringKind, nesting = decodeLexCont state let lexcont = match tag with - | FSharpTokenizerColorState.Token -> LexCont.Token ifd - | FSharpTokenizerColorState.IfDefSkip -> LexCont.IfDefSkip (ifd, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.String -> LexCont.String (ifd, LexerStringStyle.SingleQuote, stringKind, mkRange "file" p1 p1) - | FSharpTokenizerColorState.Comment -> LexCont.Comment (ifd, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.SingleLineComment -> LexCont.SingleLineComment (ifd, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.StringInComment -> LexCont.StringInComment (ifd, LexerStringStyle.SingleQuote, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.VerbatimStringInComment -> LexCont.StringInComment (ifd, LexerStringStyle.Verbatim, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.TripleQuoteStringInComment -> LexCont.StringInComment (ifd, LexerStringStyle.TripleQuote, n1, mkRange "file" p1 p1) - | FSharpTokenizerColorState.CamlOnly -> LexCont.MLOnly (ifd, mkRange "file" p1 p1) - | FSharpTokenizerColorState.VerbatimString -> LexCont.String (ifd, LexerStringStyle.Verbatim, stringKind, mkRange "file" p1 p1) - | FSharpTokenizerColorState.TripleQuoteString -> LexCont.String (ifd, LexerStringStyle.TripleQuote, stringKind, mkRange "file" p1 p1) - | FSharpTokenizerColorState.EndLineThenSkip -> LexCont.EndLine(LexerEndlineContinuation.Skip(ifd, n1, mkRange "file" p1 p1)) - | FSharpTokenizerColorState.EndLineThenToken -> LexCont.EndLine(LexerEndlineContinuation.Token ifd) - | _ -> LexCont.Token [] + | FSharpTokenizerColorState.Token -> + LexCont.Token (ifdefs, nesting) + | FSharpTokenizerColorState.IfDefSkip -> + LexCont.IfDefSkip (ifdefs, nesting, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.String -> + LexCont.String (ifdefs, nesting, LexerStringStyle.SingleQuote, stringKind, mkRange "file" p1 p1) + | FSharpTokenizerColorState.Comment -> + LexCont.Comment (ifdefs, nesting, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.SingleLineComment -> + LexCont.SingleLineComment (ifdefs, nesting, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.StringInComment -> + LexCont.StringInComment (ifdefs, nesting, LexerStringStyle.SingleQuote, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.VerbatimStringInComment -> + LexCont.StringInComment (ifdefs, nesting, LexerStringStyle.Verbatim, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.TripleQuoteStringInComment -> + LexCont.StringInComment (ifdefs, nesting, LexerStringStyle.TripleQuote, n1, mkRange "file" p1 p1) + | FSharpTokenizerColorState.CamlOnly -> + LexCont.MLOnly (ifdefs, nesting, mkRange "file" p1 p1) + | FSharpTokenizerColorState.VerbatimString -> + LexCont.String (ifdefs, nesting, LexerStringStyle.Verbatim, stringKind, mkRange "file" p1 p1) + | FSharpTokenizerColorState.TripleQuoteString -> + LexCont.String (ifdefs, nesting, LexerStringStyle.TripleQuote, stringKind, mkRange "file" p1 p1) + | FSharpTokenizerColorState.EndLineThenSkip -> + LexCont.EndLine(ifdefs, nesting, LexerEndlineContinuation.Skip(n1, mkRange "file" p1 p1)) + | FSharpTokenizerColorState.EndLineThenToken -> + LexCont.EndLine(ifdefs, nesting, LexerEndlineContinuation.Token) + | _ -> LexCont.Token ([], nesting) lightSyntaxStatusInitial, lexcont + let argsWithIfDefs ifdefs args = + if args.ifdefStack = ifdefs then + args + else + {args with ifdefStack = ifdefs} + + let argsWithInterpolatedStringNesting nesting args = + if args.stringNest = nesting then + args + else + {args with stringNest = nesting} + let callLexCont lexcont args skip lexbuf = - let argsWithIfDefs ifd = - if args.ifdefStack = ifd then - args - else - {args with ifdefStack = ifd} + match lexcont with - | LexCont.EndLine cont -> Lexer.endline cont args skip lexbuf - | LexCont.Token ifd -> Lexer.token (argsWithIfDefs ifd) skip lexbuf - | LexCont.IfDefSkip (ifd, n, m) -> Lexer.ifdefSkip n m (argsWithIfDefs ifd) skip lexbuf - // Q: What's this magic 100 number for? Q: it's just an initial buffer size. - | LexCont.String (ifd, style, kind, m) -> + | LexCont.EndLine (ifdefs, nesting, cont) -> + let args = argsWithIfDefs ifdefs args + let args = argsWithInterpolatedStringNesting nesting args + Lexer.endline cont args skip lexbuf + + | LexCont.Token (ifdefs, nesting) -> + let args = argsWithIfDefs ifdefs args + let args = argsWithInterpolatedStringNesting nesting args + Lexer.token args skip lexbuf + + | LexCont.IfDefSkip (ifdefs, nesting, n, m) -> + let args = argsWithIfDefs ifdefs args + let args = argsWithInterpolatedStringNesting nesting args + Lexer.ifdefSkip n m args skip lexbuf + + | LexCont.String (ifdefs, nesting, style, kind, m) -> + let args = argsWithIfDefs ifdefs args + let args = argsWithInterpolatedStringNesting nesting args let buf = ByteBuffer.Create 100 - let args = (buf, LexerStringFinisher.Default, m, kind, argsWithIfDefs ifd) + let args = (buf, LexerStringFinisher.Default, m, kind, args) match style with | LexerStringStyle.SingleQuote -> Lexer.singleQuoteString args skip lexbuf | LexerStringStyle.Verbatim -> Lexer.verbatimString args skip lexbuf | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteString args skip lexbuf - | LexCont.Comment (ifd, n, m) -> Lexer.comment (n, m, argsWithIfDefs ifd) skip lexbuf - // The first argument is 'None' because we don't need XML comments when called from VS - | LexCont.SingleLineComment (ifd, n, m) -> Lexer.singleLineComment (None, n, m, argsWithIfDefs ifd) skip lexbuf - | LexCont.StringInComment (ifd, style, n, m) -> + + | LexCont.Comment (ifdefs, nesting, n, m) -> + let args = argsWithIfDefs ifdefs args + let args = argsWithInterpolatedStringNesting nesting args + Lexer.comment (n, m, args) skip lexbuf + + | LexCont.SingleLineComment (ifdefs, nesting, n, m) -> + let args = argsWithIfDefs ifdefs args + let args = argsWithInterpolatedStringNesting nesting args + // The first argument is 'None' because we don't need XML comments when called from VS tokenizer + Lexer.singleLineComment (None, n, m, args) skip lexbuf + + | LexCont.StringInComment (ifdefs, nesting, style, n, m) -> + let args = argsWithIfDefs ifdefs args + let args = argsWithInterpolatedStringNesting nesting args match style with - | LexerStringStyle.SingleQuote -> Lexer.stringInComment n m (argsWithIfDefs ifd) skip lexbuf - | LexerStringStyle.Verbatim -> Lexer.verbatimStringInComment n m (argsWithIfDefs ifd) skip lexbuf - | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteStringInComment n m (argsWithIfDefs ifd) skip lexbuf - | LexCont.MLOnly (ifd, m) -> Lexer.mlOnly m (argsWithIfDefs ifd) skip lexbuf + | LexerStringStyle.SingleQuote -> Lexer.stringInComment n m args skip lexbuf + | LexerStringStyle.Verbatim -> Lexer.verbatimStringInComment n m args skip lexbuf + | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteStringInComment n m args skip lexbuf + + | LexCont.MLOnly (ifdefs, nesting, m) -> + let args = argsWithIfDefs ifdefs args + let args = argsWithInterpolatedStringNesting nesting args + Lexer.mlOnly m args skip lexbuf //---------------------------------------------------------------------------- // Colorization @@ -555,16 +671,17 @@ type SingleLineTokenState = [] type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, maxLength: int option, - filename: Option, - lexArgsLightOn: lexargs, - lexArgsLightOff: lexargs) = + filename: string option, + lexArgsLightOn: LexArgs, + lexArgsLightOff: LexArgs) = let skip = false // don't skip whitespace in the lexer let mutable singleLineTokenState = SingleLineTokenState.BeforeHash - let fsx = match filename with - | None -> false - | Some value -> CompileOps.IsScript value + let fsx = + match filename with + | None -> false + | Some value -> CompileOps.IsScript value // ---------------------------------------------------------------------------------- // This implements post-processing of #directive tokens - not very elegant, but it works... @@ -824,14 +941,14 @@ type FSharpSourceTokenizer(defineConstants: string list, filename: string option let lexResourceManager = new Lexhelp.LexResourceManager() - let lexArgsLightOn = mkLexargs(filename, defineConstants, LightSyntaxStatus(true, false), lexResourceManager, [], DiscardErrorsLogger, PathMap.empty) - let lexArgsLightOff = mkLexargs(filename, defineConstants, LightSyntaxStatus(false, false), lexResourceManager, [], DiscardErrorsLogger, PathMap.empty) + let lexArgsLightOn = mkLexargs(defineConstants, LightSyntaxStatus(true, false), lexResourceManager, [], DiscardErrorsLogger, PathMap.empty) + let lexArgsLightOff = mkLexargs(defineConstants, LightSyntaxStatus(false, false), lexResourceManager, [], DiscardErrorsLogger, PathMap.empty) - member this.CreateLineTokenizer(lineText: string) = + member _.CreateLineTokenizer(lineText: string) = let lexbuf = UnicodeLexing.StringAsLexbuf(isFeatureSupported, lineText) FSharpLineTokenizer(lexbuf, Some lineText.Length, filename, lexArgsLightOn, lexArgsLightOff) - member this.CreateBufferTokenizer bufferFiller = + member _.CreateBufferTokenizer bufferFiller = let lexbuf = UnicodeLexing.FunctionAsLexbuf(isFeatureSupported, bufferFiller) FSharpLineTokenizer(lexbuf, None, filename, lexArgsLightOn, lexArgsLightOff) @@ -846,14 +963,7 @@ module Keywords = module Lexer = open System.Threading - open FSharp.Compiler.Features - open FSharp.Compiler.Lexhelp - open FSharp.Compiler.Parser - open FSharp.Compiler.Range - open FSharp.Compiler.SyntaxTree open FSharp.Compiler.Text - open FSharp.Compiler.UnicodeLexing - open Internal.Utilities [] type FSharpLexerFlags = @@ -1416,7 +1526,7 @@ module Lexer = | FSharpSyntaxTokenKind.LineCommentTrivia -> true | _ -> false - let lexWithErrorLogger (text: ISourceText) (filePath: string) conditionalCompilationDefines (flags: FSharpLexerFlags) supportsFeature errorLogger onToken pathMap (ct: CancellationToken) = + let lexWithErrorLogger (text: ISourceText) conditionalCompilationDefines (flags: FSharpLexerFlags) supportsFeature errorLogger onToken pathMap (ct: CancellationToken) = let canSkipTrivia = (flags &&& FSharpLexerFlags.SkipTrivia) = FSharpLexerFlags.SkipTrivia let isLightSyntaxOn = (flags &&& FSharpLexerFlags.LightSyntaxOn) = FSharpLexerFlags.LightSyntaxOn let isCompiling = (flags &&& FSharpLexerFlags.Compiling) = FSharpLexerFlags.Compiling @@ -1425,7 +1535,7 @@ module Lexer = let lexbuf = UnicodeLexing.SourceTextAsLexbuf(supportsFeature, text) let lightSyntaxStatus = LightSyntaxStatus(isLightSyntaxOn, true) - let lexargs = mkLexargs (filePath, conditionalCompilationDefines, lightSyntaxStatus, Lexhelp.LexResourceManager(0), [], errorLogger, pathMap) + let lexargs = mkLexargs (conditionalCompilationDefines, lightSyntaxStatus, Lexhelp.LexResourceManager(0), [], errorLogger, pathMap) let lexargs = { lexargs with applyLineDirectives = isCompiling } let getNextToken = @@ -1443,17 +1553,17 @@ module Lexer = ct.ThrowIfCancellationRequested () onToken (getNextToken lexbuf) lexbuf.LexemeRange - let lex text filePath conditionalCompilationDefines flags supportsFeature lexCallback pathMap ct = + let lex text conditionalCompilationDefines flags supportsFeature lexCallback pathMap ct = let errorLogger = CompilationErrorLogger("Lexer", ErrorLogger.FSharpErrorSeverityOptions.Default) - lexWithErrorLogger text filePath conditionalCompilationDefines flags supportsFeature errorLogger lexCallback pathMap ct + lexWithErrorLogger text conditionalCompilationDefines flags supportsFeature errorLogger lexCallback pathMap ct [] type FSharpLexer = - static member Lex(text: ISourceText, tokenCallback, ?langVersion, ?filePath, ?conditionalCompilationDefines, ?flags, ?pathMap, ?ct) = + static member Lex(text: ISourceText, tokenCallback, ?langVersion, ?filePath: string, ?conditionalCompilationDefines, ?flags, ?pathMap, ?ct) = let langVersion = defaultArg langVersion "latestmajor" let flags = defaultArg flags FSharpLexerFlags.Default - let filePath = defaultArg filePath String.Empty + ignore filePath // can be removed at later point let conditionalCompilationDefines = defaultArg conditionalCompilationDefines [] let pathMap = defaultArg pathMap Map.Empty let ct = defaultArg ct CancellationToken.None @@ -1471,4 +1581,4 @@ module Lexer = | FSharpSyntaxTokenKind.None -> () | _ -> tokenCallback fsTok - lex text filePath conditionalCompilationDefines flags supportsFeature onToken pathMap ct \ No newline at end of file + lex text conditionalCompilationDefines flags supportsFeature onToken pathMap ct \ No newline at end of file diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index a21228ea386..c431c27efb2 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -237,6 +237,16 @@ Funkce správy balíčků vyžaduje jazykovou verzi 5.0, použijte /langversion:preview. + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Neočekávaný symbol . v definici členu. Očekávalo se with, = nebo jiný token. diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index 1f5408225ee..d953a11d093 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -237,6 +237,16 @@ Für das Paketverwaltungsfeature ist Sprachversion 5.0 erforderlich. Verwenden Sie /langversion:preview. + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Unerwartetes Symbol "." in der Memberdefinition. Erwartet wurde "with", "=" oder ein anderes Token. diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index 3b7d89e217a..81e74d8412b 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -237,6 +237,16 @@ La característica de administración de paquetes requiere la versión de lenguaje 5.0; use /langversion:preview + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Símbolo inesperado "." en la definición de miembro. Se esperaba "with", "=" u otro token. diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 9f789df22f5..ebb073c4b14 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -237,6 +237,16 @@ La fonctionnalité de gestion des packages nécessite la version 5.0 du langage. Utilisez /langversion:preview + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Symbole '.' inattendu dans la définition du membre. 'with','=' ou autre jeton attendu. diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 08a952c93d4..e416d725fff 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -237,6 +237,16 @@ Con la funzionalità di gestione pacchetti è richiesta la versione 5.0 del linguaggio. Usare /langversion:preview + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Simbolo '.' imprevisto nella definizione di membro. È previsto 'with', '=' o un altro token. diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 3a8851c514b..1029f0a516f 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -237,6 +237,16 @@ パッケージ管理機能では、言語バージョン 5.0 で /langversion:preview を使用する必要があります + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. メンバー定義に予期しない記号 '.' があります。'with'、'=' またはその他のトークンが必要です。 diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 45152342c98..be6ed328678 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -237,6 +237,16 @@ 패키지 관리 기능을 사용하려면 언어 버전 5.0이 필요합니다. /langversion:preview를 사용하세요. + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. 멤버 정의의 예기치 않은 기호 '.'입니다. 'with', '=' 또는 기타 토큰이 필요합니다. diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 2a6f60b04af..61c9154e6d6 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -237,6 +237,16 @@ Funkcja zarządzania pakietami wymaga języka w wersji 5.0, użyj parametru /langversion:preview + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Nieoczekiwany symbol „.” w definicji składowej. Oczekiwano ciągu „with”, znaku „=” lub innego tokenu. diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 217fa520c15..2382d9e52ce 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -237,6 +237,16 @@ O recurso de gerenciamento de pacotes requer a versão de idioma 5.0. Use /langversion:preview + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Símbolo inesperado '.' na definição de membro. Esperado 'com', '=' ou outro token. diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index 92749ca8e05..a9b3450965a 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -237,6 +237,16 @@ Для функции управления пакетами требуется версия языка 5.0, используйте параметр /langversion:preview + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Неожиданный символ "." в определении члена. Ожидаемые инструкции: "with", "=" или другие токены. diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index d14480996e5..9f715676bc2 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -237,6 +237,16 @@ Paket yönetimi özelliği dil sürümü 5.0 gerektiriyor, /langversion:preview kullanın + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Üye tanımında '.' sembolü beklenmiyordu. 'with', '=' veya başka bir belirteç bekleniyordu. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 684f538e86f..ec9bb11c806 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -237,6 +237,16 @@ 包管理功能需要语言版本 5.0,请使用 /langversion:preview + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. 成员定义中有意外的符号 "."。预期 "with"、"+" 或其他标记。 diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 69feb5de782..f0121957c56 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -237,6 +237,16 @@ 套件管理功能需要語言版本 5.0,請使用 /langversion:preview + + Incomplete interpolated string + Incomplete interpolated string + + + + Incomplete interpolated string expression fill + Incomplete interpolated string expression fill + + Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. 成員定義中的非預期符號 '.'。預期為 'with'、'=' 或其他語彙基元。 diff --git a/tests/FSharp.Compiler.UnitTests/HashIfExpression.fs b/tests/FSharp.Compiler.UnitTests/HashIfExpression.fs index 6481801adb3..705ce465b1d 100644 --- a/tests/FSharp.Compiler.UnitTests/HashIfExpression.fs +++ b/tests/FSharp.Compiler.UnitTests/HashIfExpression.fs @@ -49,21 +49,21 @@ type public HashIfExpression() = sb.ToString () let createParser () = - let errors = ResizeArray() - let warnings = ResizeArray() + let errors = ResizeArray() + let warnings = ResizeArray() - let errorLogger = + let errorLogger = { new ErrorLogger("TestErrorLogger") with member x.DiagnosticSink(e, isError) = if isError then errors.Add e else warnings.Add e member x.ErrorCount = errors.Count } - let lightSyntax = LightSyntaxStatus(true, false) + let lightSyntax = LightSyntaxStatus(true, false) let resourceManager = LexResourceManager () - let defines = [] - let startPos = Position.Empty - let args = mkLexargs ("dummy", defines, lightSyntax, resourceManager, [], errorLogger, PathMap.empty) + let defines= [] + let startPos = Position.Empty + let args = mkLexargs (defines, lightSyntax, resourceManager, [], errorLogger, PathMap.empty) CompileThreadStatic.ErrorLogger <- errorLogger diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 04709703f97..7b02f5ef6a0 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -579,25 +579,25 @@ let xe = $"%A{{1}}" // fake expression (delimiters escaped) 'string' but here has type 'int' """); - (FSharpErrorSeverity.Error, 3354, (4, 10, 4, 19), + (FSharpErrorSeverity.Error, 3376, (4, 10, 4, 19), "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); - (FSharpErrorSeverity.Error, 3354, (5, 10, 5, 19), + (FSharpErrorSeverity.Error, 3376, (5, 10, 5, 19), "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); - (FSharpErrorSeverity.Error, 3354, (6, 10, 6, 19), + (FSharpErrorSeverity.Error, 3376, (6, 10, 6, 19), "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); - (FSharpErrorSeverity.Error, 3354, (7, 10, 7, 19), + (FSharpErrorSeverity.Error, 3376, (7, 10, 7, 19), "Invalid interpolated string. The '%P' specifier may not be used explicitly."); - (FSharpErrorSeverity.Error, 3361, (8, 10, 8, 21), + (FSharpErrorSeverity.Error, 3371, (8, 10, 8, 21), "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); - (FSharpErrorSeverity.Error, 3361, (9, 10, 9, 24), + (FSharpErrorSeverity.Error, 3371, (9, 10, 9, 24), "Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'"); - (FSharpErrorSeverity.Error, 3354, (10, 10, 10, 19), + (FSharpErrorSeverity.Error, 3376, (10, 10, 10, 19), "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'."); - (FSharpErrorSeverity.Error, 3354, (11, 10, 11, 24), + (FSharpErrorSeverity.Error, 3376, (11, 10, 11, 24), "Invalid interpolated string. .NET-style format specifiers such as '{x,3}' or '{x:N5}' may not be mixed with '%' format specifiers.") - (FSharpErrorSeverity.Error, 3354, (12, 10, 12, 16), + (FSharpErrorSeverity.Error, 3376, (12, 10, 12, 16), "Invalid interpolated string. Bad precision in format specifier") - (FSharpErrorSeverity.Error, 3354, (13, 10, 13, 20), + (FSharpErrorSeverity.Error, 3376, (13, 10, 13, 20), "Invalid interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{1+1}'.") |] @@ -633,11 +633,11 @@ let x3 : FormattableString = $"one %10s{String.Empty}" // no %10s in Formattable """ CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] code - [|(FSharpErrorSeverity.Error, 3354, (4, 30, 4, 44), + [|(FSharpErrorSeverity.Error, 3376, (4, 30, 4, 44), "Invalid interpolated string. Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); - (FSharpErrorSeverity.Error, 3354, (5, 30, 5, 53), + (FSharpErrorSeverity.Error, 3376, (5, 30, 5, 53), "Invalid interpolated string. Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used."); - (FSharpErrorSeverity.Error, 3354, (6, 30, 6, 55), + (FSharpErrorSeverity.Error, 3376, (6, 30, 6, 55), "Invalid interpolated string. Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{expr}', '{expr,3}' or '{expr:N5}' may be used.")|] @@ -658,23 +658,23 @@ let s9 = @$"123{456}789{$@"012"}345" """ CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] code - [|(FSharpErrorSeverity.Error, 3363, (4, 24, 4, 25), + [|(FSharpErrorSeverity.Error, 3373, (4, 24, 4, 25), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); - (FSharpErrorSeverity.Error, 3363, (5, 24, 5, 26), + (FSharpErrorSeverity.Error, 3373, (5, 24, 5, 26), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); - (FSharpErrorSeverity.Error, 3363, (6, 24, 6, 26), + (FSharpErrorSeverity.Error, 3373, (6, 24, 6, 26), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); - (FSharpErrorSeverity.Error, 3363, (7, 25, 7, 26), + (FSharpErrorSeverity.Error, 3373, (7, 25, 7, 26), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); - (FSharpErrorSeverity.Error, 3363, (8, 25, 8, 26), + (FSharpErrorSeverity.Error, 3373, (8, 25, 8, 26), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); - (FSharpErrorSeverity.Error, 3363, (9, 25, 9, 27), + (FSharpErrorSeverity.Error, 3373, (9, 25, 9, 27), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); - (FSharpErrorSeverity.Error, 3363, (10, 25, 10, 27), + (FSharpErrorSeverity.Error, 3373, (10, 25, 10, 27), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); - (FSharpErrorSeverity.Error, 3363, (11, 25, 11, 28), + (FSharpErrorSeverity.Error, 3373, (11, 25, 11, 28), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal."); - (FSharpErrorSeverity.Error, 3363, (12, 25, 12, 28), + (FSharpErrorSeverity.Error, 3373, (12, 25, 12, 28), "Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal.")|] [] @@ -691,16 +691,36 @@ let TripleInterpolatedInVerbatimInterpolated = $\"123{456}789{$\"\"\"012\"\"\"}3 " CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] code - [|(FSharpErrorSeverity.Error, 3364, (4, 52, 4, 55), + [|(FSharpErrorSeverity.Error, 3374, (4, 52, 4, 55), "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); - (FSharpErrorSeverity.Error, 3364, (5, 50, 5, 53), + (FSharpErrorSeverity.Error, 3374, (5, 50, 5, 53), "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); - (FSharpErrorSeverity.Error, 3364, (6, 50, 6, 53), + (FSharpErrorSeverity.Error, 3374, (6, 50, 6, 53), "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); - (FSharpErrorSeverity.Error, 3364, (7, 64, 7, 68), + (FSharpErrorSeverity.Error, 3374, (7, 64, 7, 68), "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); - (FSharpErrorSeverity.Error, 3364, (8, 62, 8, 66), + (FSharpErrorSeverity.Error, 3374, (8, 62, 8, 66), "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression."); - (FSharpErrorSeverity.Error, 3364, (9, 62, 9, 66), + (FSharpErrorSeverity.Error, 3374, (9, 62, 9, 66), "Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression.")|] + [] + let ``String interpolation negative incomplete string`` () = + let code = """ + +let x1 = $"one %d{System.String.Empty} // incomplete string +""" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [||] + + [] + let ``String interpolation negative incomplete string fill`` () = + let code = """ + +let x1 = $"one %d{System.String.Empty // incomplete string fill +""" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [||] + diff --git a/tests/service/TokenizerTests.fs b/tests/service/TokenizerTests.fs index 5f14a0b8aa5..7d89b8a1764 100644 --- a/tests/service/TokenizerTests.fs +++ b/tests/service/TokenizerTests.fs @@ -69,9 +69,7 @@ let ``Tokenizer test 2``() = "let hello2 = $\"Hello world {1+1} = {2}\" " "let hello0v = @$\"\"" "let hello1v = @$\"Hello world\" " - "let hello2v = @$\"Hello world {1+1} = {2}\" " - "let hello1t = @$\"\"\"abc\"\"\"" - "let hello2t = @$\"\"\"Hello world {1+1} = {2}\"\"\" " |] + "let hello2v = @$\"Hello world {1+1} = {2}\" " |] let actual = [ for lineNo, lineToks in tokenizedLines do @@ -119,22 +117,30 @@ let ``Tokenizer test 2``() = ("INT32", "1"); ("STRING_TEXT", "}"); ("STRING_TEXT", " "); ("STRING_TEXT", "="); ("STRING_TEXT", " "); ("INTERP_STRING_PART", "{"); ("INT32", "2"); ("STRING_TEXT", "}"); ("INTERP_STRING_END", "\""); - ("WHITESPACE", " ")]); - (7, + ("WHITESPACE", " ")]);] + + if actual <> expected then + printfn "actual = %A" actual + printfn "expected = %A" expected + Assert.Fail(sprintf "actual and expected did not match,actual =\n%A\nexpected=\n%A\n" actual expected) + + +[] +let ``Tokenizer test 3``() = + let tokenizedLines = + tokenizeLines + [| "let hello1t = $\"\"\"abc {1+" + " 1} def\"\"\"" |] + + let actual = + [ for lineNo, lineToks in tokenizedLines do + yield lineNo, [ for str, info in lineToks do yield info.TokenName, str ] ] + let expected = + [(0, [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello1t"); ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); - ("STRING_TEXT", "@$\""); ("STRING_TEXT", "\"\""); ("STRING_TEXT", "abc"); - ("STRING_TEXT", "\"\""); ("INTERP_STRING_BEGIN_END", "\"")]); - (8, - [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello2t"); - ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); - ("STRING_TEXT", "@$\""); ("STRING_TEXT", "\"\""); ("STRING_TEXT", "Hello"); - ("STRING_TEXT", " "); ("STRING_TEXT", "world"); ("STRING_TEXT", " "); - ("INTERP_STRING_BEGIN_PART", "{"); ("INT32", "1"); ("PLUS_MINUS_OP", "+"); - ("INT32", "1"); ("STRING_TEXT", "}"); ("STRING_TEXT", " "); - ("STRING_TEXT", "="); ("STRING_TEXT", " "); ("INTERP_STRING_PART", "{"); - ("INT32", "2"); ("STRING_TEXT", "}"); ("STRING_TEXT", "\"\""); - ("INTERP_STRING_END", "\""); ("WHITESPACE", " ")])] + ("STRING_TEXT", "$\"\"\""); ("STRING_TEXT", "abc")]); + (1, [("STRING_TEXT", "def"); ("INTERP_STRING_BEGIN_END", "\"\"\"")])] if actual <> expected then printfn "actual = %A" actual From fbde3315d315e39c9debb955561594c82b658043 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Jul 2020 17:25:04 +0100 Subject: [PATCH 59/87] fix lexer continuations and colorization for multi-line interpolated strings --- src/fsharp/CompileOps.fs | 6 +- src/fsharp/FSComp.txt | 6 +- src/fsharp/LexFilter.fs | 8 +- src/fsharp/ParseHelpers.fs | 6 +- src/fsharp/lex.fsl | 94 ++-- src/fsharp/lexhelp.fs | 34 +- src/fsharp/lexhelp.fsi | 17 +- src/fsharp/pars.fsy | 87 +++- src/fsharp/service/FSharpCheckerResults.fs | 10 +- src/fsharp/service/ServiceLexing.fs | 461 +++++++++--------- src/fsharp/service/ServiceLexing.fsi | 22 +- src/fsharp/xlf/FSComp.txt.cs.xlf | 22 +- src/fsharp/xlf/FSComp.txt.de.xlf | 22 +- src/fsharp/xlf/FSComp.txt.es.xlf | 22 +- src/fsharp/xlf/FSComp.txt.fr.xlf | 22 +- src/fsharp/xlf/FSComp.txt.it.xlf | 22 +- src/fsharp/xlf/FSComp.txt.ja.xlf | 22 +- src/fsharp/xlf/FSComp.txt.ko.xlf | 22 +- src/fsharp/xlf/FSComp.txt.pl.xlf | 22 +- src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 22 +- src/fsharp/xlf/FSComp.txt.ru.xlf | 22 +- src/fsharp/xlf/FSComp.txt.tr.xlf | 22 +- src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 22 +- src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 22 +- .../Compiler/Language/StringInterpolation.fs | 43 +- tests/service/TokenizerTests.fs | 56 ++- 26 files changed, 700 insertions(+), 436 deletions(-) diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 2ec8e8938c7..b8baaf97c4b 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -3542,13 +3542,13 @@ let ParseOneInputLexbuf (tcConfig: TcConfig, lexResourceManager, conditionalComp use unwindbuildphase = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse try let skip = true in (* don't report whitespace from lexer *) - let lightSyntaxStatus = LightSyntaxStatus (tcConfig.ComputeLightSyntaxInitialStatus filename, true) - let lexargs = mkLexargs (conditionalCompilationDefines@tcConfig.conditionalCompilationDefines, lightSyntaxStatus, lexResourceManager, [], errorLogger, tcConfig.pathMap) + let lightStatus = LightSyntaxStatus (tcConfig.ComputeLightSyntaxInitialStatus filename, true) + let lexargs = mkLexargs (conditionalCompilationDefines@tcConfig.conditionalCompilationDefines, lightStatus, lexResourceManager, [], errorLogger, tcConfig.pathMap) let shortFilename = SanitizeFileName filename tcConfig.implicitIncludeDir let input = Lexhelp.usingLexbufForParsing (lexbuf, filename) (fun lexbuf -> if verbose then dprintn ("Parsing... "+shortFilename) - let tokenizer = LexFilter.LexFilter(lightSyntaxStatus, tcConfig.compilingFslib, Lexer.token lexargs skip, lexbuf) + let tokenizer = LexFilter.LexFilter(lightStatus, tcConfig.compilingFslib, Lexer.token lexargs skip, lexbuf) if tcConfig.tokenizeOnly then while true do diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index f01e866759f..6e62c03786b 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1527,7 +1527,9 @@ forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable 3372,tcInvalidAlignmentInInterpolatedString,"Invalid alignment in interpolated string" 3373,lexSingleQuoteInSingleQuote,"Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal." 3374,lexTripleQuoteInTripleQuote,"Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression." -3375,parsNoEndOfInterpString,"Incomplete interpolated string" 3376,tcUnableToParseInterpolatedString,"Invalid interpolated string. %s" 3377,lexByteStringMayNotBeInterpolated,"a byte string may not be interpolated" -3375,parsNoEndOfInterpStringFill,"Incomplete interpolated string expression fill" +3378,parsEofInInterpolatedStringFill,"Incomplete interpolated string expression fill begun at or before here" +3379,parsEofInInterpolatedString,"Incomplete interpolated string begun at or before here" +3380,parsEofInInterpolatedVerbatimString,"Incomplete interpolated verbatim string begun at or before here" +3381,parsEofInInterpolatedTripleQuoteString,"Incomplete interpolated triple-quote string begun at or before here" diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index 01e77951c97..3fcd8341f2b 100644 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -528,7 +528,7 @@ type PositionWithColumn = //---------------------------------------------------------------------------- // build a LexFilter //--------------------------------------------------------------------------*) -type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbuf: UnicodeLexing.Lexbuf) = +type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbuf: UnicodeLexing.Lexbuf) = //---------------------------------------------------------------------------- // Part I. Building a new lex stream from an old @@ -2354,7 +2354,7 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, let _firstTokenTup = peekInitial() () - if lightSyntaxStatus.Status + if lightStatus.Status then hwTokenFetch true else swTokenFetch() @@ -2362,8 +2362,8 @@ type LexFilterImpl (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, // LexFilterImpl does the majority of the work for offsides rules and other magic. // LexFilter just wraps it with light post-processing that introduces a few more 'coming soon' symbols, to // make it easier for the parser to 'look ahead' and safely shift tokens in a number of recovery scenarios. -type LexFilter (lightSyntaxStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbuf: UnicodeLexing.Lexbuf) = - let inner = new LexFilterImpl (lightSyntaxStatus, compilingFsLib, lexer, lexbuf) +type LexFilter (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbuf: UnicodeLexing.Lexbuf) = + let inner = new LexFilterImpl (lightStatus, compilingFsLib, lexer, lexbuf) // We don't interact with lexbuf state at all, any inserted tokens have same state/location as the real one read, so // we don't have to do any of the wrapped lexbuf magic that you see in LexFilterImpl. diff --git a/src/fsharp/ParseHelpers.fs b/src/fsharp/ParseHelpers.fs index 963c3d3d6c4..815c08070c6 100644 --- a/src/fsharp/ParseHelpers.fs +++ b/src/fsharp/ParseHelpers.fs @@ -170,7 +170,7 @@ type LexerInterpolatedStringNesting = (int * LexerStringStyle * range) list /// the whitespace. [] [] -type LexerWhitespaceContinuation = +type LexerContinuation = | Token of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting | IfDefSkip of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * int * range: range | String of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * style: LexerStringStyle * kind: LexerStringKind * range: range @@ -180,6 +180,8 @@ type LexerWhitespaceContinuation = | MLOnly of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * range: range | EndLine of ifdef: LexerIfdefStackEntries * nesting: LexerInterpolatedStringNesting * LexerEndlineContinuation + static member Default = LexCont.Token([],[]) + member x.LexerIfdefStack = match x with | LexCont.Token (ifdef=ifd) @@ -202,7 +204,7 @@ type LexerWhitespaceContinuation = | LexCont.EndLine (nesting=nesting) | LexCont.MLOnly (nesting=nesting) -> nesting -and LexCont = LexerWhitespaceContinuation +and LexCont = LexerContinuation //------------------------------------------------------------------------ // Parse IL assembly code diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index 16f90ca7f99..51674ff072c 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -116,33 +116,33 @@ let startString args (lexbuf: UnicodeLexing.Lexbuf) = let m = lexbuf.LexemeRange let startp = lexbuf.StartPos let fin = - LexerStringFinisher (fun buf kind isPart -> + LexerStringFinisher (fun buf kind isPart cont -> // Adjust the start-of-token mark back to the true start of the token lexbuf.StartPos <- startp if kind.IsByteString then if kind.IsInterpolated then fail args lexbuf (FSComp.SR.lexByteStringMayNotBeInterpolated()) () - BYTEARRAY (Lexhelp.stringBufferAsBytes buf) + BYTEARRAY (Lexhelp.stringBufferAsBytes buf, cont) elif Lexhelp.stringBufferIsBytes buf then - BYTEARRAY (Lexhelp.stringBufferAsBytes buf) + BYTEARRAY (Lexhelp.stringBufferAsBytes buf, cont) else fail args lexbuf (FSComp.SR.lexByteArrayCannotEncode()) () - BYTEARRAY (Lexhelp.stringBufferAsBytes buf) + BYTEARRAY (Lexhelp.stringBufferAsBytes buf, cont) elif kind.IsInterpolated then let s = Lexhelp.stringBufferAsString buf if kind.IsInterpolatedFirst then if isPart then - INTERP_STRING_BEGIN_PART s + INTERP_STRING_BEGIN_PART (s, cont) else - INTERP_STRING_BEGIN_END s + INTERP_STRING_BEGIN_END (s, cont) else if isPart then - INTERP_STRING_PART s + INTERP_STRING_PART (s, cont) else - INTERP_STRING_END s + INTERP_STRING_END (s, cont) else let s = Lexhelp.stringBufferAsString buf - STRING s) + STRING (s, cont)) buf,fin,m @@ -553,7 +553,7 @@ rule token args skip = parse // Single quote in triple quote ok, others disallowed match args.stringNest with | (_, LexerStringStyle.TripleQuote, _) :: _ -> () - | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) + | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), m)) | [] -> () if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, LexerStringKind.String, m)) @@ -564,7 +564,7 @@ rule token args skip = parse // Single quote in triple quote ok, others disallowed match args.stringNest with - | _ :: _ -> errorR(Error(FSComp.SR.lexTripleQuoteInTripleQuote(), lexbuf.LexemeRange)) + | _ :: _ -> errorR(Error(FSComp.SR.lexTripleQuoteInTripleQuote(), m)) | [] -> () if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, LexerStringKind.InterpolatedStringFirst, m)) @@ -576,7 +576,7 @@ rule token args skip = parse // Single quote in triple quote ok, others disallowed match args.stringNest with | (_, LexerStringStyle.TripleQuote, _) :: _ -> () - | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) + | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), m)) | _ -> () if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, LexerStringKind.InterpolatedStringFirst, m)) @@ -587,7 +587,7 @@ rule token args skip = parse // Single quote in triple quote ok, others disallowed match args.stringNest with - | _ :: _ -> errorR(Error(FSComp.SR.lexTripleQuoteInTripleQuote(), lexbuf.LexemeRange)) + | _ :: _ -> errorR(Error(FSComp.SR.lexTripleQuoteInTripleQuote(), m)) | _ -> () if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, LexerStringKind.String, m)) @@ -599,7 +599,7 @@ rule token args skip = parse // Single quote in triple quote ok, others disallowed match args.stringNest with | (_, LexerStringStyle.TripleQuote, _) :: _ -> () - | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) + | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), m)) | _ -> () if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, LexerStringKind.String, m)) @@ -611,7 +611,7 @@ rule token args skip = parse // Single quote in triple quote ok, others disallowed match args.stringNest with | (_, LexerStringStyle.TripleQuote, _) :: _ -> () - | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), lexbuf.LexemeRange)) + | _ :: _ -> errorR(Error(FSComp.SR.lexSingleQuoteInSingleQuote(), m)) | _ -> () if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, LexerStringKind.InterpolatedStringFirst, m)) @@ -622,7 +622,7 @@ rule token args skip = parse else WHITESPACE (LexCont.Token(args.ifdefStack, args.stringNest)) } | offwhite+ - { if args.lightSyntaxStatus.Status then errorR(Error(FSComp.SR.lexTabsNotAllowed(), lexbuf.LexemeRange)) + { if args.lightStatus.Status then errorR(Error(FSComp.SR.lexTabsNotAllowed(), lexbuf.LexemeRange)) if not skip then WHITESPACE (LexCont.Token(args.ifdefStack, args.stringNest)) else token args skip lexbuf } @@ -792,6 +792,8 @@ rule token args skip = parse match args.stringNest with | [] -> () | (counter, style, m) :: rest -> + // Note, we do not update the 'm', any incomplete-interpolation error + // will be reported w.r.t. the first '{' args.stringNest <- (counter + 1, style, m) :: rest LBRACE } @@ -813,9 +815,13 @@ rule token args skip = parse | LexerStringStyle.Verbatim -> verbatimString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.SingleQuote -> singleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf | LexerStringStyle.TripleQuote -> tripleQuoteString (buf, fin, m, LexerStringKind.InterpolatedStringPart, args) skip lexbuf + | (counter, style, m) :: rest -> + // Note, we do not update the 'm', any incomplete-interpolation error + // will be reported w.r.t. the first '{' args.stringNest <- (counter - 1, style, m) :: rest RBRACE + | _ -> RBRACE } @@ -861,15 +867,15 @@ rule token args skip = parse | "#light" anywhite* | ("#indent" | "#light") anywhite+ "\"on\"" - { if args.lightSyntaxStatus.ExplicitlySet && args.lightSyntaxStatus.WarnOnMultipleTokens then + { if args.lightStatus.ExplicitlySet && args.lightStatus.WarnOnMultipleTokens then warning(Error((0,"#light should only occur as the first non-comment text in an F# source file"), lexbuf.LexemeRange)) // TODO unreachable error above, I think? - brianmcn - args.lightSyntaxStatus.Status <- true + args.lightStatus.Status <- true if not skip then HASH_LIGHT (LexCont.Token(args.ifdefStack, args.stringNest)) else token args skip lexbuf } | ("#indent" | "#light") anywhite+ "\"off\"" - { args.lightSyntaxStatus.Status <- false + { args.lightStatus.Status <- false mlCompatWarning (FSComp.SR.lexIndentOffForML()) lexbuf.LexemeRange if not skip then HASH_LIGHT (LexCont.Token (args.ifdefStack, args.stringNest)) else token args skip lexbuf } @@ -1083,12 +1089,16 @@ and singleQuoteString sargs skip = parse tok } | '"' - { let (buf, fin, _m, kind, _args) = sargs - fin.Finish buf kind false } + { let (buf, fin, _m, kind, args) = sargs + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + fin.Finish buf kind false cont + } | '"''B' - { let (buf, fin, _m, kind, _args) = sargs - fin.Finish buf { kind with IsByteString = true } false } + { let (buf, fin, _m, kind, args) = sargs + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + fin.Finish buf { kind with IsByteString = true } false cont + } | ("{{" | "}}") { let (buf, _fin, m, kind, args) = sargs @@ -1100,8 +1110,11 @@ and singleQuoteString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.stringNest <- (1, LexerStringStyle.SingleQuote, m) :: args.stringNest - fin.Finish buf kind true + // get a new range for where the fill starts + let m2 = lexbuf.LexemeRange + args.stringNest <- (1, LexerStringStyle.SingleQuote, m2) :: args.stringNest + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + fin.Finish buf kind true cont else addUnicodeString buf (lexeme lexbuf) if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) @@ -1154,12 +1167,16 @@ and verbatimString sargs skip = parse else verbatimString sargs skip lexbuf } | '"' - { let (buf, fin, _m, kind, _args) = sargs - fin.Finish buf kind false } + { let (buf, fin, _m, kind, args) = sargs + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + fin.Finish buf kind false cont + } | '"''B' - { let (buf, fin, _m, kind, _args) = sargs - fin.Finish buf { kind with IsByteString = true } false } + { let (buf, fin, _m, kind, args) = sargs + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + fin.Finish buf { kind with IsByteString = true } false cont + } | newline { let (buf, _fin, m, kind, args) = sargs @@ -1178,8 +1195,11 @@ and verbatimString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.stringNest <- (1, LexerStringStyle.Verbatim, m) :: args.stringNest - fin.Finish buf kind true + // get a new range for where the fill starts + let m2 = lexbuf.LexemeRange + args.stringNest <- (1, LexerStringStyle.Verbatim, m2) :: args.stringNest + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + fin.Finish buf kind true cont else addUnicodeString buf (lexeme lexbuf) if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) @@ -1218,8 +1238,9 @@ and verbatimString sargs skip = parse and tripleQuoteString sargs skip = parse | '"' '"' '"' - { let (buf, fin, _m, kind, _args) = sargs - fin.Finish buf kind false } + { let (buf, fin, _m, kind, args) = sargs + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + fin.Finish buf kind false cont } | newline { let (buf, _fin, m, kind, args) = sargs @@ -1258,8 +1279,11 @@ and tripleQuoteString sargs skip = parse | "{" { let (buf, fin, m, kind, args) = sargs if kind.IsInterpolated then - args.stringNest <- (1, LexerStringStyle.TripleQuote, m) :: args.stringNest - fin.Finish buf kind true + // get a new range for where the fill starts + let m2 = lexbuf.LexemeRange + args.stringNest <- (1, LexerStringStyle.TripleQuote, m2) :: args.stringNest + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + fin.Finish buf kind true cont else addUnicodeString buf (lexeme lexbuf) if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) diff --git a/src/fsharp/lexhelp.fs b/src/fsharp/lexhelp.fs index 4e724c08194..0600da601cf 100644 --- a/src/fsharp/lexhelp.fs +++ b/src/fsharp/lexhelp.fs @@ -50,14 +50,16 @@ type LexResourceManager(?capacity: int) = /// Lexer parameters type LexArgs = - { defines: string list - mutable ifdefStack: LexerIfdefStack + { + defines: string list resourceManager: LexResourceManager - lightSyntaxStatus : LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool + pathMap: PathMap + mutable ifdefStack: LexerIfdefStack + mutable lightStatus : LightSyntaxStatus mutable stringNest: LexerInterpolatedStringNesting - pathMap: PathMap } + } /// possible results of lexing a long Unicode escape sequence in a string literal, e.g. "\U0001F47D", /// "\U000000E7", or "\UDEADBEEF" returning SurrogatePair, SingleChar, or Invalid, respectively @@ -66,11 +68,11 @@ type LongUnicodeLexResult = | SingleChar of uint16 | Invalid -let mkLexargs (defines, lightSyntaxStatus, resourceManager, ifdefStack, errorLogger, pathMap:PathMap) = +let mkLexargs (defines, lightStatus, resourceManager, ifdefStack, errorLogger, pathMap:PathMap) = { defines = defines ifdefStack= ifdefStack - lightSyntaxStatus=lightSyntaxStatus + lightStatus=lightStatus resourceManager=resourceManager errorLogger=errorLogger applyLineDirectives=true @@ -120,30 +122,30 @@ let stringBufferAsBytes (buf: ByteBuffer) = Array.init (bytes.Length / 2) (fun i -> bytes.[i*2]) type LexerStringFinisher = - | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> bool -> token) + | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> bool -> LexerContinuation -> token) - member fin.Finish (buf: ByteBuffer) kind isPart = + member fin.Finish (buf: ByteBuffer) kind isPart cont = let (LexerStringFinisher f) = fin - f buf kind isPart + f buf kind isPart cont static member Default = - LexerStringFinisher (fun buf kind isPart -> + LexerStringFinisher (fun buf kind isPart cont -> if kind.IsInterpolated then let s = stringBufferAsString buf if kind.IsInterpolatedFirst then if isPart then - INTERP_STRING_BEGIN_PART s + INTERP_STRING_BEGIN_PART (s, cont) else - INTERP_STRING_BEGIN_END s + INTERP_STRING_BEGIN_END (s, cont) else if isPart then - INTERP_STRING_PART s + INTERP_STRING_PART (s, cont) else - INTERP_STRING_END s + INTERP_STRING_END (s, cont) elif kind.IsByteString then - BYTEARRAY (stringBufferAsBytes buf) + BYTEARRAY (stringBufferAsBytes buf, cont) else - STRING (stringBufferAsString buf) + STRING (stringBufferAsString buf, cont) ) let addUnicodeString (buf: ByteBuffer) (x:string) = diff --git a/src/fsharp/lexhelp.fsi b/src/fsharp/lexhelp.fsi index ece37441bdf..c6de1a90395 100644 --- a/src/fsharp/lexhelp.fsi +++ b/src/fsharp/lexhelp.fsi @@ -14,6 +14,8 @@ open FSharp.Compiler.Range val stdinMockFilename: string +/// Lexer args: status of #light processing. Mutated when a #light +/// directive is processed. This alters the behaviour of the lexfilter. [] type LightSyntaxStatus = new: initial:bool * warn: bool -> LightSyntaxStatus @@ -26,15 +28,18 @@ type LightSyntaxStatus = type LexResourceManager = new: ?capacity: int -> LexResourceManager +/// The context applicable to all lexing functions (tokens, strings etc.) type LexArgs = - { defines: string list - mutable ifdefStack: LexerIfdefStack + { + defines: string list resourceManager: LexResourceManager - lightSyntaxStatus: LightSyntaxStatus errorLogger: ErrorLogger applyLineDirectives: bool + pathMap: PathMap + mutable ifdefStack: LexerIfdefStack + mutable lightStatus : LightSyntaxStatus mutable stringNest: LexerInterpolatedStringNesting - pathMap: PathMap } + } type LongUnicodeLexResult = | SurrogatePair of uint16 * uint16 @@ -50,9 +55,9 @@ val reusingLexbufForParsing: UnicodeLexing.Lexbuf -> (unit -> 'a) -> 'a val usingLexbufForParsing: UnicodeLexing.Lexbuf * string -> (UnicodeLexing.Lexbuf -> 'a) -> 'a type LexerStringFinisher = - | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> bool -> token) + | LexerStringFinisher of (ByteBuffer -> LexerStringKind -> bool -> LexerContinuation -> token) - member Finish: buf: ByteBuffer -> kind: LexerStringKind -> isInterpolatedStringPart: bool -> token + member Finish: buf: ByteBuffer -> kind: LexerStringKind -> isInterpolatedStringPart: bool -> cont: LexerContinuation -> token static member Default: LexerStringFinisher diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index a18976f24d1..2ffa6559fee 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -91,19 +91,51 @@ let raiseParseErrorAt m s = // This initiates error recovery raise RecoverableParseError +/// Report a good error at the end of file, e.g. for non-terminated strings let checkEndOfFileError t = match t with - | LexCont.IfDefSkip(_, _, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInHashIf()) - | LexCont.String (_, _, LexerStringStyle.SingleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInString()) - | LexCont.String (_, _, LexerStringStyle.TripleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteString()) - | LexCont.String (_, _, LexerStringStyle.Verbatim, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimString()) - | LexCont.Comment (_, _, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) - | LexCont.SingleLineComment (_, _, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInComment()) - | LexCont.StringInComment (_, _, LexerStringStyle.SingleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInStringInComment()) - | LexCont.StringInComment (_, _, LexerStringStyle.Verbatim, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInVerbatimStringInComment()) - | LexCont.StringInComment (_, _, LexerStringStyle.TripleQuote, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteStringInComment()) - | LexCont.MLOnly (_, _, m) -> reportParseErrorAt m (FSComp.SR.parsEofInIfOcaml()) - | LexCont.EndLine(_, _, LexerEndlineContinuation.Skip(_, m)) -> reportParseErrorAt m (FSComp.SR.parsEofInDirective()) + | LexCont.IfDefSkip(_, _, _, m) -> + reportParseErrorAt m (FSComp.SR.parsEofInHashIf()) + + | LexCont.String (_, _, LexerStringStyle.SingleQuote, kind, m) -> + if kind.IsInterpolated then + reportParseErrorAt m (FSComp.SR.parsEofInInterpolatedString()) + else + reportParseErrorAt m (FSComp.SR.parsEofInString()) + + | LexCont.String (_, _, LexerStringStyle.TripleQuote, kind, m) -> + if kind.IsInterpolated then + reportParseErrorAt m (FSComp.SR.parsEofInInterpolatedTripleQuoteString()) + else + reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteString()) + + | LexCont.String (_, _, LexerStringStyle.Verbatim, kind, m) -> + if kind.IsInterpolated then + reportParseErrorAt m (FSComp.SR.parsEofInInterpolatedVerbatimString()) + else + reportParseErrorAt m (FSComp.SR.parsEofInVerbatimString()) + + | LexCont.Comment (_, _, _, m) -> + reportParseErrorAt m (FSComp.SR.parsEofInComment()) + + | LexCont.SingleLineComment (_, _, _, m) -> + reportParseErrorAt m (FSComp.SR.parsEofInComment()) + + | LexCont.StringInComment (_, _, LexerStringStyle.SingleQuote, _, m) -> + reportParseErrorAt m (FSComp.SR.parsEofInStringInComment()) + + | LexCont.StringInComment (_, _, LexerStringStyle.Verbatim, _, m) -> + reportParseErrorAt m (FSComp.SR.parsEofInVerbatimStringInComment()) + + | LexCont.StringInComment (_, _, LexerStringStyle.TripleQuote, _, m) -> + reportParseErrorAt m (FSComp.SR.parsEofInTripleQuoteStringInComment()) + + | LexCont.MLOnly (_, _, m) -> + reportParseErrorAt m (FSComp.SR.parsEofInIfOcaml()) + + | LexCont.EndLine(_, _, LexerEndlineContinuation.Skip(_, m)) -> + reportParseErrorAt m (FSComp.SR.parsEofInDirective()) + | LexCont.EndLine(endifs, nesting, LexerEndlineContinuation.Token) | LexCont.Token(endifs, nesting) -> match endifs with @@ -111,8 +143,7 @@ let checkEndOfFileError t = | (_, m) :: _ -> reportParseErrorAt m (FSComp.SR.parsNoHashEndIfFound()) match nesting with | [] -> () - | (1, _, m) :: _ -> reportParseErrorAt m (FSComp.SR.parsNoEndOfInterpStringFill()) - | (_, _, m) :: _ -> reportParseErrorAt m (FSComp.SR.parsNoEndOfInterpString()) + | (_, _, m) :: _ -> reportParseErrorAt m (FSComp.SR.parsEofInInterpolatedStringFill()) type BindingSet = BindingSetPreAttrs of range * bool * bool * (SynAttributes -> SynAccess option -> SynAttributes * SynBinding list) * range @@ -159,12 +190,14 @@ let rangeOfLongIdent(lid:LongIdent) = %} -%token BYTEARRAY -%token STRING -%token INTERP_STRING_BEGIN_END -%token INTERP_STRING_BEGIN_PART -%token INTERP_STRING_PART -%token INTERP_STRING_END +// Producing these changes the lex state, e.g. string --> token +%token BYTEARRAY +%token STRING +%token INTERP_STRING_BEGIN_END +%token INTERP_STRING_BEGIN_PART +%token INTERP_STRING_PART +%token INTERP_STRING_END + %token KEYWORD_STRING // Like __SOURCE_DIRECTORY__ %token IDENT %token INFIX_STAR_STAR_OP @@ -269,8 +302,8 @@ let rangeOfLongIdent(lid:LongIdent) = /* These are artificial */ %token LEX_FAILURE -%token COMMENT WHITESPACE HASH_LINE HASH_LIGHT INACTIVECODE LINE_COMMENT STRING_TEXT EOF -%token HASH_IF HASH_ELSE HASH_ENDIF +%token COMMENT WHITESPACE HASH_LINE HASH_LIGHT INACTIVECODE LINE_COMMENT STRING_TEXT EOF +%token HASH_IF HASH_ELSE HASH_ENDIF %start signatureFile implementationFile interaction typedSeqExprEOF typEOF %type typedSeqExprEOF @@ -2842,7 +2875,7 @@ rawConstant: { SynConst.String ($1, lhs parseState) } | BYTEARRAY - { SynConst.Bytes ($1, lhs parseState) } + { SynConst.Bytes (fst $1, lhs parseState) } rationalConstant: | INT32 INFIX_STAR_DIV_MOD_OP INT32 @@ -5452,7 +5485,7 @@ colonOrEquals: /* A literal string or a string from a keyword like __SOURCE_FILE__ */ stringOrKeywordString: - | STRING { $1 } + | STRING { fst $1 } | KEYWORD_STRING { $1 } interpolatedStringFill: @@ -5464,20 +5497,20 @@ interpolatedStringFill: interpolatedStringParts: | INTERP_STRING_END - { [ Choice1Of2 $1 ] } + { [ Choice1Of2 (fst $1) ] } | INTERP_STRING_PART interpolatedStringFill interpolatedStringParts - { Choice1Of2 $1 :: Choice2Of2 $2 :: $3 } + { Choice1Of2 (fst $1) :: Choice2Of2 $2 :: $3 } /* INTERP_STRING_BEGIN_END */ /* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_END */ /* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_PART int32 INTERP_STRING_END */ interpolatedString: | INTERP_STRING_BEGIN_PART interpolatedStringFill interpolatedStringParts - { Choice1Of2 $1 :: Choice2Of2 $2 :: $3 } + { Choice1Of2 (fst $1) :: Choice2Of2 $2 :: $3 } | INTERP_STRING_BEGIN_END - { [ Choice1Of2 $1 ] } + { [ Choice1Of2 (fst $1) ] } opt_HIGH_PRECEDENCE_APP: | HIGH_PRECEDENCE_BRACK_APP { } diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index 9df21f8919a..6e8aa46f5c5 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -1431,11 +1431,11 @@ module internal ParseAndCheckFile = let getLightSyntaxStatus fileName options = let lower = String.lowercase fileName let lightOnByDefault = List.exists (Filename.checkSuffix lower) FSharpLightSyntaxFileSuffixes - let lightSyntaxStatus = if lightOnByDefault then (options.LightSyntax <> Some false) else (options.LightSyntax = Some true) - LightSyntaxStatus(lightSyntaxStatus, true) + let lightStatus = if lightOnByDefault then (options.LightSyntax <> Some false) else (options.LightSyntax = Some true) + LightSyntaxStatus(lightStatus, true) let createLexerFunction fileName options lexbuf (errHandler: ErrorHandler) = - let lightSyntaxStatus = getLightSyntaxStatus fileName options + let lightStatus = getLightSyntaxStatus fileName options // If we're editing a script then we define INTERACTIVE otherwise COMPILED. // Since this parsing for intellisense we always define EDITING. @@ -1446,10 +1446,10 @@ module internal ParseAndCheckFile = // When analyzing files using ParseOneFile, i.e. for the use of editing clients, we do not apply line directives. // TODO(pathmap): expose PathMap on the service API, and thread it through here - let lexargs = mkLexargs(defines, lightSyntaxStatus, lexResourceManager, [], errHandler.ErrorLogger, PathMap.empty) + let lexargs = mkLexargs(defines, lightStatus, lexResourceManager, [], errHandler.ErrorLogger, PathMap.empty) let lexargs = { lexargs with applyLineDirectives = false } - let tokenizer = LexFilter.LexFilter(lightSyntaxStatus, options.CompilingFsLib, Lexer.token lexargs true, lexbuf) + let tokenizer = LexFilter.LexFilter(lightStatus, options.CompilingFsLib, Lexer.token lexargs true, lexbuf) tokenizer.Lexer // Public callers are unable to answer LanguageVersion feature support questions. diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index b751c45fd77..53819a8ba78 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -27,15 +27,16 @@ type Position = int * int type Range = Position * Position module FSharpTokenTag = + let Identifier = tagOfToken (IDENT "a") - let String = tagOfToken (STRING "a") + let String = tagOfToken (STRING ("a", LexCont.Default)) let IDENT = tagOfToken (IDENT "a") - let STRING = tagOfToken (STRING "a") - let INTERP_STRING_BEGIN_END = tagOfToken (INTERP_STRING_BEGIN_END "a") - let INTERP_STRING_BEGIN_PART = tagOfToken (INTERP_STRING_BEGIN_PART "a") - let INTERP_STRING_PART = tagOfToken (INTERP_STRING_PART "a") - let INTERP_STRING_END = tagOfToken (INTERP_STRING_END "a") + let STRING = String + let INTERP_STRING_BEGIN_END = tagOfToken (INTERP_STRING_BEGIN_END ("a", LexCont.Default)) + let INTERP_STRING_BEGIN_PART = tagOfToken (INTERP_STRING_BEGIN_PART ("a", LexCont.Default)) + let INTERP_STRING_PART = tagOfToken (INTERP_STRING_PART ("a", LexCont.Default)) + let INTERP_STRING_END = tagOfToken (INTERP_STRING_END ("a", LexCont.Default)) let LPAREN = tagOfToken LPAREN let RPAREN = tagOfToken RPAREN let LBRACK = tagOfToken LBRACK @@ -360,33 +361,29 @@ type FSharpTokenizerColorState = module internal LexerStateEncoding = - let computeNextLexState token (prevLexcont: LexerWhitespaceContinuation) = + let computeNextLexState token (prevLexcont: LexerContinuation) = match token with - | HASH_LINE s - | HASH_LIGHT s - | HASH_IF(_, _, s) - | HASH_ELSE(_, _, s) - | HASH_ENDIF(_, _, s) - | INACTIVECODE s - | WHITESPACE s - | COMMENT s - | LINE_COMMENT s - | STRING_TEXT s - | EOF s -> s - - // These all indicate the production of a string part or string after a sequence - // of STRING_TEXT - | INTERP_STRING_BEGIN_PART _ - | INTERP_STRING_PART _ - | INTERP_STRING_BEGIN_END _ - | INTERP_STRING_END _ - | BYTEARRAY _ - | STRING _ -> LexCont.Token(prevLexcont.LexerIfdefStack, prevLexcont.LexerInterpStringNesting) - + | HASH_LINE cont + | HASH_LIGHT cont + | HASH_IF(_, _, cont) + | HASH_ELSE(_, _, cont) + | HASH_ENDIF(_, _, cont) + | INACTIVECODE cont + | WHITESPACE cont + | COMMENT cont + | LINE_COMMENT cont + | STRING_TEXT cont + | EOF cont + | INTERP_STRING_BEGIN_PART (_, cont) + | INTERP_STRING_PART (_, cont) + | INTERP_STRING_BEGIN_END (_, cont) + | INTERP_STRING_END (_, cont) + | BYTEARRAY (_, cont) + | STRING (_, cont) -> cont | _ -> prevLexcont // Note that this will discard all lexcont state, including the ifdefStack. - let revertToDefaultLexCont = LexCont.Token ([], []) + let revertToDefaultLexCont = LexCont.Default let lexstateNumBits = 4 let ncommentsNumBits = 4 @@ -422,10 +419,10 @@ module internal LexerStateEncoding = let bitOfBool b = if b then 1 else 0 let boolOfBit n = (n = 1L) - let inline colorStateOfLexState (state: FSharpTokenizerLexState) = + let colorStateOfLexState (state: FSharpTokenizerLexState) = enum (int32 ((state.OtherBits &&& lexstateMask) >>> lexstateStart)) - let inline lexStateOfColorState (state: FSharpTokenizerColorState) = + let lexStateOfColorState (state: FSharpTokenizerColorState) = (int64 state <<< lexstateStart) &&& lexstateMask let encodeStringStyle kind = @@ -441,14 +438,14 @@ module internal LexerStateEncoding = | 2 -> LexerStringStyle.TripleQuote | _ -> assert false; LexerStringStyle.SingleQuote - let encodeLexCont (colorState: FSharpTokenizerColorState, numComments, b: pos, ifdefStack, light, stringKind: LexerStringKind, nesting) = + let encodeLexCont (colorState: FSharpTokenizerColorState, numComments, b: pos, ifdefStack, light, stringKind: LexerStringKind, stringNest) = let mutable ifdefStackCount = 0 let mutable ifdefStackBits = 0 for ifOrElse in ifdefStack do match ifOrElse with - | (IfDefIf, _) -> () - | (IfDefElse, _) -> - ifdefStackBits <- (ifdefStackBits ||| (1 <<< ifdefStackCount)) + | (IfDefIf, _) -> () + | (IfDefElse, _) -> + ifdefStackBits <- (ifdefStackBits ||| (1 <<< ifdefStackCount)) ifdefStackCount <- ifdefStackCount + 1 let stringKindValue = @@ -458,7 +455,7 @@ module internal LexerStateEncoding = let nestingValue = let tag1, i1, kind1, rest = - match nesting with + match stringNest with | [] -> false, 0, 0, [] | (i1, kind1, _)::rest -> true, i1, encodeStringStyle kind1, rest let tag2, i2, kind2 = @@ -511,7 +508,7 @@ module internal LexerStateEncoding = let hardwhite = boolOfBit ((bits &&& hardwhitePosMask) >>> hardwhitePosStart) let nestingValue = int32 ((bits &&& nestingMask) >>> nestingStart) - let nesting : LexerInterpolatedStringNesting = + let stringNest : LexerInterpolatedStringNesting = let tag1 = ((nestingValue &&& 0b100000000000) = 0b100000000000) let tag2 = ((nestingValue &&& 0b010000000000) = 0b010000000000) let i1 = ((nestingValue &&& 0b001110000000) >>> 7) @@ -524,138 +521,74 @@ module internal LexerStateEncoding = i2, decodeStringStyle kind2, range0 ] - (colorState, ncomments, pos, ifDefs, hardwhite, stringKind, nesting) + (colorState, ncomments, pos, ifDefs, hardwhite, stringKind, stringNest) - let encodeLexInt lightStatus (lexcont: LexerWhitespaceContinuation) = + let encodeLexInt lightStatus (lexcont: LexerContinuation) = match lexcont with - | LexCont.Token (ifdefs, nesting) -> - encodeLexCont (FSharpTokenizerColorState.Token, 0L, pos0, ifdefs, lightStatus, LexerStringKind.String, nesting) - | LexCont.IfDefSkip (ifdefs, nesting, n, m) -> - encodeLexCont (FSharpTokenizerColorState.IfDefSkip, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) - | LexCont.EndLine(ifdefs, nesting, econt) -> + | LexCont.Token (ifdefs, stringNest) -> + encodeLexCont (FSharpTokenizerColorState.Token, 0L, pos0, ifdefs, lightStatus, LexerStringKind.String, stringNest) + | LexCont.IfDefSkip (ifdefs, stringNest, n, m) -> + encodeLexCont (FSharpTokenizerColorState.IfDefSkip, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, stringNest) + | LexCont.EndLine(ifdefs, stringNest, econt) -> match econt with | LexerEndlineContinuation.Skip(n, m) -> - encodeLexCont (FSharpTokenizerColorState.EndLineThenSkip, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) + encodeLexCont (FSharpTokenizerColorState.EndLineThenSkip, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, stringNest) | LexerEndlineContinuation.Token -> - encodeLexCont (FSharpTokenizerColorState.EndLineThenToken, 0L, pos0, ifdefs, lightStatus, LexerStringKind.String, nesting) - | LexCont.String (ifdefs, nesting, style, kind, m) -> + encodeLexCont (FSharpTokenizerColorState.EndLineThenToken, 0L, pos0, ifdefs, lightStatus, LexerStringKind.String, stringNest) + | LexCont.String (ifdefs, stringNest, style, kind, m) -> let state = match style with | LexerStringStyle.SingleQuote -> FSharpTokenizerColorState.String | LexerStringStyle.Verbatim -> FSharpTokenizerColorState.VerbatimString | LexerStringStyle.TripleQuote -> FSharpTokenizerColorState.TripleQuoteString - encodeLexCont (state, 0L, m.Start, ifdefs, lightStatus, kind, nesting) - | LexCont.Comment (ifdefs, nesting, n, m) -> - encodeLexCont (FSharpTokenizerColorState.Comment, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) - | LexCont.SingleLineComment (ifdefs, nesting, n, m) -> - encodeLexCont (FSharpTokenizerColorState.SingleLineComment, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) - | LexCont.StringInComment (ifdefs, nesting, style, n, m) -> + encodeLexCont (state, 0L, m.Start, ifdefs, lightStatus, kind, stringNest) + | LexCont.Comment (ifdefs, stringNest, n, m) -> + encodeLexCont (FSharpTokenizerColorState.Comment, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, stringNest) + | LexCont.SingleLineComment (ifdefs, stringNest, n, m) -> + encodeLexCont (FSharpTokenizerColorState.SingleLineComment, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, stringNest) + | LexCont.StringInComment (ifdefs, stringNest, style, n, m) -> let state = match style with | LexerStringStyle.SingleQuote -> FSharpTokenizerColorState.StringInComment | LexerStringStyle.Verbatim -> FSharpTokenizerColorState.VerbatimStringInComment | LexerStringStyle.TripleQuote -> FSharpTokenizerColorState.TripleQuoteStringInComment - encodeLexCont (state, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) - | LexCont.MLOnly (ifdefs, nesting, m) -> - encodeLexCont (FSharpTokenizerColorState.CamlOnly, 0L, m.Start, ifdefs, lightStatus, LexerStringKind.String, nesting) + encodeLexCont (state, int64 n, m.Start, ifdefs, lightStatus, LexerStringKind.String, stringNest) + | LexCont.MLOnly (ifdefs, stringNest, m) -> + encodeLexCont (FSharpTokenizerColorState.CamlOnly, 0L, m.Start, ifdefs, lightStatus, LexerStringKind.String, stringNest) let decodeLexInt (state: FSharpTokenizerLexState) = - let tag, n1, p1, ifdefs, lightSyntaxStatusInitial, stringKind, nesting = decodeLexCont state + let tag, n1, p1, ifdefs, lightSyntaxStatusInitial, stringKind, stringNest = decodeLexCont state let lexcont = match tag with | FSharpTokenizerColorState.Token -> - LexCont.Token (ifdefs, nesting) + LexCont.Token (ifdefs, stringNest) | FSharpTokenizerColorState.IfDefSkip -> - LexCont.IfDefSkip (ifdefs, nesting, n1, mkRange "file" p1 p1) + LexCont.IfDefSkip (ifdefs, stringNest, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.String -> - LexCont.String (ifdefs, nesting, LexerStringStyle.SingleQuote, stringKind, mkRange "file" p1 p1) + LexCont.String (ifdefs, stringNest, LexerStringStyle.SingleQuote, stringKind, mkRange "file" p1 p1) | FSharpTokenizerColorState.Comment -> - LexCont.Comment (ifdefs, nesting, n1, mkRange "file" p1 p1) + LexCont.Comment (ifdefs, stringNest, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.SingleLineComment -> - LexCont.SingleLineComment (ifdefs, nesting, n1, mkRange "file" p1 p1) + LexCont.SingleLineComment (ifdefs, stringNest, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.StringInComment -> - LexCont.StringInComment (ifdefs, nesting, LexerStringStyle.SingleQuote, n1, mkRange "file" p1 p1) + LexCont.StringInComment (ifdefs, stringNest, LexerStringStyle.SingleQuote, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.VerbatimStringInComment -> - LexCont.StringInComment (ifdefs, nesting, LexerStringStyle.Verbatim, n1, mkRange "file" p1 p1) + LexCont.StringInComment (ifdefs, stringNest, LexerStringStyle.Verbatim, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.TripleQuoteStringInComment -> - LexCont.StringInComment (ifdefs, nesting, LexerStringStyle.TripleQuote, n1, mkRange "file" p1 p1) + LexCont.StringInComment (ifdefs, stringNest, LexerStringStyle.TripleQuote, n1, mkRange "file" p1 p1) | FSharpTokenizerColorState.CamlOnly -> - LexCont.MLOnly (ifdefs, nesting, mkRange "file" p1 p1) + LexCont.MLOnly (ifdefs, stringNest, mkRange "file" p1 p1) | FSharpTokenizerColorState.VerbatimString -> - LexCont.String (ifdefs, nesting, LexerStringStyle.Verbatim, stringKind, mkRange "file" p1 p1) + LexCont.String (ifdefs, stringNest, LexerStringStyle.Verbatim, stringKind, mkRange "file" p1 p1) | FSharpTokenizerColorState.TripleQuoteString -> - LexCont.String (ifdefs, nesting, LexerStringStyle.TripleQuote, stringKind, mkRange "file" p1 p1) + LexCont.String (ifdefs, stringNest, LexerStringStyle.TripleQuote, stringKind, mkRange "file" p1 p1) | FSharpTokenizerColorState.EndLineThenSkip -> - LexCont.EndLine(ifdefs, nesting, LexerEndlineContinuation.Skip(n1, mkRange "file" p1 p1)) + LexCont.EndLine(ifdefs, stringNest, LexerEndlineContinuation.Skip(n1, mkRange "file" p1 p1)) | FSharpTokenizerColorState.EndLineThenToken -> - LexCont.EndLine(ifdefs, nesting, LexerEndlineContinuation.Token) - | _ -> LexCont.Token ([], nesting) + LexCont.EndLine(ifdefs, stringNest, LexerEndlineContinuation.Token) + | _ -> LexCont.Token ([], stringNest) lightSyntaxStatusInitial, lexcont - let argsWithIfDefs ifdefs args = - if args.ifdefStack = ifdefs then - args - else - {args with ifdefStack = ifdefs} - - let argsWithInterpolatedStringNesting nesting args = - if args.stringNest = nesting then - args - else - {args with stringNest = nesting} - - let callLexCont lexcont args skip lexbuf = - - match lexcont with - | LexCont.EndLine (ifdefs, nesting, cont) -> - let args = argsWithIfDefs ifdefs args - let args = argsWithInterpolatedStringNesting nesting args - Lexer.endline cont args skip lexbuf - - | LexCont.Token (ifdefs, nesting) -> - let args = argsWithIfDefs ifdefs args - let args = argsWithInterpolatedStringNesting nesting args - Lexer.token args skip lexbuf - - | LexCont.IfDefSkip (ifdefs, nesting, n, m) -> - let args = argsWithIfDefs ifdefs args - let args = argsWithInterpolatedStringNesting nesting args - Lexer.ifdefSkip n m args skip lexbuf - - | LexCont.String (ifdefs, nesting, style, kind, m) -> - let args = argsWithIfDefs ifdefs args - let args = argsWithInterpolatedStringNesting nesting args - let buf = ByteBuffer.Create 100 - let args = (buf, LexerStringFinisher.Default, m, kind, args) - match style with - | LexerStringStyle.SingleQuote -> Lexer.singleQuoteString args skip lexbuf - | LexerStringStyle.Verbatim -> Lexer.verbatimString args skip lexbuf - | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteString args skip lexbuf - - | LexCont.Comment (ifdefs, nesting, n, m) -> - let args = argsWithIfDefs ifdefs args - let args = argsWithInterpolatedStringNesting nesting args - Lexer.comment (n, m, args) skip lexbuf - - | LexCont.SingleLineComment (ifdefs, nesting, n, m) -> - let args = argsWithIfDefs ifdefs args - let args = argsWithInterpolatedStringNesting nesting args - // The first argument is 'None' because we don't need XML comments when called from VS tokenizer - Lexer.singleLineComment (None, n, m, args) skip lexbuf - - | LexCont.StringInComment (ifdefs, nesting, style, n, m) -> - let args = argsWithIfDefs ifdefs args - let args = argsWithInterpolatedStringNesting nesting args - match style with - | LexerStringStyle.SingleQuote -> Lexer.stringInComment n m args skip lexbuf - | LexerStringStyle.Verbatim -> Lexer.verbatimStringInComment n m args skip lexbuf - | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteStringInComment n m args skip lexbuf - - | LexCont.MLOnly (ifdefs, nesting, m) -> - let args = argsWithIfDefs ifdefs args - let args = argsWithInterpolatedStringNesting nesting args - Lexer.mlOnly m args skip lexbuf - //---------------------------------------------------------------------------- // Colorization //---------------------------------------------------------------------------- @@ -672,8 +605,7 @@ type SingleLineTokenState = type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, maxLength: int option, filename: string option, - lexArgsLightOn: LexArgs, - lexArgsLightOff: LexArgs) = + lexargs: LexArgs) = let skip = false // don't skip whitespace in the lexer @@ -744,102 +676,157 @@ type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, let offset = beforeIdent + identLength processWhiteAndComment str offset delay cont ) - // ---------------------------------------------------------------------------------- - + // Set up the initial file position do match filename with | None -> lexbuf.EndPos <- Internal.Utilities.Text.Lexing.Position.Empty | Some value -> resetLexbufPos value lexbuf - member x.ScanToken lexintInitial: FSharpTokenInfo option * FSharpTokenizerLexState = + // Call the given continuation, reusing the same 'lexargs' each time but adjust + // its mutable entries to set up the right state + let callLexCont lexcont lightStatus skip = + + // Set up the arguments to lexing + lexargs.lightStatus <- lightStatus + + match lexcont with + | LexCont.EndLine (ifdefs, stringNest, cont) -> + lexargs.ifdefStack <- ifdefs + lexargs.stringNest <- stringNest + Lexer.endline cont lexargs skip lexbuf + + | LexCont.Token (ifdefs, stringNest) -> + lexargs.ifdefStack <- ifdefs + lexargs.stringNest <- stringNest + Lexer.token lexargs skip lexbuf + + | LexCont.IfDefSkip (ifdefs, stringNest, n, m) -> + lexargs.ifdefStack <- ifdefs + lexargs.stringNest <- stringNest + Lexer.ifdefSkip n m lexargs skip lexbuf + + | LexCont.String (ifdefs, stringNest, style, kind, m) -> + lexargs.ifdefStack <- ifdefs + lexargs.stringNest <- stringNest + let buf = ByteBuffer.Create 100 + let args = (buf, LexerStringFinisher.Default, m, kind, lexargs) + match style with + | LexerStringStyle.SingleQuote -> Lexer.singleQuoteString args skip lexbuf + | LexerStringStyle.Verbatim -> Lexer.verbatimString args skip lexbuf + | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteString args skip lexbuf + + | LexCont.Comment (ifdefs, stringNest, n, m) -> + lexargs.ifdefStack <- ifdefs + lexargs.stringNest <- stringNest + Lexer.comment (n, m, lexargs) skip lexbuf + + | LexCont.SingleLineComment (ifdefs, stringNest, n, m) -> + lexargs.ifdefStack <- ifdefs + lexargs.stringNest <- stringNest + // The first argument is 'None' because we don't need XML comments when called from VS tokenizer + Lexer.singleLineComment (None, n, m, lexargs) skip lexbuf + + | LexCont.StringInComment (ifdefs, stringNest, style, n, m) -> + lexargs.ifdefStack <- ifdefs + lexargs.stringNest <- stringNest + match style with + | LexerStringStyle.SingleQuote -> Lexer.stringInComment n m lexargs skip lexbuf + | LexerStringStyle.Verbatim -> Lexer.verbatimStringInComment n m lexargs skip lexbuf + | LexerStringStyle.TripleQuote -> Lexer.tripleQuoteStringInComment n m lexargs skip lexbuf + + | LexCont.MLOnly (ifdefs, stringNest, m) -> + lexargs.ifdefStack <- ifdefs + lexargs.stringNest <- stringNest + Lexer.mlOnly m lexargs skip lexbuf + + let columnsOfCurrentToken() = + let leftp = lexbuf.StartPos + let rightp = lexbuf.EndPos + let leftc = leftp.Column + let rightc = + match maxLength with + | Some mx when rightp.Line > leftp.Line -> mx + | _ -> rightp.Column + let rightc = rightc - 1 + struct (leftc, rightc) + + let getTokenWithPosition lexcont lightStatus = + // Column of token + // Get the token & position - either from a stack or from the lexer + try + if (tokenStack.Count > 0) then + true, tokenStack.Pop() + else + // Choose which lexer entry point to call and call it + let token = callLexCont lexcont lightStatus skip + let struct (leftc, rightc) = columnsOfCurrentToken() + + // Splits tokens like ">." into multiple tokens - this duplicates behavior from the 'lexfilter' + // which cannot be (easily) used from the language service. The rules here are not always valid, + // because sometimes token shouldn't be split. However it is just for colorization & + // for VS (which needs to recognize when user types "."). + match token with + | HASH_IF (m, lineStr, cont) when lineStr <> "" -> + false, processHashIfLine m.StartColumn lineStr cont + | HASH_ELSE (m, lineStr, cont) when lineStr <> "" -> + false, processHashEndElse m.StartColumn lineStr 4 cont + | HASH_ENDIF (m, lineStr, cont) when lineStr <> "" -> + false, processHashEndElse m.StartColumn lineStr 5 cont + | RQUOTE_DOT (s, raw) -> + delayToken(DOT, rightc, rightc) + false, (RQUOTE (s, raw), leftc, rightc - 1) + | INFIX_COMPARE_OP (LexFilter.TyparsCloseOp(greaters, afterOp) as opstr) -> + match afterOp with + | None -> () + | Some tok -> delayToken(tok, leftc + greaters.Length, rightc) + for i = greaters.Length - 1 downto 1 do + delayToken(greaters.[i] false, leftc + i, rightc - opstr.Length + i + 1) + false, (greaters.[0] false, leftc, rightc - opstr.Length + 1) + // break up any operators that start with '.' so that we can get auto-popup-completion for e.g. "x.+1" when typing the dot + | INFIX_STAR_STAR_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(INFIX_STAR_STAR_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | PLUS_MINUS_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(PLUS_MINUS_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | INFIX_COMPARE_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(INFIX_COMPARE_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | INFIX_AT_HAT_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(INFIX_AT_HAT_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | INFIX_BAR_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(INFIX_BAR_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | PREFIX_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(PREFIX_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | INFIX_STAR_DIV_MOD_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(INFIX_STAR_DIV_MOD_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | INFIX_AMP_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(INFIX_AMP_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | ADJACENT_PREFIX_OP opstr when opstr.StartsWithOrdinal(".") -> + delayToken(ADJACENT_PREFIX_OP(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | FUNKY_OPERATOR_NAME opstr when opstr.StartsWithOrdinal(".") -> + delayToken(FUNKY_OPERATOR_NAME(opstr.Substring 1), leftc+1, rightc) + false, (DOT, leftc, leftc) + | _ -> false, (token, leftc, rightc) + with _ -> + false, (EOF LexerStateEncoding.revertToDefaultLexCont, 0, 0) + + // Scan a token starting with the given lexer state + member x.ScanToken (lexState: FSharpTokenizerLexState) : FSharpTokenInfo option * FSharpTokenizerLexState = use unwindBP = PushThreadBuildPhaseUntilUnwind BuildPhase.Parse use unwindEL = PushErrorLoggerPhaseUntilUnwind (fun _ -> DiscardErrorsLogger) - let lightSyntaxStatusInitial, lexcontInitial = LexerStateEncoding.decodeLexInt lexintInitial - let lightSyntaxStatus = LightSyntaxStatus(lightSyntaxStatusInitial, false) - - // Build the arguments to the lexer function - let lexargs = if lightSyntaxStatusInitial then lexArgsLightOn else lexArgsLightOff - - let GetTokenWithPosition lexcontInitial = - // Column of token - let ColumnsOfCurrentToken() = - let leftp = lexbuf.StartPos - let rightp = lexbuf.EndPos - let leftc = leftp.Column - let rightc = - match maxLength with - | Some mx when rightp.Line > leftp.Line -> mx - | _ -> rightp.Column - let rightc = rightc - 1 - struct (leftc, rightc) - - // Get the token & position - either from a stack or from the lexer - try - if (tokenStack.Count > 0) then true, tokenStack.Pop() - else - // Choose which lexer entry point to call and call it - let token = LexerStateEncoding.callLexCont lexcontInitial lexargs skip lexbuf - let struct (leftc, rightc) = ColumnsOfCurrentToken() - - // Splits tokens like ">." into multiple tokens - this duplicates behavior from the 'lexfilter' - // which cannot be (easily) used from the language service. The rules here are not always valid, - // because sometimes token shouldn't be split. However it is just for colorization & - // for VS (which needs to recognize when user types "."). - match token with - | HASH_IF (m, lineStr, cont) when lineStr <> "" -> - false, processHashIfLine m.StartColumn lineStr cont - | HASH_ELSE (m, lineStr, cont) when lineStr <> "" -> - false, processHashEndElse m.StartColumn lineStr 4 cont - | HASH_ENDIF (m, lineStr, cont) when lineStr <> "" -> - false, processHashEndElse m.StartColumn lineStr 5 cont - | RQUOTE_DOT (s, raw) -> - delayToken(DOT, rightc, rightc) - false, (RQUOTE (s, raw), leftc, rightc - 1) - | INFIX_COMPARE_OP (LexFilter.TyparsCloseOp(greaters, afterOp) as opstr) -> - match afterOp with - | None -> () - | Some tok -> delayToken(tok, leftc + greaters.Length, rightc) - for i = greaters.Length - 1 downto 1 do - delayToken(greaters.[i] false, leftc + i, rightc - opstr.Length + i + 1) - false, (greaters.[0] false, leftc, rightc - opstr.Length + 1) - // break up any operators that start with '.' so that we can get auto-popup-completion for e.g. "x.+1" when typing the dot - | INFIX_STAR_STAR_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(INFIX_STAR_STAR_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | PLUS_MINUS_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(PLUS_MINUS_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | INFIX_COMPARE_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(INFIX_COMPARE_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | INFIX_AT_HAT_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(INFIX_AT_HAT_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | INFIX_BAR_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(INFIX_BAR_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | PREFIX_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(PREFIX_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | INFIX_STAR_DIV_MOD_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(INFIX_STAR_DIV_MOD_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | INFIX_AMP_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(INFIX_AMP_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | ADJACENT_PREFIX_OP opstr when opstr.StartsWithOrdinal(".") -> - delayToken(ADJACENT_PREFIX_OP(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | FUNKY_OPERATOR_NAME opstr when opstr.StartsWithOrdinal(".") -> - delayToken(FUNKY_OPERATOR_NAME(opstr.Substring 1), leftc+1, rightc) - false, (DOT, leftc, leftc) - | _ -> false, (token, leftc, rightc) - with - | e -> false, (EOF LexerStateEncoding.revertToDefaultLexCont, 0, 0) // REVIEW: report lex failure here + let lightStatus, lexcont = LexerStateEncoding.decodeLexInt lexState + let lightStatus = LightSyntaxStatus(lightStatus, false) // Grab a token - let isCached, (token, leftc, rightc) = GetTokenWithPosition lexcontInitial + let isCached, (token, leftc, rightc) = getTokenWithPosition lexcont lightStatus // Check for end-of-string and failure let tokenDataOption, lexcontFinal, tokenTag = @@ -855,7 +842,8 @@ type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, let lexcontFinal = // If we're using token from cache, we don't move forward with lexing - if isCached then lexcontInitial else LexerStateEncoding.computeNextLexState token lexcontInitial + if isCached then lexcont + else LexerStateEncoding.computeNextLexState token lexcont let tokenTag = tagOfToken token @@ -874,19 +862,15 @@ type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, FullMatchedLength=fullMatchedLength} Some tokenData, lexcontFinal, tokenTag - // Get the final lex int and color state - let FinalState lexcontFinal = - LexerStateEncoding.encodeLexInt lightSyntaxStatus.Status lexcontFinal - // Check for patterns like #-IDENT and see if they look like meta commands for .fsx files. If they do then merge them into a single token. let tokenDataOption, lexintFinal = - let lexintFinal = FinalState lexcontFinal + let lexintFinal = LexerStateEncoding.encodeLexInt lightStatus.Status lexcontFinal match tokenDataOption, singleLineTokenState, tokenTagToTokenId tokenTag with | Some tokenData, SingleLineTokenState.BeforeHash, TOKEN_HASH -> // Don't allow further matches. singleLineTokenState <- SingleLineTokenState.NoFurtherMatchPossible // Peek at the next token - let isCached, (nextToken, _, rightc) = GetTokenWithPosition lexcontInitial + let isCached, (nextToken, _, rightc) = getTokenWithPosition lexcont lightStatus match nextToken with | IDENT possibleMetaCommand -> match fsx, possibleMetaCommand with @@ -911,9 +895,9 @@ type FSharpLineTokenizer(lexbuf: UnicodeLexing.Lexbuf, // These are for script and non-script | _, "nowarn" -> // Merge both tokens into one. - let lexcontFinal = if isCached then lexcontInitial else LexerStateEncoding.computeNextLexState token lexcontInitial + let lexcontFinal = if isCached then lexcont else LexerStateEncoding.computeNextLexState token lexcont let tokenData = {tokenData with RightColumn=rightc;ColorClass=FSharpTokenColorKind.PreprocessorKeyword;CharClass=FSharpTokenCharKind.Keyword;FSharpTokenTriggerClass=FSharpTokenTriggerClass.None} - let lexintFinal = FinalState lexcontFinal + let lexintFinal = LexerStateEncoding.encodeLexInt lightStatus.Status lexcontFinal Some tokenData, lexintFinal | _ -> tokenDataOption, lexintFinal | _ -> tokenDataOption, lexintFinal @@ -941,16 +925,15 @@ type FSharpSourceTokenizer(defineConstants: string list, filename: string option let lexResourceManager = new Lexhelp.LexResourceManager() - let lexArgsLightOn = mkLexargs(defineConstants, LightSyntaxStatus(true, false), lexResourceManager, [], DiscardErrorsLogger, PathMap.empty) - let lexArgsLightOff = mkLexargs(defineConstants, LightSyntaxStatus(false, false), lexResourceManager, [], DiscardErrorsLogger, PathMap.empty) + let lexargs = mkLexargs(defineConstants, LightSyntaxStatus(true, false), lexResourceManager, [], DiscardErrorsLogger, PathMap.empty) member _.CreateLineTokenizer(lineText: string) = let lexbuf = UnicodeLexing.StringAsLexbuf(isFeatureSupported, lineText) - FSharpLineTokenizer(lexbuf, Some lineText.Length, filename, lexArgsLightOn, lexArgsLightOff) + FSharpLineTokenizer(lexbuf, Some lineText.Length, filename, lexargs) member _.CreateBufferTokenizer bufferFiller = let lexbuf = UnicodeLexing.FunctionAsLexbuf(isFeatureSupported, bufferFiller) - FSharpLineTokenizer(lexbuf, None, filename, lexArgsLightOn, lexArgsLightOff) + FSharpLineTokenizer(lexbuf, None, filename, lexargs) module Keywords = open FSharp.Compiler.Lexhelp.Keywords @@ -1534,15 +1517,15 @@ module Lexer = let canUseLexFilter = (flags &&& FSharpLexerFlags.UseLexFilter) = FSharpLexerFlags.UseLexFilter let lexbuf = UnicodeLexing.SourceTextAsLexbuf(supportsFeature, text) - let lightSyntaxStatus = LightSyntaxStatus(isLightSyntaxOn, true) - let lexargs = mkLexargs (conditionalCompilationDefines, lightSyntaxStatus, Lexhelp.LexResourceManager(0), [], errorLogger, pathMap) + let lightStatus = LightSyntaxStatus(isLightSyntaxOn, true) + let lexargs = mkLexargs (conditionalCompilationDefines, lightStatus, Lexhelp.LexResourceManager(0), [], errorLogger, pathMap) let lexargs = { lexargs with applyLineDirectives = isCompiling } let getNextToken = let lexer = Lexer.token lexargs canSkipTrivia if canUseLexFilter then - LexFilter.LexFilter(lexargs.lightSyntaxStatus, isCompilingFSharpCore, lexer, lexbuf).Lexer + LexFilter.LexFilter(lexargs.lightStatus, isCompilingFSharpCore, lexer, lexbuf).Lexer else lexer diff --git a/src/fsharp/service/ServiceLexing.fsi b/src/fsharp/service/ServiceLexing.fsi index 43d0fd71942..9e05df2732d 100755 --- a/src/fsharp/service/ServiceLexing.fsi +++ b/src/fsharp/service/ServiceLexing.fsi @@ -244,15 +244,29 @@ type FSharpTokenInfo = [] type FSharpLineTokenizer = /// Scan one token from the line - member ScanToken : lexState:FSharpTokenizerLexState -> FSharpTokenInfo option * FSharpTokenizerLexState - static member ColorStateOfLexState : FSharpTokenizerLexState -> FSharpTokenizerColorState - static member LexStateOfColorState : FSharpTokenizerColorState -> FSharpTokenizerLexState + member ScanToken: lexState:FSharpTokenizerLexState -> FSharpTokenInfo option * FSharpTokenizerLexState + + /// Get the color state from the lexer state + static member ColorStateOfLexState: FSharpTokenizerLexState -> FSharpTokenizerColorState + + /// Get a default lexer state for a color state. + /// + /// NOTE: This may result in an inaccurate lexer state + /// not taking into account things such as the #if/#endif and string interpolation context + /// within the file + static member LexStateOfColorState: FSharpTokenizerColorState -> FSharpTokenizerLexState /// Tokenizer for a source file. Holds some expensive-to-compute resources at the scope of the file. [] type FSharpSourceTokenizer = + + /// Create a tokenizer for a source file. new : conditionalDefines:string list * fileName:string option -> FSharpSourceTokenizer - member CreateLineTokenizer : lineText:string -> FSharpLineTokenizer + + /// Create a tokenizer for a line of this source file + member CreateLineTokenizer: lineText:string -> FSharpLineTokenizer + + /// Create a tokenizer for a line of this source file using a buffer filler member CreateBufferTokenizer : bufferFiller:(char[] * int * int -> int) -> FSharpLineTokenizer module internal TestExpose = diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index c431c27efb2..893a720e447 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -237,14 +237,24 @@ Funkce správy balíčků vyžaduje jazykovou verzi 5.0, použijte /langversion:preview. - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index d953a11d093..b15dd953bb6 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -237,14 +237,24 @@ Für das Paketverwaltungsfeature ist Sprachversion 5.0 erforderlich. Verwenden Sie /langversion:preview. - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index 81e74d8412b..a1a5af689b4 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -237,14 +237,24 @@ La característica de administración de paquetes requiere la versión de lenguaje 5.0; use /langversion:preview - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index ebb073c4b14..9f212bd5e2e 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -237,14 +237,24 @@ La fonctionnalité de gestion des packages nécessite la version 5.0 du langage. Utilisez /langversion:preview - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index e416d725fff..712651d37fe 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -237,14 +237,24 @@ Con la funzionalità di gestione pacchetti è richiesta la versione 5.0 del linguaggio. Usare /langversion:preview - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 1029f0a516f..0ff189524fe 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -237,14 +237,24 @@ パッケージ管理機能では、言語バージョン 5.0 で /langversion:preview を使用する必要があります - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index be6ed328678..833251d38ad 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -237,14 +237,24 @@ 패키지 관리 기능을 사용하려면 언어 버전 5.0이 필요합니다. /langversion:preview를 사용하세요. - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index 61c9154e6d6..cf86c6ba832 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -237,14 +237,24 @@ Funkcja zarządzania pakietami wymaga języka w wersji 5.0, użyj parametru /langversion:preview - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 2382d9e52ce..58e36c1a15b 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -237,14 +237,24 @@ O recurso de gerenciamento de pacotes requer a versão de idioma 5.0. Use /langversion:preview - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index a9b3450965a..d1a227cc9d9 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -237,14 +237,24 @@ Для функции управления пакетами требуется версия языка 5.0, используйте параметр /langversion:preview - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 9f715676bc2..995d58db8df 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -237,14 +237,24 @@ Paket yönetimi özelliği dil sürümü 5.0 gerektiriyor, /langversion:preview kullanın - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index ec9bb11c806..04502431fe3 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -237,14 +237,24 @@ 包管理功能需要语言版本 5.0,请使用 /langversion:preview - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index f0121957c56..80396dd9f3a 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -237,14 +237,24 @@ 套件管理功能需要語言版本 5.0,請使用 /langversion:preview - - Incomplete interpolated string - Incomplete interpolated string + + Incomplete interpolated string begun at or before here + Incomplete interpolated string begun at or before here - - Incomplete interpolated string expression fill - Incomplete interpolated string expression fill + + Incomplete interpolated string expression fill begun at or before here + Incomplete interpolated string expression fill begun at or before here + + + + Incomplete interpolated triple-quote string begun at or before here + Incomplete interpolated triple-quote string begun at or before here + + + + Incomplete interpolated verbatim string begun at or before here + Incomplete interpolated verbatim string begun at or before here diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 7b02f5ef6a0..561966f7054 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -706,21 +706,44 @@ let TripleInterpolatedInVerbatimInterpolated = $\"123{456}789{$\"\"\"012\"\"\"}3 [] let ``String interpolation negative incomplete string`` () = - let code = """ - -let x1 = $"one %d{System.String.Empty} // incomplete string -""" + let code = """let x1 = $"one %d{System.String.Empty}""" CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] code - [||] + [|(FSharpErrorSeverity.Error, 10, (1, 1, 1, 39), + "Incomplete structured construct at or before this point in binding. Expected interpolated string (final part), interpolated string (part) or other token."); + (FSharpErrorSeverity.Error, 3379, (1, 38, 1, 39), + "Incomplete interpolated string begun at or before here")|] [] let ``String interpolation negative incomplete string fill`` () = - let code = """ - -let x1 = $"one %d{System.String.Empty // incomplete string fill -""" + let code = """let x1 = $"one %d{System.String.Empty""" CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] code - [||] + [|(FSharpErrorSeverity.Error, 10, (1, 1, 1, 38), + "Incomplete structured construct at or before this point in binding. Expected interpolated string (final part), interpolated string (part) or other token."); + (FSharpErrorSeverity.Error, 3378, (1, 18, 1, 19), + "Incomplete interpolated string expression fill begun at or before here")|] + [] + let ``String interpolation negative incomplete verbatim string`` () = + let code = """let x1 = @$"one %d{System.String.Empty} """ + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 10, (1, 1, 1, 41), + "Incomplete structured construct at or before this point in binding. Expected interpolated string (final part), interpolated string (part) or other token."); + (FSharpErrorSeverity.Error, 3380, (1, 39, 1, 40), + "Incomplete interpolated verbatim string begun at or before here")|] + + [] + let ``String interpolation negative incomplete triple quote string`` () = + let code = "let x1 = $\"\"\"one" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Warning, 58, (1, 1, 1, 17), + "Possible incorrect indentation: this token is offside of context started at position (1:1). Try indenting this token further or using standard formatting conventions."); + (FSharpErrorSeverity.Warning, 58, (1, 17, 1, 17), + "Possible incorrect indentation: this token is offside of context started at position (1:1). Try indenting this token further or using standard formatting conventions."); + (FSharpErrorSeverity.Error, 10, (1, 1, 1, 17), + "Incomplete structured construct at or before this point in binding"); + (FSharpErrorSeverity.Error, 3381, (1, 10, 1, 14), + "Incomplete interpolated triple-quote string begun at or before here")|] diff --git a/tests/service/TokenizerTests.fs b/tests/service/TokenizerTests.fs index 7d89b8a1764..4bf058970d6 100644 --- a/tests/service/TokenizerTests.fs +++ b/tests/service/TokenizerTests.fs @@ -32,7 +32,7 @@ let tokenizeLines (lines:string[]) = yield n, parseLine(line, state, tokenizer) |> List.ofSeq ] [] -let ``Tokenizer test 1``() = +let ``Tokenizer test - simple let with string``() = let tokenizedLines = tokenizeLines [| "// Sets the hello wrold variable" @@ -60,7 +60,7 @@ let ``Tokenizer test 1``() = Assert.Fail(sprintf "actual and expected did not match,actual =\n%A\nexpected=\n%A\n" actual expected) [] -let ``Tokenizer test 2``() = +let ``Tokenizer test 2 - single line non-nested string interpolation``() = let tokenizedLines = tokenizeLines [| "// Tests tokenizing string interpolation" @@ -126,7 +126,7 @@ let ``Tokenizer test 2``() = [] -let ``Tokenizer test 3``() = +let ``Tokenizer test - multiline non-nested string interpolation``() = let tokenizedLines = tokenizeLines [| "let hello1t = $\"\"\"abc {1+" @@ -139,8 +139,54 @@ let ``Tokenizer test 3``() = [(0, [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello1t"); ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); - ("STRING_TEXT", "$\"\"\""); ("STRING_TEXT", "abc")]); - (1, [("STRING_TEXT", "def"); ("INTERP_STRING_BEGIN_END", "\"\"\"")])] + ("STRING_TEXT", "$\"\"\""); ("STRING_TEXT", "abc"); ("STRING_TEXT", " "); + ("INTERP_STRING_BEGIN_PART", "{"); ("INT32", "1"); ("PLUS_MINUS_OP", "+")]); + (1, + [("WHITESPACE", " "); ("INT32", "1"); ("STRING_TEXT", "}"); + ("STRING_TEXT", " "); ("STRING_TEXT", "def"); ("INTERP_STRING_END", "\"\"\"")])] + + if actual <> expected then + printfn "actual = %A" actual + printfn "expected = %A" expected + Assert.Fail(sprintf "actual and expected did not match,actual =\n%A\nexpected=\n%A\n" actual expected) + +[] +// checks nested '{' and nested single-quote strings +let ``Tokenizer test - multi-line nested string interpolation``() = + let tokenizedLines = + tokenizeLines + [| "let hello1t = $\"\"\"abc {\"a\" + " + " { " + " contents = \"b\" " + " }.contents " + " } def\"\"\"" |] + + let actual = + [ for lineNo, lineToks in tokenizedLines do + yield lineNo, [ for str, info in lineToks do yield info.TokenName, str ] ] + let expected = + [(0, + [("LET", "let"); ("WHITESPACE", " "); ("IDENT", "hello1t"); + ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); + ("STRING_TEXT", "$\"\"\""); ("STRING_TEXT", "abc"); ("STRING_TEXT", " "); + ("INTERP_STRING_BEGIN_PART", "{"); ("STRING_TEXT", "\""); ("STRING_TEXT", "a"); + ("STRING", "\""); ("WHITESPACE", " "); ("PLUS_MINUS_OP", "+"); + ("WHITESPACE", " ")]); + (1, + [("WHITESPACE", " "); ("LBRACE", "{"); + ("WHITESPACE", " ")]); + (2, + [("WHITESPACE", " "); ("IDENT", "contents"); + ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); + ("STRING_TEXT", "\""); ("STRING_TEXT", "b"); ("STRING", "\""); + ("WHITESPACE", " ")]); + (3, + [("WHITESPACE", " "); ("STRING_TEXT", "}"); + ("STRING_TEXT", "."); ("STRING_TEXT", "contents"); + ("STRING_TEXT", " ")]); + (4, + [("STRING_TEXT", " "); ("STRING_TEXT", "}"); + ("STRING_TEXT", " "); ("STRING_TEXT", "def"); ("INTERP_STRING_END", "\"\"\"")])] if actual <> expected then printfn "actual = %A" actual From b6a2ede7cf5a38747dd2ff5727eeba39976e0adc Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Jul 2020 17:31:55 +0100 Subject: [PATCH 60/87] revert xlf changes --- src/fsharp/xlf/FSComp.txt.cs.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.de.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.es.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.fr.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.it.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.ja.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.ko.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.pl.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.pt-BR.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.ru.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.tr.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.zh-Hans.xlf | 80 --------------------------- src/fsharp/xlf/FSComp.txt.zh-Hant.xlf | 80 --------------------------- src/fsharp/xlf/FSStrings.cs.xlf | 20 ------- src/fsharp/xlf/FSStrings.de.xlf | 20 ------- src/fsharp/xlf/FSStrings.es.xlf | 20 ------- src/fsharp/xlf/FSStrings.fr.xlf | 20 ------- src/fsharp/xlf/FSStrings.it.xlf | 20 ------- src/fsharp/xlf/FSStrings.ja.xlf | 20 ------- src/fsharp/xlf/FSStrings.ko.xlf | 20 ------- src/fsharp/xlf/FSStrings.pl.xlf | 20 ------- src/fsharp/xlf/FSStrings.pt-BR.xlf | 20 ------- src/fsharp/xlf/FSStrings.ru.xlf | 20 ------- src/fsharp/xlf/FSStrings.tr.xlf | 20 ------- src/fsharp/xlf/FSStrings.zh-Hans.xlf | 20 ------- src/fsharp/xlf/FSStrings.zh-Hant.xlf | 20 ------- 26 files changed, 1300 deletions(-) diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 893a720e447..85af396cf5c 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Dostupná přetížení:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} – {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. Stream nezačíná zdrojem s hodnotou null a není ve formátu .RES. @@ -237,26 +192,6 @@ Funkce správy balíčků vyžaduje jazykovou verzi 5.0, použijte /langversion:preview. - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Neočekávaný symbol . v definici členu. Očekávalo se with, = nebo jiný token. @@ -297,16 +232,6 @@ Atributy nejde použít pro rozšíření typů. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use! se nedá kombinovat s and!. @@ -317,11 +242,6 @@ Konstrukt let! ... and! ... se dá použít jen v případě, že tvůrce výpočetních výrazů definuje buď metodu {0}, nebo vhodné metody MergeSource a Bind. - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index b15dd953bb6..ba71a1e94f4 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Verfügbare Überladungen:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. Der Stream beginnt nicht mit einer NULL-Ressource und ist nicht im RES-Format. @@ -237,26 +192,6 @@ Für das Paketverwaltungsfeature ist Sprachversion 5.0 erforderlich. Verwenden Sie /langversion:preview. - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Unerwartetes Symbol "." in der Memberdefinition. Erwartet wurde "with", "=" oder ein anderes Token. @@ -297,16 +232,6 @@ Attribute können nicht auf Typerweiterungen angewendet werden. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! "use!" darf nicht mit "and!" kombiniert werden. @@ -317,11 +242,6 @@ Das Konstrukt "let! ... and! ..." kann nur verwendet werden, wenn der Berechnungsausdrucks-Generator entweder eine {0}-Methode oder geeignete MergeSource- und Bind-Methoden definiert. - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index a1a5af689b4..6a8d27fdd3d 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Sobrecargas disponibles:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. El flujo no comienza con un recurso nulo ni está en formato ".RES". @@ -237,26 +192,6 @@ La característica de administración de paquetes requiere la versión de lenguaje 5.0; use /langversion:preview - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Símbolo inesperado "." en la definición de miembro. Se esperaba "with", "=" u otro token. @@ -297,16 +232,6 @@ Los atributos no se pueden aplicar a las extensiones de tipo. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! No se puede combinar use! con and! @@ -317,11 +242,6 @@ La construcción "let! ... and! ..." solo se puede usar si el generador de expresiones de cálculo define un método "{0}" o bien los métodos "MergeSource" y "Bind" adecuados. - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 9f212bd5e2e..cc158d8cf4e 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Surcharges disponibles :\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. Le flux ne commence pas par une ressource null et n'est pas au format '.RES'. @@ -237,26 +192,6 @@ La fonctionnalité de gestion des packages nécessite la version 5.0 du langage. Utilisez /langversion:preview - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Symbole '.' inattendu dans la définition du membre. 'with','=' ou autre jeton attendu. @@ -297,16 +232,6 @@ Impossible d'appliquer des attributs aux extensions de type. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use! ne peut pas être combiné avec and! @@ -317,11 +242,6 @@ La construction 'let! ... and! ...' peut uniquement être utilisée si le générateur d'expressions de calcul définit une méthode '{0}' ou les méthodes 'MergeSource' et 'Bind' appropriées - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 712651d37fe..28a352f71a5 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Overload disponibili:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. Il flusso non inizia con una risorsa Null e non è in formato '.RES'. @@ -237,26 +192,6 @@ Con la funzionalità di gestione pacchetti è richiesta la versione 5.0 del linguaggio. Usare /langversion:preview - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Simbolo '.' imprevisto nella definizione di membro. È previsto 'with', '=' o un altro token. @@ -297,16 +232,6 @@ Gli attributi non possono essere applicati a estensioni di tipo. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! Non è possibile combinare use! con and! @@ -317,11 +242,6 @@ È possibile usare il costrutto 'let! ... and! ...' solo se il generatore di espressioni di calcolo definisce un metodo '{0}' o metodi 'MergeSource' e 'Bind' appropriati - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 0ff189524fe..53d07ef597a 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} 使用可能なオーバーロード:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. ストリームは null リソースでは始まらず、'RES' 形式でもありません。 @@ -237,26 +192,6 @@ パッケージ管理機能では、言語バージョン 5.0 で /langversion:preview を使用する必要があります - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. メンバー定義に予期しない記号 '.' があります。'with'、'=' またはその他のトークンが必要です。 @@ -297,16 +232,6 @@ 属性を型拡張に適用することはできません。 - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use! を and! と組み合わせて使用することはできません @@ -317,11 +242,6 @@ 'let! ... and! ...' コンストラクトは、コンピュテーション式ビルダーが '{0}' メソッドまたは適切な 'MergeSource' および 'Bind' メソッドのいずれかを定義している場合にのみ使用できます - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 833251d38ad..cd857ba2135 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} 사용 가능한 오버로드:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. 스트림은 null 리소스로 시작되지 않으며 '.RES' 형식이 아닙니다. @@ -237,26 +192,6 @@ 패키지 관리 기능을 사용하려면 언어 버전 5.0이 필요합니다. /langversion:preview를 사용하세요. - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. 멤버 정의의 예기치 않은 기호 '.'입니다. 'with', '=' 또는 기타 토큰이 필요합니다. @@ -297,16 +232,6 @@ 형식 확장에 특성을 적용할 수 없습니다. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use!는 and!와 함께 사용할 수 없습니다. @@ -317,11 +242,6 @@ 'let! ... and! ...' 구문은 계산 식 작성기에서 '{0}' 메서드 또는 적절한 'MergeSource' 및 'Bind' 메서드를 정의한 경우에만 사용할 수 있습니다. - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index cf86c6ba832..8b9d38f36a1 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Dostępne przeciążenia:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} — {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. Strumień nie zaczyna się od zasobu o wartości null i nie jest w formacie „.RES”. @@ -237,26 +192,6 @@ Funkcja zarządzania pakietami wymaga języka w wersji 5.0, użyj parametru /langversion:preview - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Nieoczekiwany symbol „.” w definicji składowej. Oczekiwano ciągu „with”, znaku „=” lub innego tokenu. @@ -297,16 +232,6 @@ Atrybutów nie można stosować do rozszerzeń typu. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! Elementu use! nie można łączyć z elementem and! @@ -317,11 +242,6 @@ Konstrukcji „let! ... and! ...” można użyć tylko wtedy, gdy konstruktor wyrażeń obliczeniowych definiuje metodę „{0}” lub odpowiednie metody „MergeSource” i „Bind” - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 58e36c1a15b..4dd0ec18ee8 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Sobrecargas disponíveis:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. Stream não começa com um recurso nulo e não está no formato '.RES'. @@ -237,26 +192,6 @@ O recurso de gerenciamento de pacotes requer a versão de idioma 5.0. Use /langversion:preview - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Símbolo inesperado '.' na definição de membro. Esperado 'com', '=' ou outro token. @@ -297,16 +232,6 @@ Os atributos não podem ser aplicados às extensões de tipo. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use! não pode ser combinado com and! @@ -317,11 +242,6 @@ O constructo 'let! ... and! ...' só pode ser usado se o construtor de expressões de computação definir um método '{0}' ou um método 'MergeSource' ou 'Bind' apropriado - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index d1a227cc9d9..56b8c542b09 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Доступные перегрузки:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. Поток не начинается с нулевого ресурса и не соответствует формату ".RES". @@ -237,26 +192,6 @@ Для функции управления пакетами требуется версия языка 5.0, используйте параметр /langversion:preview - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Неожиданный символ "." в определении члена. Ожидаемые инструкции: "with", "=" или другие токены. @@ -297,16 +232,6 @@ Атрибуты не могут быть применены к расширениям типа. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use! запрещено сочетать с and! @@ -317,11 +242,6 @@ Конструкцию "let! ... and! ..." можно использовать только в том случае, если построитель выражений с вычислениями определяет либо метод "{0}", либо соответствующие методы "MergeSource" и "Bind" - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index 995d58db8df..dc434b9351f 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} Kullanılabilir aşırı yüklemeler:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. Akış null kaynakla başlamıyor ve '.RES' biçiminde değil. @@ -237,26 +192,6 @@ Paket yönetimi özelliği dil sürümü 5.0 gerektiriyor, /langversion:preview kullanın - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. Üye tanımında '.' sembolü beklenmiyordu. 'with', '=' veya başka bir belirteç bekleniyordu. @@ -297,16 +232,6 @@ Öznitelikler tür uzantılarına uygulanamaz. - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use!, and! ile birleştirilemez @@ -317,11 +242,6 @@ 'let! ... and! ...' yapısı, yalnızca hesaplama ifadesi oluşturucu bir '{0}' metodunu ya da uygun 'MergeSource' ve 'Bind' metotlarını tanımlarsa kullanılabilir - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 04502431fe3..3dddbf576b1 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} 可用重载:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. 流应以空资源开头并且应采用 .RES 格式。 @@ -237,26 +192,6 @@ 包管理功能需要语言版本 5.0,请使用 /langversion:preview - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. 成员定义中有意外的符号 "."。预期 "with"、"+" 或其他标记。 @@ -297,16 +232,6 @@ 属性不可应用于类型扩展。 - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use! 不得与 and! 结合使用 @@ -317,11 +242,6 @@ 仅当计算表达式生成器定义了 "{0}" 方法或适当的 "MergeSource" 和 "Bind" 方法时,才可以使用 "let! ... and! ..." 构造 - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 80396dd9f3a..cff0c650647 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -12,11 +12,6 @@ Feature '{0}' is not supported by target runtime. - - Feature '{0}' requires the F# library for language version {1} or greater. - Feature '{0}' requires the F# library for language version {1} or greater. - - Available overloads:\n{0} 可用的多載:\n{0} @@ -142,11 +137,6 @@ single underscore pattern - - string interpolation - string interpolation - - wild card in for loop wild card in for loop @@ -157,26 +147,6 @@ witness passing for trait constraints in F# quotations - - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}'. - - - - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - .NET-style format specifiers such as '{{x,3}}' or '{{x:N5}}' may not be mixed with '%' format specifiers. - - - - The '%P' specifier may not be used explicitly. - The '%P' specifier may not be used explicitly. - - - - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - Interpolated strings used as type IFormattable or type FormattableString may not use '%' specifiers, only .NET-style interpolands such as '{{expr}}', '{{expr,3}}' or '{{expr:N5}}' may be used. - - - {0} - {0} @@ -192,21 +162,6 @@ Invalid directive '#{0} {1}' - - a byte string may not be interpolated - a byte string may not be interpolated - - - - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - Invalid interpolated string. Single quote or verbatim string literals may not be used in interpolated expressions in single quote or verbatim strings. Consider using an explicit 'let' binding for the interpolation expression or use a triple quote string as the outer string literal. - - - - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - Invalid interpolated string. Triple quote string literals may not be used in interpolated expressions. Consider using an explicit 'let' binding for the interpolation expression. - - Stream does not begin with a null resource and is not in '.RES' format. 資料流未以 null 資源開頭,並且未使用 '.RES' 格式。 @@ -237,26 +192,6 @@ 套件管理功能需要語言版本 5.0,請使用 /langversion:preview - - Incomplete interpolated string begun at or before here - Incomplete interpolated string begun at or before here - - - - Incomplete interpolated string expression fill begun at or before here - Incomplete interpolated string expression fill begun at or before here - - - - Incomplete interpolated triple-quote string begun at or before here - Incomplete interpolated triple-quote string begun at or before here - - - - Incomplete interpolated verbatim string begun at or before here - Incomplete interpolated verbatim string begun at or before here - - Unexpected symbol '.' in member definition. Expected 'with', '=' or other token. 成員定義中的非預期符號 '.'。預期為 'with'、'=' 或其他語彙基元。 @@ -297,16 +232,6 @@ 屬性無法套用到類型延伸模組。 - - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - Mismatch in interpolated string. Interpolated strings may not use '%' format specifiers unless each is given an expression, e.g. '%d{{1+1}}' - - - - Invalid alignment in interpolated string - Invalid alignment in interpolated string - - use! may not be combined with and! use! 不可與 and! 合併 @@ -317,11 +242,6 @@ 只有在計算運算式產生器定義 '{0}' 方法或正確的 'MergeSource' 和 'Bind' 方法時,才可使用 'let! ... and! ...' 建構 - - Invalid interpolated string. {0} - Invalid interpolated string. {0} - - Interface member '{0}' does not have a most specific implementation. Interface member '{0}' does not have a most specific implementation. diff --git a/src/fsharp/xlf/FSStrings.cs.xlf b/src/fsharp/xlf/FSStrings.cs.xlf index dc79e7e1234..fc719cd81a5 100644 --- a/src/fsharp/xlf/FSStrings.cs.xlf +++ b/src/fsharp/xlf/FSStrings.cs.xlf @@ -7,26 +7,6 @@ symbol ..^ - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . Viz taky {0}. diff --git a/src/fsharp/xlf/FSStrings.de.xlf b/src/fsharp/xlf/FSStrings.de.xlf index 47a0a129698..c9bf6de009b 100644 --- a/src/fsharp/xlf/FSStrings.de.xlf +++ b/src/fsharp/xlf/FSStrings.de.xlf @@ -7,26 +7,6 @@ Symbol "..^" - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . Siehe auch "{0}". diff --git a/src/fsharp/xlf/FSStrings.es.xlf b/src/fsharp/xlf/FSStrings.es.xlf index be6a28471d1..21f3c4ab7ad 100644 --- a/src/fsharp/xlf/FSStrings.es.xlf +++ b/src/fsharp/xlf/FSStrings.es.xlf @@ -7,26 +7,6 @@ símbolo "..^" - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . Vea también {0}. diff --git a/src/fsharp/xlf/FSStrings.fr.xlf b/src/fsharp/xlf/FSStrings.fr.xlf index 62128872410..0b3644d7313 100644 --- a/src/fsharp/xlf/FSStrings.fr.xlf +++ b/src/fsharp/xlf/FSStrings.fr.xlf @@ -7,26 +7,6 @@ symbole '..^' - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . Voir aussi {0}. diff --git a/src/fsharp/xlf/FSStrings.it.xlf b/src/fsharp/xlf/FSStrings.it.xlf index c8ada1cb297..9715ec16824 100644 --- a/src/fsharp/xlf/FSStrings.it.xlf +++ b/src/fsharp/xlf/FSStrings.it.xlf @@ -7,26 +7,6 @@ simbolo '..^' - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . Vedere anche {0}. diff --git a/src/fsharp/xlf/FSStrings.ja.xlf b/src/fsharp/xlf/FSStrings.ja.xlf index f01f224d636..e055cf262f6 100644 --- a/src/fsharp/xlf/FSStrings.ja.xlf +++ b/src/fsharp/xlf/FSStrings.ja.xlf @@ -7,26 +7,6 @@ シンボル '..^' - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. 。{0} も参照してください。 diff --git a/src/fsharp/xlf/FSStrings.ko.xlf b/src/fsharp/xlf/FSStrings.ko.xlf index 17372265a93..62147543f85 100644 --- a/src/fsharp/xlf/FSStrings.ko.xlf +++ b/src/fsharp/xlf/FSStrings.ko.xlf @@ -7,26 +7,6 @@ 기호 '..^' - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. {0}도 참조하세요. diff --git a/src/fsharp/xlf/FSStrings.pl.xlf b/src/fsharp/xlf/FSStrings.pl.xlf index decd387ed69..0feea19067e 100644 --- a/src/fsharp/xlf/FSStrings.pl.xlf +++ b/src/fsharp/xlf/FSStrings.pl.xlf @@ -7,26 +7,6 @@ symbol „..^” - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . Zobacz też {0}. diff --git a/src/fsharp/xlf/FSStrings.pt-BR.xlf b/src/fsharp/xlf/FSStrings.pt-BR.xlf index 272d0a24545..dc96593ddff 100644 --- a/src/fsharp/xlf/FSStrings.pt-BR.xlf +++ b/src/fsharp/xlf/FSStrings.pt-BR.xlf @@ -7,26 +7,6 @@ símbolo '..^' - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . Consulte também {0}. diff --git a/src/fsharp/xlf/FSStrings.ru.xlf b/src/fsharp/xlf/FSStrings.ru.xlf index 2e83330f130..c55e25658ea 100644 --- a/src/fsharp/xlf/FSStrings.ru.xlf +++ b/src/fsharp/xlf/FSStrings.ru.xlf @@ -7,26 +7,6 @@ символ "..^" - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . См. также {0}. diff --git a/src/fsharp/xlf/FSStrings.tr.xlf b/src/fsharp/xlf/FSStrings.tr.xlf index 7ad1316f1ec..702a7a35169 100644 --- a/src/fsharp/xlf/FSStrings.tr.xlf +++ b/src/fsharp/xlf/FSStrings.tr.xlf @@ -7,26 +7,6 @@ '..^' sembolü - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. . Ayrıca bkz. {0}. diff --git a/src/fsharp/xlf/FSStrings.zh-Hans.xlf b/src/fsharp/xlf/FSStrings.zh-Hans.xlf index a2aab5530b3..80f7968de17 100644 --- a/src/fsharp/xlf/FSStrings.zh-Hans.xlf +++ b/src/fsharp/xlf/FSStrings.zh-Hans.xlf @@ -7,26 +7,6 @@ 符号 "..^" - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. 。请参见 {0}。 diff --git a/src/fsharp/xlf/FSStrings.zh-Hant.xlf b/src/fsharp/xlf/FSStrings.zh-Hant.xlf index 8d3f3a5cea5..215554d12ea 100644 --- a/src/fsharp/xlf/FSStrings.zh-Hant.xlf +++ b/src/fsharp/xlf/FSStrings.zh-Hant.xlf @@ -7,26 +7,6 @@ 符號 '..^' - - interpolated string - interpolated string - - - - interpolated string (first part) - interpolated string (first part) - - - - interpolated string (final part) - interpolated string (final part) - - - - interpolated string (part) - interpolated string (part) - - . See also {0}. 。請參閱 {0}。 From 110876daa87e5479b8992cd8e560cabbf9718c01 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Jul 2020 17:32:30 +0100 Subject: [PATCH 61/87] fix assert --- src/fsharp/service/ServiceLexing.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index 53819a8ba78..30fd7918bbb 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -427,9 +427,9 @@ module internal LexerStateEncoding = let encodeStringStyle kind = match kind with + | LexerStringStyle.SingleQuote -> 0 | LexerStringStyle.Verbatim -> 1 | LexerStringStyle.TripleQuote -> 2 - | LexerStringStyle.SingleQuote -> 3 let decodeStringStyle kind = match kind with From 07394d398256af80f5dadd2a1c203ff739a92a53 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 23 Jul 2020 23:23:15 +0100 Subject: [PATCH 62/87] completion and brace matching (not all tests passing yet) --- src/fsharp/service/FSharpCheckerResults.fs | 49 +++++++++++++++++-- tests/service/Common.fs | 10 ++++ tests/service/EditorTests.fs | 49 +++++++++++++++++++ .../BraceCompletionSessionProvider.fs | 45 ++++++++++++----- .../UnitTests/BraceMatchingServiceTests.fs | 12 +++++ .../UnitTests/CompletionProviderTests.fs | 35 +++++++++++-- 6 files changed, 179 insertions(+), 21 deletions(-) diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index 6e8aa46f5c5..bdb5b02f401 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -1480,7 +1480,12 @@ module internal ParseAndCheckFile = | (LPAREN, RPAREN) | (LPAREN, RPAREN_IS_HERE) | (LBRACE, RBRACE) + | (LBRACE_BAR, BAR_RBRACE) | (LBRACE, RBRACE_IS_HERE) + | (INTERP_STRING_BEGIN_PART _, INTERP_STRING_END _) + | (INTERP_STRING_BEGIN_PART _, INTERP_STRING_PART _) + | (INTERP_STRING_PART _, INTERP_STRING_PART _) + | (INTERP_STRING_PART _, INTERP_STRING_END _) | (SIG, END) | (STRUCT, END) | (LBRACK_BAR, BAR_RBRACK) @@ -1489,13 +1494,49 @@ module internal ParseAndCheckFile = | (BEGIN, END) -> true | (LQUOTE q1, RQUOTE q2) -> q1 = q2 | _ -> false + let rec matchBraces stack = match lexfun lexbuf, stack with - | tok2, ((tok1, m1) :: stack') when parenTokensBalance tok1 tok2 -> - matchingBraces.Add(m1, lexbuf.LexemeRange) - matchBraces stack' - | ((LPAREN | LBRACE | LBRACK | LBRACK_BAR | LQUOTE _ | LBRACK_LESS) as tok), _ -> + | tok2, ((tok1, m1) :: stackAfterMatch) when parenTokensBalance tok1 tok2 -> + let m2 = lexbuf.LexemeRange + + // For INTERP_STRING_PART and INTERP_STRING_END grab the one character + // range that corresponds to the "}" at the start of the token + let m2Start = + match tok2 with + | INTERP_STRING_PART _ + | INTERP_STRING_END _ -> + Range.mkFileIndexRange m2.FileIndex m2.Start (mkPos m2.Start.Line (m2.Start.Column+1)) + | _ -> m2 + + matchingBraces.Add(m1, m2Start) + + // INTERP_STRING_PART corresponds to both "} ... {" i.e. both the completion + // of a match and the start of a potential new one. + let stackAfterMatch = + match tok2 with + | INTERP_STRING_PART _ -> + let m2End = Range.mkFileIndexRange m2.FileIndex (mkPos m2.End.Line (max (m2.End.Column-1) 0)) m2.End + (tok2, m2End) :: stackAfterMatch + | _ -> stackAfterMatch + + matchBraces stackAfterMatch + + | ((LPAREN | LBRACE | LBRACK | LBRACE_BAR | LBRACK_BAR | LQUOTE _ | LBRACK_LESS) as tok), _ -> matchBraces ((tok, lexbuf.LexemeRange) :: stack) + + // INTERP_STRING_BEGIN_PART corresponds to $"... {" at the start of an interpolated string + // + // INTERP_STRING_PART corresponds to "} ... {" in the middle of an interpolated string (in + // this case it msut not have matched something on the stack, e.g. an incomplete '[' in the + // interpolation expression) + // + // Either way we start a new potential match at the last character + | ((INTERP_STRING_BEGIN_PART _ | INTERP_STRING_PART _) as tok), _ -> + let m = lexbuf.LexemeRange + let m2 = Range.mkFileIndexRange m.FileIndex (mkPos m.End.Line (max (m.End.Column-1) 0)) m.End + matchBraces ((tok, m2) :: stack) + | (EOF _ | LEX_FAILURE _), _ -> () | _ -> matchBraces stack matchBraces []) diff --git a/tests/service/Common.fs b/tests/service/Common.fs index 5b48e637d96..d815abf08cb 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -202,6 +202,16 @@ let parseSourceCode (name: string, code: string) = let parseResults = checker.ParseFile(filePath, FSharp.Compiler.Text.SourceText.ofString code, options) |> Async.RunSynchronously parseResults.ParseTree +let matchBraces (name: string, code: string) = + let location = Path.Combine(Path.GetTempPath(),"test"+string(hash (name, code))) + try Directory.CreateDirectory(location) |> ignore with _ -> () + let filePath = Path.Combine(location, name + ".fs") + let dllPath = Path.Combine(location, name + ".dll") + let args = mkProjectCommandLineArgs(dllPath, [filePath]) + let options, errors = checker.GetParsingOptionsFromCommandLineArgs(List.ofArray args) + let braces = checker.MatchBraces(filePath, FSharp.Compiler.Text.SourceText.ofString code, options) |> Async.RunSynchronously + braces + let parseSourceCodeAndGetModule (source: string) = match parseSourceCode ("test", source) with | Some (ParsedInput.ImplFile (ParsedImplFileInput (_, _, _, _, _, [ moduleOrNamespace ], _))) -> moduleOrNamespace diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index ca0ce605d0a..35b049608d5 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -1372,3 +1372,52 @@ let ``Inherit ctor arg recovery`` () = let x = this """ assertHasSymbolUsages ["x"] checkResults + +[] +let ``Brace matching smoke test`` () = + let input = + """ +let x1 = { contents = 1 } +let x2 = {| contents = 1 |} +let x3 = [ 1 ] +let x4 = [| 1 |] +let x5 = $"abc{1}def" +""" + let file = "/home/user/Test.fsx" + let braces = matchBraces(file, input) + + braces + |> Array.map (fun (r1,r2) -> + (r1.StartLine, r1.StartColumn, r1.EndLine, r1.EndColumn), + (r2.StartLine, r2.StartColumn, r2.EndLine, r2.EndColumn)) + |> shouldEqual + [|((2, 9, 2, 10), (2, 24, 2, 25)); + ((3, 9, 3, 11), (3, 25, 3, 27)); + ((4, 9, 4, 10), (4, 13, 4, 14)); + ((5, 9, 5, 11), (5, 14, 5, 16)); + ((6, 8, 6, 15), (6, 16, 6, 17))|] + + +[] +let ``Brace matching in interpolated strings`` () = + let input = + " +let x5 = $\"abc{1}def\" +let x6 = $\"abc{1}def{2}hij\" +let x7 = $\"\"\"abc{1}def{2}hij\"\"\" +let x8 = $\"\"\"abc{ {contents=1} }def{2}hij\"\"\" +" + let file = "/home/user/Test.fsx" + let braces = matchBraces(file, input) + + braces + |> Array.map (fun (r1,r2) -> + (r1.StartLine, r1.StartColumn, r1.EndLine, r1.EndColumn), + (r2.StartLine, r2.StartColumn, r2.EndLine, r2.EndColumn)) + |> shouldEqual + [|((2, 14, 2, 15), (2, 16, 2, 17)); ((3, 14, 3, 15), (3, 16, 3, 17)); + ((3, 20, 3, 21), (3, 22, 3, 23)); ((4, 16, 4, 17), (4, 18, 4, 19)); + ((4, 22, 4, 23), (4, 24, 4, 25)); ((5, 19, 5, 20), (5, 30, 5, 31)); + ((5, 16, 5, 17), (5, 32, 5, 33)); ((5, 36, 5, 37), (5, 38, 5, 39))|] + + diff --git a/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs b/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs index dd3be821094..0240041a801 100644 --- a/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs +++ b/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs @@ -459,6 +459,17 @@ type AsteriskCompletionSession() = [, FSharpConstants.FSharpLanguageName)>] type EditorBraceCompletionSessionFactory() = + let spanIsString (span: ClassifiedSpan) = + match span.ClassificationType with + | ClassificationTypeNames.StringLiteral -> true + | _ -> false + + let spanIsNotCommentOrString (span: ClassifiedSpan) = + match span.ClassificationType with + | ClassificationTypeNames.Comment + | ClassificationTypeNames.StringLiteral -> false + | _ -> true + member __.IsSupportedOpeningBrace openingBrace = match openingBrace with | Parenthesis.OpenCharacter | CurlyBrackets.OpenCharacter | SquareBrackets.OpenCharacter @@ -466,23 +477,33 @@ type EditorBraceCompletionSessionFactory() = | Asterisk.OpenCharacter -> true | _ -> false - member __.CheckCodeContext(document: Document, position: int, _openingBrace, cancellationToken) = - // We need to know if we are inside a F# comment. If we are, then don't do automatic completion. + member __.CheckCodeContext(document: Document, position: int, openingBrace:char, cancellationToken) = + // We need to know if we are inside a F# string or comment. If we are, then don't do automatic completion. let sourceCodeTask = document.GetTextAsync(cancellationToken) sourceCodeTask.Wait(cancellationToken) let sourceCode = sourceCodeTask.Result position = 0 - || let colorizationData = Tokenizer.getClassifiedSpans(document.Id, sourceCode, TextSpan(position - 1, 1), Some (document.FilePath), [ ], cancellationToken) - in colorizationData.Count = 0 - || colorizationData.Exists(fun classifiedSpan -> - classifiedSpan.TextSpan.IntersectsWith position && - ( - match classifiedSpan.ClassificationType with - | ClassificationTypeNames.Comment - | ClassificationTypeNames.StringLiteral -> false - | _ -> true // anything else is a valid classification type - )) + || (let colorizationData = Tokenizer.getClassifiedSpans(document.Id, sourceCode, TextSpan(position - 1, 1), Some (document.FilePath), [ ], cancellationToken) + colorizationData.Count = 0 + || + colorizationData.Exists(fun classifiedSpan -> + classifiedSpan.TextSpan.IntersectsWith position && + spanIsNotCommentOrString classifiedSpan) + || + + // Check the case where '{' has been pressed in a string and the next position + // is known not to be a string. This corresponds to the end of an interpolated string part. + (openingBrace = '{' && + colorizationData.Exists(fun classifiedSpan -> + classifiedSpan.TextSpan.IntersectsWith (position-1) && + spanIsString classifiedSpan) && + let colorizationData2 = Tokenizer.getClassifiedSpans(document.Id, sourceCode, TextSpan(position, 1), Some (document.FilePath), [ ], cancellationToken) + (colorizationData2.Count = 0 + || + colorizationData2.Exists(fun classifiedSpan -> + classifiedSpan.TextSpan.IntersectsWith position && + not (spanIsString classifiedSpan))))) member __.CreateEditorSession(_document, _openingPosition, openingBrace, _cancellationToken) = match openingBrace with diff --git a/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs b/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs index 0e71493d22b..0f39c2d7314 100644 --- a/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs +++ b/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs @@ -86,6 +86,18 @@ type BraceMatchingServiceTests() = member this.BracketInExpression() = this.VerifyBraceMatch("let x = (3*5)-1", "(3*", ")-1") + [] + member this.BraceInInterpolatedStringSimple() = + this.VerifyBraceMatch("let x = $\"abc{1}def\"", "abc{", "}def") + + [] + member this.BraceInInterpolatedStringTwoHoles() = + this.VerifyBraceMatch("let x = $\"abc{1}def{2+3}hij\"", "def{", "}hij") + + [] + member this.BraceInInterpolatedStringNestedRecord() = + this.VerifyBraceMatch("let x = $\"abc{ id{contents=3}.contents }\"", "id{", "}.contents") + [] [] member this.BraceInMultiLineCommentShouldNotBeMatched(startMarker: string) = diff --git a/vsintegration/tests/UnitTests/CompletionProviderTests.fs b/vsintegration/tests/UnitTests/CompletionProviderTests.fs index 97ea7cbeff8..aa85b28eecf 100644 --- a/vsintegration/tests/UnitTests/CompletionProviderTests.fs +++ b/vsintegration/tests/UnitTests/CompletionProviderTests.fs @@ -35,12 +35,12 @@ open FSharp.Compiler.SourceCodeServices open UnitTests.TestLib.LanguageService let filePath = "C:\\test.fs" -let internal projectOptions = { +let internal projectOptions opts = { ProjectFileName = "C:\\test.fsproj" ProjectId = None SourceFiles = [| filePath |] ReferencedProjects = [| |] - OtherOptions = [| |] + OtherOptions = opts IsIncompleteTypeCheckEnvironment = true UseScriptResolutionRules = false LoadTime = DateTime.MaxValue @@ -53,10 +53,10 @@ let internal projectOptions = { let formatCompletions(completions : string seq) = "\n\t" + String.Join("\n\t", completions) -let VerifyCompletionList(fileContents: string, marker: string, expected: string list, unexpected: string list) = +let VerifyCompletionListWithOptions(fileContents: string, marker: string, expected: string list, unexpected: string list, opts) = let caretPosition = fileContents.IndexOf(marker) + marker.Length let results = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions, filePath, 0, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions opts, filePath, 0, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.map(fun result -> result.DisplayText) @@ -99,12 +99,15 @@ let VerifyCompletionList(fileContents: string, marker: string, expected: string let msg = sprintf "%s%s%s" expectedNotFoundMsg unexpectedFoundMsg completionsMsg Assert.Fail(msg) +let VerifyCompletionList(fileContents, marker, expected, unexpected) = + VerifyCompletionListWithOptions(fileContents, marker, expected, unexpected, [| |]) + let VerifyCompletionListExactly(fileContents: string, marker: string, expected: string list) = let caretPosition = fileContents.IndexOf(marker) + marker.Length let actual = - FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions, filePath, 0, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) + FSharpCompletionProvider.ProvideCompletionsAsyncAux(checker, SourceText.From(fileContents), caretPosition, projectOptions [| |], filePath, 0, (fun _ -> []), LanguageServicePerformanceOptions.Default, IntelliSenseOptions.Default) |> Async.RunSynchronously |> Option.defaultValue (ResizeArray()) |> Seq.toList @@ -180,6 +183,18 @@ System.Console.WriteLine() let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) Assert.IsFalse(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should not trigger") +[] +let ShouldTriggerCompletionInInterpolatedString() = + let fileContents = """ + +let x = $\" {System.Console.WriteLine()}\" +""" + let caretPosition = fileContents.IndexOf("System.") + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) + Assert.IsTrue(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should trigger") + [] let ShouldNotTriggerCompletionInExcludedCode() = let fileContents = """ @@ -306,6 +321,16 @@ System.Console.WriteLine() """ VerifyCompletionList(fileContents, "System.", ["Console"; "Array"; "String"], ["T1"; "M1"; "M2"]) +[] +let ShouldDisplaySystemNamespaceInInterpolatedString() = + let fileContents = """ +type T1 = + member this.M1 = 5 + member this.M2 = "literal" +let x = $"1 not the same as {System.Int32.MaxValue} is it" +""" + VerifyCompletionListWithOptions(fileContents, "System.", ["Console"; "Array"; "String"], ["T1"; "M1"; "M2"], [| "/langversion:preview" |]) + [] let ``Class instance members are ordered according to their kind and where they are defined (simple case, by a variable)``() = let fileContents = """ From 458a5e6838ea41fed3c9e3c8c1d3be8ea03c38fa Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Fri, 24 Jul 2020 00:07:35 -0700 Subject: [PATCH 63/87] Fix rebuild --- FSharpBuild.Directory.Build.targets | 39 +++++++++++++++++++++++------ eng/tests/UpToDate.ps1 | 12 ++++++++- 2 files changed, 43 insertions(+), 8 deletions(-) diff --git a/FSharpBuild.Directory.Build.targets b/FSharpBuild.Directory.Build.targets index 1d3fefdedd2..66b5508aa56 100644 --- a/FSharpBuild.Directory.Build.targets +++ b/FSharpBuild.Directory.Build.targets @@ -69,24 +69,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + diff --git a/eng/tests/UpToDate.ps1 b/eng/tests/UpToDate.ps1 index a7e50a26d60..e29bb9d8787 100644 --- a/eng/tests/UpToDate.ps1 +++ b/eng/tests/UpToDate.ps1 @@ -23,10 +23,19 @@ try { # gather assembly timestamps $ArtifactsBinDir = Join-Path $RepoRoot "artifacts" | Join-Path -ChildPath "bin" -Resolve $FSharpAssemblyDirs = Get-ChildItem -Path $ArtifactsBinDir -Filter "FSharp.*" - $FSharpAssemblyPaths = $FSharpAssemblyDirs | ForEach-Object { Get-ChildItem -Path (Join-Path $ArtifactsBinDir $_) -Recurse -Filter "$_.dll" } | ForEach-Object { $_.FullName } + $FscAssemblyDir = Get-ChildItem -Path $ArtifactsBinDir -Filter "fsc" + $FsiAssemblyDir = Get-ChildItem -Path $ArtifactsBinDir -Filter "fsi" + $FsiAnyCpuAssemblyDir = Get-ChildItem -Path $ArtifactsBinDir -Filter "fsiAnyCpu" + $DmAssemblyDir = Get-ChildItem -Path $ArtifactsBinDir -Filter "Microsoft.DotNet.DependencyManager" + $ProjectSystemAssemblyDirs = Get-ChildItem -Path $ArtifactsBinDir -Filter "ProjectSystem*" + $FSharpDirs = @($FSharpAssemblyDirs) + @($FscAssemblyDir) + @($FsiAssemblyDir) + @($FsiAnyCpuAssemblyDir) + @($DmAssemblyDir) + @($ProjectSystemAssemblyDirs) + $FSharpDllPaths = $FSharpDirs | ForEach-Object { Get-ChildItem -Path (Join-Path $ArtifactsBinDir $_) -Recurse -Filter "*.dll" } | ForEach-Object { $_.FullName } + $FSharpExePaths = $FSharpDirs | ForEach-Object { Get-ChildItem -Path (Join-Path $ArtifactsBinDir $_) -Recurse -Filter "*.exe" } | ForEach-Object { $_.FullName } + $FSharpAssemblyPaths = @($FSharpDllPaths) + @($FSharpExePaths) $InitialAssembliesAndTimes = @{} foreach ($asm in $FSharpAssemblyPaths) { + Write-Host "Assembly : $asm" $LastWriteTime = (Get-Item $asm).LastWriteTimeUtc $InitialAssembliesAndTimes.Add($asm, $LastWriteTime) } @@ -53,6 +62,7 @@ try { $InitialTime = $InitialAssembliesAndTimes[$asm] $FinalTime = $FinalAssembliesAndTimes[$asm] if ($InitialTime -ne $FinalTime) { + Write-Host "Rebuilt assembly: $asm" $RecompiledFiles += $asm } } From 646fc9454df2bb04e8cd1664bd0498b093f124cb Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Jul 2020 13:29:44 +0100 Subject: [PATCH 64/87] fix various niggles and get tests working --- src/fsharp/FSharp.Core/printf.fs | 13 ++-- src/fsharp/LexFilter.fs | 44 ++++++------- src/fsharp/lex.fsl | 10 ++- src/fsharp/pars.fsy | 7 +- src/fsharp/service/FSharpCheckerResults.fs | 6 +- src/fsharp/service/ServiceLexing.fs | 13 ++-- tests/service/TokenizerTests.fs | 23 +++++++ .../BraceCompletionSessionProvider.fs | 37 ++++++----- .../Completion/CompletionUtils.fs | 4 +- .../UnitTests/BraceMatchingServiceTests.fs | 7 +- .../UnitTests/CompletionProviderTests.fs | 40 ++++++++---- .../UnitTests/GoToDefinitionServiceTests.fs | 64 ++++++++++++------- .../Tests.LanguageService.General.fs | 4 +- 13 files changed, 167 insertions(+), 105 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 43a685e7a9b..791facf5b42 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -250,25 +250,24 @@ module internal PrintfImpl = | StepString prefix -> if not (String.IsNullOrEmpty prefix) then count <- count + 1 | StepLittleT(prefix) -> - if String.IsNullOrEmpty prefix then count <- count + 1 + if not (String.IsNullOrEmpty prefix) then count <- count + 1 count <- count + 1 | StepLittleA(prefix) -> - if String.IsNullOrEmpty prefix then count <- count + 1 + if not (String.IsNullOrEmpty prefix) then count <- count + 1 count <- count + 1 | StepStar1(prefix, _conv) -> - if String.IsNullOrEmpty prefix then count <- count + 1 + if not (String.IsNullOrEmpty prefix) then count <- count + 1 count <- count + 1 | StepPercentStar1(prefix) -> - if String.IsNullOrEmpty prefix then count <- count + 1 + if not (String.IsNullOrEmpty prefix) then count <- count + 1 count <- count + 1 | StepStar2(prefix, _conv) -> - if String.IsNullOrEmpty prefix then count <- count + 1 + if not (String.IsNullOrEmpty prefix) then count <- count + 1 count <- count + 1 | StepPercentStar2(prefix) -> - if String.IsNullOrEmpty prefix then count <- count + 1 + if not (String.IsNullOrEmpty prefix) then count <- count + 1 count <- count + 1 count - /// Abstracts generated printer from the details of particular environment: how to write text, how to produce results etc... [] diff --git a/src/fsharp/LexFilter.fs b/src/fsharp/LexFilter.fs index 3fcd8341f2b..1a40e579d58 100644 --- a/src/fsharp/LexFilter.fs +++ b/src/fsharp/LexFilter.fs @@ -271,7 +271,7 @@ let rec isTypeContinuator token = // end with <--- 'end' HERE // static member M() = 1 // end - | RBRACE | WITH | BAR | AND | END -> true + | RBRACE _ | WITH | BAR | AND | END -> true // The following arise during reprocessing of the inserted tokens when we hit a DONE | ORIGHT_BLOCK_END | OBLOCKEND | ODECLEND -> true @@ -340,7 +340,7 @@ let rec isSeqBlockElementContinuator token = // ... // ), <------- NOTE RPAREN HERE // Shortcut.CtrlO) - | END | AND | WITH | THEN | RPAREN | RBRACE | BAR_RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ -> true + | END | AND | WITH | THEN | RPAREN | RBRACE _ | BAR_RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ -> true // The following arise during reprocessing of the inserted tokens when we hit a DONE | ORIGHT_BLOCK_END | OBLOCKEND | ODECLEND -> true @@ -368,7 +368,7 @@ let isAtomicExprEndToken token = | UINT8 _ | UINT16 _ | UINT32 _ | UINT64 _ | UNATIVEINT _ | DECIMAL _ | BIGNUM _ | STRING _ | BYTEARRAY _ | CHAR _ | IEEE32 _ | IEEE64 _ - | RPAREN | RBRACK | RBRACE | BAR_RBRACE | BAR_RBRACK | END + | RPAREN | RBRACK | RBRACE _ | BAR_RBRACE | BAR_RBRACK | END | NULL | FALSE | TRUE | UNDERSCORE -> true | _ -> false @@ -378,7 +378,7 @@ let isAtomicExprEndToken token = let parenTokensBalance t1 t2 = match t1, t2 with | (LPAREN, RPAREN) - | (LBRACE, RBRACE) + | (LBRACE _, RBRACE _) | (LBRACE_BAR, BAR_RBRACE) | (LBRACK, RBRACK) | (INTERFACE, END) @@ -655,7 +655,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu let detectJoinInCtxt stack = let rec check s = match s with - | CtxtParen(LBRACE, _) :: _ -> true + | CtxtParen(LBRACE _, _) :: _ -> true | (CtxtSeqBlock _ | CtxtDo _ | CtxtFor _) :: rest -> check rest | _ -> false match stack with @@ -711,9 +711,9 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu // 'f ...{' places no limit until we hit a CtxtLetDecl etc... // 'f ...[' places no limit until we hit a CtxtLetDecl etc... // 'f ...[|' places no limit until we hit a CtxtLetDecl etc... - | _, (CtxtParen ((LBRACE | LBRACK | LBRACK_BAR), _) :: CtxtSeqBlock _ :: rest) - | _, (CtxtParen ((LBRACE | LBRACK | LBRACK_BAR), _) :: CtxtVanilla _ :: CtxtSeqBlock _ :: rest) - | _, (CtxtSeqBlock _ :: CtxtParen((LBRACE | LBRACK | LBRACK_BAR), _) :: CtxtVanilla _ :: CtxtSeqBlock _ :: rest) + | _, (CtxtParen ((LBRACE _ | LBRACK | LBRACK_BAR), _) :: CtxtSeqBlock _ :: rest) + | _, (CtxtParen ((LBRACE _ | LBRACK | LBRACK_BAR), _) :: CtxtVanilla _ :: CtxtSeqBlock _ :: rest) + | _, (CtxtSeqBlock _ :: CtxtParen((LBRACE _ | LBRACK | LBRACK_BAR), _) :: CtxtVanilla _ :: CtxtSeqBlock _ :: rest) -> undentationLimit false rest // MAJOR PERMITTED UNDENTATION This is allowing: @@ -791,8 +791,8 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu // 'if ... else [' limited by 'if' // 'if ... else [|' limited by 'if' | _, (CtxtParen ((SIG | STRUCT | BEGIN), _) :: CtxtSeqBlock _ :: (CtxtModuleBody (_, false) as limitCtxt) :: _) - | _, (CtxtParen ((BEGIN | LPAREN | LBRACK | LBRACE | LBRACE_BAR | LBRACK_BAR), _) :: CtxtSeqBlock _ :: CtxtThen _ :: (CtxtIf _ as limitCtxt) :: _) - | _, (CtxtParen ((BEGIN | LPAREN | LBRACK | LBRACE | LBRACE_BAR | LBRACK_BAR | LBRACK_LESS), _) :: CtxtSeqBlock _ :: CtxtElse _ :: (CtxtIf _ as limitCtxt) :: _) + | _, (CtxtParen ((BEGIN | LPAREN | LBRACK | LBRACE _ | LBRACE_BAR | LBRACK_BAR), _) :: CtxtSeqBlock _ :: CtxtThen _ :: (CtxtIf _ as limitCtxt) :: _) + | _, (CtxtParen ((BEGIN | LPAREN | LBRACK | LBRACE _ | LBRACE_BAR | LBRACK_BAR | LBRACK_LESS), _) :: CtxtSeqBlock _ :: CtxtElse _ :: (CtxtIf _ as limitCtxt) :: _) // 'f ... (' in seqblock limited by 'f' // 'f ... {' in seqblock limited by 'f' NOTE: this is covered by the more generous case above @@ -818,7 +818,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu // REVIEW: document these | _, (CtxtSeqBlock _ :: CtxtParen((BEGIN | LPAREN | LBRACK | LBRACK_BAR), _) :: CtxtVanilla _ :: (CtxtSeqBlock _ as limitCtxt) :: _) - | (CtxtSeqBlock _), (CtxtParen ((BEGIN | LPAREN | LBRACE | LBRACE_BAR | LBRACK | LBRACK_BAR), _) :: CtxtSeqBlock _ :: ((CtxtTypeDefns _ | CtxtLetDecl _ | CtxtMemberBody _ | CtxtWithAsLet _) as limitCtxt) :: _) + | (CtxtSeqBlock _), (CtxtParen ((BEGIN | LPAREN | LBRACE _ | LBRACE_BAR | LBRACK | LBRACK_BAR), _) :: CtxtSeqBlock _ :: ((CtxtTypeDefns _ | CtxtLetDecl _ | CtxtMemberBody _ | CtxtWithAsLet _) as limitCtxt) :: _) -> PositionWithColumn(limitCtxt.StartPos, limitCtxt.StartCol + 1) // Permitted inner-construct (e.g. "then" block and "else" block in overall @@ -1059,7 +1059,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu // WITH balances except in the following contexts.... Phew - an overused keyword! | WITH, ( ((CtxtMatch _ | CtxtException _ | CtxtMemberHead _ | CtxtInterfaceHead _ | CtxtTry _ | CtxtTypeDefns _ | CtxtMemberBody _) :: _) // This is the nasty record/object-expression case - | (CtxtSeqBlock _ :: CtxtParen((LBRACE | LBRACE_BAR), _) :: _) ) + | (CtxtSeqBlock _ :: CtxtParen((LBRACE _ | LBRACE_BAR), _) :: _) ) | FINALLY, (CtxtTry _ :: _) -> true @@ -1183,7 +1183,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu false // no member currently on the stack, nothing to pop else // there is a member context - if List.exists (function CtxtParen(LBRACE, _) -> true | _ -> false) ctxtStack then + if List.exists (function CtxtParen(LBRACE _, _) -> true | _ -> false) ctxtStack then false // an LBRACE could mean an object expression, and object expressions can have 'member' tokens in them, so do not pop, to be safe elif List.count (function CtxtParen(LPAREN, _) -> true | _ -> false) ctxtStack >= 2 then false // static member constraints always are embedded in at least two LPARENS, so do not pop, to be safe @@ -1229,7 +1229,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu | IN | RPAREN | GREATER true - | RBRACE + | RBRACE _ | BAR_RBRACE | RBRACK | BAR_RBRACK @@ -1271,7 +1271,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu while not offsideStack.IsEmpty && (not(nextOuterMostInterestingContextIsNamespaceOrModule offsideStack)) && (match offsideStack.Head with // open-parens of sorts - | CtxtParen((LPAREN|LBRACK|LBRACE|LBRACE_BAR|LBRACK_BAR), _) -> true + | CtxtParen((LPAREN|LBRACK|LBRACE _ |LBRACE_BAR|LBRACK_BAR), _) -> true // seq blocks | CtxtSeqBlock _ -> true // vanillas @@ -1370,7 +1370,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu hwTokenFetch useBlockRule // Balancing rule. Encountering a ')' or '}' balances with a '(' or '{', even if not offside - | ((END | RPAREN | RBRACE | BAR_RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ | GREATER true | INTERP_STRING_END _ | INTERP_STRING_PART _) as t2), (CtxtParen (t1, _) :: _) + | ((END | RPAREN | RBRACE _ | BAR_RBRACE | RBRACK | BAR_RBRACK | RQUOTE _ | GREATER true | INTERP_STRING_END _ | INTERP_STRING_PART _) as t2), (CtxtParen (t1, _) :: _) when parenTokensBalance t1 t2 -> if debug then dprintf "RPAREN/RBRACE/BAR_RBRACE/RBRACK/BAR_RBRACK/RQUOTE/END at %a terminates CtxtParen()\n" outputPos tokenStartPos popCtxt() @@ -1882,7 +1882,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu // '{ id1 = 1 // M.id2 = ... ' ~~~> CtxtSeqBlock | EQUALS, ((CtxtWithAsLet _) :: _) // This detects 'with = '. - | EQUALS, ((CtxtVanilla (_, true)) :: (CtxtSeqBlock _) :: (CtxtWithAsLet _ | CtxtParen((LBRACE | LBRACE_BAR), _)) :: _) -> + | EQUALS, ((CtxtVanilla (_, true)) :: (CtxtSeqBlock _) :: (CtxtWithAsLet _ | CtxtParen((LBRACE _ | LBRACE_BAR), _)) :: _) -> if debug then dprintf "CtxtLetDecl/CtxtWithAsLet: EQUALS, pushing CtxtSeqBlock\n" // We don't insert begin/end block tokens for single-line bindings since we can't properly distinguish single-line *) // record update expressions such as "{ t with gbuckets=Array.copy t.gbuckets; gcount=t.gcount }" *) @@ -1909,7 +1909,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu // $".... { ... } ... { ....} " pushes a block context at first { // ~~~~~~~~ // ^---------INTERP_STRING_BEGIN_PART - | (BEGIN | LPAREN | SIG | LBRACE | LBRACE_BAR | LBRACK | LBRACK_BAR | LQUOTE _ | LESS true | INTERP_STRING_BEGIN_PART _), _ -> + | (BEGIN | LPAREN | SIG | LBRACE _ | LBRACE_BAR | LBRACK | LBRACK_BAR | LQUOTE _ | LESS true | INTERP_STRING_BEGIN_PART _), _ -> if debug then dprintf "LPAREN etc., pushes CtxtParen, pushing CtxtSeqBlock, tokenStartPos = %a\n" outputPos tokenStartPos let pos = match token with | INTERP_STRING_BEGIN_PART _ -> tokenTup.LexbufState.EndPos @@ -1938,7 +1938,7 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu // comprehension/match | (CtxtWhile _ | CtxtFor _ | CtxtWhen _ | CtxtMatchClauses _ | CtxtFun _) :: _ -> true // comprehension - | (CtxtSeqBlock _ :: CtxtParen ((LBRACK | LBRACE | LBRACE_BAR | LBRACK_BAR), _) :: _) -> true + | (CtxtSeqBlock _ :: CtxtParen ((LBRACK | LBRACE _ | LBRACE_BAR | LBRACK_BAR), _) :: _) -> true // comprehension | (CtxtSeqBlock _ :: (CtxtDo _ | CtxtWhile _ | CtxtFor _ | CtxtWhen _ | CtxtMatchClauses _ | CtxtTry _ | CtxtThen _ | CtxtElse _) :: _) -> true | _ -> false) -> @@ -1989,11 +1989,11 @@ type LexFilterImpl (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbu returnToken tokenLexbufState token | WITH, (((CtxtException _ | CtxtTypeDefns _ | CtxtMemberHead _ | CtxtInterfaceHead _ | CtxtMemberBody _) as limCtxt) :: _) - | WITH, ((CtxtSeqBlock _) as limCtxt :: CtxtParen((LBRACE | LBRACE_BAR), _) :: _) -> + | WITH, ((CtxtSeqBlock _) as limCtxt :: CtxtParen((LBRACE _ | LBRACE_BAR), _) :: _) -> let lookaheadTokenTup = peekNextTokenTup() let lookaheadTokenStartPos = startPosOfTokenTup lookaheadTokenTup match lookaheadTokenTup.Token with - | RBRACE + | RBRACE _ | IDENT _ // The next clause detects the access annotations after the 'with' in: // member x.PublicGetSetProperty @@ -2388,7 +2388,7 @@ type LexFilter (lightStatus: LightSyntaxStatus, compilingFsLib, lexer, lexbuf: U let rec loop() = let token = popNextToken() match token with - | RBRACE -> + | RBRACE _ -> insertComingSoonTokens RBRACE_COMING_SOON RBRACE_IS_HERE loop() | RPAREN -> diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index 51674ff072c..fd7759561d9 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -795,7 +795,9 @@ rule token args skip = parse // Note, we do not update the 'm', any incomplete-interpolation error // will be reported w.r.t. the first '{' args.stringNest <- (counter + 1, style, m) :: rest - LBRACE + // To continue token-by-token lexing may involve picking up the new args.stringNes + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + LBRACE cont } | "|" { BAR } @@ -820,10 +822,12 @@ rule token args skip = parse // Note, we do not update the 'm', any incomplete-interpolation error // will be reported w.r.t. the first '{' args.stringNest <- (counter - 1, style, m) :: rest - RBRACE + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + RBRACE cont | _ -> - RBRACE + let cont = LexCont.Token(args.ifdefStack, args.stringNest) + RBRACE cont } | "$" { DOLLAR } diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 2ffa6559fee..7f263c9c3be 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -190,13 +190,14 @@ let rangeOfLongIdent(lid:LongIdent) = %} -// Producing these changes the lex state, e.g. string --> token +// Producing these changes the lex state, e.g. string --> token, or nesting level of braces in interpolated strings %token BYTEARRAY %token STRING %token INTERP_STRING_BEGIN_END %token INTERP_STRING_BEGIN_PART %token INTERP_STRING_PART %token INTERP_STRING_END +%token LBRACE RBRACE %token KEYWORD_STRING // Like __SOURCE_DIRECTORY__ %token IDENT @@ -239,9 +240,9 @@ let rangeOfLongIdent(lid:LongIdent) = %token OPEN OR REC THEN TO TRUE TRY TYPE VAL INLINE INTERFACE INSTANCE CONST %token WHEN WHILE WITH HASH AMP AMP_AMP QUOTE LPAREN RPAREN RPAREN_COMING_SOON RPAREN_IS_HERE STAR COMMA RARROW GREATER_BAR_RBRACK LPAREN_STAR_RPAREN %token QMARK QMARK_QMARK DOT COLON COLON_COLON COLON_GREATER COLON_QMARK_GREATER COLON_QMARK COLON_EQUALS SEMICOLON -%token SEMICOLON_SEMICOLON LARROW EQUALS LBRACK LBRACK_BAR LBRACE_BAR LBRACK_LESS LBRACE +%token SEMICOLON_SEMICOLON LARROW EQUALS LBRACK LBRACK_BAR LBRACE_BAR LBRACK_LESS %token BAR_RBRACK BAR_RBRACE UNDERSCORE -%token BAR RBRACK RBRACE RBRACE_COMING_SOON RBRACE_IS_HERE MINUS DOLLAR +%token BAR RBRACK RBRACE_COMING_SOON RBRACE_IS_HERE MINUS DOLLAR %token GREATER_RBRACK STRUCT SIG %token STATIC MEMBER CLASS ABSTRACT OVERRIDE DEFAULT CONSTRUCTOR INHERIT %token EXTERN VOID PUBLIC PRIVATE INTERNAL GLOBAL diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index bdb5b02f401..8de2a871d52 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -1479,9 +1479,9 @@ module internal ParseAndCheckFile = match t1, t2 with | (LPAREN, RPAREN) | (LPAREN, RPAREN_IS_HERE) - | (LBRACE, RBRACE) + | (LBRACE _, RBRACE _) | (LBRACE_BAR, BAR_RBRACE) - | (LBRACE, RBRACE_IS_HERE) + | (LBRACE _, RBRACE_IS_HERE) | (INTERP_STRING_BEGIN_PART _, INTERP_STRING_END _) | (INTERP_STRING_BEGIN_PART _, INTERP_STRING_PART _) | (INTERP_STRING_PART _, INTERP_STRING_PART _) @@ -1522,7 +1522,7 @@ module internal ParseAndCheckFile = matchBraces stackAfterMatch - | ((LPAREN | LBRACE | LBRACK | LBRACE_BAR | LBRACK_BAR | LQUOTE _ | LBRACK_LESS) as tok), _ -> + | ((LPAREN | LBRACE _ | LBRACK | LBRACE_BAR | LBRACK_BAR | LQUOTE _ | LBRACK_LESS) as tok), _ -> matchBraces ((tok, lexbuf.LexemeRange) :: stack) // INTERP_STRING_BEGIN_PART corresponds to $"... {" at the start of an interpolated string diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index 30fd7918bbb..93bfd922b6a 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -41,8 +41,8 @@ module FSharpTokenTag = let RPAREN = tagOfToken RPAREN let LBRACK = tagOfToken LBRACK let RBRACK = tagOfToken RBRACK - let LBRACE = tagOfToken LBRACE - let RBRACE = tagOfToken RBRACE + let LBRACE = tagOfToken (LBRACE LexCont.Default) + let RBRACE = tagOfToken (RBRACE LexCont.Default) let LBRACK_LESS = tagOfToken LBRACK_LESS let GREATER_RBRACK = tagOfToken GREATER_RBRACK let LESS = tagOfToken (LESS true) @@ -255,13 +255,13 @@ module internal TokenClassifications = | LBRACK_LESS -> (FSharpTokenColorKind.Punctuation, FSharpTokenCharKind.Delimiter, FSharpTokenTriggerClass.None ) - | LQUOTE _ | LBRACK | LBRACE | LBRACK_BAR | LBRACE_BAR -> + | LQUOTE _ | LBRACK | LBRACE _ | LBRACK_BAR | LBRACE_BAR -> (FSharpTokenColorKind.Punctuation, FSharpTokenCharKind.Delimiter, FSharpTokenTriggerClass.MatchBraces ) | GREATER_RBRACK | GREATER_BAR_RBRACK -> (FSharpTokenColorKind.Punctuation, FSharpTokenCharKind.Delimiter, FSharpTokenTriggerClass.None ) - | RQUOTE _ | RBRACK | RBRACE | RBRACE_COMING_SOON | RBRACE_IS_HERE | BAR_RBRACK | BAR_RBRACE -> + | RQUOTE _ | RBRACK | RBRACE _ | RBRACE_COMING_SOON | RBRACE_IS_HERE | BAR_RBRACK | BAR_RBRACE -> (FSharpTokenColorKind.Punctuation, FSharpTokenCharKind.Delimiter, FSharpTokenTriggerClass.MatchBraces ) | PUBLIC | PRIVATE | INTERNAL | BASE | GLOBAL @@ -378,6 +378,7 @@ module internal LexerStateEncoding = | INTERP_STRING_PART (_, cont) | INTERP_STRING_BEGIN_END (_, cont) | INTERP_STRING_END (_, cont) + | LBRACE cont | BYTEARRAY (_, cont) | STRING (_, cont) -> cont | _ -> prevLexcont @@ -1220,7 +1221,7 @@ module Lexer = | SIG -> FSharpSyntaxTokenKind.Sig | BAR -> FSharpSyntaxTokenKind.Bar | RBRACK -> FSharpSyntaxTokenKind.RightBracket - | RBRACE -> FSharpSyntaxTokenKind.RightBrace + | RBRACE _ -> FSharpSyntaxTokenKind.RightBrace | MINUS -> FSharpSyntaxTokenKind.Minus | DOLLAR -> FSharpSyntaxTokenKind.Dollar | BAR_RBRACK -> FSharpSyntaxTokenKind.BarRightBracket @@ -1233,7 +1234,7 @@ module Lexer = | LBRACK_BAR -> FSharpSyntaxTokenKind.LeftBracketBar | LBRACE_BAR -> FSharpSyntaxTokenKind.LeftBraceBar | LBRACK_LESS -> FSharpSyntaxTokenKind.LeftBracketLess - | LBRACE -> FSharpSyntaxTokenKind.LeftBrace + | LBRACE _ -> FSharpSyntaxTokenKind.LeftBrace | QMARK -> FSharpSyntaxTokenKind.QuestionMark | QMARK_QMARK -> FSharpSyntaxTokenKind.QuestionMarkQuestionMark | DOT -> FSharpSyntaxTokenKind.Dot diff --git a/tests/service/TokenizerTests.fs b/tests/service/TokenizerTests.fs index 4bf058970d6..b777df7feee 100644 --- a/tests/service/TokenizerTests.fs +++ b/tests/service/TokenizerTests.fs @@ -193,3 +193,26 @@ let ``Tokenizer test - multi-line nested string interpolation``() = printfn "expected = %A" expected Assert.Fail(sprintf "actual and expected did not match,actual =\n%A\nexpected=\n%A\n" actual expected) +[] +let ``Tokenizer test - single-line nested string interpolation``() = + let tokenizedLines = + tokenizeLines + [| " $\"abc { { contents = 1 } }\" " |] + + let actual = + [ for lineNo, lineToks in tokenizedLines do + yield lineNo, [ for str, info in lineToks do yield info.TokenName, str ] ] + let expected = + [(0, + [("WHITESPACE", " "); ("STRING_TEXT", "$\""); ("STRING_TEXT", "abc"); + ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("WHITESPACE", " "); + ("LBRACE", "{"); ("WHITESPACE", " "); ("IDENT", "contents"); + ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); ("INT32", "1"); + ("WHITESPACE", " "); ("RBRACE", "}"); ("WHITESPACE", " "); ("RBRACE", "}"); + ("STRING_TEXT", "\""); ("STRING_TEXT", " ")])] + + if actual <> expected then + printfn "actual = %A" actual + printfn "expected = %A" expected + Assert.Fail(sprintf "actual and expected did not match,actual =\n%A\nexpected=\n%A\n" actual expected) + diff --git a/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs b/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs index 0240041a801..0ee68698c2e 100644 --- a/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs +++ b/vsintegration/src/FSharp.Editor/AutomaticCompletion/BraceCompletionSessionProvider.fs @@ -459,11 +459,6 @@ type AsteriskCompletionSession() = [, FSharpConstants.FSharpLanguageName)>] type EditorBraceCompletionSessionFactory() = - let spanIsString (span: ClassifiedSpan) = - match span.ClassificationType with - | ClassificationTypeNames.StringLiteral -> true - | _ -> false - let spanIsNotCommentOrString (span: ClassifiedSpan) = match span.ClassificationType with | ClassificationTypeNames.Comment @@ -477,7 +472,7 @@ type EditorBraceCompletionSessionFactory() = | Asterisk.OpenCharacter -> true | _ -> false - member __.CheckCodeContext(document: Document, position: int, openingBrace:char, cancellationToken) = + member __.CheckCodeContext(document: Document, position: int, _openingBrace:char, cancellationToken) = // We need to know if we are inside a F# string or comment. If we are, then don't do automatic completion. let sourceCodeTask = document.GetTextAsync(cancellationToken) sourceCodeTask.Wait(cancellationToken) @@ -489,21 +484,25 @@ type EditorBraceCompletionSessionFactory() = || colorizationData.Exists(fun classifiedSpan -> classifiedSpan.TextSpan.IntersectsWith position && - spanIsNotCommentOrString classifiedSpan) - || + spanIsNotCommentOrString classifiedSpan)) - // Check the case where '{' has been pressed in a string and the next position + // This would be the case where '{' has been pressed in a string and the next position // is known not to be a string. This corresponds to the end of an interpolated string part. - (openingBrace = '{' && - colorizationData.Exists(fun classifiedSpan -> - classifiedSpan.TextSpan.IntersectsWith (position-1) && - spanIsString classifiedSpan) && - let colorizationData2 = Tokenizer.getClassifiedSpans(document.Id, sourceCode, TextSpan(position, 1), Some (document.FilePath), [ ], cancellationToken) - (colorizationData2.Count = 0 - || - colorizationData2.Exists(fun classifiedSpan -> - classifiedSpan.TextSpan.IntersectsWith position && - not (spanIsString classifiedSpan))))) + // + // However, Roslyn doesn't activate BraceCompletionSessionProvider for string text at all (and at the time '{ + // is pressed the text is classified as a string). So this code doesn't get called at all and so + // no brace completion is available inside interpolated strings. + // + // || (openingBrace = '{' && + // colorizationData.Exists(fun classifiedSpan -> + // classifiedSpan.TextSpan.IntersectsWith (position-1) && + // spanIsString classifiedSpan) && + // let colorizationData2 = Tokenizer.getClassifiedSpans(document.Id, sourceCode, TextSpan(position, 1), Some (document.FilePath), [ ], cancellationToken) + // (colorizationData2.Count = 0 + // || + // colorizationData2.Exists(fun classifiedSpan -> + // classifiedSpan.TextSpan.IntersectsWith position && + // not (spanIsString classifiedSpan))))) member __.CreateEditorSession(_document, _openingPosition, openingBrace, _cancellationToken) = match openingBrace with diff --git a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs index 5d210d5f363..c5494a39fae 100644 --- a/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs +++ b/vsintegration/src/FSharp.Editor/Completion/CompletionUtils.fs @@ -89,7 +89,8 @@ module internal CompletionUtils = let triggerLine = textLines.GetLineFromPosition triggerPosition let classifiedSpans = Tokenizer.getClassifiedSpans(documentId, sourceText, triggerLine.Span, Some filePath, defines, CancellationToken.None) classifiedSpans.Count = 0 || // we should provide completion at the start of empty line, where there are no tokens at all - classifiedSpans.Exists (fun classifiedSpan -> + let result = + classifiedSpans.Exists (fun classifiedSpan -> classifiedSpan.TextSpan.IntersectsWith triggerPosition && ( match classifiedSpan.ClassificationType with @@ -100,6 +101,7 @@ module internal CompletionUtils = | ClassificationTypeNames.NumericLiteral -> false | _ -> true // anything else is a valid classification type )) + result let inline getKindPriority kind = match kind with diff --git a/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs b/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs index 0f39c2d7314..67d8f88b882 100644 --- a/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs +++ b/vsintegration/tests/UnitTests/BraceMatchingServiceTests.fs @@ -88,15 +88,16 @@ type BraceMatchingServiceTests() = [] member this.BraceInInterpolatedStringSimple() = - this.VerifyBraceMatch("let x = $\"abc{1}def\"", "abc{", "}def") + this.VerifyBraceMatch("let x = $\"abc{1}def\"", "{1", "}def") [] member this.BraceInInterpolatedStringTwoHoles() = - this.VerifyBraceMatch("let x = $\"abc{1}def{2+3}hij\"", "def{", "}hij") + this.VerifyBraceMatch("let x = $\"abc{1}def{2+3}hij\"", "{2", "}hij") [] member this.BraceInInterpolatedStringNestedRecord() = - this.VerifyBraceMatch("let x = $\"abc{ id{contents=3}.contents }\"", "id{", "}.contents") + this.VerifyBraceMatch("let x = $\"abc{ id{contents=3}.contents }\"", "{contents", "}.contents") + this.VerifyBraceMatch("let x = $\"abc{ id{contents=3}.contents }\"", "{ id", "}\"") [] [] diff --git a/vsintegration/tests/UnitTests/CompletionProviderTests.fs b/vsintegration/tests/UnitTests/CompletionProviderTests.fs index aa85b28eecf..520ef2b661c 100644 --- a/vsintegration/tests/UnitTests/CompletionProviderTests.fs +++ b/vsintegration/tests/UnitTests/CompletionProviderTests.fs @@ -137,18 +137,18 @@ let ShouldTriggerCompletionAtCorrectMarkers() = ("System.", true) ("Console.", true) ] - for (marker: string, shouldBeTriggered: bool) in testCases do - let fileContents = """ + for (marker, shouldBeTriggered) in testCases do + let fileContents = """ let x = 1 let y = 2 System.Console.WriteLine(x + y) """ - let caretPosition = fileContents.IndexOf(marker) + marker.Length - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.AreEqual(shouldBeTriggered, triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result") + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) + Assert.AreEqual(shouldBeTriggered, triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result") [] let ShouldNotTriggerCompletionAfterAnyTriggerOtherThanInsertionOrDeletion() = @@ -187,13 +187,27 @@ System.Console.WriteLine() let ShouldTriggerCompletionInInterpolatedString() = let fileContents = """ -let x = $\" {System.Console.WriteLine()}\" +let x = 1 +let y = 2 +let z = $"abc {System.Console.WriteLine(x + y)} def" """ - let caretPosition = fileContents.IndexOf("System.") - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let getInfo() = documentId, filePath, [] - let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) - Assert.IsTrue(triggered, "FSharpCompletionProvider.ShouldTriggerCompletionAux() should trigger") + let testCases = + [ + ("x", true) + ("y", true) + ("1", false) + ("2", false) + ("x +", false) + ("Console.Write", false) + ("System.", true) + ("Console.", true) ] + + for (marker, shouldBeTriggered) in testCases do + let caretPosition = fileContents.IndexOf(marker) + marker.Length + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let getInfo() = documentId, filePath, [] + let triggered = FSharpCompletionProvider.ShouldTriggerCompletionAux(SourceText.From(fileContents), caretPosition, CompletionTriggerKind.Insertion, getInfo, IntelliSenseOptions.Default) + Assert.AreEqual(shouldBeTriggered, triggered, sprintf "FSharpCompletionProvider.ShouldTriggerCompletionAux() should compute the correct result for marker '%s'" marker) [] let ShouldNotTriggerCompletionInExcludedCode() = diff --git a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs index 17322b8faa3..755b66ca085 100644 --- a/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs +++ b/vsintegration/tests/UnitTests/GoToDefinitionServiceTests.fs @@ -62,6 +62,37 @@ module GoToDefinitionServiceTests = | _ -> return! None } + let makeOptions filePath args = + { + ProjectFileName = "C:\\test.fsproj" + ProjectId = None + SourceFiles = [| filePath |] + ReferencedProjects = [| |] + OtherOptions = args + IsIncompleteTypeCheckEnvironment = true + UseScriptResolutionRules = false + LoadTime = DateTime.MaxValue + OriginalLoadReferences = [] + UnresolvedReferences = None + ExtraProjectInfo = None + Stamp = None + } + + let GoToDefinitionTest (fileContents: string, caretMarker: string, expected) = + + let filePath = Path.GetTempFileName() + ".fs" + let options = makeOptions filePath [| |] + File.WriteAllText(filePath, fileContents) + + let caretPosition = fileContents.IndexOf(caretMarker) + caretMarker.Length - 1 // inside the marker + let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) + let actual = + findDefinition(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, [], options, 0) + |> Option.map (fun range -> (range.StartLine, range.EndLine, range.StartColumn, range.EndColumn)) + + if actual <> expected then + Assert.Fail(sprintf "Incorrect information returned for fileContents=<<<%s>>>, caretMarker=<<<%s>>>, expected =<<<%A>>>, actual = <<<%A>>>" fileContents caretMarker expected actual) + [] let VerifyDefinition() = @@ -100,33 +131,20 @@ let _ = Module1.foo 1 for caretMarker, expected in testCases do printfn "Test case: caretMarker=<<<%s>>>" caretMarker - let filePath = Path.GetTempFileName() + ".fs" - let options: FSharpProjectOptions = { - ProjectFileName = "C:\\test.fsproj" - ProjectId = None - SourceFiles = [| filePath |] - ReferencedProjects = [| |] - OtherOptions = [| |] - IsIncompleteTypeCheckEnvironment = true - UseScriptResolutionRules = false - LoadTime = DateTime.MaxValue - OriginalLoadReferences = [] - UnresolvedReferences = None - ExtraProjectInfo = None - Stamp = None - } + GoToDefinitionTest (fileContents, caretMarker, expected) - File.WriteAllText(filePath, fileContents) + [] + let VerifyDefinitionStringInterpolation() = - let caretPosition = fileContents.IndexOf(caretMarker) + caretMarker.Length - 1 // inside the marker - let documentId = DocumentId.CreateNewId(ProjectId.CreateNewId()) - let actual = - findDefinition(checker, documentId, SourceText.From(fileContents), filePath, caretPosition, [], options, 0) - |> Option.map (fun range -> (range.StartLine, range.EndLine, range.StartColumn, range.EndColumn)) + let fileContents = """ +let xxxxx = 1 +let yyyy = $"{abc{xxxxx}def}" """ + let caretMarker = "xxxxx" + let expected = Some(2, 2, 4, 9) - if actual <> expected then - Assert.Fail(sprintf "Incorrect information returned for fileContents=<<<%s>>>, caretMarker=<<<%s>>>, expected =<<<%A>>>, actual = <<<%A>>>" fileContents caretMarker expected actual) + GoToDefinitionTest (fileContents, caretMarker, expected) #if EXE VerifyDefinition() + VerifyDefinitionStringInterpolation() #endif \ No newline at end of file diff --git a/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.General.fs b/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.General.fs index 6bc62eda151..7bf26561c21 100644 --- a/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.General.fs +++ b/vsintegration/tests/UnitTests/LegacyLanguageService/Tests.LanguageService.General.fs @@ -330,8 +330,8 @@ type UsingMSBuild() = Parser.RPAREN, (FSharpTokenColorKind.Punctuation,FSharpTokenCharKind.Delimiter, FSharpTokenTriggerClass.ParamEnd ||| FSharpTokenTriggerClass.MatchBraces) ] let matching = [ // Other cases where we expect MatchBraces - Parser.LQUOTE("", false); Parser.LBRACK; Parser.LBRACE; Parser.LBRACK_BAR; - Parser.RQUOTE("", false); Parser.RBRACK; Parser.RBRACE; Parser.BAR_RBRACK ] + Parser.LQUOTE("", false); Parser.LBRACK; Parser.LBRACE (Unchecked.defaultof<_>); Parser.LBRACK_BAR; + Parser.RQUOTE("", false); Parser.RBRACK; Parser.RBRACE (Unchecked.defaultof<_>); Parser.BAR_RBRACK ] |> List.map (fun n -> n, (FSharpTokenColorKind.Punctuation,FSharpTokenCharKind.Delimiter, FSharpTokenTriggerClass.MatchBraces)) for tok, expected in List.concat [ important; matching ] do let info = TestExpose.TokenInfo tok From 32da3a1ba64930c6968e547683d047876f283769 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Jul 2020 14:30:49 +0100 Subject: [PATCH 65/87] fix printf when '%a' in final position --- src/fsharp/FSharp.Core/printf.fs | 23 ++++++++++++----------- src/fsharp/service/ServiceLexing.fs | 1 + tests/service/TokenizerTests.fs | 13 ++++++------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 791facf5b42..064cbc77c45 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -943,7 +943,7 @@ module internal PrintfImpl = /// Parses format string and creates resulting step list and printer factory function. type FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt: string) = - let buildCaptureFunc (spec: FormatSpecifier, steps, argTys: Type[], retTy, nextInfo) = + let buildCaptureFunc (spec: FormatSpecifier, allSteps, argTys: Type[], retTy, nextInfo) = let (next:obj, nextCanCombine: bool, nextArgTys: Type[], nextRetTy, nextNextOpt) = nextInfo assert (argTys.Length > 0) @@ -960,7 +960,7 @@ module internal PrintfImpl = let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) let mi = mi.MakeGenericMethod([| argTys.[1]; retTy |]) let funcObj = mi.Invoke(null, [| next |]) - funcObj, false, argTys, retTy, Some next + funcObj, false, argTys, retTy, None | n1, n2 when nextCanCombine && n1 + n2 <= MAX_CAPTURE -> // 'next' is thrown away on this path and replaced by a combined Capture @@ -971,7 +971,7 @@ module internal PrintfImpl = let captureMethName = "CaptureFinal" + string captureCount let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) let mi = mi.MakeGenericMethod(combinedArgTys) - let funcObj = mi.Invoke(null, [| steps |]) + let funcObj = mi.Invoke(null, [| allSteps |]) funcObj, true, combinedArgTys, nextRetTy, None | Some nextNext -> let captureMethName = "Capture" + string captureCount @@ -1054,9 +1054,9 @@ module internal PrintfImpl = (step :: steps) else let i, spec = parseSpec i - let next, suffix = FormatString.findNextFormatSpecifier fmt i + let i, suffix = FormatString.findNextFormatSpecifier fmt i let step = buildStep spec null prefix - parseStepsAux (step::steps) suffix next + parseStepsAux (step::steps) suffix i let parseSteps () = let i, prefix = FormatString.findNextFormatSpecifier fmt 0 @@ -1070,17 +1070,18 @@ module internal PrintfImpl = if i >= fmt.Length then let step = StepString(prefix) - let last = Specializations<'State, 'Residue, 'Result>.Final0(steps) - (step :: steps), (box last, true, [| |], funcTy, None) + let allSteps = (step :: steps) + let last = Specializations<'State, 'Residue, 'Result>.Final0(allSteps) + allSteps, (box last, true, [| |], funcTy, None) else System.Diagnostics.Debug.Assert(fmt.[i] = '%', "s.[i] = '%'") let i, spec = parseSpec i - let next, suffix = FormatString.findNextFormatSpecifier fmt i + let i, suffix = FormatString.findNextFormatSpecifier fmt i let argTys, retTy = extractCurriedArguments funcTy spec.ArgCount let step = buildStep spec argTys prefix - let allSteps, next = parseAndCreateFuncFactoryAux (step::steps) suffix retTy next - let nextNew = buildCaptureFunc (spec, allSteps, argTys, retTy, next) - allSteps, nextNew + let allSteps, nextInfo = parseAndCreateFuncFactoryAux (step::steps) suffix retTy i + let nextInfoNew = buildCaptureFunc (spec, allSteps, argTys, retTy, nextInfo) + allSteps, nextInfoNew let parseAndCreateFuncFactory () = let funcTy = typeof<'Printer> diff --git a/src/fsharp/service/ServiceLexing.fs b/src/fsharp/service/ServiceLexing.fs index 93bfd922b6a..20f05760f33 100755 --- a/src/fsharp/service/ServiceLexing.fs +++ b/src/fsharp/service/ServiceLexing.fs @@ -379,6 +379,7 @@ module internal LexerStateEncoding = | INTERP_STRING_BEGIN_END (_, cont) | INTERP_STRING_END (_, cont) | LBRACE cont + | RBRACE cont | BYTEARRAY (_, cont) | STRING (_, cont) -> cont | _ -> prevLexcont diff --git a/tests/service/TokenizerTests.fs b/tests/service/TokenizerTests.fs index b777df7feee..dd01d755a25 100644 --- a/tests/service/TokenizerTests.fs +++ b/tests/service/TokenizerTests.fs @@ -178,14 +178,13 @@ let ``Tokenizer test - multi-line nested string interpolation``() = (2, [("WHITESPACE", " "); ("IDENT", "contents"); ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); - ("STRING_TEXT", "\""); ("STRING_TEXT", "b"); ("STRING", "\""); + ("STRING_TEXT", """); ("STRING_TEXT", "b"); ("STRING", """); ("WHITESPACE", " ")]); (3, - [("WHITESPACE", " "); ("STRING_TEXT", "}"); - ("STRING_TEXT", "."); ("STRING_TEXT", "contents"); - ("STRING_TEXT", " ")]); + [("WHITESPACE", " "); ("RBRACE", "}"); ("DOT", "."); + ("IDENT", "contents"); ("WHITESPACE", " ")]); (4, - [("STRING_TEXT", " "); ("STRING_TEXT", "}"); + [("WHITESPACE", " "); ("STRING_TEXT", "}"); ("STRING_TEXT", " "); ("STRING_TEXT", "def"); ("INTERP_STRING_END", "\"\"\"")])] if actual <> expected then @@ -208,8 +207,8 @@ let ``Tokenizer test - single-line nested string interpolation``() = ("STRING_TEXT", " "); ("INTERP_STRING_BEGIN_PART", "{"); ("WHITESPACE", " "); ("LBRACE", "{"); ("WHITESPACE", " "); ("IDENT", "contents"); ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); ("INT32", "1"); - ("WHITESPACE", " "); ("RBRACE", "}"); ("WHITESPACE", " "); ("RBRACE", "}"); - ("STRING_TEXT", "\""); ("STRING_TEXT", " ")])] + ("WHITESPACE", " "); ("RBRACE", "}"); ("WHITESPACE", " "); + ("STRING_TEXT", "}"); ("INTERP_STRING_END", "\""); ("WHITESPACE", " ")])] if actual <> expected then printfn "actual = %A" actual From 6514788a933f61dad5b1d0290f8dd1646b43bea1 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Jul 2020 14:33:41 +0100 Subject: [PATCH 66/87] fix test case --- tests/service/TokenizerTests.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/service/TokenizerTests.fs b/tests/service/TokenizerTests.fs index dd01d755a25..6dea4640543 100644 --- a/tests/service/TokenizerTests.fs +++ b/tests/service/TokenizerTests.fs @@ -178,7 +178,7 @@ let ``Tokenizer test - multi-line nested string interpolation``() = (2, [("WHITESPACE", " "); ("IDENT", "contents"); ("WHITESPACE", " "); ("EQUALS", "="); ("WHITESPACE", " "); - ("STRING_TEXT", """); ("STRING_TEXT", "b"); ("STRING", """); + ("STRING_TEXT", "\""); ("STRING_TEXT", "b"); ("STRING", "\""); ("WHITESPACE", " ")]); (3, [("WHITESPACE", " "); ("RBRACE", "}"); ("DOT", "."); From e758b4b7052609c99226b1ac5c13fe4bbd65d5c6 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Jul 2020 18:13:43 +0100 Subject: [PATCH 67/87] interpolated string specifer highlighting --- src/fsharp/CheckFormatStrings.fs | 194 ++++++++++++------ src/fsharp/CheckFormatStrings.fsi | 4 +- src/fsharp/SyntaxTree.fs | 8 +- src/fsharp/SyntaxTreeOps.fs | 5 +- src/fsharp/TypeChecker.fs | 36 +++- src/fsharp/lex.fsl | 8 +- src/fsharp/pars.fsy | 8 +- src/fsharp/service/ServiceParseTreeWalk.fs | 4 +- src/fsharp/service/ServiceUntypedParse.fs | 5 +- .../FSharp.Compiler.UnitTests.fsproj | 52 +++++ tests/service/Common.fs | 7 +- tests/service/EditorTests.fs | 35 ++++ tests/service/ProjectAnalysisTests.fs | 2 +- .../CSharp_Analysis/CSharp_Analysis.csproj | 4 +- tests/service/data/TestTP/TestTP.fsproj | 2 +- .../UnitTests/VisualFSharp.UnitTests.fsproj | 51 ----- 16 files changed, 276 insertions(+), 149 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index 38b6dc92736..af2195237a1 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -6,6 +6,7 @@ open System.Text open FSharp.Compiler open FSharp.Compiler.AbstractIL.Internal.Library open FSharp.Compiler.ConstraintSolver +open FSharp.Compiler.Lib open FSharp.Compiler.NameResolution open FSharp.Compiler.Range open FSharp.Compiler.SyntaxTree @@ -48,27 +49,78 @@ let newInfo () = addZeros = false precision = false} -let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormattableString (context: FormatStringCheckContext option) fmt printerArgTy printerResidueTy = - // 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) = +let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) isInterpolated isFormattableString (context: FormatStringCheckContext option) fmt printerArgTy printerResidueTy = + printfn "--------------------" + printfn "context.IsSome = %b" context.IsSome + printfn "fmt = <<<%s>>>" fmt + printfn "isInterpolated = %b" isInterpolated + printfn "fragRanges = %A" fragRanges + let fmt, fragments = + // In the foreground checking of the IDE we crack the string accurately + // by going back and getting the fragments of the format string from the original source. + // + // The offset is used to adjust ranges depending on whether input string is + // regular, verbatim or triple-quote etc. match context with - | Some context -> + | Some context when fragRanges.Length > 0 -> let sourceText = context.SourceText + //printfn "sourceText.IsSome = %b" sourceText.IsSome let lineStartPositions = context.LineStartPositions + printfn "lineStartPositions.Length = %d" lineStartPositions.Length let length = sourceText.Length - if m.EndLine < lineStartPositions.Length then - let startIndex = lineStartPositions.[m.StartLine-1] + m.StartColumn - let endIndex = lineStartPositions.[m.EndLine-1] + m.EndColumn - 1 - if startIndex < length-3 && sourceText.SubTextEquals("\"\"\"", startIndex) then - (3, sourceText.GetSubTextString(startIndex + 3, endIndex - startIndex)) - elif startIndex < length-2 && sourceText.SubTextEquals("@\"", startIndex) then - (2, sourceText.GetSubTextString(startIndex + 2, endIndex + 1 - startIndex)) - else (1, sourceText.GetSubTextString(startIndex + 1, endIndex - startIndex)) - else (1, fmt) - | None -> (1, fmt) - - let len = String.length fmt + let numFrags = fragRanges.Length + let fmts = + [ for i, fragRange in List.indexed fragRanges do + let m = fragRange + printfn "m.EndLine = %d" m.EndLine + if m.EndLine < lineStartPositions.Length then + let startIndex = lineStartPositions.[m.StartLine-1] + m.StartColumn + let startIndex2 = if m.StartLine+1 < lineStartPositions.Length then lineStartPositions.[m.StartLine] else startIndex + let endIndex = lineStartPositions.[m.EndLine-1] + m.EndColumn + // Note, some extra """ text may be included at end of these snippets, meaning CheckFormatString in the IDE + // may be using a slightly false format string to colorize the %d markers. This doesn't matter as there + // won't be relevant %d in these sections + // + // However we make an effort to remove these to keep the calls to GetSubStringText valid. So + // we work out how much extra text there is at the end of the last line of the fragment, + // which may or may not be quote markers. If there's no flex, we don't trim the quote marks + let endNextLineIndex = if m.EndLine + 1 < lineStartPositions.Length then lineStartPositions.[m.EndLine] else endIndex + let endIndexFlex = endNextLineIndex - endIndex + let mLength = endIndex - startIndex + let sourceLineFromOffset = sourceText.GetSubTextString(startIndex, (startIndex2 - startIndex)) + printfn "i = %d, mLength = %d, endIndexFlex = %d, sourceLineFromOffset = <<<%s>>>" i mLength endIndexFlex sourceLineFromOffset + if isInterpolated && i=0 && startIndex < length-4 && sourceText.SubTextEquals("$\"\"\"", startIndex) then + (4, sourceText.GetSubTextString(startIndex + 4, mLength - 4 - min endIndexFlex (if i = numFrags-1 then 3 else 1)), m) + elif not isInterpolated && i=0 && startIndex < length-3 && sourceText.SubTextEquals("\"\"\"", startIndex) then + (3, sourceText.GetSubTextString(startIndex + 3, mLength - min endIndexFlex (if i = numFrags-1 then 3 else 1)), m) + elif isInterpolated && i=0 && startIndex < length-3 && sourceText.SubTextEquals("$@\"", startIndex) then + (3, sourceText.GetSubTextString(startIndex + 3, mLength - 3 - min endIndexFlex 1), m) + elif isInterpolated && i=0 && startIndex < length-3 && sourceText.SubTextEquals("@$\"", startIndex) then + (3, sourceText.GetSubTextString(startIndex + 3, mLength - 3 - min endIndexFlex 1), m) + elif not isInterpolated && i=0 && startIndex < length-2 && sourceText.SubTextEquals("@\"", startIndex) then + (2, sourceText.GetSubTextString(startIndex + 2, mLength - 2 - min endIndexFlex 1), m) + elif isInterpolated && i=0 && startIndex < length-2 && sourceText.SubTextEquals("$\"", startIndex) then + (2, sourceText.GetSubTextString(startIndex + 2, mLength - 2 - min endIndexFlex 1), m) + elif isInterpolated && i <> 0 && startIndex < length-1 && sourceText.SubTextEquals("}", startIndex) then + (1, sourceText.GetSubTextString(startIndex + 1, mLength - 1 - min endIndexFlex 1), m) + else + (1, sourceText.GetSubTextString(startIndex + 1, mLength - 1 - min endIndexFlex 1), m) + else (1, fmt, m) ] + + printfn "fmts = %A" fmts + + let fmt = fmts |> List.map p23 |> String.concat "%P()" // this is only used on the IDE path + let fragments, _ = (0, fmts) ||> List.mapFold (fun i (offset, fmt, fragRange) -> (i, offset, fragRange), i + fmt.Length) + + printfn "fmt2 = <<<%s>>>" fmt + printfn "fragments = %A" fragments + fmt, fragments + | _ -> + // Don't muck with the fmt when there is no source code context to go get the original + // source code (i.e. when compiling or background checking) + fmt, [ (0, 1, m) ] + + let len = fmt.Length let specifierLocations = ResizeArray() @@ -80,7 +132,19 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta let mutable dotnetFormatStringInterpolationHoleCount = 0 let percentATys = ResizeArray<_>() - let rec parseLoop acc (i, relLine, relCol) = + // fragLine and fragCol track our location w.r.t. the marker for the start of this chunk + let rec parseLoop acc (i, fragLine, fragCol) fragments (m: range) = + + // Check if we've moved into the next fragment. Note this will always activate on + // the first step, i.e. when i=0 + let (struct (fragLine, fragCol, fragments, m)) = + match fragments with + | (idx, fragOffset, fragRange)::rest when i >= idx -> + printfn "i = %d, idx = %d, moving into next fragment at %A plus fragOffset %d" i idx fragRange fragOffset + struct (m.StartLine, m.StartColumn + fragOffset, rest, fragRange) + + | _ -> struct (fragLine, fragCol, fragments, m) + if i >= len then let argtys = if acc |> List.forall (fun (p, _) -> p = None) then // without positional specifiers @@ -90,13 +154,13 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta argtys elif System.Char.IsSurrogatePair(fmt,i) then appendToDotnetFormatString (fmt.[i..i+1]) - parseLoop acc (i+2, relLine, relCol+2) + parseLoop acc (i+2, fragLine, fragCol+2) fragments m else let c = fmt.[i] match c with | '%' -> - let startCol = relCol - let relCol = relCol+1 + let startFragCol = fragCol + let fragCol = fragCol+1 let i = i+1 if i >= len then failwithf "%s" <| FSComp.SR.forMissingFormatSpecifier() let info = newInfo() @@ -171,15 +235,15 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta let oldI = i let posi, i = position i - let relCol = relCol + i - oldI + let fragCol = fragCol + i - oldI let oldI = i let i = flags i - let relCol = relCol + i - oldI + let fragCol = fragCol + i - oldI let oldI = i let widthArg,(widthValue, (precisionArg,i)) = widthAndPrecision i - let relCol = relCol + i - oldI + let fragCol = fragCol + i - oldI if i >= len then failwithf "%s" <| FSComp.SR.forBadPrecision() @@ -234,37 +298,30 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta else failwithf "%s" <| FSComp.SR.forFormatInvalidForInterpolated3() - let collectSpecifierLocation relLine relCol numStdArgs = + let collectSpecifierLocation fragLine fragCol numStdArgs = let numArgsForSpecifier = numStdArgs + (if widthArg then 1 else 0) + (if precisionArg then 1 else 0) - match relLine with - | 0 -> - specifierLocations.Add( - (Range.mkFileIndexRange m.FileIndex - (Range.mkPos m.StartLine (startCol + offset)) - (Range.mkPos m.StartLine (relCol + offset + 1))), numArgsForSpecifier) - | _ -> - specifierLocations.Add( - (Range.mkFileIndexRange m.FileIndex - (Range.mkPos (m.StartLine + relLine) startCol) - (Range.mkPos (m.StartLine + relLine) (relCol + 1))), numArgsForSpecifier) + specifierLocations.Add( + (Range.mkFileIndexRange m.FileIndex + (Range.mkPos fragLine startFragCol) + (Range.mkPos fragLine (fragCol + 1))), numArgsForSpecifier) let ch = fmt.[i] match ch with | '%' -> - collectSpecifierLocation relLine relCol 0 + collectSpecifierLocation fragLine fragCol 0 appendToDotnetFormatString "%" - parseLoop acc (i+1, relLine, relCol+1) + parseLoop acc (i+1, fragLine, fragCol+1) fragments m | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()) - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments m | ('l' | 'L') -> if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()) - let relCol = relCol+1 + let fragCol = fragCol+1 let i = i+1 // "bad format specifier ... In F# code you can use %d, %x, %o or %u instead ..." @@ -274,100 +331,101 @@ let parseFormatStringInternal (m:range) (g: TcGlobals) isInterpolated isFormatta failwithf "%s" <| FSComp.SR.forLIsUnnecessary() match fmt.[i] with | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments m | _ -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifier() | ('h' | 'H') -> failwithf "%s" <| FSComp.SR.forHIsUnnecessary() | 'M' -> - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, mkFlexibleDecimalFormatTypar g m) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, mkFlexibleDecimalFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments m | ('f' | 'F' | 'e' | 'E' | 'g' | 'G') -> - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, mkFlexibleFloatFormatTypar g m) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, mkFlexibleFloatFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments m | 'b' -> checkOtherFlags ch - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, g.bool_ty) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, g.bool_ty) :: acc) (i, fragLine, fragCol+1) fragments m | 'c' -> checkOtherFlags ch - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, g.char_ty) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, g.char_ty) :: acc) (i, fragLine, fragCol+1) fragments m | 's' -> checkOtherFlags ch - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, g.string_ty) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, g.string_ty) :: acc) (i, fragLine, fragCol+1) fragments m | 'O' -> checkOtherFlags ch - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, NewInferenceType ()) :: acc) (i, fragLine, fragCol+1) fragments m // residue of hole "...{n}..." in interpolated strings become %P(...) | 'P' when isInterpolated -> checkOtherFlags ch let i = requireAndSkipInterpolationHoleFormat (i+1) - parseLoop ((posi, NewInferenceType ()) :: acc) (i, relLine, relCol+1) + // Note, the fragCol doesn't advance at all as these are magically inserted. + parseLoop ((posi, NewInferenceType ()) :: acc) (i, fragLine, startFragCol) fragments m | 'A' -> match info.numPrefixIfPos with | None // %A has BindingFlags=Public, %+A has BindingFlags=Public | NonPublic | Some '+' -> - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) let xty = NewInferenceType () percentATys.Add(xty) - parseLoop ((posi, xty) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, xty) :: acc) (i, fragLine, fragCol+1) fragments m | Some n -> failwithf "%s" <| FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), n.ToString()) | 'a' -> checkOtherFlags ch let xty = NewInferenceType () let fty = printerArgTy --> (xty --> printerResidueTy) - collectSpecifierLocation relLine relCol 2 + collectSpecifierLocation fragLine fragCol 2 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i, relLine, relCol+1) + parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i, fragLine, fragCol+1) fragments m | 't' -> checkOtherFlags ch - collectSpecifierLocation relLine relCol 1 + collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, printerArgTy --> printerResidueTy) :: acc) (i, relLine, relCol+1) + parseLoop ((posi, printerArgTy --> printerResidueTy) :: acc) (i, fragLine, fragCol+1) fragments m | c -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c) | '\n' -> appendToDotnetFormatString fmt.[i..i] - parseLoop acc (i+1, relLine+1, 0) + parseLoop acc (i+1, fragLine+1, 0) fragments m | _ -> appendToDotnetFormatString fmt.[i..i] - parseLoop acc (i+1, relLine, relCol+1) + parseLoop acc (i+1, fragLine, fragCol+1) fragments m - let results = parseLoop [] (0, 0, m.StartColumn) + let results = parseLoop [] (0, 0, m.StartColumn) fragments m results, Seq.toList specifierLocations, dotnetFormatString.ToString(), percentATys.ToArray() -let ParseFormatString m g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy printerResultTy = - let argTys, specifierLocations, dotnetFormatString, percentATys = parseFormatStringInternal m g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy +let ParseFormatString m ms g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy printerResultTy = + let argTys, specifierLocations, dotnetFormatString, percentATys = parseFormatStringInternal m ms g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy let printerTy = List.foldBack (-->) argTys printerResultTy let printerTupleTy = mkRefTupledTy g argTys argTys, printerTy, printerTupleTy, percentATys, specifierLocations, dotnetFormatString let TryCountFormatStringArguments m g isInterpolated fmt printerArgTy printerResidueTy = try - let argTys, _specifierLocations, _dotnetFormatString, _percentATys = parseFormatStringInternal m g isInterpolated false None fmt printerArgTy printerResidueTy + let argTys, _specifierLocations, _dotnetFormatString, _percentATys = parseFormatStringInternal m [] g isInterpolated false None fmt printerArgTy printerResidueTy Some argTys.Length with _ -> None diff --git a/src/fsharp/CheckFormatStrings.fsi b/src/fsharp/CheckFormatStrings.fsi index cbb798873b6..b96e8f88a8f 100644 --- a/src/fsharp/CheckFormatStrings.fsi +++ b/src/fsharp/CheckFormatStrings.fsi @@ -13,6 +13,6 @@ open FSharp.Compiler.Range open FSharp.Compiler.TypedTree open FSharp.Compiler.TcGlobals -val ParseFormatString : m: range -> g: TcGlobals -> isInterpolated: bool -> isFormattableString: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> TType list * TType * TType * TType[] * (range * int) list * string +val ParseFormatString : m: range -> fragmentRanges: range list -> g: TcGlobals -> isInterpolated: bool -> isFormattableString: bool -> formatStringCheckContext: FormatStringCheckContext option -> fmt: string -> bty: TType -> cty: TType -> dty: TType -> TType list * TType * TType * TType[] * (range * int) list * string -val TryCountFormatStringArguments : m:Range.range -> g:TcGlobals -> isInterpolated: bool -> fmt:string -> bty:TType -> cty:TType -> int option +val TryCountFormatStringArguments: m: range -> g: TcGlobals -> isInterpolated: bool -> fmt:string -> bty:TType -> cty:TType -> int option diff --git a/src/fsharp/SyntaxTree.fs b/src/fsharp/SyntaxTree.fs index 10c82494574..74f2be21d6f 100644 --- a/src/fsharp/SyntaxTree.fs +++ b/src/fsharp/SyntaxTree.fs @@ -1009,8 +1009,9 @@ type SynExpr = range: range /// F# syntax: interpolated string, e.g. "abc{x}" or "abc{x,3}" or "abc{x:N4}" + /// Note the string ranges include the quotes, verbatim markers, dollar sign and braces | InterpolatedString of - contents: Choice list * + contents: SynInterpolatedStringPart list * range: range /// Gets the syntax range of this construct @@ -1117,6 +1118,11 @@ type SynExpr = | SynExpr.ArbitraryAfterError _ -> true | _ -> false +[] +type SynInterpolatedStringPart = + | String of string * range + | FillExpr of SynExpr * Ident option + /// Represents a syntax tree for an F# indexer expression argument [] type SynIndexerArg = diff --git a/src/fsharp/SyntaxTreeOps.fs b/src/fsharp/SyntaxTreeOps.fs index bafa1a94d4b..a2b1949a3e6 100644 --- a/src/fsharp/SyntaxTreeOps.fs +++ b/src/fsharp/SyntaxTreeOps.fs @@ -732,6 +732,9 @@ let rec synExprContainsError inpExpr = walkExpr e1 || walkExprs [ for (_,_,_,_,e,_) in es do yield e ] || walkExpr e2 | SynExpr.InterpolatedString (parts, _m) -> - walkExprs (parts |> List.choose (function Choice1Of2 _ -> None | Choice2Of2 (x, _) -> Some x)) + walkExprs + (parts |> List.choose (function + | SynInterpolatedStringPart.String _ -> None + | SynInterpolatedStringPart.FillExpr (x, _) -> Some x)) walkExpr inpExpr diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 5745ae3ae3d..0037282543a 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7121,7 +7121,7 @@ and TcFormatStringExpr cenv overallTy env m tpenv (fmtString: string) = let normalizedString = (fmtString.Replace("\r\n", "\n").Replace("\r", "\n")) let _argTys, atyRequired, etyRequired, _percentATys, specifierLocations, _dotnetFormatString = - try CheckFormatStrings.ParseFormatString m g false false formatStringCheckContext normalizedString bty cty dty + try CheckFormatStrings.ParseFormatString m [m] g false false formatStringCheckContext normalizedString bty cty dty with Failure errString -> error (Error(FSComp.SR.tcUnableToParseFormatString errString, m)) match cenv.tcSink.CurrentSink with @@ -7140,19 +7140,25 @@ and TcFormatStringExpr cenv overallTy env m tpenv (fmtString: string) = mkString g m fmtString, tpenv /// Check an interpolated string expression -and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice list) = +and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: SynInterpolatedStringPart list) = let g = cenv.g let synFillExprs = parts |> List.choose (function - | Choice1Of2 _ -> None - | Choice2Of2 (fillExpr, _) -> + | SynInterpolatedStringPart.String _ -> None + | SynInterpolatedStringPart.FillExpr (fillExpr, _) -> match fillExpr with // Detect "x" part of "...{x,3}..." | SynExpr.Tuple (false, [e; SynExpr.Const (SynConst.Int32 _align, _)], _, _) -> Some e | e -> Some e) + let stringFragmentRanges = + parts + |> List.choose (function + | SynInterpolatedStringPart.String (_,m) -> Some m + | SynInterpolatedStringPart.FillExpr _ -> None) + let printerTy = NewInferenceType () let printerArgTy = NewInferenceType () let printerResidueTy = NewInferenceType () @@ -7219,8 +7225,8 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice List.map (function - | Choice1Of2 s -> s - | Choice2Of2 (fillExpr, format) -> + | SynInterpolatedStringPart.String (s, _) -> s + | SynInterpolatedStringPart.FillExpr (fillExpr, format) -> let alignText = match fillExpr with // Validate and detect ",3" part of "...{x,3}..." @@ -7234,9 +7240,21 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: Choice String.concat "" // Parse the format string to work out the phantom types and check for absence of '%' specifiers in FormattableString - let argTys, _printerTy, printerTupleTyRequired, percentATys, _specifierLocations, dotnetFormatString = - try CheckFormatStrings.ParseFormatString m g true isFormattableString None printfFormatString printerArgTy printerResidueTy printerResultTy - with Failure errString -> error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) + let argTys, _printerTy, printerTupleTyRequired, percentATys, specifierLocations, dotnetFormatString = + let formatStringCheckContext = + match cenv.tcSink.CurrentSink with + | None -> None + | Some sink -> sink.FormatStringCheckContext + try + CheckFormatStrings.ParseFormatString m stringFragmentRanges g true isFormattableString formatStringCheckContext printfFormatString printerArgTy printerResidueTy printerResultTy + with Failure errString -> + error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) + + match cenv.tcSink.CurrentSink with + | None -> () + | Some sink -> + for specifierLocation, numArgs in specifierLocations do + sink.NotifyFormatSpecifierLocation(specifierLocation, numArgs) // Check the expressions filling the holes if argTys.Length <> synFillExprs.Length then diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index fd7759561d9..3fcb4c2ec0a 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -1078,19 +1078,19 @@ and singleQuoteString sargs skip = parse | unicodeGraphLong { let (buf, _fin, m, kind, args) = sargs let hexChars = lexemeTrimLeft lexbuf 2 - let tok = + let result() = if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) else singleQuoteString sargs skip lexbuf match unicodeGraphLong hexChars with | Invalid -> - fail args lexbuf (FSComp.SR.lexInvalidUnicodeLiteral hexChars) tok + fail args lexbuf (FSComp.SR.lexInvalidUnicodeLiteral hexChars) (result()) | SingleChar(c) -> addUnicodeChar buf (int c) - tok + result() | SurrogatePair(hi, lo) -> addUnicodeChar buf (int hi) addUnicodeChar buf (int lo) - tok } + result() } | '"' { let (buf, fin, _m, kind, args) = sargs diff --git a/src/fsharp/pars.fsy b/src/fsharp/pars.fsy index 7f263c9c3be..28486c3218f 100644 --- a/src/fsharp/pars.fsy +++ b/src/fsharp/pars.fsy @@ -5498,20 +5498,20 @@ interpolatedStringFill: interpolatedStringParts: | INTERP_STRING_END - { [ Choice1Of2 (fst $1) ] } + { [ SynInterpolatedStringPart.String (fst $1, rhs parseState 1) ] } | INTERP_STRING_PART interpolatedStringFill interpolatedStringParts - { Choice1Of2 (fst $1) :: Choice2Of2 $2 :: $3 } + { SynInterpolatedStringPart.String (fst $1, rhs parseState 1) :: SynInterpolatedStringPart.FillExpr $2 :: $3 } /* INTERP_STRING_BEGIN_END */ /* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_END */ /* INTERP_STRING_BEGIN_PART int32 INTERP_STRING_PART int32 INTERP_STRING_END */ interpolatedString: | INTERP_STRING_BEGIN_PART interpolatedStringFill interpolatedStringParts - { Choice1Of2 (fst $1) :: Choice2Of2 $2 :: $3 } + { SynInterpolatedStringPart.String (fst $1, rhs parseState 1) :: SynInterpolatedStringPart.FillExpr $2 :: $3 } | INTERP_STRING_BEGIN_END - { [ Choice1Of2 (fst $1) ] } + { [ SynInterpolatedStringPart.String (fst $1, rhs parseState 1) ] } opt_HIGH_PRECEDENCE_APP: | HIGH_PRECEDENCE_BRACK_APP { } diff --git a/src/fsharp/service/ServiceParseTreeWalk.fs b/src/fsharp/service/ServiceParseTreeWalk.fs index 9673d4d036b..2d5d1729d97 100755 --- a/src/fsharp/service/ServiceParseTreeWalk.fs +++ b/src/fsharp/service/ServiceParseTreeWalk.fs @@ -214,8 +214,8 @@ module public AstTraversal = | SynExpr.InterpolatedString (parts, _) -> [ for part in parts do match part with - | Choice1Of2 _ -> () - | Choice2Of2 (fillExpr, _) -> + | SynInterpolatedStringPart.String _ -> () + | SynInterpolatedStringPart.FillExpr (fillExpr, _) -> yield dive fillExpr fillExpr.Range traverseSynExpr ] |> pick expr diff --git a/src/fsharp/service/ServiceUntypedParse.fs b/src/fsharp/service/ServiceUntypedParse.fs index ea4aca6879e..02054ef540a 100755 --- a/src/fsharp/service/ServiceUntypedParse.fs +++ b/src/fsharp/service/ServiceUntypedParse.fs @@ -210,7 +210,10 @@ type FSharpParseFileResults(errors: FSharpErrorInfo[], input: ParsedInput option yield! walkExpr false e | SynExpr.InterpolatedString (parts, _) -> - yield! walkExprs [ for part in parts do match part with Choice1Of2 _ -> () | Choice2Of2 (fillExpr, _) -> yield fillExpr ] + yield! walkExprs [ for part in parts do + match part with + | SynInterpolatedStringPart.String _ -> () + | SynInterpolatedStringPart.FillExpr (fillExpr, _) -> yield fillExpr ] | SynExpr.YieldOrReturn (_, e, _) | SynExpr.YieldOrReturnFrom (_, e, _) diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj index eac6510990d..25093aa8885 100644 --- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj +++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj @@ -21,12 +21,64 @@ + + CompilerService\FsUnit.fs + + + CompilerService\Common.fs + + + CompilerService\Symbols.fs + + + CompilerService\EditorTests.fs + + + CompilerService\FileSystemTests.fs + + + CompilerService\ProjectAnalysisTests.fs + + + CompilerService\TokenizerTests.fs + + + + CompilerService\PerfTests.fs + + + CompilerService\InteractiveCheckerTests.fs + + + CompilerService\ExprTests.fs + + + CompilerService\CSharpProjectAnalysis.fs + + + CompilerService\StructureTests.fs + + + CompilerService\AssemblyContentProviderTests.fs + + + CompilerService\ServiceUntypedParseTests.fs + + + CompilerService\TreeVisitorTests.fs + + + diff --git a/tests/service/Common.fs b/tests/service/Common.fs index d815abf08cb..520b54ad3f8 100644 --- a/tests/service/Common.fs +++ b/tests/service/Common.fs @@ -168,7 +168,7 @@ let parseAndCheckFile fileName source options = | parseResults, FSharpCheckFileAnswer.Succeeded(checkResults) -> parseResults, checkResults | _ -> failwithf "Parsing aborted unexpectedly..." -let parseAndCheckScript (file, input) = +let parseAndCheckScriptWithOptions (file, input, opts) = #if NETCOREAPP let dllName = Path.ChangeExtension(file, ".dll") @@ -179,9 +179,10 @@ let parseAndCheckScript (file, input) = #else let projectOptions, _diagnostics = checker.GetProjectOptionsFromScript(file, FSharp.Compiler.Text.SourceText.ofString input) |> Async.RunSynchronously - printfn "projectOptions = %A" projectOptions + //printfn "projectOptions = %A" projectOptions #endif + let projectOptions = { projectOptions with OtherOptions = Array.append opts projectOptions.OtherOptions } let parseResult, typedRes = checker.ParseAndCheckFileInProject(file, 0, FSharp.Compiler.Text.SourceText.ofString input, projectOptions) |> Async.RunSynchronously // if parseResult.Errors.Length > 0 then @@ -192,6 +193,8 @@ let parseAndCheckScript (file, input) = | FSharpCheckFileAnswer.Succeeded(res) -> parseResult, res | res -> failwithf "Parsing did not finish... (%A)" res +let parseAndCheckScript (file, input) = parseAndCheckScriptWithOptions (file, input, [| |]) + let parseSourceCode (name: string, code: string) = let location = Path.Combine(Path.GetTempPath(),"test"+string(hash (name, code))) try Directory.CreateDirectory(location) |> ignore with _ -> () diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 35b049608d5..083efab46e5 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -523,6 +523,41 @@ let _ = List.iter(printfn \"\"\"%-A (7, 29, 7, 31, 1); (7, 33, 7, 35,1 )|] +[] +let ``Printf specifiers for regular and verbatim interpolated strings`` () = + let input = + """let os = System.Text.StringBuilder() // line 1 +let _ = $"{0}" // line 2 +let _ = $"%A{0}" // line 3 +let _ = $"%7.1f{1.0}" // line 4 +let _ = $"%-8.1e{1.0}+567" // line 5 +let s = "value" // line 6 +let _ = $@"%-5s{s}" // line 7 +let _ = $@"%-A{-10}" // line 8 +let _ = @$" + %-O{-10}" // line 10 +let _ = $" + + %-O{-10}" // line 13 +let _ = List.map (fun x -> sprintf $@"%A{x} + ") // line 15 +let _ = $"\n%-8.1e{1.0}+567" // line 16 +let _ = $@"%O{1}\n%-5s{s}" // line 17 +let _ = $"%%" // line 18 +""" + + let file = "/home/user/Test.fsx" + let parseResult, typeCheckResults = parseAndCheckScriptWithOptions(file, input, [| "/langversion:preview" |]) + + typeCheckResults.Errors |> shouldEqual [||] + typeCheckResults.GetFormatSpecifierLocationsAndArity() + |> Array.map (fun (range,numArgs) -> range.StartLine, range.StartColumn, range.EndLine, range.EndColumn, numArgs) + |> shouldEqual + [|(3, 10, 3, 12, 1); (4, 10, 4, 15, 1); (5, 10, 5, 16, 1); (7, 11, 7, 15, 1); + (8, 11, 8, 14, 1); (10, 12, 10, 15, 1); (13, 12, 13, 15, 1); + (14, 38, 14, 40, 1); (16, 12, 16, 18, 1); (17, 11, 17, 13, 1); + (17, 16, 17, 20, 1); (18, 10, 18, 12, 0)|] + [] let ``Printf specifiers for user-defined functions`` () = let input = diff --git a/tests/service/ProjectAnalysisTests.fs b/tests/service/ProjectAnalysisTests.fs index 1e6b1c418cc..110db85e4f5 100644 --- a/tests/service/ProjectAnalysisTests.fs +++ b/tests/service/ProjectAnalysisTests.fs @@ -3800,7 +3800,7 @@ let ``Test Project26 parameter symbols`` () = let rec isByRef (ty: FSharpType) = if ty.IsAbbreviation then isByRef ty.AbbreviatedType - else ty.IsNamedType && ty.NamedEntity.IsByRef + else ty.HasTypeDefinition && ty.TypeDefinition.IsByRef // check we can get the CurriedParameterGroups let objMethodsCurriedParameterGroups = diff --git a/tests/service/data/CSharp_Analysis/CSharp_Analysis.csproj b/tests/service/data/CSharp_Analysis/CSharp_Analysis.csproj index 4fc4184b23d..51649639ca5 100644 --- a/tests/service/data/CSharp_Analysis/CSharp_Analysis.csproj +++ b/tests/service/data/CSharp_Analysis/CSharp_Analysis.csproj @@ -2,9 +2,9 @@ - net45 + netstandard2.0 1.0.0.0 - nunit + none $(NoWarn);0067;1591 diff --git a/tests/service/data/TestTP/TestTP.fsproj b/tests/service/data/TestTP/TestTP.fsproj index c56ec5f349b..66087982151 100644 --- a/tests/service/data/TestTP/TestTP.fsproj +++ b/tests/service/data/TestTP/TestTP.fsproj @@ -2,7 +2,7 @@ - net45 + net472 true nunit diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 06b705cf855..6803ce9cb49 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -56,59 +56,9 @@ - - CompilerService\FsUnit.fs - - - CompilerService\Common.fs - - - CompilerService\Symbols.fs - - - CompilerService\EditorTests.fs - - - CompilerService\FileSystemTests.fs - - - CompilerService\ProjectAnalysisTests.fs - - - CompilerService\TokenizerTests.fs - - - - CompilerService\PerfTests.fs - - - CompilerService\InteractiveCheckerTests.fs - - - CompilerService\ExprTests.fs - - - CompilerService\CSharpProjectAnalysis.fs - - - CompilerService\StructureTests.fs - - - CompilerService\AssemblyContentProviderTests.fs - - - CompilerService\ServiceUntypedParseTests.fs - CompilerService\UnusedOpensTests.fs - - CompilerService\TreeVisitorTests.fs - Roslyn\ProjectOptionsBuilder.fs @@ -184,7 +134,6 @@ - From 5fba57883dfe4db26b2ca2a584c5fb6d2fd70ae0 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Jul 2020 19:00:01 +0100 Subject: [PATCH 68/87] fix triple quote interpolated string specifer highlighting --- src/fsharp/CheckFormatStrings.fs | 80 +++++++++++++++++++++----------- tests/service/EditorTests.fs | 23 ++++++++- 2 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index af2195237a1..077d2240a97 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -50,32 +50,51 @@ let newInfo () = precision = false} let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) isInterpolated isFormattableString (context: FormatStringCheckContext option) fmt printerArgTy printerResidueTy = - printfn "--------------------" - printfn "context.IsSome = %b" context.IsSome - printfn "fmt = <<<%s>>>" fmt - printfn "isInterpolated = %b" isInterpolated - printfn "fragRanges = %A" fragRanges + + // As background: the F# compiler tokenizes strings on the assumption that the only thing you need from + // them is the actual corresponding text, e.g. of a string literal. This means many different textual strings + // in the input file correspond to the 'fmt' string we have here. + // + // The problem with this is that when we go to colorize the format specifiers in string, we need to do + // that with respect to the original string source text in order to lay down accurate colorizations. + // + // One approach would be to change the F# lexer to also crack every string in a more structured way, recording + // both the original source text and the actual string literal. However this would be invasive and possibly + // too expensive since the vast majority of strings don't need this treatment. + // + // So instead, for format strings alone - and only when processing in the IDE - we crack the "original" + // source of the string by going back and getting the format string from the input source file by using the + // relevant ranges + // + // For interpolated strings this may involve many fragments, e.g. + // $"abc %d{" + // "} def %s{" + // "} xyz" + // In this case we are given the range of each fragment. + // + // One annoying thing is that we must lop off the quotations, $, {, } symbols off the end of each string fragment. + // This information should probably be given to us by the lexer. let fmt, fragments = - // In the foreground checking of the IDE we crack the string accurately - // by going back and getting the fragments of the format string from the original source. - // - // The offset is used to adjust ranges depending on whether input string is - // regular, verbatim or triple-quote etc. + //printfn "--------------------" + //printfn "context.IsSome = %b" context.IsSome + //printfn "fmt = <<<%s>>>" fmt + //printfn "isInterpolated = %b" isInterpolated + //printfn "fragRanges = %A" fragRanges match context with | Some context when fragRanges.Length > 0 -> let sourceText = context.SourceText //printfn "sourceText.IsSome = %b" sourceText.IsSome let lineStartPositions = context.LineStartPositions - printfn "lineStartPositions.Length = %d" lineStartPositions.Length + //printfn "lineStartPositions.Length = %d" lineStartPositions.Length let length = sourceText.Length let numFrags = fragRanges.Length let fmts = [ for i, fragRange in List.indexed fragRanges do let m = fragRange - printfn "m.EndLine = %d" m.EndLine - if m.EndLine < lineStartPositions.Length then + //printfn "m.EndLine = %d" m.EndLine + if m.StartLine - 1 < lineStartPositions.Length && m.EndLine - 1 < lineStartPositions.Length then let startIndex = lineStartPositions.[m.StartLine-1] + m.StartColumn - let startIndex2 = if m.StartLine+1 < lineStartPositions.Length then lineStartPositions.[m.StartLine] else startIndex + //let startIndex2 = if m.StartLine < lineStartPositions.Length then lineStartPositions.[m.StartLine] else startIndex let endIndex = lineStartPositions.[m.EndLine-1] + m.EndColumn // Note, some extra """ text may be included at end of these snippets, meaning CheckFormatString in the IDE // may be using a slightly false format string to colorize the %d markers. This doesn't matter as there @@ -84,11 +103,13 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) // However we make an effort to remove these to keep the calls to GetSubStringText valid. So // we work out how much extra text there is at the end of the last line of the fragment, // which may or may not be quote markers. If there's no flex, we don't trim the quote marks - let endNextLineIndex = if m.EndLine + 1 < lineStartPositions.Length then lineStartPositions.[m.EndLine] else endIndex + let endNextLineIndex = if m.EndLine < lineStartPositions.Length then lineStartPositions.[m.EndLine] else endIndex let endIndexFlex = endNextLineIndex - endIndex let mLength = endIndex - startIndex - let sourceLineFromOffset = sourceText.GetSubTextString(startIndex, (startIndex2 - startIndex)) - printfn "i = %d, mLength = %d, endIndexFlex = %d, sourceLineFromOffset = <<<%s>>>" i mLength endIndexFlex sourceLineFromOffset + + //let sourceLineFromOffset = sourceText.GetSubTextString(startIndex, (startIndex2 - startIndex)) + //printfn "i = %d, mLength = %d, endIndexFlex = %d, sourceLineFromOffset = <<<%s>>>" i mLength endIndexFlex sourceLineFromOffset + if isInterpolated && i=0 && startIndex < length-4 && sourceText.SubTextEquals("$\"\"\"", startIndex) then (4, sourceText.GetSubTextString(startIndex + 4, mLength - 4 - min endIndexFlex (if i = numFrags-1 then 3 else 1)), m) elif not isInterpolated && i=0 && startIndex < length-3 && sourceText.SubTextEquals("\"\"\"", startIndex) then @@ -107,13 +128,19 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) (1, sourceText.GetSubTextString(startIndex + 1, mLength - 1 - min endIndexFlex 1), m) else (1, fmt, m) ] - printfn "fmts = %A" fmts + //printfn "fmts = %A" fmts - let fmt = fmts |> List.map p23 |> String.concat "%P()" // this is only used on the IDE path - let fragments, _ = (0, fmts) ||> List.mapFold (fun i (offset, fmt, fragRange) -> (i, offset, fragRange), i + fmt.Length) + // Join the fragments with holes. Note this join is only used on the IDE path, + // the TypeChecker.fs does its own joining with the right alignments etc. substituted + // On the IDE path we don't do any checking of these in this file (some checking is + // done in TypeChecker.fs) so it's ok to join with just '%P()'. + let fmt = fmts |> List.map p23 |> String.concat "%P()" + let fragments, _ = + (0, fmts) ||> List.mapFold (fun i (offset, fmt, fragRange) -> + (i, offset, fragRange), i + fmt.Length + 4) // the '4' is the length of '%P()' joins - printfn "fmt2 = <<<%s>>>" fmt - printfn "fragments = %A" fragments + //printfn "fmt2 = <<<%s>>>" fmt + //printfn "fragments = %A" fragments fmt, fragments | _ -> // Don't muck with the fmt when there is no source code context to go get the original @@ -132,16 +159,17 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let mutable dotnetFormatStringInterpolationHoleCount = 0 let percentATys = ResizeArray<_>() - // fragLine and fragCol track our location w.r.t. the marker for the start of this chunk + // fragLine, fragCol - track our location w.r.t. the marker for the start of this chunk + // let rec parseLoop acc (i, fragLine, fragCol) fragments (m: range) = // Check if we've moved into the next fragment. Note this will always activate on // the first step, i.e. when i=0 let (struct (fragLine, fragCol, fragments, m)) = match fragments with - | (idx, fragOffset, fragRange)::rest when i >= idx -> - printfn "i = %d, idx = %d, moving into next fragment at %A plus fragOffset %d" i idx fragRange fragOffset - struct (m.StartLine, m.StartColumn + fragOffset, rest, fragRange) + | (idx, fragOffset, fragRange: range)::rest when i >= idx -> + //printfn "i = %d, idx = %d, moving into next fragment at %A plus fragOffset %d" i idx fragRange fragOffset + struct (fragRange.StartLine, fragRange.StartColumn + fragOffset, rest, fragRange) | _ -> struct (fragLine, fragCol, fragments, m) diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 083efab46e5..208f1d2c160 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -544,6 +544,9 @@ let _ = List.map (fun x -> sprintf $@"%A{x} let _ = $"\n%-8.1e{1.0}+567" // line 16 let _ = $@"%O{1}\n%-5s{s}" // line 17 let _ = $"%%" // line 18 +let s2 = $"abc %d{s.Length} and %d{s.Length}def" // line 19 +let s3 = $"abc %d{s.Length} + and %d{s.Length}def" // line 21 """ let file = "/home/user/Test.fsx" @@ -556,7 +559,8 @@ let _ = $"%%" // line 18 [|(3, 10, 3, 12, 1); (4, 10, 4, 15, 1); (5, 10, 5, 16, 1); (7, 11, 7, 15, 1); (8, 11, 8, 14, 1); (10, 12, 10, 15, 1); (13, 12, 13, 15, 1); (14, 38, 14, 40, 1); (16, 12, 16, 18, 1); (17, 11, 17, 13, 1); - (17, 16, 17, 20, 1); (18, 10, 18, 12, 0)|] + (17, 18, 17, 22, 1); (18, 10, 18, 12, 0); (19, 15, 19, 17, 1); + (19, 32, 19, 34, 1); (20, 15, 20, 17, 1); (21, 20, 21, 22, 1)|] [] let ``Printf specifiers for user-defined functions`` () = @@ -581,6 +585,23 @@ let _ = debug "[LanguageService] Type checking fails for '%s' with content=%A an (4, 108, 4, 110, 1)|] [] +let ``Printf specifiers for triple quote interpolated strings`` () = + let input = + "let _ = $\"\"\"abc %d{1} and %d{2+3}def\"\"\" " + + let file = "/home/user/Test.fsx" + let parseResult, typeCheckResults = parseAndCheckScriptWithOptions(file, input, [| "/langversion:preview" |]) + + typeCheckResults.Errors |> shouldEqual [||] + typeCheckResults.GetFormatSpecifierLocationsAndArity() + |> Array.map (fun (range,numArgs) -> range.StartLine, range.StartColumn, range.EndLine, range.EndColumn, numArgs) + |> shouldEqual + [|(3, 10, 3, 12, 1); (4, 10, 4, 15, 1); (5, 10, 5, 16, 1); (7, 11, 7, 15, 1); + (8, 11, 8, 14, 1); (10, 12, 10, 15, 1); (13, 12, 13, 15, 1); + (14, 38, 14, 40, 1); (16, 12, 16, 18, 1); (17, 11, 17, 13, 1); + (17, 18, 17, 22, 1); (18, 10, 18, 12, 0); (19, 15, 19, 17, 1); + (19, 32, 19, 34, 1); (20, 15, 20, 17, 1); (21, 20, 21, 22, 1)|] +[] let ``should not report format specifiers for illformed format strings`` () = let input = """ From 56f363211fe7e4088318d2c9e81390ce0b298f90 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Jul 2020 19:02:05 +0100 Subject: [PATCH 69/87] fix triple quote interpolated string specifer highlighting --- tests/service/EditorTests.fs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 208f1d2c160..d06a331e105 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -596,11 +596,8 @@ let ``Printf specifiers for triple quote interpolated strings`` () = typeCheckResults.GetFormatSpecifierLocationsAndArity() |> Array.map (fun (range,numArgs) -> range.StartLine, range.StartColumn, range.EndLine, range.EndColumn, numArgs) |> shouldEqual - [|(3, 10, 3, 12, 1); (4, 10, 4, 15, 1); (5, 10, 5, 16, 1); (7, 11, 7, 15, 1); - (8, 11, 8, 14, 1); (10, 12, 10, 15, 1); (13, 12, 13, 15, 1); - (14, 38, 14, 40, 1); (16, 12, 16, 18, 1); (17, 11, 17, 13, 1); - (17, 18, 17, 22, 1); (18, 10, 18, 12, 0); (19, 15, 19, 17, 1); - (19, 32, 19, 34, 1); (20, 15, 20, 17, 1); (21, 20, 21, 22, 1)|] + [|(1, 16, 1, 18, 1); (1, 26, 1, 28, 1)|] + [] let ``should not report format specifiers for illformed format strings`` () = let input = From 68df7dbcce5c45a16fbe0dcea0dcdf54aee85eeb Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Jul 2020 19:25:44 +0100 Subject: [PATCH 70/87] fix build --- .../ProjectDiagnosticAnalyzerTests.fs | 22 +++++++++---------- .../tests/UnitTests/UnusedOpensTests.fs | 5 ++++- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/vsintegration/tests/UnitTests/ProjectDiagnosticAnalyzerTests.fs b/vsintegration/tests/UnitTests/ProjectDiagnosticAnalyzerTests.fs index 2d45bef6cd2..476479e9c4d 100644 --- a/vsintegration/tests/UnitTests/ProjectDiagnosticAnalyzerTests.fs +++ b/vsintegration/tests/UnitTests/ProjectDiagnosticAnalyzerTests.fs @@ -5,7 +5,7 @@ open System open System.IO open System.Threading -open FSharp.Compiler.Service.Tests.Common +//open FSharp.Compiler.Service.Tests.Common open NUnit.Framework @@ -20,15 +20,15 @@ open Microsoft.VisualStudio.FSharp.LanguageService open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.Range -[][] -type ProjectDiagnosticAnalyzerTests() = +//[][] +//type ProjectDiagnosticAnalyzerTests() = - let CreateProjectAndGetOptions(fileContents: string) = - let tempName = Path.GetTempFileName() - let fileName = Path.ChangeExtension(tempName, ".fs") - let projectName = Path.ChangeExtension(tempName, ".fsproj") - let dllName = Path.ChangeExtension(tempName, ".dll") - File.WriteAllText(fileName, fileContents) +// let CreateProjectAndGetOptions(fileContents: string) = +// let tempName = Path.GetTempFileName() +// let fileName = Path.ChangeExtension(tempName, ".fs") +// let projectName = Path.ChangeExtension(tempName, ".fsproj") +// let dllName = Path.ChangeExtension(tempName, ".dll") +// File.WriteAllText(fileName, fileContents) - let args = mkProjectCommandLineArgs (dllName, [fileName]) - checker.GetProjectOptionsFromCommandLineArgs (projectName, args) +// let args = mkProjectCommandLineArgs (dllName, [fileName]) +// checker.GetProjectOptionsFromCommandLineArgs (projectName, args) diff --git a/vsintegration/tests/UnitTests/UnusedOpensTests.fs b/vsintegration/tests/UnitTests/UnusedOpensTests.fs index 097e583fe6a..28738bfe1cb 100644 --- a/vsintegration/tests/UnitTests/UnusedOpensTests.fs +++ b/vsintegration/tests/UnitTests/UnusedOpensTests.fs @@ -6,7 +6,10 @@ open System open NUnit.Framework open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.Range -open FsUnit + + +/// like "should equal", but validates same-type +let shouldEqual (x: 'a) (y: 'a) = Assert.AreEqual(x, y, sprintf "Expected: %A\nActual: %A" x y) let private filePath = "C:\\test.fs" From f53a18b3c8de717c13a7ab82dde060e33c3d461f Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 24 Jul 2020 23:26:49 +0100 Subject: [PATCH 71/87] fix missing error message --- src/fsharp/TypeChecker.fs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 0037282543a..92e455ddea4 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7240,13 +7240,25 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: SynInterpolatedS |> String.concat "" // Parse the format string to work out the phantom types and check for absence of '%' specifiers in FormattableString + // + // If FormatStringCheckContext is set (i.e. we are doing foreground checking in the IDE) + // then we check the string twice, once to collect % positions and once to get errors. + // The process of getting % positions doesn't process the string in a semantically accurate way + // (but is enough to report % locations correctly), as it fetched the pieces from the + // original source and this may include some additional characters, + // and also doesn't raise all necessary errors + match cenv.tcSink.CurrentSink with + | Some sink when sink.FormatStringCheckContext.IsSome -> + try + CheckFormatStrings.ParseFormatString m stringFragmentRanges g true isFormattableString sink.FormatStringCheckContext printfFormatString printerArgTy printerResidueTy printerResultTy + |> ignore + with _err-> + () + | _ -> () + let argTys, _printerTy, printerTupleTyRequired, percentATys, specifierLocations, dotnetFormatString = - let formatStringCheckContext = - match cenv.tcSink.CurrentSink with - | None -> None - | Some sink -> sink.FormatStringCheckContext try - CheckFormatStrings.ParseFormatString m stringFragmentRanges g true isFormattableString formatStringCheckContext printfFormatString printerArgTy printerResidueTy printerResultTy + CheckFormatStrings.ParseFormatString m stringFragmentRanges g true isFormattableString None printfFormatString printerArgTy printerResidueTy printerResultTy with Failure errString -> error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) From e3d1a74c9e856d15ef417ac76e7391728b594ee2 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Sat, 25 Jul 2020 12:39:05 +0100 Subject: [PATCH 72/87] fix % specifiers for interpolated strings --- src/fsharp/CheckFormatStrings.fs | 106 +++++++++++++++++++------------ src/fsharp/TypeChecker.fs | 14 ++-- tests/service/EditorTests.fs | 2 +- 3 files changed, 71 insertions(+), 51 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index 077d2240a97..e267786a665 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -52,7 +52,7 @@ let newInfo () = let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) isInterpolated isFormattableString (context: FormatStringCheckContext option) fmt printerArgTy printerResidueTy = // As background: the F# compiler tokenizes strings on the assumption that the only thing you need from - // them is the actual corresponding text, e.g. of a string literal. This means many different textual strings + // them is the actual corresponding text, e.g. of a string literal. This means many different textual input strings // in the input file correspond to the 'fmt' string we have here. // // The problem with this is that when we go to colorize the format specifiers in string, we need to do @@ -70,10 +70,14 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) // $"abc %d{" // "} def %s{" // "} xyz" - // In this case we are given the range of each fragment. + // In this case we are given the range of each fragment. One annoying thing is that we must lop off the + // quotations, $, {, } symbols off the end of each string fragment. This information should probably + // be given to us by the lexer. // - // One annoying thing is that we must lop off the quotations, $, {, } symbols off the end of each string fragment. - // This information should probably be given to us by the lexer. + // Note this also means that when compiling (command-line or background IncrementalBuilder in the IDE + // there are no accurate intra-string ranges available for exact error message locations within the string. + // The 'm' range passed as an input is however accurate and covers the whole string. + /// let fmt, fragments = //printfn "--------------------" //printfn "context.IsSome = %b" context.IsSome @@ -94,7 +98,6 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) //printfn "m.EndLine = %d" m.EndLine if m.StartLine - 1 < lineStartPositions.Length && m.EndLine - 1 < lineStartPositions.Length then let startIndex = lineStartPositions.[m.StartLine-1] + m.StartColumn - //let startIndex2 = if m.StartLine < lineStartPositions.Length then lineStartPositions.[m.StartLine] else startIndex let endIndex = lineStartPositions.[m.EndLine-1] + m.EndColumn // Note, some extra """ text may be included at end of these snippets, meaning CheckFormatString in the IDE // may be using a slightly false format string to colorize the %d markers. This doesn't matter as there @@ -107,25 +110,42 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let endIndexFlex = endNextLineIndex - endIndex let mLength = endIndex - startIndex - //let sourceLineFromOffset = sourceText.GetSubTextString(startIndex, (startIndex2 - startIndex)) + let startIndex2 = if m.StartLine < lineStartPositions.Length then lineStartPositions.[m.StartLine] else startIndex + let sourceLineFromOffset = sourceText.GetSubTextString(startIndex, (startIndex2 - startIndex)) //printfn "i = %d, mLength = %d, endIndexFlex = %d, sourceLineFromOffset = <<<%s>>>" i mLength endIndexFlex sourceLineFromOffset if isInterpolated && i=0 && startIndex < length-4 && sourceText.SubTextEquals("$\"\"\"", startIndex) then - (4, sourceText.GetSubTextString(startIndex + 4, mLength - 4 - min endIndexFlex (if i = numFrags-1 then 3 else 1)), m) + // Take of the ending triple quote or '{' + let fragLength = mLength - 4 - min endIndexFlex (if i = numFrags-1 then 3 else 1) + (4, sourceText.GetSubTextString(startIndex + 4, fragLength), m) elif not isInterpolated && i=0 && startIndex < length-3 && sourceText.SubTextEquals("\"\"\"", startIndex) then - (3, sourceText.GetSubTextString(startIndex + 3, mLength - min endIndexFlex (if i = numFrags-1 then 3 else 1)), m) + // Take of the ending triple quote or '{' + let fragLength = mLength - 2 - min endIndexFlex (if i = numFrags-1 then 3 else 1) + (3, sourceText.GetSubTextString(startIndex + 3, fragLength), m) elif isInterpolated && i=0 && startIndex < length-3 && sourceText.SubTextEquals("$@\"", startIndex) then - (3, sourceText.GetSubTextString(startIndex + 3, mLength - 3 - min endIndexFlex 1), m) + // Take of the ending quote or '{', always length 1 + let fragLength = mLength - 3 - min endIndexFlex 1 + (3, sourceText.GetSubTextString(startIndex + 3, fragLength), m) elif isInterpolated && i=0 && startIndex < length-3 && sourceText.SubTextEquals("@$\"", startIndex) then - (3, sourceText.GetSubTextString(startIndex + 3, mLength - 3 - min endIndexFlex 1), m) + // Take of the ending quote or '{', always length 1 + let fragLength = mLength - 3 - min endIndexFlex 1 + (3, sourceText.GetSubTextString(startIndex + 3, fragLength), m) elif not isInterpolated && i=0 && startIndex < length-2 && sourceText.SubTextEquals("@\"", startIndex) then - (2, sourceText.GetSubTextString(startIndex + 2, mLength - 2 - min endIndexFlex 1), m) + // Take of the ending quote or '{', always length 1 + let fragLength = mLength - 2 - min endIndexFlex 1 + (2, sourceText.GetSubTextString(startIndex + 2, fragLength), m) elif isInterpolated && i=0 && startIndex < length-2 && sourceText.SubTextEquals("$\"", startIndex) then - (2, sourceText.GetSubTextString(startIndex + 2, mLength - 2 - min endIndexFlex 1), m) + // Take of the ending quote or '{', always length 1 + let fragLength = mLength - 2 - min endIndexFlex 1 + (2, sourceText.GetSubTextString(startIndex + 2, fragLength), m) elif isInterpolated && i <> 0 && startIndex < length-1 && sourceText.SubTextEquals("}", startIndex) then - (1, sourceText.GetSubTextString(startIndex + 1, mLength - 1 - min endIndexFlex 1), m) + // Take of the ending quote or '{', always length 1 + let fragLength = mLength - 1 - min endIndexFlex 1 + (1, sourceText.GetSubTextString(startIndex + 1, fragLength), m) else - (1, sourceText.GetSubTextString(startIndex + 1, mLength - 1 - min endIndexFlex 1), m) + // Take of the ending quote or '{', always length 1 + let fragLength = mLength - 1 - min endIndexFlex 1 + (1, sourceText.GetSubTextString(startIndex + 1, fragLength), m) else (1, fmt, m) ] //printfn "fmts = %A" fmts @@ -161,17 +181,18 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) // fragLine, fragCol - track our location w.r.t. the marker for the start of this chunk // - let rec parseLoop acc (i, fragLine, fragCol) fragments (m: range) = + let rec parseLoop acc (i, fragLine, fragCol) fragments = // Check if we've moved into the next fragment. Note this will always activate on // the first step, i.e. when i=0 - let (struct (fragLine, fragCol, fragments, m)) = + let (struct (fragLine, fragCol, fragments)) = match fragments with | (idx, fragOffset, fragRange: range)::rest when i >= idx -> //printfn "i = %d, idx = %d, moving into next fragment at %A plus fragOffset %d" i idx fragRange fragOffset - struct (fragRange.StartLine, fragRange.StartColumn + fragOffset, rest, fragRange) + struct (fragRange.StartLine, fragRange.StartColumn + fragOffset, rest) - | _ -> struct (fragLine, fragCol, fragments, m) + | _ -> struct (fragLine, fragCol, fragments) + //printfn "parseLoop: i = %d, fragLine = %d, fragCol = %d" i fragLine fragCol if i >= len then let argtys = @@ -182,7 +203,7 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) argtys elif System.Char.IsSurrogatePair(fmt,i) then appendToDotnetFormatString (fmt.[i..i+1]) - parseLoop acc (i+2, fragLine, fragCol+2) fragments m + parseLoop acc (i+2, fragLine, fragCol+2) fragments else let c = fmt.[i] match c with @@ -327,25 +348,28 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) failwithf "%s" <| FSComp.SR.forFormatInvalidForInterpolated3() let collectSpecifierLocation fragLine fragCol numStdArgs = - let numArgsForSpecifier = - numStdArgs + (if widthArg then 1 else 0) + (if precisionArg then 1 else 0) - specifierLocations.Add( - (Range.mkFileIndexRange m.FileIndex - (Range.mkPos fragLine startFragCol) - (Range.mkPos fragLine (fragCol + 1))), numArgsForSpecifier) + match context with + | Some _ -> + let numArgsForSpecifier = + numStdArgs + (if widthArg then 1 else 0) + (if precisionArg then 1 else 0) + specifierLocations.Add( + (Range.mkFileIndexRange m.FileIndex + (Range.mkPos fragLine startFragCol) + (Range.mkPos fragLine (fragCol + 1))), numArgsForSpecifier) + | None -> () let ch = fmt.[i] match ch with | '%' -> collectSpecifierLocation fragLine fragCol 0 appendToDotnetFormatString "%" - parseLoop acc (i+1, fragLine, fragCol+1) fragments m + parseLoop acc (i+1, fragLine, fragCol+1) fragments | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()) collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments | ('l' | 'L') -> if info.precision then failwithf "%s" <| FSComp.SR.forFormatDoesntSupportPrecision(ch.ToString()) @@ -361,7 +385,7 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) | ('d' | 'i' | 'o' | 'u' | 'x' | 'X') -> collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, mkFlexibleIntFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments | _ -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifier() | ('h' | 'H') -> @@ -370,43 +394,43 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) | 'M' -> collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, mkFlexibleDecimalFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, mkFlexibleDecimalFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments | ('f' | 'F' | 'e' | 'E' | 'g' | 'G') -> collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, mkFlexibleFloatFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, mkFlexibleFloatFormatTypar g m) :: acc) (i, fragLine, fragCol+1) fragments | 'b' -> checkOtherFlags ch collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, g.bool_ty) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, g.bool_ty) :: acc) (i, fragLine, fragCol+1) fragments | 'c' -> checkOtherFlags ch collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, g.char_ty) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, g.char_ty) :: acc) (i, fragLine, fragCol+1) fragments | 's' -> checkOtherFlags ch collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, g.string_ty) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, g.string_ty) :: acc) (i, fragLine, fragCol+1) fragments | 'O' -> checkOtherFlags ch collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, NewInferenceType ()) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, NewInferenceType ()) :: acc) (i, fragLine, fragCol+1) fragments // residue of hole "...{n}..." in interpolated strings become %P(...) | 'P' when isInterpolated -> checkOtherFlags ch let i = requireAndSkipInterpolationHoleFormat (i+1) // Note, the fragCol doesn't advance at all as these are magically inserted. - parseLoop ((posi, NewInferenceType ()) :: acc) (i, fragLine, startFragCol) fragments m + parseLoop ((posi, NewInferenceType ()) :: acc) (i, fragLine, startFragCol) fragments | 'A' -> match info.numPrefixIfPos with @@ -416,7 +440,7 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let i = skipPossibleInterpolationHole (i+1) let xty = NewInferenceType () percentATys.Add(xty) - parseLoop ((posi, xty) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, xty) :: acc) (i, fragLine, fragCol+1) fragments | Some n -> failwithf "%s" <| FSComp.SR.forDoesNotSupportPrefixFlag(ch.ToString(), n.ToString()) | 'a' -> @@ -425,24 +449,24 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let fty = printerArgTy --> (xty --> printerResidueTy) collectSpecifierLocation fragLine fragCol 2 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((Option.map ((+)1) posi, xty) :: (posi, fty) :: acc) (i, fragLine, fragCol+1) fragments | 't' -> checkOtherFlags ch collectSpecifierLocation fragLine fragCol 1 let i = skipPossibleInterpolationHole (i+1) - parseLoop ((posi, printerArgTy --> printerResidueTy) :: acc) (i, fragLine, fragCol+1) fragments m + parseLoop ((posi, printerArgTy --> printerResidueTy) :: acc) (i, fragLine, fragCol+1) fragments | c -> failwithf "%s" <| FSComp.SR.forBadFormatSpecifierGeneral(String.make 1 c) | '\n' -> appendToDotnetFormatString fmt.[i..i] - parseLoop acc (i+1, fragLine+1, 0) fragments m + parseLoop acc (i+1, fragLine+1, 0) fragments | _ -> appendToDotnetFormatString fmt.[i..i] - parseLoop acc (i+1, fragLine, fragCol+1) fragments m + parseLoop acc (i+1, fragLine, fragCol+1) fragments - let results = parseLoop [] (0, 0, m.StartColumn) fragments m + let results = parseLoop [] (0, 0, m.StartColumn) fragments results, Seq.toList specifierLocations, dotnetFormatString.ToString(), percentATys.ToArray() let ParseFormatString m ms g isInterpolated isFormattableString formatStringCheckContext fmt printerArgTy printerResidueTy printerResultTy = diff --git a/src/fsharp/TypeChecker.fs b/src/fsharp/TypeChecker.fs index 92e455ddea4..e9096b62c81 100755 --- a/src/fsharp/TypeChecker.fs +++ b/src/fsharp/TypeChecker.fs @@ -7250,24 +7250,20 @@ and TcInterpolatedStringExpr cenv overallTy env m tpenv (parts: SynInterpolatedS match cenv.tcSink.CurrentSink with | Some sink when sink.FormatStringCheckContext.IsSome -> try - CheckFormatStrings.ParseFormatString m stringFragmentRanges g true isFormattableString sink.FormatStringCheckContext printfFormatString printerArgTy printerResidueTy printerResultTy - |> ignore + let _argTys, _printerTy, _printerTupleTyRequired, _percentATys, specifierLocations, _dotnetFormatString = + CheckFormatStrings.ParseFormatString m stringFragmentRanges g true isFormattableString sink.FormatStringCheckContext printfFormatString printerArgTy printerResidueTy printerResultTy + for specifierLocation, numArgs in specifierLocations do + sink.NotifyFormatSpecifierLocation(specifierLocation, numArgs) with _err-> () | _ -> () - let argTys, _printerTy, printerTupleTyRequired, percentATys, specifierLocations, dotnetFormatString = + let argTys, _printerTy, printerTupleTyRequired, percentATys, _specifierLocations, dotnetFormatString = try CheckFormatStrings.ParseFormatString m stringFragmentRanges g true isFormattableString None printfFormatString printerArgTy printerResidueTy printerResultTy with Failure errString -> error (Error(FSComp.SR.tcUnableToParseInterpolatedString errString, m)) - match cenv.tcSink.CurrentSink with - | None -> () - | Some sink -> - for specifierLocation, numArgs in specifierLocations do - sink.NotifyFormatSpecifierLocation(specifierLocation, numArgs) - // Check the expressions filling the holes if argTys.Length <> synFillExprs.Length then error (Error(FSComp.SR.tcInterpolationMixedWithPercent(), m)) diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index d06a331e105..08e1d0e9b89 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -1448,7 +1448,7 @@ let x5 = $"abc{1}def" ((3, 9, 3, 11), (3, 25, 3, 27)); ((4, 9, 4, 10), (4, 13, 4, 14)); ((5, 9, 5, 11), (5, 14, 5, 16)); - ((6, 8, 6, 15), (6, 16, 6, 17))|] + ((6, 14, 6, 15), (6, 16, 6, 17))|] [] From 4b898cf5607d7c9a68872cb77b33d406e312883c Mon Sep 17 00:00:00 2001 From: Don Syme Date: Sat, 25 Jul 2020 12:39:16 +0100 Subject: [PATCH 73/87] fix % specifiers for interpolated strings --- src/fsharp/CheckFormatStrings.fs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/fsharp/CheckFormatStrings.fs b/src/fsharp/CheckFormatStrings.fs index e267786a665..8ef9d1a68c8 100644 --- a/src/fsharp/CheckFormatStrings.fs +++ b/src/fsharp/CheckFormatStrings.fs @@ -79,11 +79,13 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) // The 'm' range passed as an input is however accurate and covers the whole string. /// let fmt, fragments = + //printfn "--------------------" //printfn "context.IsSome = %b" context.IsSome //printfn "fmt = <<<%s>>>" fmt //printfn "isInterpolated = %b" isInterpolated //printfn "fragRanges = %A" fragRanges + match context with | Some context when fragRanges.Length > 0 -> let sourceText = context.SourceText @@ -110,8 +112,8 @@ let parseFormatStringInternal (m: range) (fragRanges: range list) (g: TcGlobals) let endIndexFlex = endNextLineIndex - endIndex let mLength = endIndex - startIndex - let startIndex2 = if m.StartLine < lineStartPositions.Length then lineStartPositions.[m.StartLine] else startIndex - let sourceLineFromOffset = sourceText.GetSubTextString(startIndex, (startIndex2 - startIndex)) + //let startIndex2 = if m.StartLine < lineStartPositions.Length then lineStartPositions.[m.StartLine] else startIndex + //let sourceLineFromOffset = sourceText.GetSubTextString(startIndex, (startIndex2 - startIndex)) //printfn "i = %d, mLength = %d, endIndexFlex = %d, sourceLineFromOffset = <<<%s>>>" i mLength endIndexFlex sourceLineFromOffset if isInterpolated && i=0 && startIndex < length-4 && sourceText.SubTextEquals("$\"\"\"", startIndex) then From 22535def2d6c2a327ad65479597c936bd9bdccc3 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Sat, 25 Jul 2020 14:26:41 +0100 Subject: [PATCH 74/87] fix FCS tests --- .../FSharp.Compiler.UnitTests.fsproj | 1 + tests/service/EditorTests.fs | 47 ++++++++++--------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj index 25093aa8885..27f0a5f4cf1 100644 --- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj +++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj @@ -7,6 +7,7 @@ netcoreapp3.1 Library true + $(DefineConstants);ASSUME_PREVIEW_FSHARP_CORE xunit $(NoWarn);3186;1104 diff --git a/tests/service/EditorTests.fs b/tests/service/EditorTests.fs index 08e1d0e9b89..6c50d1e8906 100644 --- a/tests/service/EditorTests.fs +++ b/tests/service/EditorTests.fs @@ -523,6 +523,29 @@ let _ = List.iter(printfn \"\"\"%-A (7, 29, 7, 31, 1); (7, 33, 7, 35,1 )|] +[] +let ``Printf specifiers for user-defined functions`` () = + let input = + """ +let debug msg = Printf.kprintf System.Diagnostics.Debug.WriteLine msg +let _ = debug "Message: %i - %O" 1 "Ok" +let _ = debug "[LanguageService] Type checking fails for '%s' with content=%A and %A.\nResulting exception: %A" "1" "2" "3" "4" +""" + + let file = "/home/user/Test.fsx" + let parseResult, typeCheckResults = parseAndCheckScript(file, input) + + typeCheckResults.Errors |> shouldEqual [||] + typeCheckResults.GetFormatSpecifierLocationsAndArity() + |> Array.map (fun (range, numArgs) -> range.StartLine, range.StartColumn, range.EndLine, range.EndColumn, numArgs) + |> shouldEqual [|(3, 24, 3, 26, 1); + (3, 29, 3, 31, 1); + (4, 58, 4, 60, 1); + (4, 75, 4, 77, 1); + (4, 82, 4, 84, 1); + (4, 108, 4, 110, 1)|] + +#if ASSUME_PREVIEW_FSHARP_CORE [] let ``Printf specifiers for regular and verbatim interpolated strings`` () = let input = @@ -562,28 +585,6 @@ let s3 = $"abc %d{s.Length} (17, 18, 17, 22, 1); (18, 10, 18, 12, 0); (19, 15, 19, 17, 1); (19, 32, 19, 34, 1); (20, 15, 20, 17, 1); (21, 20, 21, 22, 1)|] -[] -let ``Printf specifiers for user-defined functions`` () = - let input = - """ -let debug msg = Printf.kprintf System.Diagnostics.Debug.WriteLine msg -let _ = debug "Message: %i - %O" 1 "Ok" -let _ = debug "[LanguageService] Type checking fails for '%s' with content=%A and %A.\nResulting exception: %A" "1" "2" "3" "4" -""" - - let file = "/home/user/Test.fsx" - let parseResult, typeCheckResults = parseAndCheckScript(file, input) - - typeCheckResults.Errors |> shouldEqual [||] - typeCheckResults.GetFormatSpecifierLocationsAndArity() - |> Array.map (fun (range, numArgs) -> range.StartLine, range.StartColumn, range.EndLine, range.EndColumn, numArgs) - |> shouldEqual [|(3, 24, 3, 26, 1); - (3, 29, 3, 31, 1); - (4, 58, 4, 60, 1); - (4, 75, 4, 77, 1); - (4, 82, 4, 84, 1); - (4, 108, 4, 110, 1)|] - [] let ``Printf specifiers for triple quote interpolated strings`` () = let input = @@ -597,6 +598,8 @@ let ``Printf specifiers for triple quote interpolated strings`` () = |> Array.map (fun (range,numArgs) -> range.StartLine, range.StartColumn, range.EndLine, range.EndColumn, numArgs) |> shouldEqual [|(1, 16, 1, 18, 1); (1, 26, 1, 28, 1)|] +#endif // ASSUME_PREVIEW_FSHARP_CORE + [] let ``should not report format specifiers for illformed format strings`` () = From f32e77ff3941c28f75a63f8fbb8731448a6f1a83 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Mon, 27 Jul 2020 13:25:10 +0100 Subject: [PATCH 75/87] minor nits from code review --- src/fsharp/ParseHelpers.fs | 2 +- .../ProjectDiagnosticAnalyzerTests.fs | 34 ------------------- .../UnitTests/VisualFSharp.UnitTests.fsproj | 3 -- 3 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 vsintegration/tests/UnitTests/ProjectDiagnosticAnalyzerTests.fs diff --git a/src/fsharp/ParseHelpers.fs b/src/fsharp/ParseHelpers.fs index 815c08070c6..33e7872b186 100644 --- a/src/fsharp/ParseHelpers.fs +++ b/src/fsharp/ParseHelpers.fs @@ -150,7 +150,7 @@ type LexerStringStyle = | TripleQuote | SingleQuote -[] +[] type LexerStringKind = { IsByteString: bool IsInterpolated: bool diff --git a/vsintegration/tests/UnitTests/ProjectDiagnosticAnalyzerTests.fs b/vsintegration/tests/UnitTests/ProjectDiagnosticAnalyzerTests.fs deleted file mode 100644 index 476479e9c4d..00000000000 --- a/vsintegration/tests/UnitTests/ProjectDiagnosticAnalyzerTests.fs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace Microsoft.VisualStudio.FSharp.Editor.Tests.Roslyn - -open System -open System.IO -open System.Threading - -//open FSharp.Compiler.Service.Tests.Common - -open NUnit.Framework - -open Microsoft.CodeAnalysis -open Microsoft.CodeAnalysis.Classification -open Microsoft.CodeAnalysis.Editor -open Microsoft.CodeAnalysis.Text - -open Microsoft.VisualStudio.FSharp.Editor -open Microsoft.VisualStudio.FSharp.LanguageService - -open FSharp.Compiler.SourceCodeServices -open FSharp.Compiler.Range - -//[][] -//type ProjectDiagnosticAnalyzerTests() = - -// let CreateProjectAndGetOptions(fileContents: string) = -// let tempName = Path.GetTempFileName() -// let fileName = Path.ChangeExtension(tempName, ".fs") -// let projectName = Path.ChangeExtension(tempName, ".fsproj") -// let dllName = Path.ChangeExtension(tempName, ".dll") -// File.WriteAllText(fileName, fileContents) - -// let args = mkProjectCommandLineArgs (dllName, [fileName]) -// checker.GetProjectOptionsFromCommandLineArgs (projectName, args) diff --git a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj index 6803ce9cb49..bb3f36c8ff3 100644 --- a/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj +++ b/vsintegration/tests/UnitTests/VisualFSharp.UnitTests.fsproj @@ -91,9 +91,6 @@ Roslyn\DocumentDiagnosticAnalyzerTests.fs - - Roslyn\ProjectDiagnosticAnalyzerTests.fs - Roslyn\CompletionProviderTests.fs From f002be9c5df3af36255b10f3a54a1bc2f9ef480d Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 28 Jul 2020 13:02:15 +0100 Subject: [PATCH 76/87] code review feedback and use struct tuples in more places --- src/fsharp/FSharp.Core/printf.fs | 261 +++++++++++++++++-------------- 1 file changed, 142 insertions(+), 119 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 064cbc77c45..6fdd7f3ac0e 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -3,6 +3,9 @@ namespace Microsoft.FSharp.Core open System +open System.IO +open System.Text + open System.Collections.Concurrent open System.Globalization open System.Reflection @@ -35,6 +38,7 @@ type Format<'Printer, 'State, 'Residue, 'Result> = PrintfFormat<'Printer, 'State type Format<'Printer, 'State, 'Residue, 'Result, 'Tuple> = PrintfFormat<'Printer, 'State, 'Residue, 'Result, 'Tuple> +[] module internal PrintfImpl = /// Basic idea of implementation: @@ -88,7 +92,7 @@ module internal PrintfImpl = Precision: int Width: int Flags: FormatFlags - InteropHoleDotNetFormat: string option + InteropHoleDotNetFormat: string voption } member spec.IsStarPrecision = (spec.Precision = StarValue) @@ -108,7 +112,7 @@ module internal PrintfImpl = let n = if spec.TypeChar = '%' then n - 1 else n - System.Diagnostics.Debug.Assert(n <> 0, "n <> 0") + assert (n <> 0) n @@ -146,7 +150,7 @@ module internal PrintfImpl = if Char.IsDigit s.[i] then let n = int s.[i] - int '0' go (acc * 10 + n) (i + 1) - else acc, i + else struct (acc, i) go 0 pos let parseFlags (s: string) i = @@ -156,39 +160,39 @@ module internal PrintfImpl = | '+' -> go (flags ||| FormatFlags.PlusForPositives) (i + 1) | ' ' -> go (flags ||| FormatFlags.SpaceForPositives) (i + 1) | '-' -> go (flags ||| FormatFlags.LeftJustify) (i + 1) - | _ -> flags, i + | _ -> struct (flags, i) go FormatFlags.None i let parseWidth (s: string) i = - if s.[i] = '*' then StarValue, (i + 1) + if s.[i] = '*' then struct (StarValue, i + 1) elif Char.IsDigit s.[i] then intFromString s i - else NotSpecifiedValue, i + else struct (NotSpecifiedValue, i) let parsePrecision (s: string) i = if s.[i] = '.' then - if s.[i + 1] = '*' then StarValue, i + 2 + if s.[i + 1] = '*' then struct (StarValue, i + 2) elif Char.IsDigit s.[i + 1] then intFromString s (i + 1) else raise (ArgumentException("invalid precision value")) - else NotSpecifiedValue, i + else struct (NotSpecifiedValue, i) let parseTypeChar (s: string) i = - s.[i], (i + 1) + struct (s.[i], i + 1) let parseInterpolatedHoleDotNetFormat typeChar (s: string) i = if typeChar = 'P' then if i < s.Length && s.[i] = '(' then let i2 = s.IndexOf(")", i) if i2 = -1 then - None, i + struct (ValueNone, i) else - Some s.[i+1..i2-1], i2+1 + struct (ValueSome s.[i+1..i2-1], i2+1) else - None, i + struct (ValueNone, i) else - None, i + struct (ValueNone, i) // Skip %P() added for hole in "...%d{x}..." - let skipInterpolationHole (fmt:string) i = + let skipInterpolationHole (fmt: string) i = if i+1 < fmt.Length && fmt.[i] = '%' && fmt.[i+1] = 'P' then let i = i + 2 if i+1 < fmt.Length && fmt.[i] = '(' && fmt.[i+1] = ')' then @@ -200,15 +204,15 @@ module internal PrintfImpl = let findNextFormatSpecifier (s: string) i = let rec go i (buf: Text.StringBuilder) = if i >= s.Length then - s.Length, buf.ToString() + struct (s.Length, buf.ToString()) else let c = s.[i] if c = '%' then if i + 1 < s.Length then - let _, i1 = parseFlags s (i + 1) - let w, i2 = parseWidth s i1 - let p, i3 = parsePrecision s i2 - let typeChar, i4 = parseTypeChar s i3 + let struct (_, i1) = parseFlags s (i + 1) + let struct (w, i2) = parseWidth s i1 + let struct (p, i3) = parsePrecision s i2 + let struct (typeChar, i4) = parseTypeChar s i3 // shortcut for the simpliest case // if typeChar is not % or it has star as width\precision - resort to long path @@ -237,7 +241,8 @@ module internal PrintfImpl = | StepStar2 of prefix: string * conv: (obj -> int -> int -> string) | StepPercentStar2 of prefix: string - static member BlockCount(steps) = + // Count the number of string fragments in a sequence of steps + static member BlockCount(steps: Step[]) = let mutable count = 0 for step in steps do match step with @@ -334,7 +339,6 @@ module internal PrintfImpl = env.Write (conv arg1 star1) | StepPercentStar1(prefix) -> - //let _star1 = args.[argIndex] :?> int argIndex <- argIndex + 1 env.WriteSkipEmpty prefix env.Write("%") @@ -351,7 +355,6 @@ module internal PrintfImpl = | StepPercentStar2(prefix) -> env.WriteSkipEmpty prefix - //let _star1 = args.[argIndex] :?> int argIndex <- argIndex + 2 env.Write("%") @@ -364,51 +367,63 @@ module internal PrintfImpl = /// /// After all arguments are collected, specialization obtains concrete PrintfEnv from the thunk /// and uses it to output collected data. - type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> (obj list * PrintfEnv<'State, 'Residue, 'Result>) + /// + /// Note the arguments must be captured in an *immutable* collection. For example consider + /// let f1 = printf "%d%d%d" 3 // activation captures '3' (args --> [3]) + /// let f2 = f1 4 // same activation captures 4 (args --> [3;4]) + /// let f3 = f1 5 // same activation captures 5 (args --> [3;5]) + /// f2 7 // same activation captures 7 (args --> [3;4;7]) + /// f3 8 // same activation captures 8 (args --> [3;5;8]) + /// + /// If we captured into an mutable array then these would interfere + type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> struct (obj list * PrintfEnv<'State, 'Residue, 'Result>) type PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result> = PrintfFuncContext<'State, 'Residue, 'Result> -> 'Printer [] let MaxArgumentsInSpecialization = 3 + let revToArray (args: 'T list) = + // We've reached the end, now fill in the array, reversing steps, avoiding reallocating + let n = args.Length + let res = Array.zeroCreate n + let mutable j = 0 + for arg in args do + res.[n-j-1] <- arg + j <- j + 1 + res + type Specializations<'State, 'Residue, 'Result>() = - static let finalizeSteps steps = steps |> List.rev |> List.toArray - static let finalizeArgs args = args |> List.rev |> List.toArray - - static member Final0(steps) = - let allSteps = finalizeSteps steps + static member Final0(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> - let (args, env) = prev() - env.RunSteps(finalizeArgs args, null, allSteps) + let (struct (args, env)) = prev() + env.RunSteps(revToArray args, null, allSteps) ) - static member CaptureFinal1<'A>(steps) = - let allSteps = finalizeSteps steps + static member CaptureFinal1<'A>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> - let (args, env) = prev() + let (struct (args, env)) = prev() let finalArgs = box arg1 :: args - env.RunSteps(finalizeArgs finalArgs, null, allSteps) + env.RunSteps(revToArray finalArgs, null, allSteps) ) ) - static member CaptureFinal2<'A, 'B>(steps) = - let allSteps = finalizeSteps steps + static member CaptureFinal2<'A, 'B>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) -> - let (args, env) = prev() + let (struct (args, env)) = prev() let finalArgs = box arg2 :: box arg1 :: args - env.RunSteps(finalizeArgs finalArgs, null, allSteps) + env.RunSteps(revToArray finalArgs, null, allSteps) ) ) - static member CaptureFinal3<'A, 'B, 'C>(steps) = - let allSteps = finalizeSteps steps + static member CaptureFinal3<'A, 'B, 'C>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> - let (args, env) = prev() + let (struct (args, env)) = prev() let finalArgs = box arg3 :: box arg2 :: box arg1 :: args - env.RunSteps(finalizeArgs finalArgs, null, allSteps) + env.RunSteps(revToArray finalArgs, null, allSteps) ) ) @@ -416,8 +431,8 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> let curr() = - let (args, env) = prev() - (box arg1 :: args), env + let (struct (args, env)) = prev() + struct ((box arg1 :: args), env) next curr : 'Tail ) ) @@ -426,8 +441,8 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'A -> 'Residue) (arg1: 'A) -> let curr() = - let (args, env) = prev() - (box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args), env + let (struct (args, env)) = prev() + struct ((box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args), env) next curr : 'Tail ) ) @@ -436,8 +451,8 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) -> let curr() = - let (args, env) = prev() - (box arg2 :: box arg1 :: args), env + let (struct (args, env)) = prev() + struct ((box arg2 :: box arg1 :: args), env) next curr : 'Tail ) ) @@ -446,8 +461,8 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> let curr() = - let (args, env) = prev() - (box arg3 :: box arg2 :: box arg1 :: args), env + let (struct (args, env)) = prev() + struct ((box arg3 :: box arg2 :: box arg1 :: args), env) next curr : 'Tail ) ) @@ -831,8 +846,8 @@ module internal PrintfImpl = static member InterpolandToString(spec: FormatSpecifier) : ValueConverter = let fmt = match spec.InteropHoleDotNetFormat with - | None -> null - | Some fmt -> "{0:" + fmt + "}" + | ValueNone -> null + | ValueSome fmt -> "{0:" + fmt + "}" Basic.withPadding spec (fun (vobj: obj) -> match vobj with | null -> "" @@ -935,7 +950,7 @@ module internal PrintfImpl = | _ -> failwith (String.Format("Expected function with {0} arguments", n)) else System.Diagnostics.Debug.Assert((i = n), "i = n") - buf, ty + struct (buf, ty) go ty 0 let MAX_CAPTURE = 3 @@ -1030,11 +1045,11 @@ module internal PrintfImpl = StepWithArg (prefix, convFunc) let parseSpec i = - let flags, i = FormatString.parseFlags fmt (i + 1) - let width, i = FormatString.parseWidth fmt i - let precision, i = FormatString.parsePrecision fmt i - let typeChar, i = FormatString.parseTypeChar fmt i - let interpHoleDotnetFormat, i = FormatString.parseInterpolatedHoleDotNetFormat typeChar fmt i + let struct (flags, i) = FormatString.parseFlags fmt (i + 1) + let struct (width, i) = FormatString.parseWidth fmt i + let struct (precision, i) = FormatString.parsePrecision fmt i + let struct (typeChar, i) = FormatString.parseTypeChar fmt i + let struct (interpHoleDotnetFormat, i) = FormatString.parseInterpolatedHoleDotNetFormat typeChar fmt i // Skip %P insertion points added after %d{...} etc. in interpolated strings let i = FormatString.skipInterpolationHole fmt i @@ -1045,98 +1060,111 @@ module internal PrintfImpl = Flags = flags Width = width InteropHoleDotNetFormat = interpHoleDotnetFormat } - i, spec + struct (i, spec) + // The steps, populated on-demand. This is for the case where the string is being used + // with interpolands captured in the Format object, including the %A capture types. + // + // We may initialize this twice, but the assignment is atomic and the computation will give functionally + // identical results each time, so it is ok. + let mutable stepsForCapturedFormat = Unchecked.defaultof<_> + + // The function factory, populated on-demand, for the case where the string is being used to make a curried function for printf. + // + // We may initialize this twice, but the assignment is atomic and the computation will give functionally + // identical results each time, so it is ok. + let mutable functionFactory = Unchecked.defaultof<_> + + // The function factory, populated on-demand. + // + // We may initialize this twice, but the assignment is atomic and the computation will give functionally + // identical results each time, so it is ok. + let mutable stringCount = 0 + // A simplified parser. For the case where the string is being used with interpolands captured in the Format object. - let rec parseStepsAux steps (prefix: string) i = + let rec parseAndCreateStepsForCapturedFormatAux steps (prefix: string) i = if i >= fmt.Length then let step = StepString(prefix) - (step :: steps) + let steps = step :: steps + let allSteps = revToArray steps + stringCount <- Step.BlockCount allSteps + stepsForCapturedFormat <- allSteps else - let i, spec = parseSpec i - let i, suffix = FormatString.findNextFormatSpecifier fmt i + let struct (i, spec) = parseSpec i + let struct (i, suffix) = FormatString.findNextFormatSpecifier fmt i let step = buildStep spec null prefix - parseStepsAux (step::steps) suffix i + parseAndCreateStepsForCapturedFormatAux (step::steps) suffix i - let parseSteps () = - let i, prefix = FormatString.findNextFormatSpecifier fmt 0 - let steps = parseStepsAux [] prefix i - let count = Step.BlockCount steps - count, steps + let parseAndCreateStepsForCapturedFormat () = + let struct (i, prefix) = FormatString.findNextFormatSpecifier fmt 0 + parseAndCreateStepsForCapturedFormatAux [] prefix i - /// The more advanced parser which both builds the steps (with accurate %A types), + /// The more advanced parser which both builds the steps (with %A types extracted from the funcTy), /// and produces a curried function value of the right type guided by funcTy let rec parseAndCreateFuncFactoryAux steps (prefix: string) (funcTy: Type) i = if i >= fmt.Length then let step = StepString(prefix) - let allSteps = (step :: steps) + let steps = step :: steps + let allSteps = revToArray steps let last = Specializations<'State, 'Residue, 'Result>.Final0(allSteps) - allSteps, (box last, true, [| |], funcTy, None) + stringCount <- Step.BlockCount allSteps + let nextInfo = (box last, true, [| |], funcTy, None) + struct (allSteps, nextInfo) else - System.Diagnostics.Debug.Assert(fmt.[i] = '%', "s.[i] = '%'") - let i, spec = parseSpec i - let i, suffix = FormatString.findNextFormatSpecifier fmt i - let argTys, retTy = extractCurriedArguments funcTy spec.ArgCount + assert (fmt.[i] = '%') + let struct (i, spec) = parseSpec i + let struct (i, suffix) = FormatString.findNextFormatSpecifier fmt i + let n = spec.ArgCount + let struct (argTys, retTy) = extractCurriedArguments funcTy n let step = buildStep spec argTys prefix - let allSteps, nextInfo = parseAndCreateFuncFactoryAux (step::steps) suffix retTy i + let struct (allSteps, nextInfo) = parseAndCreateFuncFactoryAux (step::steps) suffix retTy i let nextInfoNew = buildCaptureFunc (spec, allSteps, argTys, retTy, nextInfo) - allSteps, nextInfoNew + struct (allSteps, nextInfoNew) - let parseAndCreateFuncFactory () = + let parseAndCreateFunctionFactory () = let funcTy = typeof<'Printer> // Find the first format specifier - let prefixPos, prefix = FormatString.findNextFormatSpecifier fmt 0 + let struct (prefixPos, prefix) = FormatString.findNextFormatSpecifier fmt 0 // If there are not format specifiers then take a simple path if prefixPos = fmt.Length then - 0, box (fun (initial: PrintfFuncContext<'State, 'Residue, 'Result>) -> - let (_args, env) = initial() - env.WriteSkipEmpty prefix - env.Finish()) + let factoryObj = + box (fun (initial: PrintfFuncContext<'State, 'Residue, 'Result>) -> + let (struct (_args, env)) = initial() + env.WriteSkipEmpty prefix + env.Finish()) + factoryObj else - let steps, (factoryObj, _, _, _, _) = parseAndCreateFuncFactoryAux [] prefix funcTy prefixPos - let count = Step.BlockCount steps - count, factoryObj - - // The simple steps, populated on-demand, for the case where the string is being used with interpolands captured in the Format object. - let mutable allSteps = Unchecked.defaultof<_> - - // The function factory, populated on-demand - let mutable functionFactory = Unchecked.defaultof<_> - - // The function factory, populated on-demand - let mutable stringCount = 0 + let struct (_, (factoryObj, _, _, _, _)) = parseAndCreateFuncFactoryAux [] prefix funcTy prefixPos + factoryObj /// The format string, used to help identify the cache entry (the cache index types are taken /// into account as well). member _.FormatString = fmt /// The steps involved in executing the format string when interpolands are captured - member _.Steps = - match allSteps with - | null -> - // We may initialize this twice, but the assignment is atomic and the computation will give functionally - // identical results each time it is ok - let count, steps = parseSteps () - stringCount <- count - allSteps <- steps |> List.rev |> List.toArray + /// + /// If %A patterns are involved these steps are only accurate when the %A capture types + /// are given in the format string through interpolation capture. + member _.GetStepsForCapturedFormat() = + match stepsForCapturedFormat with + | null -> parseAndCreateStepsForCapturedFormat () | _ -> () - allSteps + stepsForCapturedFormat /// The number of strings produced for a sprintf member _.BlockCount = stringCount /// The factory function used to generate the result or the resulting function. - member _.FunctionFactory = + member _.GetFunctionFactory() = match box functionFactory with | null -> - let count, funcObj = parseAndCreateFuncFactory () + let funcObj = parseAndCreateFunctionFactory () // We may initialize this twice, but the assignment is atomic and the computation will give functionally // identical results each time it is ok functionFactory <- (funcObj :?> PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result>) - stringCount <- (2 * count + 1) | _ -> () functionFactory @@ -1221,11 +1249,6 @@ module internal PrintfImpl = [] module Printf = - open System - open System.IO - open System.Text - open PrintfImpl - type BuilderFormat<'T, 'Result> = Format<'T, StringBuilder, unit, 'Result> type StringFormat<'T, 'Result> = Format<'T, unit, string, 'Result> type TextWriterFormat<'T, 'Result> = Format<'T, TextWriter, unit, 'Result> @@ -1238,12 +1261,12 @@ module Printf = match format.Captures with | null -> // The ksprintf "...%d ...." arg path, producing a function - let factory = cacheItem.FunctionFactory - let initial() = ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>) + let factory = cacheItem.GetFunctionFactory() + let initial() = (struct ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>)) factory initial | captures -> // The ksprintf $"...%d{3}...." path, running the steps straight away to produce a string - let steps = cacheItem.Steps + let steps = cacheItem.GetStepsForCapturedFormat() let env = envf cacheItem.BlockCount :> PrintfEnv<_,_,_> let res = env.RunSteps(captures, format.CaptureTypes, steps) unbox res // prove 'T = 'Result @@ -1260,12 +1283,12 @@ module Printf = match format.Captures with | null -> // The sprintf "...%d ...." arg path, producing a function - let factory = cacheItem.FunctionFactory - let initial() = ([], StringPrintfEnv cacheItem.BlockCount) + let factory = cacheItem.GetFunctionFactory() + let initial() = (struct ([], StringPrintfEnv cacheItem.BlockCount)) factory initial | captures -> // The sprintf $"...%d{3}...." path, running the steps straight away to produce a string - let steps = cacheItem.Steps + let steps = cacheItem.GetStepsForCapturedFormat() let env = StringPrintfEnv cacheItem.BlockCount let res = env.RunSteps(captures, format.CaptureTypes, steps) unbox res // proves 'T = string From 6e99014d7e8103ceccfd6c2b25eed4a87ceb8d25 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 28 Jul 2020 18:41:05 +0100 Subject: [PATCH 77/87] revert struct tuples --- src/fsharp/FSharp.Core/printf.fs | 107 ++++++++++++++++--------------- 1 file changed, 54 insertions(+), 53 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 6fdd7f3ac0e..7ceb37744da 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -145,13 +145,13 @@ module internal PrintfImpl = /// Set of helpers to parse format string module private FormatString = - let intFromString (s: string) pos = + let intFromString (s: string) i = let rec go acc i = if Char.IsDigit s.[i] then let n = int s.[i] - int '0' go (acc * 10 + n) (i + 1) - else struct (acc, i) - go 0 pos + else (acc, i) + go 0 i let parseFlags (s: string) i = let rec go flags i = @@ -160,36 +160,36 @@ module internal PrintfImpl = | '+' -> go (flags ||| FormatFlags.PlusForPositives) (i + 1) | ' ' -> go (flags ||| FormatFlags.SpaceForPositives) (i + 1) | '-' -> go (flags ||| FormatFlags.LeftJustify) (i + 1) - | _ -> struct (flags, i) + | _ -> (flags, i) go FormatFlags.None i let parseWidth (s: string) i = - if s.[i] = '*' then struct (StarValue, i + 1) + if s.[i] = '*' then (StarValue, i + 1) elif Char.IsDigit s.[i] then intFromString s i - else struct (NotSpecifiedValue, i) + else (NotSpecifiedValue, i) let parsePrecision (s: string) i = if s.[i] = '.' then - if s.[i + 1] = '*' then struct (StarValue, i + 2) + if s.[i + 1] = '*' then (StarValue, i + 2) elif Char.IsDigit s.[i + 1] then intFromString s (i + 1) else raise (ArgumentException("invalid precision value")) - else struct (NotSpecifiedValue, i) + else (NotSpecifiedValue, i) let parseTypeChar (s: string) i = - struct (s.[i], i + 1) + (s.[i], i + 1) let parseInterpolatedHoleDotNetFormat typeChar (s: string) i = if typeChar = 'P' then if i < s.Length && s.[i] = '(' then let i2 = s.IndexOf(")", i) if i2 = -1 then - struct (ValueNone, i) + (ValueNone, i) else - struct (ValueSome s.[i+1..i2-1], i2+1) + (ValueSome s.[i+1..i2-1], i2+1) else - struct (ValueNone, i) + (ValueNone, i) else - struct (ValueNone, i) + (ValueNone, i) // Skip %P() added for hole in "...%d{x}..." let skipInterpolationHole (fmt: string) i = @@ -204,15 +204,15 @@ module internal PrintfImpl = let findNextFormatSpecifier (s: string) i = let rec go i (buf: Text.StringBuilder) = if i >= s.Length then - struct (s.Length, buf.ToString()) + (s.Length, buf.ToString()) else let c = s.[i] if c = '%' then if i + 1 < s.Length then - let struct (_, i1) = parseFlags s (i + 1) - let struct (w, i2) = parseWidth s i1 - let struct (p, i3) = parsePrecision s i2 - let struct (typeChar, i4) = parseTypeChar s i3 + let (_, i1) = parseFlags s (i + 1) + let (w, i2) = parseWidth s i1 + let (p, i3) = parsePrecision s i2 + let (typeChar, i4) = parseTypeChar s i3 // shortcut for the simpliest case // if typeChar is not % or it has star as width\precision - resort to long path @@ -376,7 +376,7 @@ module internal PrintfImpl = /// f3 8 // same activation captures 8 (args --> [3;5;8]) /// /// If we captured into an mutable array then these would interfere - type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> struct (obj list * PrintfEnv<'State, 'Residue, 'Result>) + type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> (obj list * PrintfEnv<'State, 'Residue, 'Result>) type PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result> = PrintfFuncContext<'State, 'Residue, 'Result> -> 'Printer [] @@ -396,14 +396,14 @@ module internal PrintfImpl = static member Final0(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> - let (struct (args, env)) = prev() + let (args, env) = prev() env.RunSteps(revToArray args, null, allSteps) ) static member CaptureFinal1<'A>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> - let (struct (args, env)) = prev() + let (args, env) = prev() let finalArgs = box arg1 :: args env.RunSteps(revToArray finalArgs, null, allSteps) ) @@ -412,7 +412,7 @@ module internal PrintfImpl = static member CaptureFinal2<'A, 'B>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) -> - let (struct (args, env)) = prev() + let (args, env) = prev() let finalArgs = box arg2 :: box arg1 :: args env.RunSteps(revToArray finalArgs, null, allSteps) ) @@ -421,7 +421,7 @@ module internal PrintfImpl = static member CaptureFinal3<'A, 'B, 'C>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> - let (struct (args, env)) = prev() + let (args, env) = prev() let finalArgs = box arg3 :: box arg2 :: box arg1 :: args env.RunSteps(revToArray finalArgs, null, allSteps) ) @@ -431,8 +431,8 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> let curr() = - let (struct (args, env)) = prev() - struct ((box arg1 :: args), env) + let (args, env) = prev() + ((box arg1 :: args), env) next curr : 'Tail ) ) @@ -441,8 +441,8 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'A -> 'Residue) (arg1: 'A) -> let curr() = - let (struct (args, env)) = prev() - struct ((box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args), env) + let (args, env) = prev() + ((box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args), env) next curr : 'Tail ) ) @@ -451,8 +451,8 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) -> let curr() = - let (struct (args, env)) = prev() - struct ((box arg2 :: box arg1 :: args), env) + let (args, env) = prev() + ((box arg2 :: box arg1 :: args), env) next curr : 'Tail ) ) @@ -461,8 +461,8 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> let curr() = - let (struct (args, env)) = prev() - struct ((box arg3 :: box arg2 :: box arg1 :: args), env) + let (args, env) = prev() + ((box arg3 :: box arg2 :: box arg1 :: args), env) next curr : 'Tail ) ) @@ -950,12 +950,13 @@ module internal PrintfImpl = | _ -> failwith (String.Format("Expected function with {0} arguments", n)) else System.Diagnostics.Debug.Assert((i = n), "i = n") - struct (buf, ty) + (buf, ty) go ty 0 let MAX_CAPTURE = 3 /// Parses format string and creates resulting step list and printer factory function. + [] type FormatParser<'Printer, 'State, 'Residue, 'Result>(fmt: string) = let buildCaptureFunc (spec: FormatSpecifier, allSteps, argTys: Type[], retTy, nextInfo) = @@ -1045,11 +1046,11 @@ module internal PrintfImpl = StepWithArg (prefix, convFunc) let parseSpec i = - let struct (flags, i) = FormatString.parseFlags fmt (i + 1) - let struct (width, i) = FormatString.parseWidth fmt i - let struct (precision, i) = FormatString.parsePrecision fmt i - let struct (typeChar, i) = FormatString.parseTypeChar fmt i - let struct (interpHoleDotnetFormat, i) = FormatString.parseInterpolatedHoleDotNetFormat typeChar fmt i + let (flags, i) = FormatString.parseFlags fmt (i + 1) + let (width, i) = FormatString.parseWidth fmt i + let (precision, i) = FormatString.parsePrecision fmt i + let (typeChar, i) = FormatString.parseTypeChar fmt i + let (interpHoleDotnetFormat, i) = FormatString.parseInterpolatedHoleDotNetFormat typeChar fmt i // Skip %P insertion points added after %d{...} etc. in interpolated strings let i = FormatString.skipInterpolationHole fmt i @@ -1060,7 +1061,7 @@ module internal PrintfImpl = Flags = flags Width = width InteropHoleDotNetFormat = interpHoleDotnetFormat } - struct (i, spec) + (i, spec) // The steps, populated on-demand. This is for the case where the string is being used // with interpolands captured in the Format object, including the %A capture types. @@ -1090,13 +1091,13 @@ module internal PrintfImpl = stringCount <- Step.BlockCount allSteps stepsForCapturedFormat <- allSteps else - let struct (i, spec) = parseSpec i - let struct (i, suffix) = FormatString.findNextFormatSpecifier fmt i + let (i, spec) = parseSpec i + let (i, suffix) = FormatString.findNextFormatSpecifier fmt i let step = buildStep spec null prefix parseAndCreateStepsForCapturedFormatAux (step::steps) suffix i let parseAndCreateStepsForCapturedFormat () = - let struct (i, prefix) = FormatString.findNextFormatSpecifier fmt 0 + let (i, prefix) = FormatString.findNextFormatSpecifier fmt 0 parseAndCreateStepsForCapturedFormatAux [] prefix i /// The more advanced parser which both builds the steps (with %A types extracted from the funcTy), @@ -1110,34 +1111,34 @@ module internal PrintfImpl = let last = Specializations<'State, 'Residue, 'Result>.Final0(allSteps) stringCount <- Step.BlockCount allSteps let nextInfo = (box last, true, [| |], funcTy, None) - struct (allSteps, nextInfo) + (allSteps, nextInfo) else assert (fmt.[i] = '%') - let struct (i, spec) = parseSpec i - let struct (i, suffix) = FormatString.findNextFormatSpecifier fmt i + let (i, spec) = parseSpec i + let (i, suffix) = FormatString.findNextFormatSpecifier fmt i let n = spec.ArgCount - let struct (argTys, retTy) = extractCurriedArguments funcTy n + let (argTys, retTy) = extractCurriedArguments funcTy n let step = buildStep spec argTys prefix - let struct (allSteps, nextInfo) = parseAndCreateFuncFactoryAux (step::steps) suffix retTy i + let (allSteps, nextInfo) = parseAndCreateFuncFactoryAux (step::steps) suffix retTy i let nextInfoNew = buildCaptureFunc (spec, allSteps, argTys, retTy, nextInfo) - struct (allSteps, nextInfoNew) + (allSteps, nextInfoNew) let parseAndCreateFunctionFactory () = let funcTy = typeof<'Printer> // Find the first format specifier - let struct (prefixPos, prefix) = FormatString.findNextFormatSpecifier fmt 0 + let (prefixPos, prefix) = FormatString.findNextFormatSpecifier fmt 0 // If there are not format specifiers then take a simple path if prefixPos = fmt.Length then let factoryObj = box (fun (initial: PrintfFuncContext<'State, 'Residue, 'Result>) -> - let (struct (_args, env)) = initial() + let (_args, env) = initial() env.WriteSkipEmpty prefix env.Finish()) factoryObj else - let struct (_, (factoryObj, _, _, _, _)) = parseAndCreateFuncFactoryAux [] prefix funcTy prefixPos + let (_, (factoryObj, _, _, _, _)) = parseAndCreateFuncFactoryAux [] prefix funcTy prefixPos factoryObj /// The format string, used to help identify the cache entry (the cache index types are taken @@ -1182,7 +1183,7 @@ module internal PrintfImpl = static member GetParser(format: Format<'Printer, 'State, 'Residue, 'Result>) = let cacheEntry = Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent let fmt = format.Value - if cacheEntry === null then + if isNull cacheEntry then let parser = FormatParser(fmt) Cache.mostRecent <- parser parser @@ -1262,7 +1263,7 @@ module Printf = | null -> // The ksprintf "...%d ...." arg path, producing a function let factory = cacheItem.GetFunctionFactory() - let initial() = (struct ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>)) + let initial() = ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>) factory initial | captures -> // The ksprintf $"...%d{3}...." path, running the steps straight away to produce a string @@ -1284,7 +1285,7 @@ module Printf = | null -> // The sprintf "...%d ...." arg path, producing a function let factory = cacheItem.GetFunctionFactory() - let initial() = (struct ([], StringPrintfEnv cacheItem.BlockCount)) + let initial() = ([], StringPrintfEnv cacheItem.BlockCount) factory initial | captures -> // The sprintf $"...%d{3}...." path, running the steps straight away to produce a string From e864cc7bc9be5f95f83fed068aa1751dea926150 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 28 Jul 2020 19:03:07 +0100 Subject: [PATCH 78/87] use struct tuples where possible, byrefs for index --- src/fsharp/FSharp.Core/printf.fs | 238 +++++++++++++++++++++---------- 1 file changed, 164 insertions(+), 74 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 7ceb37744da..46560c6b0ba 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -145,88 +145,111 @@ module internal PrintfImpl = /// Set of helpers to parse format string module private FormatString = - let intFromString (s: string) i = - let rec go acc i = - if Char.IsDigit s.[i] then - let n = int s.[i] - int '0' - go (acc * 10 + n) (i + 1) - else (acc, i) - go 0 i - - let parseFlags (s: string) i = - let rec go flags i = + let intFromString (s: string) (i: byref) = + let mutable res = 0 + while (Char.IsDigit s.[i]) do + let n = int s.[i] - int '0' + res <- res * 10 + n + i <- i + 1 + res + + let parseFlags (s: string) (i: byref) = + let mutable flags = FormatFlags.None + let mutable fin = false + while not fin do match s.[i] with - | '0' -> go (flags ||| FormatFlags.PadWithZeros) (i + 1) - | '+' -> go (flags ||| FormatFlags.PlusForPositives) (i + 1) - | ' ' -> go (flags ||| FormatFlags.SpaceForPositives) (i + 1) - | '-' -> go (flags ||| FormatFlags.LeftJustify) (i + 1) - | _ -> (flags, i) - go FormatFlags.None i - - let parseWidth (s: string) i = - if s.[i] = '*' then (StarValue, i + 1) - elif Char.IsDigit s.[i] then intFromString s i - else (NotSpecifiedValue, i) - - let parsePrecision (s: string) i = + | '0' -> + flags <- flags ||| FormatFlags.PadWithZeros + i <- i + 1 + | '+' -> + flags <- flags ||| FormatFlags.PlusForPositives + i <- i + 1 + | ' ' -> + flags <- flags ||| FormatFlags.SpaceForPositives + i <- i + 1 + | '-' -> + flags <- flags ||| FormatFlags.LeftJustify + i <- i + 1 + | _ -> + fin <- true + flags + + let parseWidth (s: string) (i: byref) = + if s.[i] = '*' then + i <- i + 1 + StarValue + elif Char.IsDigit s.[i] then + intFromString s (&i) + else + NotSpecifiedValue + + let parsePrecision (s: string) (i: byref) = if s.[i] = '.' then - if s.[i + 1] = '*' then (StarValue, i + 2) - elif Char.IsDigit s.[i + 1] then intFromString s (i + 1) + if s.[i + 1] = '*' then + i <- i + 2 + StarValue + elif Char.IsDigit s.[i + 1] then + i <- i + 1 + intFromString s (&i) else raise (ArgumentException("invalid precision value")) - else (NotSpecifiedValue, i) + else + NotSpecifiedValue - let parseTypeChar (s: string) i = - (s.[i], i + 1) + let parseTypeChar (s: string) (i: byref) = + i <- i + 1 + s.[i] - let parseInterpolatedHoleDotNetFormat typeChar (s: string) i = + let parseInterpolatedHoleDotNetFormat typeChar (s: string) (i: byref) = if typeChar = 'P' then if i < s.Length && s.[i] = '(' then let i2 = s.IndexOf(")", i) if i2 = -1 then - (ValueNone, i) + ValueNone else - (ValueSome s.[i+1..i2-1], i2+1) + let res = s.[i+1..i2-1] + i <- i2+1 + ValueSome res else - (ValueNone, i) + ValueNone else - (ValueNone, i) + ValueNone // Skip %P() added for hole in "...%d{x}..." - let skipInterpolationHole (fmt: string) i = + let skipInterpolationHole (fmt: string) (i: byref) = if i+1 < fmt.Length && fmt.[i] = '%' && fmt.[i+1] = 'P' then - let i = i + 2 + i <- i + 2 if i+1 < fmt.Length && fmt.[i] = '(' && fmt.[i+1] = ')' then - i+2 - else - i - else i + i <- i+2 - let findNextFormatSpecifier (s: string) i = - let rec go i (buf: Text.StringBuilder) = + let findNextFormatSpecifier (s: string) (i: byref) = + let buf = StringBuilder() + let mutable fin = false + while not fin do if i >= s.Length then - (s.Length, buf.ToString()) + fin <- true else let c = s.[i] if c = '%' then if i + 1 < s.Length then - let (_, i1) = parseFlags s (i + 1) - let (w, i2) = parseWidth s i1 - let (p, i3) = parsePrecision s i2 - let (typeChar, i4) = parseTypeChar s i3 + let mutable i2 = i+1 + let _ = parseFlags s &i2 + let w = parseWidth s &i2 + let p = parsePrecision s &i2 + let typeChar = parseTypeChar s &i2 // shortcut for the simpliest case // if typeChar is not % or it has star as width\precision - resort to long path if typeChar = '%' && not (w = StarValue || p = StarValue) then buf.Append('%') |> ignore - go i4 buf + i <- i2 else - i, buf.ToString() + fin <- true else raise (ArgumentException("Missing format specifier")) else buf.Append c |> ignore - go (i + 1) buf - go i (Text.StringBuilder()) + i <- i + 1 + buf.ToString() [] /// Represents one step in the execution of a format string @@ -376,7 +399,11 @@ module internal PrintfImpl = /// f3 8 // same activation captures 8 (args --> [3;5;8]) /// /// If we captured into an mutable array then these would interfere +#if NETSTANDARD + type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> struct (obj list * PrintfEnv<'State, 'Residue, 'Result>) +#else type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> (obj list * PrintfEnv<'State, 'Residue, 'Result>) +#endif type PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result> = PrintfFuncContext<'State, 'Residue, 'Result> -> 'Printer [] @@ -396,14 +423,22 @@ module internal PrintfImpl = static member Final0(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> +#if NETSTANDARD + let struct (args, env) = prev() +#else let (args, env) = prev() +#endif env.RunSteps(revToArray args, null, allSteps) ) static member CaptureFinal1<'A>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> +#if NETSTANDARD + let struct (args, env) = prev() +#else let (args, env) = prev() +#endif let finalArgs = box arg1 :: args env.RunSteps(revToArray finalArgs, null, allSteps) ) @@ -412,7 +447,11 @@ module internal PrintfImpl = static member CaptureFinal2<'A, 'B>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) -> +#if NETSTANDARD + let struct (args, env) = prev() +#else let (args, env) = prev() +#endif let finalArgs = box arg2 :: box arg1 :: args env.RunSteps(revToArray finalArgs, null, allSteps) ) @@ -421,7 +460,11 @@ module internal PrintfImpl = static member CaptureFinal3<'A, 'B, 'C>(allSteps) = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> +#if NETSTANDARD + let struct (args, env) = prev() +#else let (args, env) = prev() +#endif let finalArgs = box arg3 :: box arg2 :: box arg1 :: args env.RunSteps(revToArray finalArgs, null, allSteps) ) @@ -431,8 +474,13 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) -> let curr() = +#if NETSTANDARD + let struct (args, env) = prev() + struct ((box arg1 :: args), env) +#else let (args, env) = prev() ((box arg1 :: args), env) +#endif next curr : 'Tail ) ) @@ -441,8 +489,17 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (f: 'State -> 'A -> 'Residue) (arg1: 'A) -> let curr() = +#if NETSTANDARD + let struct (args, env) = prev() +#else let (args, env) = prev() - ((box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args), env) +#endif + let args = box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args +#if NETSTANDARD + struct (args, env) +#else + (args, env) +#endif next curr : 'Tail ) ) @@ -451,8 +508,17 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) -> let curr() = +#if NETSTANDARD + let struct (args, env) = prev() +#else let (args, env) = prev() - ((box arg2 :: box arg1 :: args), env) +#endif + let args = box arg2 :: box arg1 :: args +#if NETSTANDARD + struct (args, env) +#else + (args, env) +#endif next curr : 'Tail ) ) @@ -461,8 +527,17 @@ module internal PrintfImpl = (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> let curr() = +#if NETSTANDARD + let struct (args, env) = prev() +#else let (args, env) = prev() - ((box arg3 :: box arg2 :: box arg1 :: args), env) +#endif + let args = box arg3 :: box arg2 :: box arg1 :: args +#if NETSTANDARD + struct (args, env) +#else + (args, env) +#endif next curr : 'Tail ) ) @@ -1045,15 +1120,16 @@ module internal PrintfImpl = let convFunc = conv.FuncObj :?> (obj -> string) StepWithArg (prefix, convFunc) - let parseSpec i = - let (flags, i) = FormatString.parseFlags fmt (i + 1) - let (width, i) = FormatString.parseWidth fmt i - let (precision, i) = FormatString.parsePrecision fmt i - let (typeChar, i) = FormatString.parseTypeChar fmt i - let (interpHoleDotnetFormat, i) = FormatString.parseInterpolatedHoleDotNetFormat typeChar fmt i + let parseSpec (i: byref) = + i <- i + 1 + let flags = FormatString.parseFlags fmt &i + let width = FormatString.parseWidth fmt &i + let precision = FormatString.parsePrecision fmt &i + let typeChar = FormatString.parseTypeChar fmt &i + let interpHoleDotnetFormat = FormatString.parseInterpolatedHoleDotNetFormat typeChar fmt &i // Skip %P insertion points added after %d{...} etc. in interpolated strings - let i = FormatString.skipInterpolationHole fmt i + FormatString.skipInterpolationHole fmt &i let spec = { TypeChar = typeChar @@ -1061,7 +1137,7 @@ module internal PrintfImpl = Flags = flags Width = width InteropHoleDotNetFormat = interpHoleDotnetFormat } - (i, spec) + spec // The steps, populated on-demand. This is for the case where the string is being used // with interpolands captured in the Format object, including the %A capture types. @@ -1083,7 +1159,7 @@ module internal PrintfImpl = let mutable stringCount = 0 // A simplified parser. For the case where the string is being used with interpolands captured in the Format object. - let rec parseAndCreateStepsForCapturedFormatAux steps (prefix: string) i = + let rec parseAndCreateStepsForCapturedFormatAux steps (prefix: string) (i: byref) = if i >= fmt.Length then let step = StepString(prefix) let steps = step :: steps @@ -1091,18 +1167,19 @@ module internal PrintfImpl = stringCount <- Step.BlockCount allSteps stepsForCapturedFormat <- allSteps else - let (i, spec) = parseSpec i - let (i, suffix) = FormatString.findNextFormatSpecifier fmt i + let spec = parseSpec &i + let suffix = FormatString.findNextFormatSpecifier fmt &i let step = buildStep spec null prefix - parseAndCreateStepsForCapturedFormatAux (step::steps) suffix i + parseAndCreateStepsForCapturedFormatAux (step::steps) suffix &i let parseAndCreateStepsForCapturedFormat () = - let (i, prefix) = FormatString.findNextFormatSpecifier fmt 0 - parseAndCreateStepsForCapturedFormatAux [] prefix i + let mutable i = 0 + let prefix = FormatString.findNextFormatSpecifier fmt &i + parseAndCreateStepsForCapturedFormatAux [] prefix &i /// The more advanced parser which both builds the steps (with %A types extracted from the funcTy), /// and produces a curried function value of the right type guided by funcTy - let rec parseAndCreateFuncFactoryAux steps (prefix: string) (funcTy: Type) i = + let rec parseAndCreateFuncFactoryAux steps (prefix: string) (funcTy: Type) (i: byref) = if i >= fmt.Length then let step = StepString(prefix) @@ -1114,12 +1191,12 @@ module internal PrintfImpl = (allSteps, nextInfo) else assert (fmt.[i] = '%') - let (i, spec) = parseSpec i - let (i, suffix) = FormatString.findNextFormatSpecifier fmt i + let spec = parseSpec &i + let suffix = FormatString.findNextFormatSpecifier fmt &i let n = spec.ArgCount let (argTys, retTy) = extractCurriedArguments funcTy n let step = buildStep spec argTys prefix - let (allSteps, nextInfo) = parseAndCreateFuncFactoryAux (step::steps) suffix retTy i + let (allSteps, nextInfo) = parseAndCreateFuncFactoryAux (step::steps) suffix retTy &i let nextInfoNew = buildCaptureFunc (spec, allSteps, argTys, retTy, nextInfo) (allSteps, nextInfoNew) @@ -1127,18 +1204,23 @@ module internal PrintfImpl = let funcTy = typeof<'Printer> // Find the first format specifier - let (prefixPos, prefix) = FormatString.findNextFormatSpecifier fmt 0 + let mutable i = 0 + let prefix = FormatString.findNextFormatSpecifier fmt &i // If there are not format specifiers then take a simple path - if prefixPos = fmt.Length then + if i = fmt.Length then let factoryObj = box (fun (initial: PrintfFuncContext<'State, 'Residue, 'Result>) -> +#if NETSTANDARD + let struct (_args, env) = initial() +#else let (_args, env) = initial() +#endif env.WriteSkipEmpty prefix env.Finish()) factoryObj else - let (_, (factoryObj, _, _, _, _)) = parseAndCreateFuncFactoryAux [] prefix funcTy prefixPos + let (_, (factoryObj, _, _, _, _)) = parseAndCreateFuncFactoryAux [] prefix funcTy &i factoryObj /// The format string, used to help identify the cache entry (the cache index types are taken @@ -1263,7 +1345,11 @@ module Printf = | null -> // The ksprintf "...%d ...." arg path, producing a function let factory = cacheItem.GetFunctionFactory() +#if NETSTANDARD + let initial() = struct ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>) +#else let initial() = ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>) +#endif factory initial | captures -> // The ksprintf $"...%d{3}...." path, running the steps straight away to produce a string @@ -1285,7 +1371,11 @@ module Printf = | null -> // The sprintf "...%d ...." arg path, producing a function let factory = cacheItem.GetFunctionFactory() +#if NETSTANDARD + let initial() = struct ([], StringPrintfEnv cacheItem.BlockCount) +#else let initial() = ([], StringPrintfEnv cacheItem.BlockCount) +#endif factory initial | captures -> // The sprintf $"...%d{3}...." path, running the steps straight away to produce a string From f834f7e09c31c780eac287cbba4cfd0f7499762d Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 28 Jul 2020 19:17:12 +0100 Subject: [PATCH 79/87] fix byref for index --- src/fsharp/FSharp.Core/printf.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 46560c6b0ba..99c98265ef3 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -196,8 +196,9 @@ module internal PrintfImpl = NotSpecifiedValue let parseTypeChar (s: string) (i: byref) = + let res = s.[i] i <- i + 1 - s.[i] + res let parseInterpolatedHoleDotNetFormat typeChar (s: string) (i: byref) = if typeChar = 'P' then From bda29f68986b7b6b74a5c3a5a457b2819f11c0cd Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 28 Jul 2020 20:22:37 +0100 Subject: [PATCH 80/87] fix ksprintf block size --- src/fsharp/FSharp.Core/printf.fs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 99c98265ef3..1a335a6e78d 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -322,13 +322,13 @@ module internal PrintfImpl = for step in steps do match step with | StepWithArg (prefix, conv) -> - env.WriteSkipEmpty(prefix) + env.WriteSkipEmpty prefix let arg = args.[argIndex] argIndex <- argIndex + 1 env.Write(conv arg) | StepWithTypedArg (prefix, conv) -> - env.WriteSkipEmpty(prefix) + env.WriteSkipEmpty prefix let arg = args.[argIndex] let argTy = argTys.[tyIndex] argIndex <- argIndex + 1 @@ -336,7 +336,7 @@ module internal PrintfImpl = env.Write(conv arg argTy) | StepString prefix -> - env.WriteSkipEmpty(prefix) + env.WriteSkipEmpty prefix | StepLittleT(prefix) -> env.WriteSkipEmpty prefix @@ -1219,6 +1219,7 @@ module internal PrintfImpl = #endif env.WriteSkipEmpty prefix env.Finish()) + stringCount <- 1 factoryObj else let (_, (factoryObj, _, _, _, _)) = parseAndCreateFuncFactoryAux [] prefix funcTy &i From cbd33aa522b022ba4f5a775a640919b63aec2d50 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Tue, 28 Jul 2020 20:49:46 +0100 Subject: [PATCH 81/87] make recent cache entry more explicit (cleanup) --- src/fsharp/FSharp.Core/printf.fs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 1a335a6e78d..35ade0aaac2 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -255,8 +255,8 @@ module internal PrintfImpl = [] /// Represents one step in the execution of a format string type Step = - | StepWithArg of prefix: string * conv1: (obj -> string) - | StepWithTypedArg of prefix: string * conv1: (obj -> Type -> string) + | StepWithArg of prefix: string * conv: (obj -> string) + | StepWithTypedArg of prefix: string * conv: (obj -> Type -> string) | StepString of prefix: string | StepLittleT of prefix: string | StepLittleA of prefix: string @@ -780,7 +780,7 @@ module internal PrintfImpl = | :? uint32 as n -> n.ToString(fmt, CultureInfo.InvariantCulture) | :? uint64 as n -> n.ToString(fmt, CultureInfo.InvariantCulture) | :? nativeint | :? unativeint -> toFormattedString fmt (eliminateNative v) - | _ -> failwith "toString: unreachable" + | _ -> failwith "toFormattedString: unreachable" let rec toUnsigned (v: obj) = match v with @@ -1265,14 +1265,14 @@ module internal PrintfImpl = static let mutable dict : ConcurrentDictionary> = null static member GetParser(format: Format<'Printer, 'State, 'Residue, 'Result>) = - let cacheEntry = Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent + let recent = Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent let fmt = format.Value - if isNull cacheEntry then + if isNull recent then let parser = FormatParser(fmt) - Cache.mostRecent <- parser + Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent <- parser parser - elif fmt.Equals cacheEntry.FormatString then - cacheEntry + elif fmt.Equals recent.FormatString then + recent else // Initialize the 2nd level cache if necessary. Note there's a race condition but it doesn't // matter if we initialize these values twice (and lose one entry) @@ -1287,7 +1287,7 @@ module internal PrintfImpl = // There's a race condition - but the computation is functional and it doesn't matter if we do it twice dict.TryAdd(fmt, parser) |> ignore parser - Cache.mostRecent <- parser + Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent <- parser parser type LargeStringPrintfEnv<'Result>(continuation, blockSize) = From b827b108718eb5719912439c15dce8e84206363b Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 29 Jul 2020 01:46:59 +0100 Subject: [PATCH 82/87] improve performance --- src/fsharp/FSharp.Core/printf.fs | 398 ++++++++++++++++--------------- 1 file changed, 211 insertions(+), 187 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 35ade0aaac2..3a9fe33c74a 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -314,7 +314,7 @@ module internal PrintfImpl = member env.WriteSkipEmpty(s: string) = if not (String.IsNullOrEmpty s) then env.Write s - + member env.RunSteps (args: obj[], argTys: Type[], steps: Step[]) = let mutable argIndex = 0 let mutable tyIndex = 0 @@ -400,20 +400,17 @@ module internal PrintfImpl = /// f3 8 // same activation captures 8 (args --> [3;5;8]) /// /// If we captured into an mutable array then these would interfere -#if NETSTANDARD - type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> struct (obj list * PrintfEnv<'State, 'Residue, 'Result>) -#else - type PrintfFuncContext<'State, 'Residue, 'Result> = unit -> (obj list * PrintfEnv<'State, 'Residue, 'Result>) -#endif - type PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result> = PrintfFuncContext<'State, 'Residue, 'Result> -> 'Printer + type PrintfInitial<'State, 'Residue, 'Result> = (unit -> PrintfEnv<'State, 'Residue, 'Result>) + type PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result> = + delegate of obj list * PrintfInitial<'State, 'Residue, 'Result> -> 'Printer [] let MaxArgumentsInSpecialization = 3 - let revToArray (args: 'T list) = + let revToArray extra (args: 'T list) = // We've reached the end, now fill in the array, reversing steps, avoiding reallocating let n = args.Length - let res = Array.zeroCreate n + let res = Array.zeroCreate (n+extra) let mutable j = 0 for arg in args do res.[n-j-1] <- arg @@ -423,126 +420,109 @@ module internal PrintfImpl = type Specializations<'State, 'Residue, 'Result>() = static member Final0(allSteps) = - (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> -#if NETSTANDARD - let struct (args, env) = prev() -#else - let (args, env) = prev() -#endif - env.RunSteps(revToArray args, null, allSteps) + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun args initial -> + let env = initial() + env.RunSteps(revToArray 0 args, null, allSteps) ) static member CaptureFinal1<'A>(allSteps) = - (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun args initial -> (fun (arg1: 'A) -> -#if NETSTANDARD - let struct (args, env) = prev() -#else - let (args, env) = prev() -#endif - let finalArgs = box arg1 :: args - env.RunSteps(revToArray finalArgs, null, allSteps) + let env = initial() + let argArray = revToArray 1 args + argArray.[argArray.Length-1] <- box arg1 + env.RunSteps(argArray, null, allSteps) ) ) static member CaptureFinal2<'A, 'B>(allSteps) = - (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun args initial -> (fun (arg1: 'A) (arg2: 'B) -> -#if NETSTANDARD - let struct (args, env) = prev() -#else - let (args, env) = prev() -#endif - let finalArgs = box arg2 :: box arg1 :: args - env.RunSteps(revToArray finalArgs, null, allSteps) + let env = initial() + let argArray = revToArray 2 args + argArray.[argArray.Length-1] <- box arg2 + argArray.[argArray.Length-2] <- box arg1 + env.RunSteps(argArray, null, allSteps) ) ) static member CaptureFinal3<'A, 'B, 'C>(allSteps) = - (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun args initial -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> -#if NETSTANDARD - let struct (args, env) = prev() -#else - let (args, env) = prev() -#endif - let finalArgs = box arg3 :: box arg2 :: box arg1 :: args - env.RunSteps(revToArray finalArgs, null, allSteps) + let env = initial() + let argArray = revToArray 3 args + argArray.[argArray.Length-1] <- box arg3 + argArray.[argArray.Length-2] <- box arg2 + argArray.[argArray.Length-3] <- box arg1 + env.RunSteps(argArray, null, allSteps) ) ) - static member Capture1<'A, 'Tail>(next) = - (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + static member Capture1<'A, 'Tail>(next: PrintfFuncFactory<_, 'State, 'Residue, 'Result>) = + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun args initial -> (fun (arg1: 'A) -> - let curr() = -#if NETSTANDARD - let struct (args, env) = prev() - struct ((box arg1 :: args), env) -#else - let (args, env) = prev() - ((box arg1 :: args), env) -#endif - next curr : 'Tail + let args = (box arg1 :: args) + next.Invoke(args, initial) : 'Tail ) ) - static member CaptureLittleA<'A, 'Tail>(next) = - (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + static member CaptureLittleA<'A, 'Tail>(next: PrintfFuncFactory<_, 'State, 'Residue, 'Result>) = + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun args initial -> (fun (f: 'State -> 'A -> 'Residue) (arg1: 'A) -> - let curr() = -#if NETSTANDARD - let struct (args, env) = prev() -#else - let (args, env) = prev() -#endif - let args = box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args -#if NETSTANDARD - struct (args, env) -#else - (args, env) -#endif - next curr : 'Tail + let args = box arg1 :: box (fun s (arg:obj) -> f s (unbox arg)) :: args + next.Invoke(args, initial) : 'Tail ) ) - static member Capture2<'A, 'B, 'Tail>(next) = - (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + static member Capture2<'A, 'B, 'Tail>(next: PrintfFuncFactory<_, 'State, 'Residue, 'Result>) = + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun args initial -> (fun (arg1: 'A) (arg2: 'B) -> - let curr() = -#if NETSTANDARD - let struct (args, env) = prev() -#else - let (args, env) = prev() -#endif - let args = box arg2 :: box arg1 :: args -#if NETSTANDARD - struct (args, env) -#else - (args, env) -#endif - next curr : 'Tail + let args = box arg2 :: box arg1 :: args + next.Invoke(args, initial) : 'Tail ) ) - static member Capture3<'A, 'B, 'C, 'Tail> (next) = - (fun (prev: PrintfFuncContext<'State, 'Residue, 'Result>) -> + static member Capture3<'A, 'B, 'C, 'Tail>(next: PrintfFuncFactory<_, 'State, 'Residue, 'Result>) = + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun args initial -> (fun (arg1: 'A) (arg2: 'B) (arg3: 'C) -> - let curr() = -#if NETSTANDARD - let struct (args, env) = prev() -#else - let (args, env) = prev() -#endif - let args = box arg3 :: box arg2 :: box arg1 :: args -#if NETSTANDARD - struct (args, env) -#else - (args, env) -#endif - next curr : 'Tail + let args = box arg3 :: box arg2 :: box arg1 :: args + next.Invoke(args, initial) : 'Tail ) ) + // Special case for format strings containing just one '%d' etc, i.e. StepWithArg then StepString. + // This avoids allocating an argument array, and unfolds the single iteration of RunSteps. + static member OneStepWithArg<'A>(prefix1, conv1, prefix2) = + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun _args initial -> + // Note this is the actual computed/stored closure for + // sprintf "prefix1 %d prefix2" + // for any simple format specifiers, where conv1 and conv2 will depend on the format specifiers etc. + (fun (arg1: 'A) -> + let env = initial() + env.WriteSkipEmpty prefix1 + env.Write(conv1 (box arg1)) + env.WriteSkipEmpty prefix2 + env.Finish()) + ) + + // Special case for format strings containing two simple formats like '%d %s' etc, i.e. + ///StepWithArg then StepWithArg then StepString. This avoids allocating an argument array, + // and unfolds the two iteration of RunSteps. + static member TwoStepWithArg<'A, 'B>(prefix1, conv1, prefix2, conv2, prefix3) = + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun _args initial -> + // Note this is the actual computed/stored closure for + // sprintf "prefix1 %d prefix2 %s prefix3" + // for any simple format specifiers, where conv1 and conv2 will depend on the format specifiers etc. + (fun (arg1: 'A) (arg2: 'B) -> + let env = initial() + env.WriteSkipEmpty prefix1 + env.Write(conv1 (box arg1)) + env.WriteSkipEmpty prefix2 + env.Write(conv2 (box arg2)) + env.WriteSkipEmpty prefix3 + env.Finish()) + ) + let inline (===) a b = Object.ReferenceEquals(a, b) let inline boolToString v = if v then "true" else "false" @@ -654,7 +634,7 @@ module internal PrintfImpl = else adaptPadded spec f right - /// contains functions to handle left\right justifications for non-numeric types (strings\bools) + /// Contains functions to handle left/right justifications for non-numeric types (strings/bools) module Basic = let leftJustify (f: obj -> string) padChar = fun (w: int) v -> @@ -668,7 +648,7 @@ module internal PrintfImpl = let padChar, _ = spec.GetPadAndPrefix false Padding.withPadding spec f (leftJustify f padChar) (rightJustify f padChar) - /// contains functions to handle left\right and no justification case for numbers + /// Contains functions to handle left/right and no justification case for numbers module GenericNumber = let isPositive (n: obj) = @@ -692,7 +672,7 @@ module internal PrintfImpl = /// this case can be tricky: /// - negative numbers, -7 should be printed as '-007', not '00-7' /// - positive numbers when prefix for positives is set: 7 should be '+007', not '00+7' - let inline rightJustifyWithZeroAsPadChar (str: string) isNumber isPositive w (prefixForPositives: string) = + let rightJustifyWithZeroAsPadChar (str: string) isNumber isPositive w (prefixForPositives: string) = System.Diagnostics.Debug.Assert(prefixForPositives.Length = 0 || prefixForPositives.Length = 1) if isNumber then if isPositive then @@ -743,6 +723,12 @@ module internal PrintfImpl = else fun (v: obj) -> noJustificationCore (f v) true (isPositive v) prefix + module Int32 = + let toString (v: obj) = + match v with + | :? int32 as n -> n.ToString(CultureInfo.InvariantCulture) + | _ -> failwith "toString: unreachable" + /// contains functions to handle left\right and no justification case for numbers module Integer = @@ -828,6 +814,9 @@ module internal PrintfImpl = fun (w: int) v -> GenericNumber.rightJustifyWithSpaceAsPadChar (f v) true (GenericNumber.isPositive v) w prefixForPositives + /// Computes a new function from 'f' that wraps the basic conversion given + /// by 'f' with padding for 0, spacing and justification, if the flags specify + /// it. If they don't, f is made into a value converter let withPadding (spec: FormatSpecifier) isUnsigned (f: obj -> string) = let allowZeroPadding = not (isLeftJustify spec.Flags) || spec.IsDecimalFormat let padChar, prefix = spec.GetPadAndPrefix allowZeroPadding @@ -1029,6 +1018,63 @@ module internal PrintfImpl = (buf, ty) go ty 0 + + type LargeStringPrintfEnv<'Result>(continuation, blockSize) = + inherit PrintfEnv(()) + let buf: string[] = Array.zeroCreate blockSize + let mutable ptr = 0 + + override _.Finish() : 'Result = continuation (String.Concat buf) + + override _.Write(s: string) = + buf.[ptr] <- s + ptr <- ptr + 1 + + override x.WriteT s = x.Write(s) + + type SmallStringPrintfEnv2() = + inherit PrintfEnv(()) + let mutable c = null + + override _.Finish() : string = if isNull c then "" else c + override _.Write(s: string) = if isNull c then c <- s else c <- c + s + override x.WriteT s = x.Write(s) + + type SmallStringPrintfEnv4() = + inherit PrintfEnv(()) + let mutable s1 : string = null + let mutable s2 : string = null + let mutable s3 : string = null + let mutable s4 : string = null + + override _.Finish() : string = String.Concat(s1, s2, s3, s4) + override _.Write(s: string) = + if isNull s1 then s1 <- s + elif isNull s2 then s2 <- s + elif isNull s3 then s3 <- s + else s4 <- s + override x.WriteT s = x.Write(s) + + let StringPrintfEnv blockSize = + if blockSize <= 2 then + SmallStringPrintfEnv2() :> PrintfEnv<_,_,_> + elif blockSize <= 4 then + SmallStringPrintfEnv4() :> PrintfEnv<_,_,_> + else + LargeStringPrintfEnv(id, blockSize) :> PrintfEnv<_,_,_> + + let StringBuilderPrintfEnv<'Result>(k, buf) = + { new PrintfEnv(buf) with + override _.Finish() : 'Result = k () + override _.Write(s: string) = ignore(buf.Append s) + override _.WriteT(()) = () } + + let TextWriterPrintfEnv<'Result>(k, tw: IO.TextWriter) = + { new PrintfEnv(tw) with + override _.Finish() : 'Result = k() + override _.Write(s: string) = tw.Write s + override _.WriteT(()) = () } + let MAX_CAPTURE = 3 /// Parses format string and creates resulting step list and printer factory function. @@ -1051,8 +1097,8 @@ module internal PrintfImpl = let captureMethName = "CaptureLittleA" let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) let mi = mi.MakeGenericMethod([| argTys.[1]; retTy |]) - let funcObj = mi.Invoke(null, [| next |]) - funcObj, false, argTys, retTy, None + let factoryObj = mi.Invoke(null, [| next |]) + factoryObj, false, argTys, retTy, None | n1, n2 when nextCanCombine && n1 + n2 <= MAX_CAPTURE -> // 'next' is thrown away on this path and replaced by a combined Capture @@ -1063,21 +1109,21 @@ module internal PrintfImpl = let captureMethName = "CaptureFinal" + string captureCount let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) let mi = mi.MakeGenericMethod(combinedArgTys) - let funcObj = mi.Invoke(null, [| allSteps |]) - funcObj, true, combinedArgTys, nextRetTy, None + let factoryObj = mi.Invoke(null, [| allSteps |]) + factoryObj, true, combinedArgTys, nextRetTy, None | Some nextNext -> let captureMethName = "Capture" + string captureCount let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) let mi = mi.MakeGenericMethod(Array.append combinedArgTys [| nextRetTy |]) - let funcObj = mi.Invoke(null, [| nextNext |]) - funcObj, true, combinedArgTys, nextRetTy, nextNextOpt + let factoryObj = mi.Invoke(null, [| nextNext |]) + factoryObj, true, combinedArgTys, nextRetTy, nextNextOpt | captureCount, _ -> let captureMethName = "Capture" + string captureCount let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) let mi = mi.MakeGenericMethod(Array.append argTys [| retTy |]) - let funcObj = mi.Invoke(null, [| next |]) - funcObj, true, argTys, retTy, Some next + let factoryObj = mi.Invoke(null, [| next |]) + factoryObj, true, argTys, retTy, Some next let buildStep (spec: FormatSpecifier) (argTys: Type[]) prefix = if spec.TypeChar = 'a' then @@ -1151,7 +1197,8 @@ module internal PrintfImpl = // // We may initialize this twice, but the assignment is atomic and the computation will give functionally // identical results each time, so it is ok. - let mutable functionFactory = Unchecked.defaultof<_> + let mutable factory = Unchecked.defaultof> + let mutable printer = Unchecked.defaultof<'Printer> // The function factory, populated on-demand. // @@ -1163,8 +1210,8 @@ module internal PrintfImpl = let rec parseAndCreateStepsForCapturedFormatAux steps (prefix: string) (i: byref) = if i >= fmt.Length then let step = StepString(prefix) - let steps = step :: steps - let allSteps = revToArray steps + let allSteps = revToArray 1 steps + allSteps.[allSteps.Length-1] <- step stringCount <- Step.BlockCount allSteps stepsForCapturedFormat <- allSteps else @@ -1184,8 +1231,8 @@ module internal PrintfImpl = if i >= fmt.Length then let step = StepString(prefix) - let steps = step :: steps - let allSteps = revToArray steps + let allSteps = revToArray 1 steps + allSteps.[allSteps.Length-1] <- step let last = Specializations<'State, 'Residue, 'Result>.Final0(allSteps) stringCount <- Step.BlockCount allSteps let nextInfo = (box last, true, [| |], funcTy, None) @@ -1208,21 +1255,34 @@ module internal PrintfImpl = let mutable i = 0 let prefix = FormatString.findNextFormatSpecifier fmt &i - // If there are not format specifiers then take a simple path - if i = fmt.Length then - let factoryObj = - box (fun (initial: PrintfFuncContext<'State, 'Residue, 'Result>) -> -#if NETSTANDARD - let struct (_args, env) = initial() -#else - let (_args, env) = initial() -#endif - env.WriteSkipEmpty prefix - env.Finish()) - stringCount <- 1 + let (allSteps, (factoryObj, _, combinedArgTys, _, _)) = parseAndCreateFuncFactoryAux [] prefix funcTy &i + + // If there are no format specifiers then take a simple path + match allSteps with + | [| StepString prefix |] -> + PrintfFuncFactory<_, 'State, 'Residue, 'Result>(fun _args initial -> + let env = initial() + env.WriteSkipEmpty prefix + env.Finish() + ) |> box + + // If there is one simple format specifier then we can create an even better factory function + | [| StepWithArg (prefix1, conv1); StepString prefix2 |] -> + let captureMethName = "OneStepWithArg" + let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) + let mi = mi.MakeGenericMethod(combinedArgTys) + let factoryObj = mi.Invoke(null, [| box prefix1; box conv1; box prefix2 |]) factoryObj - else - let (_, (factoryObj, _, _, _, _)) = parseAndCreateFuncFactoryAux [] prefix funcTy &i + + // If there are two simple format specifiers then we can create an even better factory function + | [| StepWithArg (prefix1, conv1); StepWithArg (prefix2, conv2); StepString prefix3 |] -> + let captureMethName = "TwoStepWithArg" + let mi = typeof>.GetMethod(captureMethName, NonPublicStatics) + let mi = mi.MakeGenericMethod(combinedArgTys) + let factoryObj = mi.Invoke(null, [| box prefix1; box conv1; box prefix2; box conv2; box prefix3 |]) + factoryObj + + | _ -> factoryObj /// The format string, used to help identify the cache entry (the cache index types are taken @@ -1243,15 +1303,30 @@ module internal PrintfImpl = member _.BlockCount = stringCount /// The factory function used to generate the result or the resulting function. - member _.GetFunctionFactory() = - match box functionFactory with + member _.GetCurriedPrinterFactory() = + match box factory with | null -> - let funcObj = parseAndCreateFunctionFactory () + let factoryObj = parseAndCreateFunctionFactory () + let p = (factoryObj :?> PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result>) // We may initialize this twice, but the assignment is atomic and the computation will give functionally // identical results each time it is ok - functionFactory <- (funcObj :?> PrintfFuncFactory<'Printer, 'State, 'Residue, 'Result>) - | _ -> () - functionFactory + factory <- p + p + | _ -> factory + + /// This avoids reallocation and application of 'initial' for sprintf printers + member this.GetCurriedStringPrinter() = + match box printer with + | null -> + let f = this.GetCurriedPrinterFactory() + let initial() = (StringPrintfEnv stringCount |> box :?> PrintfEnv<'State, 'Residue, 'Result>) + let p = f.Invoke([], initial) + // We may initialize this twice, but the assignment is atomic and the computation will give functionally + // identical results each time it is ok + printer <- p + p + | _ -> printer + /// 2-level cache, keyed by format string and index types type Cache<'Printer, 'State, 'Residue, 'Result>() = @@ -1290,47 +1365,6 @@ module internal PrintfImpl = Cache<'Printer, 'State, 'Residue, 'Result>.mostRecent <- parser parser - type LargeStringPrintfEnv<'Result>(continuation, blockSize) = - inherit PrintfEnv(()) - let buf: string[] = Array.zeroCreate blockSize - let mutable ptr = 0 - - override __.Finish() : 'Result = continuation (String.Concat buf) - - override __.Write(s: string) = - buf.[ptr] <- s - ptr <- ptr + 1 - - override __.WriteT s = - buf.[ptr] <- s - ptr <- ptr + 1 - - type SmallStringPrintfEnv() = - inherit PrintfEnv(()) - let mutable c = null - - override __.Finish() : string = if isNull c then "" else c - override __.Write(s: string) = if isNull c then c <- s else c <- c + s - override __.WriteT s = if isNull c then c <- s else c <- c + s - - let StringPrintfEnv blockSize = - if blockSize <= 2 then - SmallStringPrintfEnv() :> PrintfEnv<_,_,_> - else - LargeStringPrintfEnv(id, blockSize) :> PrintfEnv<_,_,_> - - let StringBuilderPrintfEnv<'Result>(k, buf) = - { new PrintfEnv(buf) with - override __.Finish() : 'Result = k () - override __.Write(s: string) = ignore(buf.Append s) - override __.WriteT(()) = () } - - let TextWriterPrintfEnv<'Result>(k, tw: IO.TextWriter) = - { new PrintfEnv(tw) with - override __.Finish() : 'Result = k() - override __.Write(s: string) = tw.Write s - override __.WriteT(()) = () } - [] module Printf = @@ -1346,13 +1380,9 @@ module Printf = match format.Captures with | null -> // The ksprintf "...%d ...." arg path, producing a function - let factory = cacheItem.GetFunctionFactory() -#if NETSTANDARD - let initial() = struct ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>) -#else - let initial() = ([], envf cacheItem.BlockCount :> PrintfEnv<_,_,_>) -#endif - factory initial + let factory = cacheItem.GetCurriedPrinterFactory() + let initial() = (envf cacheItem.BlockCount :> PrintfEnv<_,_,_>) + factory.Invoke([], initial) | captures -> // The ksprintf $"...%d{3}...." path, running the steps straight away to produce a string let steps = cacheItem.GetStepsForCapturedFormat() @@ -1372,13 +1402,7 @@ module Printf = match format.Captures with | null -> // The sprintf "...%d ...." arg path, producing a function - let factory = cacheItem.GetFunctionFactory() -#if NETSTANDARD - let initial() = struct ([], StringPrintfEnv cacheItem.BlockCount) -#else - let initial() = ([], StringPrintfEnv cacheItem.BlockCount) -#endif - factory initial + cacheItem.GetCurriedStringPrinter() | captures -> // The sprintf $"...%d{3}...." path, running the steps straight away to produce a string let steps = cacheItem.GetStepsForCapturedFormat() From f2832d3b712a2d50ac2aa8118a9974110508deff Mon Sep 17 00:00:00 2001 From: Don Syme Date: Wed, 29 Jul 2020 02:06:34 +0100 Subject: [PATCH 83/87] remove unused code --- src/fsharp/FSharp.Core/printf.fs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index 3a9fe33c74a..c2034061f87 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -723,12 +723,6 @@ module internal PrintfImpl = else fun (v: obj) -> noJustificationCore (f v) true (isPositive v) prefix - module Int32 = - let toString (v: obj) = - match v with - | :? int32 as n -> n.ToString(CultureInfo.InvariantCulture) - | _ -> failwith "toString: unreachable" - /// contains functions to handle left\right and no justification case for numbers module Integer = From bc6ea7495036334544d8e1e9440f9cd45b43d375 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Fri, 31 Jul 2020 10:33:23 -0700 Subject: [PATCH 84/87] Move existing Compiler.ComponentTests to a new Compiler.fs framework (#9839) (#9848) * Move existing Compiler.ComponentTests to a new Compiler.fs framework; Add 'parse' function * Changed some wording in error messages Co-authored-by: Vlad Zarytovskii --- .../ConstraintSolver/MemberConstraints.fs | 2 +- .../ConstraintSolver/PrimitiveConstraints.fs | 2 +- .../AccessOfTypeAbbreviationTests.fs | 63 ++-- .../ErrorMessages/AssignmentErrorTests.fs | 19 +- .../ErrorMessages/ClassesTests.fs | 110 ++++--- .../ErrorMessages/ConfusingTypeName.fs | 5 +- .../ErrorMessages/ConstructorTests.fs | 94 +++--- .../ErrorMessages/DontSuggestTests.fs | 44 ++- .../ElseBranchHasWrongTypeTests.fs | 136 ++++----- .../InvalidNumericLiteralTests.fs | 61 ++-- .../ErrorMessages/MissingElseBranch.fs | 46 ++- .../ErrorMessages/MissingExpressionTests.fs | 20 +- .../ErrorMessages/ModuleAbbreviationTests.fs | 18 +- .../ErrorMessages/NameResolutionTests.fs | 31 +- .../ErrorMessages/SuggestionsTests.fs | 283 ++++++++---------- .../ErrorMessages/TypeMismatchTests.fs | 115 ++++--- .../ErrorMessages/UnitGenericAbstactType.fs | 23 +- .../ErrorMessages/UpcastDowncastTests.fs | 46 ++- .../ErrorMessages/WarnExpressionTests.fs | 168 +++++------ .../ErrorMessages/WrongSyntaxInForLoop.fs | 15 +- .../Interop/SimpleInteropTests.fs | 3 +- .../Language/CodeQuotationTests.fs | 2 +- .../Language/CompilerDirectiveTests.fs | 4 +- tests/FSharp.Test.Utilities/Compiler.fs | 33 +- tests/FSharp.Test.Utilities/CompilerAssert.fs | 7 +- 25 files changed, 630 insertions(+), 720 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs index f14c2495c37..77ae35463fc 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ConstraintSolver.ComponentTests +namespace FSharp.Compiler.ComponentTests.ConstraintSolver open Xunit open FSharp.Test.Utilities.Compiler diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/PrimitiveConstraints.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/PrimitiveConstraints.fs index 873864edaff..a22d61db60c 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/PrimitiveConstraints.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/PrimitiveConstraints.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ConstraintSolver.ComponentTests +namespace FSharp.Compiler.ComponentTests.ConstraintSolver open Xunit open FSharp.Test.Utilities.Compiler diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AccessOfTypeAbbreviationTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AccessOfTypeAbbreviationTests.fs index 416c62d63f6..df9acf7efca 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AccessOfTypeAbbreviationTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AccessOfTypeAbbreviationTests.fs @@ -1,75 +1,74 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities +open FSharp.Test.Utilities.Compiler open FSharp.Compiler.SourceCodeServices module ``Access Of Type Abbreviation`` = + let warning44Message = "This construct is deprecated. The type 'Hidden' is less accessible than the value, member or type 'Exported' it is used in." + System.Environment.NewLine + "As of F# 4.1, the accessibility of type abbreviations is checked at compile-time. Consider changing the accessibility of the type abbreviation. Ignoring this warning might lead to runtime errors." + [] let ``Private type produces warning when trying to export``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Library = type private Hidden = Hidden of unit type Exported = Hidden - """ - FSharpErrorSeverity.Warning - 44 - (4, 8, 4, 16) - ("This construct is deprecated. The type 'Hidden' is less accessible than the value, member or type 'Exported' it is used in." + System.Environment.NewLine + "As of F# 4.1, the accessibility of type abbreviations is checked at compile-time. Consider changing the accessibility of the type abbreviation. Ignoring this warning might lead to runtime errors.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 44, Line 4, Col 8, Line 4, Col 16, warning44Message) [] let ``Internal type passes when abbrev is internal``() = - CompilerAssert.Pass - """ + FSharp """ module Library = type internal Hidden = Hidden of unit type internal Exported = Hidden - """ + """ + |> typecheck + |> shouldSucceed [] let ``Internal type produces warning when trying to export``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Library = type internal Hidden = Hidden of unit type Exported = Hidden - """ - FSharpErrorSeverity.Warning - 44 - (4, 8, 4, 16) - ("This construct is deprecated. The type 'Hidden' is less accessible than the value, member or type 'Exported' it is used in." + System.Environment.NewLine + "As of F# 4.1, the accessibility of type abbreviations is checked at compile-time. Consider changing the accessibility of the type abbreviation. Ignoring this warning might lead to runtime errors.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 44, Line 4, Col 8, Line 4, Col 16, warning44Message) [] let ``Private type produces warning when abbrev is internal``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Library = type private Hidden = Hidden of unit type internal Exported = Hidden - """ - FSharpErrorSeverity.Warning - 44 - (4, 17, 4, 25) - ("This construct is deprecated. The type 'Hidden' is less accessible than the value, member or type 'Exported' it is used in." + System.Environment.NewLine + "As of F# 4.1, the accessibility of type abbreviations is checked at compile-time. Consider changing the accessibility of the type abbreviation. Ignoring this warning might lead to runtime errors.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 44, Line 4, Col 17, Line 4, Col 25, warning44Message) [] let ``Private type passes when abbrev is private``() = - CompilerAssert.Pass - """ + FSharp """ module Library = type private Hidden = Hidden of unit type private Exported = Hidden - """ + """ + |> typecheck + |> shouldSucceed [] let ``Default access type passes when abbrev is default``() = - CompilerAssert.Pass - """ + FSharp """ module Library = type Hidden = Hidden of unit type Exported = Hidden - """ + """ + |> typecheck + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentErrorTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentErrorTests.fs index f2f598d9dba..dd986cf5009 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentErrorTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentErrorTests.fs @@ -1,22 +1,19 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices - +open FSharp.Test.Utilities.Compiler module ``Errors assigning to mutable objects`` = [] let ``Assign to immutable error``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 x <- 20 - """ - FSharpErrorSeverity.Error - 27 - (3, 1, 3, 8) - "This value is not mutable. Consider using the mutable keyword, e.g. 'let mutable x = expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 27, Line 3, Col 1, Line 3, Col 8, + "This value is not mutable. Consider using the mutable keyword, e.g. 'let mutable x = expression'.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs index a51e92ed944..e1941a3d7eb 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs @@ -1,17 +1,15 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Classes`` = [] let ``Tuple In Abstract Method``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type IInterface = abstract Function : (int32 * int32) -> unit @@ -19,47 +17,44 @@ let x = { new IInterface with member this.Function (i, j) = () } - """ - [| - FSharpErrorSeverity.Error, 768, (7, 16, 7, 36), "The member 'Function' does not accept the correct number of arguments. 1 argument(s) are expected, but 2 were given. The required signature is 'member IInterface.Function : (int32 * int32) -> unit'.\nA tuple type is required for one or more arguments. Consider wrapping the given arguments in additional parentheses or review the definition of the interface." - FSharpErrorSeverity.Error, 17, (7, 21, 7, 29), "The member 'Function : 'a * 'b -> unit' does not have the correct type to override the corresponding abstract method. The required signature is 'Function : (int32 * int32) -> unit'." - FSharpErrorSeverity.Error, 783, (6, 9, 6, 19), "At least one override did not correctly implement its corresponding abstract member" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 768, Line 7, Col 16, Line 7, Col 36, "The member 'Function' does not accept the correct number of arguments. 1 argument(s) are expected, but 2 were given. The required signature is 'member IInterface.Function : (int32 * int32) -> unit'.\nA tuple type is required for one or more arguments. Consider wrapping the given arguments in additional parentheses or review the definition of the interface.") + (Error 17, Line 7, Col 21, Line 7, Col 29, "The member 'Function : 'a * 'b -> unit' does not have the correct type to override the corresponding abstract method. The required signature is 'Function : (int32 * int32) -> unit'.") + (Error 783, Line 6, Col 9, Line 6, Col 19, "At least one override did not correctly implement its corresponding abstract member")] [] let ``Wrong Arity``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyType() = static member MyMember(arg1, arg2:int ) = () static member MyMember(arg1, arg2:byte) = () MyType.MyMember("", 0, 0) - """ - FSharpErrorSeverity.Error - 503 - (7, 1, 7, 26) - "A member or object constructor 'MyMember' taking 3 arguments is not accessible from this code location. All accessible versions of method 'MyMember' take 2 arguments." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 503, Line 7, Col 1, Line 7, Col 26, + "A member or object constructor 'MyMember' taking 3 arguments is not accessible from this code location. All accessible versions of method 'MyMember' take 2 arguments.") [] let ``Method Is Not Static``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Class1() = member this.X() = "F#" let x = Class1.X() - """ - FSharpErrorSeverity.Error - 3214 - (5, 9, 5, 17) - "Method or object constructor 'X' is not static" + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3214, Line 5, Col 9, Line 5, Col 17, "Method or object constructor 'X' is not static") [] let ``Matching Method With Same Name Is Not Abstract``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Foo(x : int) = member v.MyX() = x @@ -67,17 +62,17 @@ let foo = { new Foo(3) with member v.MyX() = 4 } - """ - [| - FSharpErrorSeverity.Error, 767, (8, 16, 8, 23), "The type Foo contains the member 'MyX' but it is not a virtual or abstract method that is available to override or implement." - FSharpErrorSeverity.Error, 17, (8, 18, 8, 21), "The member 'MyX : unit -> int' does not have the correct type to override any given virtual method" - FSharpErrorSeverity.Error, 783, (6, 11, 6, 14), "At least one override did not correctly implement its corresponding abstract member" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 767, Line 8, Col 16, Line 8, Col 23, "The type Foo contains the member 'MyX' but it is not a virtual or abstract method that is available to override or implement.") + (Error 17, Line 8, Col 18, Line 8, Col 21, "The member 'MyX : unit -> int' does not have the correct type to override any given virtual method") + (Error 783, Line 6, Col 11, Line 6, Col 14, "At least one override did not correctly implement its corresponding abstract member")] [] let ``No Matching Abstract Method With Same Name``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type IInterface = abstract MyFunction : int32 * int32 -> unit abstract SomeOtherFunction : int32 * int32 -> unit @@ -86,18 +81,18 @@ let x = { new IInterface with member this.Function (i, j) = () } - """ - [| - FSharpErrorSeverity.Error, 767, (8, 14, 8, 34), "The member 'Function' does not correspond to any abstract or virtual method available to override or implement. Maybe you want one of the following:" + System.Environment.NewLine + " MyFunction" - FSharpErrorSeverity.Error, 17, (8, 19, 8, 27), "The member 'Function : 'a * 'b -> unit' does not have the correct type to override any given virtual method" - FSharpErrorSeverity.Error, 366, (7, 3, 9, 4), "No implementation was given for those members: " + System.Environment.NewLine + "\t'abstract member IInterface.MyFunction : int32 * int32 -> unit'" + System.Environment.NewLine + "\t'abstract member IInterface.SomeOtherFunction : int32 * int32 -> unit'" + System.Environment.NewLine + "Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'." - FSharpErrorSeverity.Error, 783, (7, 9, 7, 19), "At least one override did not correctly implement its corresponding abstract member" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 767, Line 8, Col 14, Line 8, Col 34, "The member 'Function' does not correspond to any abstract or virtual method available to override or implement. Maybe you want one of the following:" + System.Environment.NewLine + " MyFunction") + (Error 17, Line 8, Col 19, Line 8, Col 27, "The member 'Function : 'a * 'b -> unit' does not have the correct type to override any given virtual method") + (Error 366, Line 7, Col 3, Line 9, Col 4, "No implementation was given for those members: " + System.Environment.NewLine + "\t'abstract member IInterface.MyFunction : int32 * int32 -> unit'" + System.Environment.NewLine + "\t'abstract member IInterface.SomeOtherFunction : int32 * int32 -> unit'" + System.Environment.NewLine + "Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'.") + (Error 783, Line 7, Col 9, Line 7, Col 19, "At least one override did not correctly implement its corresponding abstract member")] [] let ``Member Has Multiple Possible Dispatch Slots``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type IOverload = abstract member Bar : int -> int abstract member Bar : double -> int @@ -105,23 +100,24 @@ type IOverload = type Overload = interface IOverload with override __.Bar _ = 1 - """ - [| - FSharpErrorSeverity.Error, 366, (7, 15, 7, 24), "No implementation was given for those members: " + System.Environment.NewLine + "\t'abstract member IOverload.Bar : double -> int'" + System.Environment.NewLine + "\t'abstract member IOverload.Bar : int -> int'" + System.Environment.NewLine + "Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'." - FSharpErrorSeverity.Error, 3213, (8, 21, 8, 24), "The member 'Bar<'a0> : 'a0 -> int' matches multiple overloads of the same method.\nPlease restrict it to one of the following:" + System.Environment.NewLine + " Bar : double -> int" + System.Environment.NewLine + " Bar : int -> int." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 366, Line 7, Col 15, Line 7, Col 24, "No implementation was given for those members: " + System.Environment.NewLine + "\t'abstract member IOverload.Bar : double -> int'" + System.Environment.NewLine + "\t'abstract member IOverload.Bar : int -> int'" + System.Environment.NewLine + "Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'.") + (Error 3213, Line 8, Col 21, Line 8, Col 24, "The member 'Bar<'a0> : 'a0 -> int' matches multiple overloads of the same method.\nPlease restrict it to one of the following:" + System.Environment.NewLine + " Bar : double -> int" + System.Environment.NewLine + " Bar : int -> int.")] [] let ``Do Cannot Have Visibility Declarations``() = - CompilerAssert.ParseWithErrors - """ + FSharp """ type X() = do () private do () static member Y() = 1 - """ - [| - FSharpErrorSeverity.Error, 531, (4, 5, 4, 12), "Accessibility modifiers should come immediately prior to the identifier naming a construct" - FSharpErrorSeverity.Error, 512, (4, 13, 4, 18), "Accessibility modifiers are not permitted on 'do' bindings, but 'Private' was given." - FSharpErrorSeverity.Error, 222, (2, 1, 3, 1), "Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule'. Only the last source file of an application may omit such a declaration." - |] + """ + |> parse + |> shouldFail + |> withDiagnostics [ + (Error 531, Line 4, Col 5, Line 4, Col 12, "Accessibility modifiers should come immediately prior to the identifier naming a construct") + (Error 512, Line 4, Col 13, Line 4, Col 18, "Accessibility modifiers are not permitted on 'do' bindings, but 'Private' was given.") + (Error 222, Line 2, Col 1, Line 3, Col 1, "Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule'. Only the last source file of an application may omit such a declaration.")] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConfusingTypeName.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConfusingTypeName.fs index 3c7e569007d..9e3e75b606b 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConfusingTypeName.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConfusingTypeName.fs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities open FSharp.Test.Utilities.Compiler -open FSharp.Test.Utilities.Utilities -open FSharp.Compiler.SourceCodeServices module ``Confusing Type Name`` = diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConstructorTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConstructorTests.fs index c9636e711a5..1806defe4e4 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConstructorTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConstructorTests.fs @@ -1,42 +1,40 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Constructor`` = [] let ``Invalid Record``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Record = {field1:int; field2:int} let doSomething (xs) = List.map (fun {field1=x} -> x) xs doSomething {Record.field1=0; field2=0} - """ - [| - FSharpErrorSeverity.Error, 1, (4, 13, 4, 40), "This expression was expected to have type\n 'Record list' \nbut here has type\n 'Record' " - FSharpErrorSeverity.Warning, 20, (4, 1, 4, 40), "The result of this expression has type 'int list' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 20, Line 4, Col 1, Line 4, Col 40, "The result of this expression has type 'int list' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + (Error 1, Line 4, Col 13, Line 4, Col 40, "This expression was expected to have type\n 'Record list' \nbut here has type\n 'Record' ")] [] let ``Comma In Rec Ctor``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Person = { Name : string; Age : int; City : string } let x = { Name = "Isaac", Age = 21, City = "London" } - """ - [| - FSharpErrorSeverity.Error, 1, (3, 18, 3, 52), "This expression was expected to have type\n 'string' \nbut here has type\n ''a * 'b * 'c' " + System.Environment.NewLine + "A ';' is used to separate field values in records. Consider replacing ',' with ';'." - FSharpErrorSeverity.Error, 764, (3, 9, 3, 54), "No assignment given for field 'Age' of type 'Test.Person'" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1, Line 3, Col 18, Line 3, Col 52, "This expression was expected to have type\n 'string' \nbut here has type\n ''a * 'b * 'c' " + System.Environment.NewLine + "A ';' is used to separate field values in records. Consider replacing ',' with ';'.") + (Error 764, Line 3, Col 9, Line 3, Col 54, "No assignment given for field 'Age' of type 'Test.Person'")] [] let ``Missing Comma In Ctor``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Person() = member val Name = "" with get,set member val Age = 0 with get,set @@ -44,18 +42,18 @@ type Person() = let p = Person(Name = "Fred" Age = 18) - """ - [| - FSharpErrorSeverity.Error, 39, (7, 12, 7, 16), "The value or constructor 'Name' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " nan" - FSharpErrorSeverity.Warning, 20, (7, 12, 7, 25), "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'." - FSharpErrorSeverity.Error, 39, (8, 12, 8, 15), "The value or constructor 'Age' is not defined." - FSharpErrorSeverity.Error, 501, (7, 5, 8, 21), "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'. If some of the arguments are meant to assign values to properties, consider separating those arguments with a comma (',')." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 20, Line 7, Col 12, Line 7, Col 25, "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'.") + (Error 39, Line 7, Col 12, Line 7, Col 16, "The value or constructor 'Name' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " nan") + (Error 39, Line 8, Col 12, Line 8, Col 15, "The value or constructor 'Age' is not defined.") + (Error 501, Line 7, Col 5, Line 8, Col 21, "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'. If some of the arguments are meant to assign values to properties, consider separating those arguments with a comma (',').")] [] let ``Missing Ctor Value``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Person(x:int) = member val Name = "" with get,set member val Age = x with get,set @@ -63,32 +61,29 @@ type Person(x:int) = let p = Person(Name = "Fred", Age = 18) - """ - FSharpErrorSeverity.Error - 496 - (7, 5, 8, 21) - "The member or object constructor 'Person' requires 1 argument(s). The required signature is 'new : x:int -> Person'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 496, Line 7, Col 5, Line 8, Col 21, "The member or object constructor 'Person' requires 1 argument(s). The required signature is 'new : x:int -> Person'.") [] let ``Extra Argument In Ctor``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Person() = member val Name = "" with get,set member val Age = 0 with get,set let p = Person(1) - """ - FSharpErrorSeverity.Error - 501 - (7, 5, 7, 14) - "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 501, Line 7, Col 5, Line 7, Col 14, + "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'.") [] let ``Extra Argument In Ctor2``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Person() = member val Name = "" with get,set member val Age = 0 with get,set @@ -97,18 +92,17 @@ let b = 1 let p = Person(1=b) - """ - FSharpErrorSeverity.Error - 501 - (9, 5, 9, 16) - "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 501, Line 9, Col 5, Line 9, Col 16, + "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'.") [] let ``Valid Comma In Rec Ctor``() = - CompilerAssert.Pass - """ + FSharp """ type Person = { Name : string * bool * bool } let Age = 22 let City = "London" let x = { Name = "Isaac", Age = 21, City = "London" } - """ + """ |> typecheck |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/DontSuggestTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/DontSuggestTests.fs index 13a6f6b0dcc..d1511da554c 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/DontSuggestTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/DontSuggestTests.fs @@ -1,28 +1,25 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Don't Suggest`` = [] let ``Dont Suggest Completely Wrong Stuff``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let _ = Path.GetFullPath "images" - """ - FSharpErrorSeverity.Error - 39 - (2, 9, 2, 13) - ("The value, namespace, type or module 'Path' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Math") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 9, Line 2, Col 13, + "The value, namespace, type or module 'Path' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Math") [] let ``Dont Suggest When Things Are Open``() = - CompilerAssert.ParseWithErrors - """ + FSharp """ module N = let name = "hallo" @@ -30,19 +27,18 @@ type T = static member myMember = 1 let x = N. - """ - [| - FSharpErrorSeverity.Error, 599, (8, 10, 8, 11), "Missing qualification after '.'" - FSharpErrorSeverity.Error, 222, (2, 1, 3, 1), "Files in libraries or multiple-file applications must begin with a namespace or module declaration. When using a module declaration at the start of a file the '=' sign is not allowed. If this is a top-level module, consider removing the = to resolve this error." - |] + """ + |> parse + |> shouldFail + |> withDiagnostics [ + (Error 599, Line 8, Col 10, Line 8, Col 11, "Missing qualification after '.'") + (Error 222, Line 2, Col 1, Line 3, Col 1, "Files in libraries or multiple-file applications must begin with a namespace or module declaration. When using a module declaration at the start of a file the '=' sign is not allowed. If this is a top-level module, consider removing the = to resolve this error.")] [] let ``Dont Suggest Intentionally Unused Variables``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let hober xy _xyz = xyz - """ - FSharpErrorSeverity.Error - 39 - (2, 21, 2, 24) - "The value or constructor 'xyz' is not defined." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 21, Line 2, Col 24, "The value or constructor 'xyz' is not defined.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ElseBranchHasWrongTypeTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ElseBranchHasWrongTypeTests.fs index 54d5c41a68c..8c412854488 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ElseBranchHasWrongTypeTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ElseBranchHasWrongTypeTests.fs @@ -1,48 +1,43 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices - +open FSharp.Test.Utilities.Compiler module ``Else branch has wrong type`` = [] let ``Else branch is int while if branch is string``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let y = if test > 10 then "test" else 123 - """ - FSharpErrorSeverity.Error - 1 - (5, 10, 5, 13) - "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 5, Col 10, Line 5, Col 13, + "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'.") [] let ``Else branch is a function that returns int while if branch is string``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let f x = test let y = if test > 10 then "test" else f 10 - """ - FSharpErrorSeverity.Error - 1 - (6, 10, 6, 14) - "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 6, Col 10, Line 6, Col 14, + "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'.") [] let ``Else branch is a sequence of expressions that returns int while if branch is string``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let f x = x + 4 let y = @@ -51,17 +46,16 @@ let y = else "" |> ignore (f 5) - """ - FSharpErrorSeverity.Error - 1 - (9, 10, 9, 13) - "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 9, Col 10, Line 9, Col 13, + "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'.") [] let ``Else branch is a longer sequence of expressions that returns int while if branch is string``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let f x = x + 4 let y = @@ -72,33 +66,31 @@ let y = let z = f 4 let a = 3 * z (f a) - """ - FSharpErrorSeverity.Error - 1 - (11, 10, 11, 13) - "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 11, Col 10, Line 11, Col 13, + "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'.") [] let ``Else branch context doesn't propagate into function application``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let f x : string = x let y = if test > 10 then "test" else f 123 - """ - FSharpErrorSeverity.Error - 1 - (7, 11, 7, 14) - "This expression was expected to have type\n 'string' \nbut here has type\n 'int' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 7, Col 11, Line 7, Col 14, + "This expression was expected to have type\n 'string' \nbut here has type\n 'int' ") [] let ``Else branch context doesn't propagate into function application even if not last expr``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let f x = printfn "%s" x let y = @@ -106,16 +98,15 @@ let y = else f 123 "test" - """ - FSharpErrorSeverity.Error - 1 - (7, 11, 7, 14) - "This expression was expected to have type\n 'string' \nbut here has type\n 'int' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 7, Col 11, Line 7, Col 14, + "This expression was expected to have type\n 'string' \nbut here has type\n 'int' ") [] let ``Else branch context doesn't propagate into for loop``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let list = [1..10] let y = @@ -125,16 +116,15 @@ let y = printfn "%s" x "test" - """ - FSharpErrorSeverity.Error - 1 - (7, 14, 7, 22) - "This expression was expected to have type\n 'int' \nbut here has type\n 'string' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 7, Col 14, Line 7, Col 22, + "This expression was expected to have type\n 'int' \nbut here has type\n 'string' ") [] let ``Else branch context doesn't propagate to lines before last line``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let list = [1..10] let y = @@ -143,35 +133,39 @@ let y = printfn "%s" 1 "test" - """ - FSharpErrorSeverity.Error - 1 - (7, 22, 7, 23) - "This expression was expected to have type\n 'string' \nbut here has type\n 'int' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 7, Col 22, Line 7, Col 23, + "This expression was expected to have type\n 'string' \nbut here has type\n 'int' ") [] let ``Else branch should not have wrong context type``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ let x = 1 let y : bool = if x = 2 then "A" else "B" - """ - [| FSharpErrorSeverity.Error, 1, (4, 19, 4, 22), "The 'if' expression needs to have type 'bool' to satisfy context type requirements. It currently has type 'string'." - FSharpErrorSeverity.Error, 1, (5, 10, 5, 13), "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'." |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1, Line 4, Col 19, Line 4, Col 22, "The 'if' expression needs to have type 'bool' to satisfy context type requirements. It currently has type 'string'.") + (Error 1, Line 5, Col 10, Line 5, Col 13, "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'.")] [] let ``Else branch has wrong type in nested if``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ let x = 1 if x = 1 then true else if x = 2 then "A" else "B" - """ - [| FSharpErrorSeverity.Error, 1, (5, 19, 5, 22), "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'." - FSharpErrorSeverity.Error, 1, (6, 10, 6, 13), "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'." - FSharpErrorSeverity.Warning, 20, (3, 1, 6, 13), "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 20, Line 3, Col 1, Line 6, Col 13, "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + (Error 1, Line 5, Col 19, Line 5, Col 22, "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'.") + (Error 1, Line 6, Col 10, Line 6, Col 13, "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'.")] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/InvalidNumericLiteralTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/InvalidNumericLiteralTests.fs index 720f18a06da..4862927ba56 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/InvalidNumericLiteralTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/InvalidNumericLiteralTests.fs @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit open FSharp.Test.Utilities +open FSharp.Test.Utilities.Compiler open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.AbstractIL.Internal @@ -25,51 +26,41 @@ module ``Numeric Literals`` = [] [] let ``Invalid Numeric Literals`` literal = - CompilerAssert.TypeCheckSingleError - ("let x = " + literal) - FSharpErrorSeverity.Error - 1156 - (1, 9, 1, 9 + (String.length literal)) - "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger)." + FSharp ("let x = " + literal) + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1156, Line 1, Col 9, Line 1, Col (9 + (String.length literal)), + "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger).") [] let ``3_(dot)1415F is invalid numeric literal``() = - CompilerAssert.TypeCheckWithErrors - """ -let x = 3_.1415F - """ - [| - FSharpErrorSeverity.Error, 1156, (2, 9, 2, 11), "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger)."; - FSharpErrorSeverity.Error, 599, (2, 11, 2, 12),"Missing qualification after '.'" - |] + FSharp "let x = 3_.1415F" + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1156, Line 1, Col 9, Line 1, Col 11, "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger).";) + (Error 599, Line 1, Col 11, Line 1, Col 12,"Missing qualification after '.'")] [] let ``_52 is invalid numeric literal``() = - CompilerAssert.TypeCheckSingleError - """ -let x = _52 - """ - FSharpErrorSeverity.Error - 39 - (2, 9, 2, 12) - "The value or constructor '_52' is not defined." + FSharp "let x = _52" + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 1, Col 9, Line 1, Col 12, "The value or constructor '_52' is not defined.") [] let ``1N is invalid numeric literal``() = - CompilerAssert.TypeCheckSingleError - """ -let x = 1N - """ - FSharpErrorSeverity.Error - 0784 - (2, 9, 2, 11) - "This numeric literal requires that a module 'NumericLiteralN' defining functions FromZero, FromOne, FromInt32, FromInt64 and FromString be in scope" + FSharp "let x = 1N" + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 0784, Line 1, Col 9, Line 1, Col 11, + "This numeric literal requires that a module 'NumericLiteralN' defining functions FromZero, FromOne, FromInt32, FromInt64 and FromString be in scope") [] let ``1N is invalid numeric literal in FSI``() = if Utils.runningOnMono then () - else + else CompilerAssert.RunScriptWithOptions [| "--langversion:preview"; "--test:ErrorRanges" |] """ let x = 1N @@ -79,10 +70,10 @@ let x = 1N "Operation could not be completed due to earlier error" ] + // Regressiont test for FSharp1.0: 2543 - Decimal literals do not support exponents [] [] [] let ``Valid Numeric Literals`` literal = - // Regressiont test for FSharp1.0: 2543 - Decimal literals do not support exponents - - CompilerAssert.Pass ("let x = " + literal) \ No newline at end of file + FSharp ("let x = " + literal) + |> typecheck |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingElseBranch.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingElseBranch.fs index 9ab09f8a9ea..07a40b5c9a0 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingElseBranch.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingElseBranch.fs @@ -1,50 +1,46 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Else branch is missing`` = [] let ``Fail if else branch is missing``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = if x > 10 then "test" - """ - FSharpErrorSeverity.Error - 1 - (4, 19, 4, 25) - "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'string'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 4, Col 19, Line 4, Col 25, + "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'string'.") [] let ``Fail on type error in condition``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = - if x > 10 then + if x > 10 then if x <> "test" then printfn "test" () - """ - FSharpErrorSeverity.Error - 1 - (5, 14, 5, 20) - "This expression was expected to have type\n 'int' \nbut here has type\n 'string' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 5, Col 14, Line 5, Col 20, + "This expression was expected to have type\n 'int' \nbut here has type\n 'string' ") [] let ``Fail if else branch is missing in nesting``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = if x > 10 then ("test") - """ - FSharpErrorSeverity.Error - 1 - (4, 20, 4, 26) - "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'string'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 4, Col 20, Line 4, Col 26, + "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'string'.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingExpressionTests.fs index 802bcfd6750..f91d8b3d683 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingExpressionTests.fs @@ -1,23 +1,21 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Missing Expression`` = - + [] let ``Missing Expression after let``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let sum = 0 for x in 0 .. 10 do let sum = sum + x - """ - FSharpErrorSeverity.Error - 588 - (4,5,4,8) - "The block following this 'let' is unfinished. Every code block is an expression and must have a result. 'let' cannot be the final code element in a block. Consider giving this block an explicit result." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 588, Line 4, Col 5, Line 4, Col 8, + "The block following this 'let' is unfinished. Every code block is an expression and must have a result. 'let' cannot be the final code element in a block. Consider giving this block an explicit result.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleAbbreviationTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleAbbreviationTests.fs index 5c9647fb070..932fde27aa0 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleAbbreviationTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleAbbreviationTests.fs @@ -1,21 +1,17 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Module Abbreviations`` = [] let ``Public Module Abbreviation``() = - CompilerAssert.TypeCheckSingleError - """ -module public L1 = List - """ - FSharpErrorSeverity.Error - 536 - (2, 1, 2, 7) - "The 'Public' accessibility attribute is not allowed on module abbreviation. Module abbreviations are always private." + FSharp "module public L1 = List" + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 536, Line 1, Col 1, Line 1, Col 7, + "The 'Public' accessibility attribute is not allowed on module abbreviation. Module abbreviations are always private.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/NameResolutionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/NameResolutionTests.fs index c48a5c9113b..016bbdcadce 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/NameResolutionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/NameResolutionTests.fs @@ -1,17 +1,15 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module NameResolutionTests = [] let FieldNotInRecord () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type A = { Hello:string; World:string } type B = { Size:int; Height:int } type C = { Wheels:int } @@ -20,16 +18,15 @@ type E = { Unknown:string } type F = { Wallis:int; Size:int; Height:int; } let r:F = { Size=3; Height=4; Wall=1 } - """ - FSharpErrorSeverity.Error - 1129 - (9, 31, 9, 35) - ("The record type 'F' does not contain a label 'Wall'. Maybe you want one of the following:" + System.Environment.NewLine + " Wallis") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1129, Line 9, Col 31, Line 9, Col 35, + ("The record type 'F' does not contain a label 'Wall'. Maybe you want one of the following:" + System.Environment.NewLine + " Wallis")) [] let RecordFieldProposal () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type A = { Hello:string; World:string } type B = { Size:int; Height:int } type C = { Wheels:int } @@ -38,8 +35,8 @@ type E = { Unknown:string } type F = { Wallis:int; Size:int; Height:int; } let r = { Size=3; Height=4; Wall=1 } - """ - FSharpErrorSeverity.Error - 39 - (9, 29, 9, 33) - ("The record label 'Wall' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Walls" + System.Environment.NewLine + " Wallis") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 9, Col 29, Line 9, Col 33, + ("The record label 'Wall' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Walls" + System.Environment.NewLine + " Wallis")) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/SuggestionsTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/SuggestionsTests.fs index bf26f8a2102..abdc7b92ca1 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/SuggestionsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/SuggestionsTests.fs @@ -1,87 +1,69 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module Suggestions = [] let ``Field Suggestion`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Person = { Name : string; } let x = { Person.Names = "Isaac" } - """ - FSharpErrorSeverity.Error - 39 - (4, 18, 4, 23) - ("The type 'Person' does not define the field, constructor or member 'Names'. Maybe you want one of the following:" + System.Environment.NewLine + " Name") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 4, Col 18, Line 4, Col 23, + ("The type 'Person' does not define the field, constructor or member 'Names'. Maybe you want one of the following:" + System.Environment.NewLine + " Name")) [] let ``Suggest Array Module Functions`` () = - CompilerAssert.TypeCheckSingleError - """ -let f = - Array.blt - """ - FSharpErrorSeverity.Error - 39 - (3, 11, 3, 14) - ("The value, constructor, namespace or type 'blt' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " blit") - + FSharp "let f = Array.blt" + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 1, Col 15, Line 1, Col 18, + ("The value, constructor, namespace or type 'blt' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " blit")) [] let ``Suggest Async Module`` () = - CompilerAssert.TypeCheckSingleError - """ -let f = - Asnc.Sleep 1000 - """ - FSharpErrorSeverity.Error - 39 - (3, 5, 3, 9) - ("The value, namespace, type or module 'Asnc' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Async" + System.Environment.NewLine + " async" + System.Environment.NewLine + " asin" + System.Environment.NewLine + " snd") - + FSharp "let f = Asnc.Sleep 1000" + |> typecheck + |> shouldFail + |> withSingleDiagnostic( Error 39, Line 1, Col 9, Line 1, Col 13, + ("The value, namespace, type or module 'Asnc' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Async" + System.Environment.NewLine + " async" + System.Environment.NewLine + " asin" + System.Environment.NewLine + " snd")) [] let ``Suggest Attribute`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyClass<'Bar>() = abstract M<'T> : 'T -> 'T abstract M2<'T> : 'T -> 'Bar - """ - FSharpErrorSeverity.Error - 39 - (2, 3, 2, 15) - ("The type 'AbstractClas' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " AbstractClass" + System.Environment.NewLine + " AbstractClassAttribute") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 3, Line 2, Col 15, + ("The type 'AbstractClas' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " AbstractClass" + System.Environment.NewLine + " AbstractClassAttribute")) [] let ``Suggest Double Backtick Identifiers`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module N = let ``longer name`` = "hallo" let x = N.``longe name`` - """ - FSharpErrorSeverity.Error - 39 - (5, 11, 5, 25) - ("The value, constructor, namespace or type 'longe name' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " longer name") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 5, Col 11, Line 5, Col 25, + ("The value, constructor, namespace or type 'longe name' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " longer name")) [] let ``Suggest Double Backtick Unions`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module N = type MyUnion = | ``My Case1`` @@ -90,46 +72,41 @@ module N = open N let x = N.MyUnion.``My Case2`` - """ - FSharpErrorSeverity.Error - 39 - (9, 19, 9,31) - ("The type 'MyUnion' does not define the field, constructor or member 'My Case2'. Maybe you want one of the following:" + System.Environment.NewLine + " My Case1" + System.Environment.NewLine + " Case2") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 9, Col 19, Line 9,Col 31, + ("The type 'MyUnion' does not define the field, constructor or member 'My Case2'. Maybe you want one of the following:" + System.Environment.NewLine + " My Case1" + System.Environment.NewLine + " Case2")) [] let ``Suggest Fields In Constructor`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyClass() = member val MyProperty = "" with get, set member val MyProperty2 = "" with get, set member val ABigProperty = "" with get, set let c = MyClass(Property = "") - """ - FSharpErrorSeverity.Error - 495 - (7, 17, 7, 25) - ("The object constructor 'MyClass' has no argument or settable return property 'Property'. The required signature is new : unit -> MyClass. Maybe you want one of the following:" + System.Environment.NewLine + " MyProperty" + System.Environment.NewLine + " MyProperty2" + System.Environment.NewLine + " ABigProperty") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 495, Line 7, Col 17, Line 7, Col 25, + ("The object constructor 'MyClass' has no argument or settable return property 'Property'. The required signature is new : unit -> MyClass. Maybe you want one of the following:" + System.Environment.NewLine + " MyProperty" + System.Environment.NewLine + " MyProperty2" + System.Environment.NewLine + " ABigProperty")) [] let ``Suggest Generic Type`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type T = System.Collections.Generic.Dictionary - """ - FSharpErrorSeverity.Error - 39 - (2, 48, 2, 53) - ("The type 'int11' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " int16" + System.Environment.NewLine + " int16`1" + System.Environment.NewLine + " int8" + System.Environment.NewLine + " uint16" + System.Environment.NewLine + " int") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 48, Line 2, Col 53, + ("The type 'int11' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " int16" + System.Environment.NewLine + " int16`1" + System.Environment.NewLine + " int8" + System.Environment.NewLine + " uint16" + System.Environment.NewLine + " int")) [] let ``Suggest Methods`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Test2 = type D() = @@ -138,61 +115,53 @@ module Test2 = member x.Method1() = 10 D.Method2() - """ - FSharpErrorSeverity.Error - 39 - (9, 7, 9, 14) - ("The type 'D' does not define the field, constructor or member 'Method2'. Maybe you want one of the following:" + System.Environment.NewLine + " Method1") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 9, Col 7, Line 9, Col 14, + ("The type 'D' does not define the field, constructor or member 'Method2'. Maybe you want one of the following:" + System.Environment.NewLine + " Method1")) [] let ``Suggest Modules`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Collections = let f () = printfn "%s" "Hello" open Collectons - """ - FSharpErrorSeverity.Error - 39 - (6, 6, 6, 16) - ("The namespace or module 'Collectons' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Collections") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 6, Col 6, Line 6, Col 16, + ("The namespace or module 'Collectons' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Collections")) [] let ``Suggest Namespaces`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ open System.Collectons - """ - FSharpErrorSeverity.Error - 39 - (2, 13, 2, 23) - "The namespace 'Collectons' is not defined." - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 13, Line 2, Col 23, + "The namespace 'Collectons' is not defined.") [] let ``Suggest Record Labels`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyRecord = { Hello: int; World: bool} let r = { Hello = 2 ; World = true} let x = r.ello - """ - FSharpErrorSeverity.Error - 39 - (6, 11, 6, 15) - ("The type 'MyRecord' does not define the field, constructor or member 'ello'. Maybe you want one of the following:" + System.Environment.NewLine + " Hello") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 6, Col 11, Line 6, Col 15, + ("The type 'MyRecord' does not define the field, constructor or member 'ello'. Maybe you want one of the following:" + System.Environment.NewLine + " Hello")) [] let ``Suggest Record Type for RequireQualifiedAccess Records`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyRecord = { Field1: string @@ -200,17 +169,15 @@ type MyRecord = { } let r = { Field1 = "hallo"; Field2 = 1 } - """ - FSharpErrorSeverity.Error - 39 - (8, 11, 8, 17) - ("The record label 'Field1' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " MyRecord.Field1") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 8, Col 11, Line 8, Col 17, + ("The record label 'Field1' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " MyRecord.Field1")) [] let ``Suggest To Use Indexer`` () = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ let d = [1,1] |> dict let y = d[1] @@ -218,90 +185,82 @@ let z = d[|1|] let f() = d let a = (f())[1] - """ - [| - FSharpErrorSeverity.Error, 3217, (3, 9, 3, 10), "This value is not a function and cannot be applied. Did you intend to access the indexer via d.[index] instead?" - FSharpErrorSeverity.Error, 3, (5, 9, 5, 10), "This value is not a function and cannot be applied." - FSharpErrorSeverity.Error, 3217, (8, 10, 8, 13), "This expression is not a function and cannot be applied. Did you intend to access the indexer via expr.[index] instead?" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3217, Line 3, Col 9, Line 3, Col 10, "This value is not a function and cannot be applied. Did you intend to access the indexer via d.[index] instead?") + (Error 3, Line 5, Col 9, Line 5, Col 10, "This value is not a function and cannot be applied.") + (Error 3217, Line 8, Col 10, Line 8, Col 13, "This expression is not a function and cannot be applied. Did you intend to access the indexer via expr.[index] instead?")] [] let ``Suggest Type Parameters`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyClass<'Bar>() = abstract M<'T> : 'T -> 'T abstract M2<'T> : 'T -> 'Bar abstract M3<'T> : 'T -> 'B - """ - FSharpErrorSeverity.Error - 39 - (6, 28, 6, 30) - "The type parameter 'B is not defined." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 6, Col 28, Line 6, Col 30, + "The type parameter 'B is not defined.") [] let ``Suggest Types in Module`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x : System.Collections.Generic.Lst = ResizeArray() - """ - FSharpErrorSeverity.Error - 39 - (2, 36, 2, 39) - ("The type 'Lst' is not defined in 'System.Collections.Generic'. Maybe you want one of the following:" + System.Environment.NewLine + " List" + System.Environment.NewLine + " IList" + System.Environment.NewLine + " List`1") + """ |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 36, Line 2, Col 39, + ("The type 'Lst' is not defined in 'System.Collections.Generic'. Maybe you want one of the following:" + System.Environment.NewLine + " List" + System.Environment.NewLine + " IList" + System.Environment.NewLine + " List`1")) [] let ``Suggest Types in Namespace`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = System.DateTie.MaxValue - """ - FSharpErrorSeverity.Error - 39 - (2, 16, 2, 23) - ("The value, constructor, namespace or type 'DateTie' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " DateTime" + System.Environment.NewLine + " DateTimeKind" + System.Environment.NewLine + " DateTimeOffset" + System.Environment.NewLine + " Data") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 16, Line 2, Col 23, + ("The value, constructor, namespace or type 'DateTie' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " DateTime" + System.Environment.NewLine + " DateTimeKind" + System.Environment.NewLine + " DateTimeOffset" + System.Environment.NewLine + " Data")) [] let ``Suggest Union Cases`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyUnion = | ASimpleCase | AnotherCase of int let u = MyUnion.AntherCase - """ - FSharpErrorSeverity.Error - 39 - (6, 17, 6, 27) - ("The type 'MyUnion' does not define the field, constructor or member 'AntherCase'. Maybe you want one of the following:" + System.Environment.NewLine + " AnotherCase") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 6, Col 17, Line 6, Col 27, + ("The type 'MyUnion' does not define the field, constructor or member 'AntherCase'. Maybe you want one of the following:" + System.Environment.NewLine + " AnotherCase")) [] let ``Suggest Union Type for RequireQualifiedAccess Unions`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyUnion = | MyCase1 | MyCase2 of string let x : MyUnion = MyCase1 - """ - FSharpErrorSeverity.Error - 39 - (7, 19, 7, 26) - ("The value or constructor 'MyCase1' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " MyUnion.MyCase1") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 7, Col 19, Line 7, Col 26, + ("The value or constructor 'MyCase1' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " MyUnion.MyCase1")) [] let ``Suggest Unions in PatternMatch`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyUnion = | Case1 @@ -313,8 +272,8 @@ let x = match y with | MyUnion.Cas1 -> 1 | _ -> 2 - """ - FSharpErrorSeverity.Error - 39 - (11, 15, 11, 19) - ("The type 'MyUnion' does not define the field, constructor or member 'Cas1'. Maybe you want one of the following:" + System.Environment.NewLine + " Case1" + System.Environment.NewLine + " Case2") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 11, Col 15, Line 11, Col 19, + ("The type 'MyUnion' does not define the field, constructor or member 'Cas1'. Maybe you want one of the following:" + System.Environment.NewLine + " Case1" + System.Environment.NewLine + " Case2")) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs index 817e0a588c6..8d2d86d1d7c 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs @@ -1,81 +1,75 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Type Mismatch`` = [] let ``return Instead Of return!``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let rec foo() = async { return foo() } - """ - FSharpErrorSeverity.Error - 1 - (2, 32, 2, 37) - "Type mismatch. Expecting a\n ''a' \nbut given a\n 'Async<'a>' \nThe types ''a' and 'Async<'a>' cannot be unified. Consider using 'return!' instead of 'return'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 2, Col 32, Line 2, Col 37, + "Type mismatch. Expecting a\n ''a' \nbut given a\n 'Async<'a>' \nThe types ''a' and 'Async<'a>' cannot be unified. Consider using 'return!' instead of 'return'.") [] let ``yield Instead Of yield!``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Foo() = member this.Yield(x) = [x] let rec f () = Foo() { yield f ()} - """ - FSharpErrorSeverity.Error - 1 - (5, 30, 5, 34) - "Type mismatch. Expecting a\n ''a' \nbut given a\n ''a list' \nThe types ''a' and ''a list' cannot be unified. Consider using 'yield!' instead of 'yield'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 5, Col 30, Line 5, Col 34, + "Type mismatch. Expecting a\n ''a' \nbut given a\n ''a list' \nThe types ''a' and ''a list' cannot be unified. Consider using 'yield!' instead of 'yield'.") [] let ``Ref Cell Instead Of Not``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = true if !x then printfn "hello" - """ - FSharpErrorSeverity.Error - 1 - (3, 5, 3, 6) - ("This expression was expected to have type\n 'bool ref' \nbut here has type\n 'bool' " + System.Environment.NewLine + "The '!' operator is used to dereference a ref cell. Consider using 'not expr' here.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 3, Col 5, Line 3, Col 6, + ("This expression was expected to have type\n 'bool ref' \nbut here has type\n 'bool' " + System.Environment.NewLine + "The '!' operator is used to dereference a ref cell. Consider using 'not expr' here.")) [] let ``Ref Cell Instead Of Not 2``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = true let y = !x - """ - FSharpErrorSeverity.Error - 1 - (3, 10, 3, 11) - ("This expression was expected to have type\n ''a ref' \nbut here has type\n 'bool' " + System.Environment.NewLine + "The '!' operator is used to dereference a ref cell. Consider using 'not expr' here.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 3, Col 10, Line 3, Col 11, + ("This expression was expected to have type\n ''a ref' \nbut here has type\n 'bool' " + System.Environment.NewLine + "The '!' operator is used to dereference a ref cell. Consider using 'not expr' here.")) [] let ``Guard Has Wrong Type``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ let x = 1 match x with | 1 when "s" -> true | _ -> false - """ - [| - FSharpErrorSeverity.Error, 1, (4, 10, 4, 13), "A pattern match guard must be of type 'bool', but this 'when' expression is of type 'string'." - FSharpErrorSeverity.Warning, 20, (3, 1, 5, 13), "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 20, Line 3, Col 1, Line 5, Col 13, "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + (Error 1, Line 4, Col 10, Line 4, Col 13, "A pattern match guard must be of type 'bool', but this 'when' expression is of type 'string'.")] [] let ``Runtime Type Test In Pattern``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() @@ -84,16 +78,16 @@ let c = match orig with | :? IDictionary -> "yes" | _ -> "no" - """ - [| - FSharpErrorSeverity.Warning, 67, (8, 5, 8, 28), "This type test or downcast will always hold" - FSharpErrorSeverity.Error, 193, (8, 5, 8, 28), "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 67, Line 8, Col 5, Line 8, Col 28, "This type test or downcast will always hold") + (Error 193, Line 8, Col 5, Line 8, Col 28, "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n")] [] let ``Runtime Type Test In Pattern 2``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() @@ -102,16 +96,16 @@ let c = match orig with | :? IDictionary as y -> "yes" + y.ToString() | _ -> "no" - """ - [| - FSharpErrorSeverity.Warning, 67, (8, 5, 8, 28), "This type test or downcast will always hold" - FSharpErrorSeverity.Error, 193, (8, 5, 8, 28), "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 67, Line 8, Col 5, Line 8, Col 28, "This type test or downcast will always hold") + (Error 193, Line 8, Col 5, Line 8, Col 28, "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n")] [] let ``Override Errors``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Base() = abstract member Member: int * string -> string default x.Member (i, s) = s @@ -127,9 +121,10 @@ type Derived2() = type Derived3() = inherit Base() override x.Member (s : string, i : int) = sprintf "Hello %s" s - """ - [| - FSharpErrorSeverity.Error, 856, (8, 16, 8, 22), "This override takes a different number of arguments to the corresponding abstract member. The following abstract members were found:" + System.Environment.NewLine + " abstract member Base.Member : int * string -> string" - FSharpErrorSeverity.Error, 856, (12, 16, 12, 22), "This override takes a different number of arguments to the corresponding abstract member. The following abstract members were found:" + System.Environment.NewLine + " abstract member Base.Member : int * string -> string" - FSharpErrorSeverity.Error, 1, (16, 24, 16, 34), "This expression was expected to have type\n 'int' \nbut here has type\n 'string' " - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 856, Line 8, Col 16, Line 8, Col 22, "This override takes a different number of arguments to the corresponding abstract member. The following abstract members were found:" + System.Environment.NewLine + " abstract member Base.Member : int * string -> string") + (Error 856, Line 12, Col 16, Line 12, Col 22, "This override takes a different number of arguments to the corresponding abstract member. The following abstract members were found:" + System.Environment.NewLine + " abstract member Base.Member : int * string -> string") + (Error 1, Line 16, Col 24, Line 16, Col 34, "This expression was expected to have type\n 'int' \nbut here has type\n 'string' ")] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnitGenericAbstactType.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnitGenericAbstactType.fs index 11ec30ab19a..2b244e12150 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnitGenericAbstactType.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnitGenericAbstactType.fs @@ -1,28 +1,25 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Unit generic abstract Type`` = [] - let ``Unit can not be used as return type of abstract method paramete on return type``() = - CompilerAssert.TypeCheckSingleError - """ + let ``Unit can not be used as return type of abstract method paramete on return type``() = + FSharp """ type EDF<'S> = abstract member Apply : int -> 'S type SomeEDF () = interface EDF with - member this.Apply d = + member this.Apply d = // [ERROR] The member 'Apply' does not have the correct type to override the corresponding abstract method. () - """ - FSharpErrorSeverity.Error - 17 - (6, 21, 6, 26) - "The member 'Apply : int -> unit' is specialized with 'unit' but 'unit' can't be used as return type of an abstract method parameterized on return type." - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 17, Line 6, Col 21, Line 6, Col 26, + "The member 'Apply : int -> unit' is specialized with 'unit' but 'unit' can't be used as return type of an abstract method parameterized on return type.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UpcastDowncastTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UpcastDowncastTests.fs index 79e32f9e3e0..61fcaadf12d 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UpcastDowncastTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UpcastDowncastTests.fs @@ -1,51 +1,49 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Upcast and Downcast`` = [] let ``Downcast Instead Of Upcast``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() :> IDictionary let c = orig :> Dictionary - """ - FSharpErrorSeverity.Error - 193 - (5, 9, 5, 36) - "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n" + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 193, Line 5, Col 9, Line 5, Col 36, + "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n") [] let ``Upcast Instead Of Downcast``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() let c = orig :?> IDictionary - """ - [| - FSharpErrorSeverity.Warning, 67, (5, 9, 5, 38), "This type test or downcast will always hold" - FSharpErrorSeverity.Error, 3198, (5, 9, 5, 38), "The conversion from Dictionary to IDictionary is a compile-time safe upcast, not a downcast. Consider using the :> (upcast) operator instead of the :?> (downcast) operator." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 67, Line 5, Col 9, Line 5, Col 38, "This type test or downcast will always hold") + (Error 3198, Line 5, Col 9, Line 5, Col 38, "The conversion from Dictionary to IDictionary is a compile-time safe upcast, not a downcast. Consider using the :> (upcast) operator instead of the :?> (downcast) operator.")] [] let ``Upcast Function Instead Of Downcast``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() let c : IDictionary = downcast orig - """ - [| - FSharpErrorSeverity.Warning, 67, (5, 32, 5, 45), "This type test or downcast will always hold" - FSharpErrorSeverity.Error, 3198, (5, 32, 5, 45), "The conversion from Dictionary to IDictionary is a compile-time safe upcast, not a downcast. Consider using 'upcast' instead of 'downcast'." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 67, Line 5, Col 32, Line 5, Col 45, "This type test or downcast will always hold") + (Error 3198, Line 5, Col 32, Line 5, Col 45, "The conversion from Dictionary to IDictionary is a compile-time safe upcast, not a downcast. Consider using 'upcast' instead of 'downcast'.")] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs index 565e423407b..28487c97aa6 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs @@ -1,62 +1,57 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Warn Expression`` = [] let ``Warn If Expression Result Unused``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ 1 + 2 printfn "%d" 3 - """ - FSharpErrorSeverity.Warning - 20 - (2, 1, 2, 6) - "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 2, Col 1, Line 2, Col 6, + "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn If Possible Assignment``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = "hello" let changeX() = x = 20 y = "test" - """ - FSharpErrorSeverity.Warning - 20 - (6, 5, 6, 11) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to mutate a value, then mark the value 'mutable' and use the '<-' operator e.g. 'x <- expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 11, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to mutate a value, then mark the value 'mutable' and use the '<-' operator e.g. 'x <- expression'.") [] let ``Warn If Possible Assignment To Mutable``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let mutable x = 10 let y = "hello" let changeX() = x = 20 y = "test" - """ - FSharpErrorSeverity.Warning - 20 - (6, 5, 6, 11) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to mutate a value, then use the '<-' operator e.g. 'x <- expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 11, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to mutate a value, then use the '<-' operator e.g. 'x <- expression'.") [] let ``Warn If Possible dotnet Property Setter``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System let z = System.Timers.Timer() @@ -65,16 +60,16 @@ let y = "hello" let changeProperty() = z.Enabled = true y = "test" - """ - [| - FSharpErrorSeverity.Warning, 760, (4, 9, 4, 30), "It is recommended that objects supporting the IDisposable interface are created using the syntax 'new Type(args)', rather than 'Type(args)' or 'Type' as a function value representing the constructor, to indicate that resources may be owned by the generated value" - FSharpErrorSeverity.Warning, 20, (8, 5, 8, 21), "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to set a value to a property, then use the '<-' operator e.g. 'z.Enabled <- expression'." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 760, Line 4, Col 9, Line 4, Col 30, "It is recommended that objects supporting the IDisposable interface are created using the syntax 'new Type(args)', rather than 'Type(args)' or 'Type' as a function value representing the constructor, to indicate that resources may be owned by the generated value") + (Warning 20, Line 8, Col 5, Line 8, Col 21, "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to set a value to a property, then use the '<-' operator e.g. 'z.Enabled <- expression'.")] [] let ``Don't Warn If Property Without Setter``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyClass(property1 : int) = member val Property2 = "" with get @@ -84,33 +79,30 @@ let y = "hello" let changeProperty() = x.Property2 = "22" y = "test" - """ - FSharpErrorSeverity.Warning - 20 - (9, 5, 9, 23) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 9, Col 5, Line 9, Col 23, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'.") [] let ``Warn If Implicitly Discarded``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = 20 let changeX() = y * x = 20 y = 30 - """ - FSharpErrorSeverity.Warning - 20 - (6, 5, 6, 15) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 15, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'.") [] let ``Warn If Discarded In List``() = - CompilerAssert.TypeCheckWithErrorsAndOptions - [| "--langversion:4.6" |] - """ + FSharp """ let div _ _ = 1 let subView _ _ = [1; 2] @@ -120,19 +112,16 @@ let view model dispatch = yield! subView model dispatch div [] [] ] - """ - [| - FSharpErrorSeverity.Warning, - 3221, - (9, 8, 9, 17), - "This expression returns a value of type 'int' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield'." - |] + """ + |> withOptions ["--langversion:4.6"] + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3221, Line 9, Col 8, Line 9, Col 17, + "This expression returns a value of type 'int' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield'.") [] let ``Warn If Discarded In List 2``() = - CompilerAssert.TypeCheckWithErrorsAndOptions - [| "--langversion:4.6" |] - """ + FSharp """ // stupid things to make the sample compile let div _ _ = 1 let subView _ _ = [1; 2] @@ -147,19 +136,16 @@ let view model dispatch = | _ -> subView model dispatch ] ] - """ - [| - FSharpErrorSeverity.Warning, - 3222, - (13, 19, 13, 41), - "This expression returns a value of type 'int list' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield!'." - |] + """ + |> withOptions ["--langversion:4.6"] + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3222, Line 13, Col 19, Line 13, Col 41, + "This expression returns a value of type 'int list' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield!'.") [] let ``Warn If Discarded In List 3``() = - CompilerAssert.TypeCheckWithErrorsAndOptions - [| "--langversion:4.6" |] - """ + FSharp """ // stupid things to make the sample compile let div _ _ = 1 let subView _ _ = true @@ -174,33 +160,30 @@ let view model dispatch = | _ -> subView model dispatch ] ] - """ - [| - FSharpErrorSeverity.Warning, - 20, - (13, 19, 13, 41), - "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." - |] + """ + |> withOptions ["--langversion:4.6"] + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 13, Col 19, Line 13, Col 41, + "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn Only On Last Expression``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let mutable x = 0 while x < 1 do printfn "unneeded" x <- x + 1 true - """ - FSharpErrorSeverity.Warning - 20 - (6, 5, 6, 9) - "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 9, + "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn If Possible Property Setter``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyClass(property1 : int) = member val Property1 = property1 member val Property2 = "" with get, set @@ -211,17 +194,16 @@ let y = "hello" let changeProperty() = x.Property2 = "20" y = "test" - """ - FSharpErrorSeverity.Warning - 20 - (10, 5, 10, 23) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to set a value to a property, then use the '<-' operator e.g. 'x.Property2 <- expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 10, Col 5, Line 10, Col 23, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to set a value to a property, then use the '<-' operator e.g. 'x.Property2 <- expression'.") [] let ``Dont warn external function as unused``() = - CompilerAssert.Pass - """ + FSharp """ open System open System.Runtime.InteropServices @@ -240,4 +222,6 @@ let main _argv = let _ = Test.ExtractIconEx("", 0, [| |], [| |], 0u) 0 - """ + """ + |> typecheck + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WrongSyntaxInForLoop.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WrongSyntaxInForLoop.fs index d7cd6068103..89e3707c243 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WrongSyntaxInForLoop.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WrongSyntaxInForLoop.fs @@ -1,20 +1,21 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Wrong syntax in for loop`` = [] let ``Equals instead of in``() = - CompilerAssert.ParseWithErrors - """ + FSharp """ module X for i = 0 .. 100 do () - """ - [|FSharpErrorSeverity.Error, 3215, (3, 7, 3, 8), "Unexpected symbol '=' in expression. Did you intend to use 'for x in y .. z do' instead?" |] + """ + |> parse + |> shouldFail + |> withSingleDiagnostic (Error 3215, Line 3, Col 7, Line 3, Col 8, + "Unexpected symbol '=' in expression. Did you intend to use 'for x in y .. z do' instead?") diff --git a/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs b/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs index 929eab5c809..0c8b76a9da0 100644 --- a/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.Interop.ComponentTests +namespace FSharp.Compiler.ComponentTests.Interop open Xunit -open FSharp.Test.Utilities open FSharp.Test.Utilities.Compiler module ``C# <-> F# basic interop`` = diff --git a/tests/FSharp.Compiler.ComponentTests/Language/CodeQuotationTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/CodeQuotationTests.fs index 16ee721cd0c..03712d32e1a 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/CodeQuotationTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/CodeQuotationTests.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.Language.CodeQuatation +namespace FSharp.Compiler.ComponentTests.Language open Xunit open FSharp.Test.Utilities.Compiler diff --git a/tests/FSharp.Compiler.ComponentTests/Language/CompilerDirectiveTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/CompilerDirectiveTests.fs index 9f9d949667c..32219920d93 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/CompilerDirectiveTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/CompilerDirectiveTests.fs @@ -1,11 +1,9 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.Language.ComponentTests +namespace FSharp.Compiler.ComponentTests.Language open Xunit -open FSharp.Test.Utilities open FSharp.Test.Utilities.Compiler -open FSharp.Compiler.SourceCodeServices module ``Test Compiler Directives`` = diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 88c137194ce..b31da1d4866 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -197,11 +197,11 @@ module rec Compiler = let result = compileFSharpCompilation cmpl false match result with | Failure f -> - let message = sprintf "Compilation failed (expected to succeed).\n All errors:\n%A" (f.Errors @ f.Warnings) + let message = sprintf "Operation failed (expected to succeed).\n All errors:\n%A" (f.Errors @ f.Warnings) failwith message | Success s -> match s.OutputPath with - | None -> failwith "Compilation didn't produce any output!" + | None -> failwith "Operation didn't produce any output!" | Some p -> p |> MetadataReference.CreateFromFile | _ -> failwith "Conversion isn't possible" @@ -318,6 +318,31 @@ module rec Compiler = | CS cs -> compileCSharp cs | _ -> failwith "TODO" + let private parseFSharp (fsSource: FSharpCompilationSource) : TestResult = + let source = getSource fsSource.Source + let parseResults = CompilerAssert.Parse source + let failed = parseResults.ParseHadErrors + + let (errors, warnings) = parseResults.Errors |> fromFSharpErrorInfo + + let result = + { OutputPath = None + Dependencies = [] + Adjust = 0 + Warnings = errors + Errors = warnings + Output = None } + + if failed then + Failure result + else + Success result + + let parse (cUnit: CompilationUnit) : TestResult = + match cUnit with + | FS fs -> parseFSharp fs + | _ -> failwith "Parsing only supported for F#." + let private typecheckFSharpWithBaseline (options: string list) (dir: string) (file: string) : TestResult = // Since TypecheckWithErrorsAndOptionsAgainsBaseLine throws if doesn't match expected baseline, // We return a successfull TestResult if it succeeds. @@ -436,12 +461,12 @@ module rec Compiler = match result with | Success _ -> result | Failure r -> - let message = sprintf "Compilation failed (expected to succeed).\n All errors:\n%A" (r.Errors @ r.Warnings) + let message = sprintf "Operation failed (expected to succeed).\n All errors:\n%A" (r.Errors @ r.Warnings) failwith message let shouldFail (result: TestResult) : TestResult = match result with - | Success _ -> failwith "Compilation was \"Success\" (expected: \"Failure\")." + | Success _ -> failwith "Operation was succeeded (expected to fail)." | Failure _ -> result let private assertResultsCategory (what: string) (selector: Output -> ErrorInfo list) (expected: ErrorInfo list) (result: TestResult) : TestResult = diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 5ec91cf5ba9..4958d738593 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -665,10 +665,13 @@ let main argv = 0""" static member RunScript source expectedErrorMessages = CompilerAssert.RunScriptWithOptions [||] source expectedErrorMessages - static member ParseWithErrors (source: string) expectedParseErrors = + static member Parse (source: string) = let sourceFileName = "test.fs" let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| sourceFileName |] } - let parseResults = checker.ParseFile(sourceFileName, SourceText.ofString source, parsingOptions) |> Async.RunSynchronously + checker.ParseFile(sourceFileName, SourceText.ofString source, parsingOptions) |> Async.RunSynchronously + + static member ParseWithErrors (source: string) expectedParseErrors = + let parseResults = CompilerAssert.Parse source Assert.True(parseResults.ParseHadErrors) From 0a88a668ef4cca711e703866d9b07470fe01eaec Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 30 Jul 2020 22:26:38 +0200 Subject: [PATCH 85/87] Move existing Compiler.ComponentTests to a new Compiler.fs framework (#9839) * Move existing Compiler.ComponentTests to a new Compiler.fs framework; Add 'parse' function * Changed some wording in error messages --- .../ConstraintSolver/MemberConstraints.fs | 2 +- .../ConstraintSolver/PrimitiveConstraints.fs | 2 +- .../AccessOfTypeAbbreviationTests.fs | 63 ++-- .../ErrorMessages/AssignmentErrorTests.fs | 19 +- .../ErrorMessages/ClassesTests.fs | 110 ++++--- .../ErrorMessages/ConfusingTypeName.fs | 5 +- .../ErrorMessages/ConstructorTests.fs | 94 +++--- .../ErrorMessages/DontSuggestTests.fs | 44 ++- .../ElseBranchHasWrongTypeTests.fs | 136 ++++----- .../InvalidNumericLiteralTests.fs | 61 ++-- .../ErrorMessages/MissingElseBranch.fs | 46 ++- .../ErrorMessages/MissingExpressionTests.fs | 20 +- .../ErrorMessages/ModuleAbbreviationTests.fs | 18 +- .../ErrorMessages/NameResolutionTests.fs | 31 +- .../ErrorMessages/SuggestionsTests.fs | 283 ++++++++---------- .../ErrorMessages/TypeMismatchTests.fs | 115 ++++--- .../ErrorMessages/UnitGenericAbstactType.fs | 23 +- .../ErrorMessages/UpcastDowncastTests.fs | 46 ++- .../ErrorMessages/WarnExpressionTests.fs | 168 +++++------ .../ErrorMessages/WrongSyntaxInForLoop.fs | 15 +- .../Interop/SimpleInteropTests.fs | 3 +- .../Language/CodeQuotationTests.fs | 2 +- .../Language/CompilerDirectiveTests.fs | 4 +- tests/FSharp.Test.Utilities/Compiler.fs | 33 +- tests/FSharp.Test.Utilities/CompilerAssert.fs | 7 +- 25 files changed, 630 insertions(+), 720 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs index f14c2495c37..77ae35463fc 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/MemberConstraints.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ConstraintSolver.ComponentTests +namespace FSharp.Compiler.ComponentTests.ConstraintSolver open Xunit open FSharp.Test.Utilities.Compiler diff --git a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/PrimitiveConstraints.fs b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/PrimitiveConstraints.fs index 873864edaff..a22d61db60c 100644 --- a/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/PrimitiveConstraints.fs +++ b/tests/FSharp.Compiler.ComponentTests/ConstraintSolver/PrimitiveConstraints.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ConstraintSolver.ComponentTests +namespace FSharp.Compiler.ComponentTests.ConstraintSolver open Xunit open FSharp.Test.Utilities.Compiler diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AccessOfTypeAbbreviationTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AccessOfTypeAbbreviationTests.fs index 416c62d63f6..df9acf7efca 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AccessOfTypeAbbreviationTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AccessOfTypeAbbreviationTests.fs @@ -1,75 +1,74 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities +open FSharp.Test.Utilities.Compiler open FSharp.Compiler.SourceCodeServices module ``Access Of Type Abbreviation`` = + let warning44Message = "This construct is deprecated. The type 'Hidden' is less accessible than the value, member or type 'Exported' it is used in." + System.Environment.NewLine + "As of F# 4.1, the accessibility of type abbreviations is checked at compile-time. Consider changing the accessibility of the type abbreviation. Ignoring this warning might lead to runtime errors." + [] let ``Private type produces warning when trying to export``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Library = type private Hidden = Hidden of unit type Exported = Hidden - """ - FSharpErrorSeverity.Warning - 44 - (4, 8, 4, 16) - ("This construct is deprecated. The type 'Hidden' is less accessible than the value, member or type 'Exported' it is used in." + System.Environment.NewLine + "As of F# 4.1, the accessibility of type abbreviations is checked at compile-time. Consider changing the accessibility of the type abbreviation. Ignoring this warning might lead to runtime errors.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 44, Line 4, Col 8, Line 4, Col 16, warning44Message) [] let ``Internal type passes when abbrev is internal``() = - CompilerAssert.Pass - """ + FSharp """ module Library = type internal Hidden = Hidden of unit type internal Exported = Hidden - """ + """ + |> typecheck + |> shouldSucceed [] let ``Internal type produces warning when trying to export``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Library = type internal Hidden = Hidden of unit type Exported = Hidden - """ - FSharpErrorSeverity.Warning - 44 - (4, 8, 4, 16) - ("This construct is deprecated. The type 'Hidden' is less accessible than the value, member or type 'Exported' it is used in." + System.Environment.NewLine + "As of F# 4.1, the accessibility of type abbreviations is checked at compile-time. Consider changing the accessibility of the type abbreviation. Ignoring this warning might lead to runtime errors.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 44, Line 4, Col 8, Line 4, Col 16, warning44Message) [] let ``Private type produces warning when abbrev is internal``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Library = type private Hidden = Hidden of unit type internal Exported = Hidden - """ - FSharpErrorSeverity.Warning - 44 - (4, 17, 4, 25) - ("This construct is deprecated. The type 'Hidden' is less accessible than the value, member or type 'Exported' it is used in." + System.Environment.NewLine + "As of F# 4.1, the accessibility of type abbreviations is checked at compile-time. Consider changing the accessibility of the type abbreviation. Ignoring this warning might lead to runtime errors.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 44, Line 4, Col 17, Line 4, Col 25, warning44Message) [] let ``Private type passes when abbrev is private``() = - CompilerAssert.Pass - """ + FSharp """ module Library = type private Hidden = Hidden of unit type private Exported = Hidden - """ + """ + |> typecheck + |> shouldSucceed [] let ``Default access type passes when abbrev is default``() = - CompilerAssert.Pass - """ + FSharp """ module Library = type Hidden = Hidden of unit type Exported = Hidden - """ + """ + |> typecheck + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentErrorTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentErrorTests.fs index f2f598d9dba..dd986cf5009 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentErrorTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/AssignmentErrorTests.fs @@ -1,22 +1,19 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices - +open FSharp.Test.Utilities.Compiler module ``Errors assigning to mutable objects`` = [] let ``Assign to immutable error``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 x <- 20 - """ - FSharpErrorSeverity.Error - 27 - (3, 1, 3, 8) - "This value is not mutable. Consider using the mutable keyword, e.g. 'let mutable x = expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 27, Line 3, Col 1, Line 3, Col 8, + "This value is not mutable. Consider using the mutable keyword, e.g. 'let mutable x = expression'.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs index a51e92ed944..e1941a3d7eb 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ClassesTests.fs @@ -1,17 +1,15 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Classes`` = [] let ``Tuple In Abstract Method``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type IInterface = abstract Function : (int32 * int32) -> unit @@ -19,47 +17,44 @@ let x = { new IInterface with member this.Function (i, j) = () } - """ - [| - FSharpErrorSeverity.Error, 768, (7, 16, 7, 36), "The member 'Function' does not accept the correct number of arguments. 1 argument(s) are expected, but 2 were given. The required signature is 'member IInterface.Function : (int32 * int32) -> unit'.\nA tuple type is required for one or more arguments. Consider wrapping the given arguments in additional parentheses or review the definition of the interface." - FSharpErrorSeverity.Error, 17, (7, 21, 7, 29), "The member 'Function : 'a * 'b -> unit' does not have the correct type to override the corresponding abstract method. The required signature is 'Function : (int32 * int32) -> unit'." - FSharpErrorSeverity.Error, 783, (6, 9, 6, 19), "At least one override did not correctly implement its corresponding abstract member" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 768, Line 7, Col 16, Line 7, Col 36, "The member 'Function' does not accept the correct number of arguments. 1 argument(s) are expected, but 2 were given. The required signature is 'member IInterface.Function : (int32 * int32) -> unit'.\nA tuple type is required for one or more arguments. Consider wrapping the given arguments in additional parentheses or review the definition of the interface.") + (Error 17, Line 7, Col 21, Line 7, Col 29, "The member 'Function : 'a * 'b -> unit' does not have the correct type to override the corresponding abstract method. The required signature is 'Function : (int32 * int32) -> unit'.") + (Error 783, Line 6, Col 9, Line 6, Col 19, "At least one override did not correctly implement its corresponding abstract member")] [] let ``Wrong Arity``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyType() = static member MyMember(arg1, arg2:int ) = () static member MyMember(arg1, arg2:byte) = () MyType.MyMember("", 0, 0) - """ - FSharpErrorSeverity.Error - 503 - (7, 1, 7, 26) - "A member or object constructor 'MyMember' taking 3 arguments is not accessible from this code location. All accessible versions of method 'MyMember' take 2 arguments." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 503, Line 7, Col 1, Line 7, Col 26, + "A member or object constructor 'MyMember' taking 3 arguments is not accessible from this code location. All accessible versions of method 'MyMember' take 2 arguments.") [] let ``Method Is Not Static``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Class1() = member this.X() = "F#" let x = Class1.X() - """ - FSharpErrorSeverity.Error - 3214 - (5, 9, 5, 17) - "Method or object constructor 'X' is not static" + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 3214, Line 5, Col 9, Line 5, Col 17, "Method or object constructor 'X' is not static") [] let ``Matching Method With Same Name Is Not Abstract``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Foo(x : int) = member v.MyX() = x @@ -67,17 +62,17 @@ let foo = { new Foo(3) with member v.MyX() = 4 } - """ - [| - FSharpErrorSeverity.Error, 767, (8, 16, 8, 23), "The type Foo contains the member 'MyX' but it is not a virtual or abstract method that is available to override or implement." - FSharpErrorSeverity.Error, 17, (8, 18, 8, 21), "The member 'MyX : unit -> int' does not have the correct type to override any given virtual method" - FSharpErrorSeverity.Error, 783, (6, 11, 6, 14), "At least one override did not correctly implement its corresponding abstract member" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 767, Line 8, Col 16, Line 8, Col 23, "The type Foo contains the member 'MyX' but it is not a virtual or abstract method that is available to override or implement.") + (Error 17, Line 8, Col 18, Line 8, Col 21, "The member 'MyX : unit -> int' does not have the correct type to override any given virtual method") + (Error 783, Line 6, Col 11, Line 6, Col 14, "At least one override did not correctly implement its corresponding abstract member")] [] let ``No Matching Abstract Method With Same Name``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type IInterface = abstract MyFunction : int32 * int32 -> unit abstract SomeOtherFunction : int32 * int32 -> unit @@ -86,18 +81,18 @@ let x = { new IInterface with member this.Function (i, j) = () } - """ - [| - FSharpErrorSeverity.Error, 767, (8, 14, 8, 34), "The member 'Function' does not correspond to any abstract or virtual method available to override or implement. Maybe you want one of the following:" + System.Environment.NewLine + " MyFunction" - FSharpErrorSeverity.Error, 17, (8, 19, 8, 27), "The member 'Function : 'a * 'b -> unit' does not have the correct type to override any given virtual method" - FSharpErrorSeverity.Error, 366, (7, 3, 9, 4), "No implementation was given for those members: " + System.Environment.NewLine + "\t'abstract member IInterface.MyFunction : int32 * int32 -> unit'" + System.Environment.NewLine + "\t'abstract member IInterface.SomeOtherFunction : int32 * int32 -> unit'" + System.Environment.NewLine + "Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'." - FSharpErrorSeverity.Error, 783, (7, 9, 7, 19), "At least one override did not correctly implement its corresponding abstract member" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 767, Line 8, Col 14, Line 8, Col 34, "The member 'Function' does not correspond to any abstract or virtual method available to override or implement. Maybe you want one of the following:" + System.Environment.NewLine + " MyFunction") + (Error 17, Line 8, Col 19, Line 8, Col 27, "The member 'Function : 'a * 'b -> unit' does not have the correct type to override any given virtual method") + (Error 366, Line 7, Col 3, Line 9, Col 4, "No implementation was given for those members: " + System.Environment.NewLine + "\t'abstract member IInterface.MyFunction : int32 * int32 -> unit'" + System.Environment.NewLine + "\t'abstract member IInterface.SomeOtherFunction : int32 * int32 -> unit'" + System.Environment.NewLine + "Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'.") + (Error 783, Line 7, Col 9, Line 7, Col 19, "At least one override did not correctly implement its corresponding abstract member")] [] let ``Member Has Multiple Possible Dispatch Slots``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type IOverload = abstract member Bar : int -> int abstract member Bar : double -> int @@ -105,23 +100,24 @@ type IOverload = type Overload = interface IOverload with override __.Bar _ = 1 - """ - [| - FSharpErrorSeverity.Error, 366, (7, 15, 7, 24), "No implementation was given for those members: " + System.Environment.NewLine + "\t'abstract member IOverload.Bar : double -> int'" + System.Environment.NewLine + "\t'abstract member IOverload.Bar : int -> int'" + System.Environment.NewLine + "Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'." - FSharpErrorSeverity.Error, 3213, (8, 21, 8, 24), "The member 'Bar<'a0> : 'a0 -> int' matches multiple overloads of the same method.\nPlease restrict it to one of the following:" + System.Environment.NewLine + " Bar : double -> int" + System.Environment.NewLine + " Bar : int -> int." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 366, Line 7, Col 15, Line 7, Col 24, "No implementation was given for those members: " + System.Environment.NewLine + "\t'abstract member IOverload.Bar : double -> int'" + System.Environment.NewLine + "\t'abstract member IOverload.Bar : int -> int'" + System.Environment.NewLine + "Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'.") + (Error 3213, Line 8, Col 21, Line 8, Col 24, "The member 'Bar<'a0> : 'a0 -> int' matches multiple overloads of the same method.\nPlease restrict it to one of the following:" + System.Environment.NewLine + " Bar : double -> int" + System.Environment.NewLine + " Bar : int -> int.")] [] let ``Do Cannot Have Visibility Declarations``() = - CompilerAssert.ParseWithErrors - """ + FSharp """ type X() = do () private do () static member Y() = 1 - """ - [| - FSharpErrorSeverity.Error, 531, (4, 5, 4, 12), "Accessibility modifiers should come immediately prior to the identifier naming a construct" - FSharpErrorSeverity.Error, 512, (4, 13, 4, 18), "Accessibility modifiers are not permitted on 'do' bindings, but 'Private' was given." - FSharpErrorSeverity.Error, 222, (2, 1, 3, 1), "Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule'. Only the last source file of an application may omit such a declaration." - |] + """ + |> parse + |> shouldFail + |> withDiagnostics [ + (Error 531, Line 4, Col 5, Line 4, Col 12, "Accessibility modifiers should come immediately prior to the identifier naming a construct") + (Error 512, Line 4, Col 13, Line 4, Col 18, "Accessibility modifiers are not permitted on 'do' bindings, but 'Private' was given.") + (Error 222, Line 2, Col 1, Line 3, Col 1, "Files in libraries or multiple-file applications must begin with a namespace or module declaration, e.g. 'namespace SomeNamespace.SubNamespace' or 'module SomeNamespace.SomeModule'. Only the last source file of an application may omit such a declaration.")] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConfusingTypeName.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConfusingTypeName.fs index 3c7e569007d..9e3e75b606b 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConfusingTypeName.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConfusingTypeName.fs @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities open FSharp.Test.Utilities.Compiler -open FSharp.Test.Utilities.Utilities -open FSharp.Compiler.SourceCodeServices module ``Confusing Type Name`` = diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConstructorTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConstructorTests.fs index c9636e711a5..1806defe4e4 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConstructorTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ConstructorTests.fs @@ -1,42 +1,40 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Constructor`` = [] let ``Invalid Record``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Record = {field1:int; field2:int} let doSomething (xs) = List.map (fun {field1=x} -> x) xs doSomething {Record.field1=0; field2=0} - """ - [| - FSharpErrorSeverity.Error, 1, (4, 13, 4, 40), "This expression was expected to have type\n 'Record list' \nbut here has type\n 'Record' " - FSharpErrorSeverity.Warning, 20, (4, 1, 4, 40), "The result of this expression has type 'int list' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 20, Line 4, Col 1, Line 4, Col 40, "The result of this expression has type 'int list' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + (Error 1, Line 4, Col 13, Line 4, Col 40, "This expression was expected to have type\n 'Record list' \nbut here has type\n 'Record' ")] [] let ``Comma In Rec Ctor``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Person = { Name : string; Age : int; City : string } let x = { Name = "Isaac", Age = 21, City = "London" } - """ - [| - FSharpErrorSeverity.Error, 1, (3, 18, 3, 52), "This expression was expected to have type\n 'string' \nbut here has type\n ''a * 'b * 'c' " + System.Environment.NewLine + "A ';' is used to separate field values in records. Consider replacing ',' with ';'." - FSharpErrorSeverity.Error, 764, (3, 9, 3, 54), "No assignment given for field 'Age' of type 'Test.Person'" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1, Line 3, Col 18, Line 3, Col 52, "This expression was expected to have type\n 'string' \nbut here has type\n ''a * 'b * 'c' " + System.Environment.NewLine + "A ';' is used to separate field values in records. Consider replacing ',' with ';'.") + (Error 764, Line 3, Col 9, Line 3, Col 54, "No assignment given for field 'Age' of type 'Test.Person'")] [] let ``Missing Comma In Ctor``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Person() = member val Name = "" with get,set member val Age = 0 with get,set @@ -44,18 +42,18 @@ type Person() = let p = Person(Name = "Fred" Age = 18) - """ - [| - FSharpErrorSeverity.Error, 39, (7, 12, 7, 16), "The value or constructor 'Name' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " nan" - FSharpErrorSeverity.Warning, 20, (7, 12, 7, 25), "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'." - FSharpErrorSeverity.Error, 39, (8, 12, 8, 15), "The value or constructor 'Age' is not defined." - FSharpErrorSeverity.Error, 501, (7, 5, 8, 21), "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'. If some of the arguments are meant to assign values to properties, consider separating those arguments with a comma (',')." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 20, Line 7, Col 12, Line 7, Col 25, "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'.") + (Error 39, Line 7, Col 12, Line 7, Col 16, "The value or constructor 'Name' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " nan") + (Error 39, Line 8, Col 12, Line 8, Col 15, "The value or constructor 'Age' is not defined.") + (Error 501, Line 7, Col 5, Line 8, Col 21, "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'. If some of the arguments are meant to assign values to properties, consider separating those arguments with a comma (',').")] [] let ``Missing Ctor Value``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Person(x:int) = member val Name = "" with get,set member val Age = x with get,set @@ -63,32 +61,29 @@ type Person(x:int) = let p = Person(Name = "Fred", Age = 18) - """ - FSharpErrorSeverity.Error - 496 - (7, 5, 8, 21) - "The member or object constructor 'Person' requires 1 argument(s). The required signature is 'new : x:int -> Person'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 496, Line 7, Col 5, Line 8, Col 21, "The member or object constructor 'Person' requires 1 argument(s). The required signature is 'new : x:int -> Person'.") [] let ``Extra Argument In Ctor``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Person() = member val Name = "" with get,set member val Age = 0 with get,set let p = Person(1) - """ - FSharpErrorSeverity.Error - 501 - (7, 5, 7, 14) - "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 501, Line 7, Col 5, Line 7, Col 14, + "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'.") [] let ``Extra Argument In Ctor2``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Person() = member val Name = "" with get,set member val Age = 0 with get,set @@ -97,18 +92,17 @@ let b = 1 let p = Person(1=b) - """ - FSharpErrorSeverity.Error - 501 - (9, 5, 9, 16) - "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 501, Line 9, Col 5, Line 9, Col 16, + "The object constructor 'Person' takes 0 argument(s) but is here given 1. The required signature is 'new : unit -> Person'.") [] let ``Valid Comma In Rec Ctor``() = - CompilerAssert.Pass - """ + FSharp """ type Person = { Name : string * bool * bool } let Age = 22 let City = "London" let x = { Name = "Isaac", Age = 21, City = "London" } - """ + """ |> typecheck |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/DontSuggestTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/DontSuggestTests.fs index 13a6f6b0dcc..d1511da554c 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/DontSuggestTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/DontSuggestTests.fs @@ -1,28 +1,25 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Don't Suggest`` = [] let ``Dont Suggest Completely Wrong Stuff``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let _ = Path.GetFullPath "images" - """ - FSharpErrorSeverity.Error - 39 - (2, 9, 2, 13) - ("The value, namespace, type or module 'Path' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Math") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 9, Line 2, Col 13, + "The value, namespace, type or module 'Path' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Math") [] let ``Dont Suggest When Things Are Open``() = - CompilerAssert.ParseWithErrors - """ + FSharp """ module N = let name = "hallo" @@ -30,19 +27,18 @@ type T = static member myMember = 1 let x = N. - """ - [| - FSharpErrorSeverity.Error, 599, (8, 10, 8, 11), "Missing qualification after '.'" - FSharpErrorSeverity.Error, 222, (2, 1, 3, 1), "Files in libraries or multiple-file applications must begin with a namespace or module declaration. When using a module declaration at the start of a file the '=' sign is not allowed. If this is a top-level module, consider removing the = to resolve this error." - |] + """ + |> parse + |> shouldFail + |> withDiagnostics [ + (Error 599, Line 8, Col 10, Line 8, Col 11, "Missing qualification after '.'") + (Error 222, Line 2, Col 1, Line 3, Col 1, "Files in libraries or multiple-file applications must begin with a namespace or module declaration. When using a module declaration at the start of a file the '=' sign is not allowed. If this is a top-level module, consider removing the = to resolve this error.")] [] let ``Dont Suggest Intentionally Unused Variables``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let hober xy _xyz = xyz - """ - FSharpErrorSeverity.Error - 39 - (2, 21, 2, 24) - "The value or constructor 'xyz' is not defined." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 21, Line 2, Col 24, "The value or constructor 'xyz' is not defined.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ElseBranchHasWrongTypeTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ElseBranchHasWrongTypeTests.fs index 54d5c41a68c..8c412854488 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ElseBranchHasWrongTypeTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ElseBranchHasWrongTypeTests.fs @@ -1,48 +1,43 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices - +open FSharp.Test.Utilities.Compiler module ``Else branch has wrong type`` = [] let ``Else branch is int while if branch is string``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let y = if test > 10 then "test" else 123 - """ - FSharpErrorSeverity.Error - 1 - (5, 10, 5, 13) - "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 5, Col 10, Line 5, Col 13, + "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'.") [] let ``Else branch is a function that returns int while if branch is string``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let f x = test let y = if test > 10 then "test" else f 10 - """ - FSharpErrorSeverity.Error - 1 - (6, 10, 6, 14) - "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 6, Col 10, Line 6, Col 14, + "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'.") [] let ``Else branch is a sequence of expressions that returns int while if branch is string``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let f x = x + 4 let y = @@ -51,17 +46,16 @@ let y = else "" |> ignore (f 5) - """ - FSharpErrorSeverity.Error - 1 - (9, 10, 9, 13) - "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 9, Col 10, Line 9, Col 13, + "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'.") [] let ``Else branch is a longer sequence of expressions that returns int while if branch is string``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let f x = x + 4 let y = @@ -72,33 +66,31 @@ let y = let z = f 4 let a = 3 * z (f a) - """ - FSharpErrorSeverity.Error - 1 - (11, 10, 11, 13) - "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 11, Col 10, Line 11, Col 13, + "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'string'. This branch returns a value of type 'int'.") [] let ``Else branch context doesn't propagate into function application``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let f x : string = x let y = if test > 10 then "test" else f 123 - """ - FSharpErrorSeverity.Error - 1 - (7, 11, 7, 14) - "This expression was expected to have type\n 'string' \nbut here has type\n 'int' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 7, Col 11, Line 7, Col 14, + "This expression was expected to have type\n 'string' \nbut here has type\n 'int' ") [] let ``Else branch context doesn't propagate into function application even if not last expr``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let f x = printfn "%s" x let y = @@ -106,16 +98,15 @@ let y = else f 123 "test" - """ - FSharpErrorSeverity.Error - 1 - (7, 11, 7, 14) - "This expression was expected to have type\n 'string' \nbut here has type\n 'int' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 7, Col 11, Line 7, Col 14, + "This expression was expected to have type\n 'string' \nbut here has type\n 'int' ") [] let ``Else branch context doesn't propagate into for loop``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let list = [1..10] let y = @@ -125,16 +116,15 @@ let y = printfn "%s" x "test" - """ - FSharpErrorSeverity.Error - 1 - (7, 14, 7, 22) - "This expression was expected to have type\n 'int' \nbut here has type\n 'string' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 7, Col 14, Line 7, Col 22, + "This expression was expected to have type\n 'int' \nbut here has type\n 'string' ") [] let ``Else branch context doesn't propagate to lines before last line``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let test = 100 let list = [1..10] let y = @@ -143,35 +133,39 @@ let y = printfn "%s" 1 "test" - """ - FSharpErrorSeverity.Error - 1 - (7, 22, 7, 23) - "This expression was expected to have type\n 'string' \nbut here has type\n 'int' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 7, Col 22, Line 7, Col 23, + "This expression was expected to have type\n 'string' \nbut here has type\n 'int' ") [] let ``Else branch should not have wrong context type``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ let x = 1 let y : bool = if x = 2 then "A" else "B" - """ - [| FSharpErrorSeverity.Error, 1, (4, 19, 4, 22), "The 'if' expression needs to have type 'bool' to satisfy context type requirements. It currently has type 'string'." - FSharpErrorSeverity.Error, 1, (5, 10, 5, 13), "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'." |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1, Line 4, Col 19, Line 4, Col 22, "The 'if' expression needs to have type 'bool' to satisfy context type requirements. It currently has type 'string'.") + (Error 1, Line 5, Col 10, Line 5, Col 13, "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'.")] [] let ``Else branch has wrong type in nested if``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ let x = 1 if x = 1 then true else if x = 2 then "A" else "B" - """ - [| FSharpErrorSeverity.Error, 1, (5, 19, 5, 22), "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'." - FSharpErrorSeverity.Error, 1, (6, 10, 6, 13), "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'." - FSharpErrorSeverity.Warning, 20, (3, 1, 6, 13), "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 20, Line 3, Col 1, Line 6, Col 13, "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + (Error 1, Line 5, Col 19, Line 5, Col 22, "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'.") + (Error 1, Line 6, Col 10, Line 6, Col 13, "All branches of an 'if' expression must return values of the same type as the first branch, which here is 'bool'. This branch returns a value of type 'string'.")] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/InvalidNumericLiteralTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/InvalidNumericLiteralTests.fs index 720f18a06da..4862927ba56 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/InvalidNumericLiteralTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/InvalidNumericLiteralTests.fs @@ -1,9 +1,10 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit open FSharp.Test.Utilities +open FSharp.Test.Utilities.Compiler open FSharp.Compiler.SourceCodeServices open FSharp.Compiler.AbstractIL.Internal @@ -25,51 +26,41 @@ module ``Numeric Literals`` = [] [] let ``Invalid Numeric Literals`` literal = - CompilerAssert.TypeCheckSingleError - ("let x = " + literal) - FSharpErrorSeverity.Error - 1156 - (1, 9, 1, 9 + (String.length literal)) - "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger)." + FSharp ("let x = " + literal) + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1156, Line 1, Col 9, Line 1, Col (9 + (String.length literal)), + "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger).") [] let ``3_(dot)1415F is invalid numeric literal``() = - CompilerAssert.TypeCheckWithErrors - """ -let x = 3_.1415F - """ - [| - FSharpErrorSeverity.Error, 1156, (2, 9, 2, 11), "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger)."; - FSharpErrorSeverity.Error, 599, (2, 11, 2, 12),"Missing qualification after '.'" - |] + FSharp "let x = 3_.1415F" + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 1156, Line 1, Col 9, Line 1, Col 11, "This is not a valid numeric literal. Valid numeric literals include 1, 0x1, 0o1, 0b1, 1l (int), 1u (uint32), 1L (int64), 1UL (uint64), 1s (int16), 1y (sbyte), 1uy (byte), 1.0 (float), 1.0f (float32), 1.0m (decimal), 1I (BigInteger).";) + (Error 599, Line 1, Col 11, Line 1, Col 12,"Missing qualification after '.'")] [] let ``_52 is invalid numeric literal``() = - CompilerAssert.TypeCheckSingleError - """ -let x = _52 - """ - FSharpErrorSeverity.Error - 39 - (2, 9, 2, 12) - "The value or constructor '_52' is not defined." + FSharp "let x = _52" + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 1, Col 9, Line 1, Col 12, "The value or constructor '_52' is not defined.") [] let ``1N is invalid numeric literal``() = - CompilerAssert.TypeCheckSingleError - """ -let x = 1N - """ - FSharpErrorSeverity.Error - 0784 - (2, 9, 2, 11) - "This numeric literal requires that a module 'NumericLiteralN' defining functions FromZero, FromOne, FromInt32, FromInt64 and FromString be in scope" + FSharp "let x = 1N" + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 0784, Line 1, Col 9, Line 1, Col 11, + "This numeric literal requires that a module 'NumericLiteralN' defining functions FromZero, FromOne, FromInt32, FromInt64 and FromString be in scope") [] let ``1N is invalid numeric literal in FSI``() = if Utils.runningOnMono then () - else + else CompilerAssert.RunScriptWithOptions [| "--langversion:preview"; "--test:ErrorRanges" |] """ let x = 1N @@ -79,10 +70,10 @@ let x = 1N "Operation could not be completed due to earlier error" ] + // Regressiont test for FSharp1.0: 2543 - Decimal literals do not support exponents [] [] [] let ``Valid Numeric Literals`` literal = - // Regressiont test for FSharp1.0: 2543 - Decimal literals do not support exponents - - CompilerAssert.Pass ("let x = " + literal) \ No newline at end of file + FSharp ("let x = " + literal) + |> typecheck |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingElseBranch.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingElseBranch.fs index 9ab09f8a9ea..07a40b5c9a0 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingElseBranch.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingElseBranch.fs @@ -1,50 +1,46 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Else branch is missing`` = [] let ``Fail if else branch is missing``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = if x > 10 then "test" - """ - FSharpErrorSeverity.Error - 1 - (4, 19, 4, 25) - "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'string'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 4, Col 19, Line 4, Col 25, + "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'string'.") [] let ``Fail on type error in condition``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = - if x > 10 then + if x > 10 then if x <> "test" then printfn "test" () - """ - FSharpErrorSeverity.Error - 1 - (5, 14, 5, 20) - "This expression was expected to have type\n 'int' \nbut here has type\n 'string' " + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 5, Col 14, Line 5, Col 20, + "This expression was expected to have type\n 'int' \nbut here has type\n 'string' ") [] let ``Fail if else branch is missing in nesting``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = if x > 10 then ("test") - """ - FSharpErrorSeverity.Error - 1 - (4, 20, 4, 26) - "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'string'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 4, Col 20, Line 4, Col 26, + "This 'if' expression is missing an 'else' branch. Because 'if' is an expression, and not a statement, add an 'else' branch which also returns a value of type 'string'.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingExpressionTests.fs index 802bcfd6750..f91d8b3d683 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/MissingExpressionTests.fs @@ -1,23 +1,21 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Missing Expression`` = - + [] let ``Missing Expression after let``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let sum = 0 for x in 0 .. 10 do let sum = sum + x - """ - FSharpErrorSeverity.Error - 588 - (4,5,4,8) - "The block following this 'let' is unfinished. Every code block is an expression and must have a result. 'let' cannot be the final code element in a block. Consider giving this block an explicit result." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 588, Line 4, Col 5, Line 4, Col 8, + "The block following this 'let' is unfinished. Every code block is an expression and must have a result. 'let' cannot be the final code element in a block. Consider giving this block an explicit result.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleAbbreviationTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleAbbreviationTests.fs index 5c9647fb070..932fde27aa0 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleAbbreviationTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/ModuleAbbreviationTests.fs @@ -1,21 +1,17 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Module Abbreviations`` = [] let ``Public Module Abbreviation``() = - CompilerAssert.TypeCheckSingleError - """ -module public L1 = List - """ - FSharpErrorSeverity.Error - 536 - (2, 1, 2, 7) - "The 'Public' accessibility attribute is not allowed on module abbreviation. Module abbreviations are always private." + FSharp "module public L1 = List" + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 536, Line 1, Col 1, Line 1, Col 7, + "The 'Public' accessibility attribute is not allowed on module abbreviation. Module abbreviations are always private.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/NameResolutionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/NameResolutionTests.fs index c48a5c9113b..016bbdcadce 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/NameResolutionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/NameResolutionTests.fs @@ -1,17 +1,15 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module NameResolutionTests = [] let FieldNotInRecord () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type A = { Hello:string; World:string } type B = { Size:int; Height:int } type C = { Wheels:int } @@ -20,16 +18,15 @@ type E = { Unknown:string } type F = { Wallis:int; Size:int; Height:int; } let r:F = { Size=3; Height=4; Wall=1 } - """ - FSharpErrorSeverity.Error - 1129 - (9, 31, 9, 35) - ("The record type 'F' does not contain a label 'Wall'. Maybe you want one of the following:" + System.Environment.NewLine + " Wallis") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1129, Line 9, Col 31, Line 9, Col 35, + ("The record type 'F' does not contain a label 'Wall'. Maybe you want one of the following:" + System.Environment.NewLine + " Wallis")) [] let RecordFieldProposal () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type A = { Hello:string; World:string } type B = { Size:int; Height:int } type C = { Wheels:int } @@ -38,8 +35,8 @@ type E = { Unknown:string } type F = { Wallis:int; Size:int; Height:int; } let r = { Size=3; Height=4; Wall=1 } - """ - FSharpErrorSeverity.Error - 39 - (9, 29, 9, 33) - ("The record label 'Wall' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Walls" + System.Environment.NewLine + " Wallis") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 9, Col 29, Line 9, Col 33, + ("The record label 'Wall' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Walls" + System.Environment.NewLine + " Wallis")) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/SuggestionsTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/SuggestionsTests.fs index bf26f8a2102..abdc7b92ca1 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/SuggestionsTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/SuggestionsTests.fs @@ -1,87 +1,69 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module Suggestions = [] let ``Field Suggestion`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Person = { Name : string; } let x = { Person.Names = "Isaac" } - """ - FSharpErrorSeverity.Error - 39 - (4, 18, 4, 23) - ("The type 'Person' does not define the field, constructor or member 'Names'. Maybe you want one of the following:" + System.Environment.NewLine + " Name") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 4, Col 18, Line 4, Col 23, + ("The type 'Person' does not define the field, constructor or member 'Names'. Maybe you want one of the following:" + System.Environment.NewLine + " Name")) [] let ``Suggest Array Module Functions`` () = - CompilerAssert.TypeCheckSingleError - """ -let f = - Array.blt - """ - FSharpErrorSeverity.Error - 39 - (3, 11, 3, 14) - ("The value, constructor, namespace or type 'blt' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " blit") - + FSharp "let f = Array.blt" + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 1, Col 15, Line 1, Col 18, + ("The value, constructor, namespace or type 'blt' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " blit")) [] let ``Suggest Async Module`` () = - CompilerAssert.TypeCheckSingleError - """ -let f = - Asnc.Sleep 1000 - """ - FSharpErrorSeverity.Error - 39 - (3, 5, 3, 9) - ("The value, namespace, type or module 'Asnc' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Async" + System.Environment.NewLine + " async" + System.Environment.NewLine + " asin" + System.Environment.NewLine + " snd") - + FSharp "let f = Asnc.Sleep 1000" + |> typecheck + |> shouldFail + |> withSingleDiagnostic( Error 39, Line 1, Col 9, Line 1, Col 13, + ("The value, namespace, type or module 'Asnc' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Async" + System.Environment.NewLine + " async" + System.Environment.NewLine + " asin" + System.Environment.NewLine + " snd")) [] let ``Suggest Attribute`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyClass<'Bar>() = abstract M<'T> : 'T -> 'T abstract M2<'T> : 'T -> 'Bar - """ - FSharpErrorSeverity.Error - 39 - (2, 3, 2, 15) - ("The type 'AbstractClas' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " AbstractClass" + System.Environment.NewLine + " AbstractClassAttribute") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 3, Line 2, Col 15, + ("The type 'AbstractClas' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " AbstractClass" + System.Environment.NewLine + " AbstractClassAttribute")) [] let ``Suggest Double Backtick Identifiers`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module N = let ``longer name`` = "hallo" let x = N.``longe name`` - """ - FSharpErrorSeverity.Error - 39 - (5, 11, 5, 25) - ("The value, constructor, namespace or type 'longe name' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " longer name") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 5, Col 11, Line 5, Col 25, + ("The value, constructor, namespace or type 'longe name' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " longer name")) [] let ``Suggest Double Backtick Unions`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module N = type MyUnion = | ``My Case1`` @@ -90,46 +72,41 @@ module N = open N let x = N.MyUnion.``My Case2`` - """ - FSharpErrorSeverity.Error - 39 - (9, 19, 9,31) - ("The type 'MyUnion' does not define the field, constructor or member 'My Case2'. Maybe you want one of the following:" + System.Environment.NewLine + " My Case1" + System.Environment.NewLine + " Case2") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 9, Col 19, Line 9,Col 31, + ("The type 'MyUnion' does not define the field, constructor or member 'My Case2'. Maybe you want one of the following:" + System.Environment.NewLine + " My Case1" + System.Environment.NewLine + " Case2")) [] let ``Suggest Fields In Constructor`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyClass() = member val MyProperty = "" with get, set member val MyProperty2 = "" with get, set member val ABigProperty = "" with get, set let c = MyClass(Property = "") - """ - FSharpErrorSeverity.Error - 495 - (7, 17, 7, 25) - ("The object constructor 'MyClass' has no argument or settable return property 'Property'. The required signature is new : unit -> MyClass. Maybe you want one of the following:" + System.Environment.NewLine + " MyProperty" + System.Environment.NewLine + " MyProperty2" + System.Environment.NewLine + " ABigProperty") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 495, Line 7, Col 17, Line 7, Col 25, + ("The object constructor 'MyClass' has no argument or settable return property 'Property'. The required signature is new : unit -> MyClass. Maybe you want one of the following:" + System.Environment.NewLine + " MyProperty" + System.Environment.NewLine + " MyProperty2" + System.Environment.NewLine + " ABigProperty")) [] let ``Suggest Generic Type`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type T = System.Collections.Generic.Dictionary - """ - FSharpErrorSeverity.Error - 39 - (2, 48, 2, 53) - ("The type 'int11' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " int16" + System.Environment.NewLine + " int16`1" + System.Environment.NewLine + " int8" + System.Environment.NewLine + " uint16" + System.Environment.NewLine + " int") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 48, Line 2, Col 53, + ("The type 'int11' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " int16" + System.Environment.NewLine + " int16`1" + System.Environment.NewLine + " int8" + System.Environment.NewLine + " uint16" + System.Environment.NewLine + " int")) [] let ``Suggest Methods`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Test2 = type D() = @@ -138,61 +115,53 @@ module Test2 = member x.Method1() = 10 D.Method2() - """ - FSharpErrorSeverity.Error - 39 - (9, 7, 9, 14) - ("The type 'D' does not define the field, constructor or member 'Method2'. Maybe you want one of the following:" + System.Environment.NewLine + " Method1") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 9, Col 7, Line 9, Col 14, + ("The type 'D' does not define the field, constructor or member 'Method2'. Maybe you want one of the following:" + System.Environment.NewLine + " Method1")) [] let ``Suggest Modules`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ module Collections = let f () = printfn "%s" "Hello" open Collectons - """ - FSharpErrorSeverity.Error - 39 - (6, 6, 6, 16) - ("The namespace or module 'Collectons' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Collections") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 6, Col 6, Line 6, Col 16, + ("The namespace or module 'Collectons' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " Collections")) [] let ``Suggest Namespaces`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ open System.Collectons - """ - FSharpErrorSeverity.Error - 39 - (2, 13, 2, 23) - "The namespace 'Collectons' is not defined." - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 13, Line 2, Col 23, + "The namespace 'Collectons' is not defined.") [] let ``Suggest Record Labels`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyRecord = { Hello: int; World: bool} let r = { Hello = 2 ; World = true} let x = r.ello - """ - FSharpErrorSeverity.Error - 39 - (6, 11, 6, 15) - ("The type 'MyRecord' does not define the field, constructor or member 'ello'. Maybe you want one of the following:" + System.Environment.NewLine + " Hello") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 6, Col 11, Line 6, Col 15, + ("The type 'MyRecord' does not define the field, constructor or member 'ello'. Maybe you want one of the following:" + System.Environment.NewLine + " Hello")) [] let ``Suggest Record Type for RequireQualifiedAccess Records`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyRecord = { Field1: string @@ -200,17 +169,15 @@ type MyRecord = { } let r = { Field1 = "hallo"; Field2 = 1 } - """ - FSharpErrorSeverity.Error - 39 - (8, 11, 8, 17) - ("The record label 'Field1' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " MyRecord.Field1") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 8, Col 11, Line 8, Col 17, + ("The record label 'Field1' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " MyRecord.Field1")) [] let ``Suggest To Use Indexer`` () = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ let d = [1,1] |> dict let y = d[1] @@ -218,90 +185,82 @@ let z = d[|1|] let f() = d let a = (f())[1] - """ - [| - FSharpErrorSeverity.Error, 3217, (3, 9, 3, 10), "This value is not a function and cannot be applied. Did you intend to access the indexer via d.[index] instead?" - FSharpErrorSeverity.Error, 3, (5, 9, 5, 10), "This value is not a function and cannot be applied." - FSharpErrorSeverity.Error, 3217, (8, 10, 8, 13), "This expression is not a function and cannot be applied. Did you intend to access the indexer via expr.[index] instead?" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3217, Line 3, Col 9, Line 3, Col 10, "This value is not a function and cannot be applied. Did you intend to access the indexer via d.[index] instead?") + (Error 3, Line 5, Col 9, Line 5, Col 10, "This value is not a function and cannot be applied.") + (Error 3217, Line 8, Col 10, Line 8, Col 13, "This expression is not a function and cannot be applied. Did you intend to access the indexer via expr.[index] instead?")] [] let ``Suggest Type Parameters`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyClass<'Bar>() = abstract M<'T> : 'T -> 'T abstract M2<'T> : 'T -> 'Bar abstract M3<'T> : 'T -> 'B - """ - FSharpErrorSeverity.Error - 39 - (6, 28, 6, 30) - "The type parameter 'B is not defined." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 6, Col 28, Line 6, Col 30, + "The type parameter 'B is not defined.") [] let ``Suggest Types in Module`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x : System.Collections.Generic.Lst = ResizeArray() - """ - FSharpErrorSeverity.Error - 39 - (2, 36, 2, 39) - ("The type 'Lst' is not defined in 'System.Collections.Generic'. Maybe you want one of the following:" + System.Environment.NewLine + " List" + System.Environment.NewLine + " IList" + System.Environment.NewLine + " List`1") + """ |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 36, Line 2, Col 39, + ("The type 'Lst' is not defined in 'System.Collections.Generic'. Maybe you want one of the following:" + System.Environment.NewLine + " List" + System.Environment.NewLine + " IList" + System.Environment.NewLine + " List`1")) [] let ``Suggest Types in Namespace`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = System.DateTie.MaxValue - """ - FSharpErrorSeverity.Error - 39 - (2, 16, 2, 23) - ("The value, constructor, namespace or type 'DateTie' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " DateTime" + System.Environment.NewLine + " DateTimeKind" + System.Environment.NewLine + " DateTimeOffset" + System.Environment.NewLine + " Data") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 2, Col 16, Line 2, Col 23, + ("The value, constructor, namespace or type 'DateTie' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " DateTime" + System.Environment.NewLine + " DateTimeKind" + System.Environment.NewLine + " DateTimeOffset" + System.Environment.NewLine + " Data")) [] let ``Suggest Union Cases`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyUnion = | ASimpleCase | AnotherCase of int let u = MyUnion.AntherCase - """ - FSharpErrorSeverity.Error - 39 - (6, 17, 6, 27) - ("The type 'MyUnion' does not define the field, constructor or member 'AntherCase'. Maybe you want one of the following:" + System.Environment.NewLine + " AnotherCase") - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 6, Col 17, Line 6, Col 27, + ("The type 'MyUnion' does not define the field, constructor or member 'AntherCase'. Maybe you want one of the following:" + System.Environment.NewLine + " AnotherCase")) [] let ``Suggest Union Type for RequireQualifiedAccess Unions`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyUnion = | MyCase1 | MyCase2 of string let x : MyUnion = MyCase1 - """ - FSharpErrorSeverity.Error - 39 - (7, 19, 7, 26) - ("The value or constructor 'MyCase1' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " MyUnion.MyCase1") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 7, Col 19, Line 7, Col 26, + ("The value or constructor 'MyCase1' is not defined. Maybe you want one of the following:" + System.Environment.NewLine + " MyUnion.MyCase1")) [] let ``Suggest Unions in PatternMatch`` () = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ [] type MyUnion = | Case1 @@ -313,8 +272,8 @@ let x = match y with | MyUnion.Cas1 -> 1 | _ -> 2 - """ - FSharpErrorSeverity.Error - 39 - (11, 15, 11, 19) - ("The type 'MyUnion' does not define the field, constructor or member 'Cas1'. Maybe you want one of the following:" + System.Environment.NewLine + " Case1" + System.Environment.NewLine + " Case2") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 39, Line 11, Col 15, Line 11, Col 19, + ("The type 'MyUnion' does not define the field, constructor or member 'Cas1'. Maybe you want one of the following:" + System.Environment.NewLine + " Case1" + System.Environment.NewLine + " Case2")) diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs index 817e0a588c6..8d2d86d1d7c 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/TypeMismatchTests.fs @@ -1,81 +1,75 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Type Mismatch`` = [] let ``return Instead Of return!``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let rec foo() = async { return foo() } - """ - FSharpErrorSeverity.Error - 1 - (2, 32, 2, 37) - "Type mismatch. Expecting a\n ''a' \nbut given a\n 'Async<'a>' \nThe types ''a' and 'Async<'a>' cannot be unified. Consider using 'return!' instead of 'return'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 2, Col 32, Line 2, Col 37, + "Type mismatch. Expecting a\n ''a' \nbut given a\n 'Async<'a>' \nThe types ''a' and 'Async<'a>' cannot be unified. Consider using 'return!' instead of 'return'.") [] let ``yield Instead Of yield!``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type Foo() = member this.Yield(x) = [x] let rec f () = Foo() { yield f ()} - """ - FSharpErrorSeverity.Error - 1 - (5, 30, 5, 34) - "Type mismatch. Expecting a\n ''a' \nbut given a\n ''a list' \nThe types ''a' and ''a list' cannot be unified. Consider using 'yield!' instead of 'yield'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 5, Col 30, Line 5, Col 34, + "Type mismatch. Expecting a\n ''a' \nbut given a\n ''a list' \nThe types ''a' and ''a list' cannot be unified. Consider using 'yield!' instead of 'yield'.") [] let ``Ref Cell Instead Of Not``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = true if !x then printfn "hello" - """ - FSharpErrorSeverity.Error - 1 - (3, 5, 3, 6) - ("This expression was expected to have type\n 'bool ref' \nbut here has type\n 'bool' " + System.Environment.NewLine + "The '!' operator is used to dereference a ref cell. Consider using 'not expr' here.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 3, Col 5, Line 3, Col 6, + ("This expression was expected to have type\n 'bool ref' \nbut here has type\n 'bool' " + System.Environment.NewLine + "The '!' operator is used to dereference a ref cell. Consider using 'not expr' here.")) [] let ``Ref Cell Instead Of Not 2``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = true let y = !x - """ - FSharpErrorSeverity.Error - 1 - (3, 10, 3, 11) - ("This expression was expected to have type\n ''a ref' \nbut here has type\n 'bool' " + System.Environment.NewLine + "The '!' operator is used to dereference a ref cell. Consider using 'not expr' here.") + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 1, Line 3, Col 10, Line 3, Col 11, + ("This expression was expected to have type\n ''a ref' \nbut here has type\n 'bool' " + System.Environment.NewLine + "The '!' operator is used to dereference a ref cell. Consider using 'not expr' here.")) [] let ``Guard Has Wrong Type``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ let x = 1 match x with | 1 when "s" -> true | _ -> false - """ - [| - FSharpErrorSeverity.Error, 1, (4, 10, 4, 13), "A pattern match guard must be of type 'bool', but this 'when' expression is of type 'string'." - FSharpErrorSeverity.Warning, 20, (3, 1, 5, 13), "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 20, Line 3, Col 1, Line 5, Col 13, "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") + (Error 1, Line 4, Col 10, Line 4, Col 13, "A pattern match guard must be of type 'bool', but this 'when' expression is of type 'string'.")] [] let ``Runtime Type Test In Pattern``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() @@ -84,16 +78,16 @@ let c = match orig with | :? IDictionary -> "yes" | _ -> "no" - """ - [| - FSharpErrorSeverity.Warning, 67, (8, 5, 8, 28), "This type test or downcast will always hold" - FSharpErrorSeverity.Error, 193, (8, 5, 8, 28), "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 67, Line 8, Col 5, Line 8, Col 28, "This type test or downcast will always hold") + (Error 193, Line 8, Col 5, Line 8, Col 28, "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n")] [] let ``Runtime Type Test In Pattern 2``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() @@ -102,16 +96,16 @@ let c = match orig with | :? IDictionary as y -> "yes" + y.ToString() | _ -> "no" - """ - [| - FSharpErrorSeverity.Warning, 67, (8, 5, 8, 28), "This type test or downcast will always hold" - FSharpErrorSeverity.Error, 193, (8, 5, 8, 28), "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n" - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 67, Line 8, Col 5, Line 8, Col 28, "This type test or downcast will always hold") + (Error 193, Line 8, Col 5, Line 8, Col 28, "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n")] [] let ``Override Errors``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ type Base() = abstract member Member: int * string -> string default x.Member (i, s) = s @@ -127,9 +121,10 @@ type Derived2() = type Derived3() = inherit Base() override x.Member (s : string, i : int) = sprintf "Hello %s" s - """ - [| - FSharpErrorSeverity.Error, 856, (8, 16, 8, 22), "This override takes a different number of arguments to the corresponding abstract member. The following abstract members were found:" + System.Environment.NewLine + " abstract member Base.Member : int * string -> string" - FSharpErrorSeverity.Error, 856, (12, 16, 12, 22), "This override takes a different number of arguments to the corresponding abstract member. The following abstract members were found:" + System.Environment.NewLine + " abstract member Base.Member : int * string -> string" - FSharpErrorSeverity.Error, 1, (16, 24, 16, 34), "This expression was expected to have type\n 'int' \nbut here has type\n 'string' " - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 856, Line 8, Col 16, Line 8, Col 22, "This override takes a different number of arguments to the corresponding abstract member. The following abstract members were found:" + System.Environment.NewLine + " abstract member Base.Member : int * string -> string") + (Error 856, Line 12, Col 16, Line 12, Col 22, "This override takes a different number of arguments to the corresponding abstract member. The following abstract members were found:" + System.Environment.NewLine + " abstract member Base.Member : int * string -> string") + (Error 1, Line 16, Col 24, Line 16, Col 34, "This expression was expected to have type\n 'int' \nbut here has type\n 'string' ")] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnitGenericAbstactType.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnitGenericAbstactType.fs index 11ec30ab19a..2b244e12150 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnitGenericAbstactType.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UnitGenericAbstactType.fs @@ -1,28 +1,25 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Unit generic abstract Type`` = [] - let ``Unit can not be used as return type of abstract method paramete on return type``() = - CompilerAssert.TypeCheckSingleError - """ + let ``Unit can not be used as return type of abstract method paramete on return type``() = + FSharp """ type EDF<'S> = abstract member Apply : int -> 'S type SomeEDF () = interface EDF with - member this.Apply d = + member this.Apply d = // [ERROR] The member 'Apply' does not have the correct type to override the corresponding abstract method. () - """ - FSharpErrorSeverity.Error - 17 - (6, 21, 6, 26) - "The member 'Apply : int -> unit' is specialized with 'unit' but 'unit' can't be used as return type of an abstract method parameterized on return type." - + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 17, Line 6, Col 21, Line 6, Col 26, + "The member 'Apply : int -> unit' is specialized with 'unit' but 'unit' can't be used as return type of an abstract method parameterized on return type.") diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UpcastDowncastTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UpcastDowncastTests.fs index 79e32f9e3e0..61fcaadf12d 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UpcastDowncastTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/UpcastDowncastTests.fs @@ -1,51 +1,49 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Upcast and Downcast`` = [] let ``Downcast Instead Of Upcast``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() :> IDictionary let c = orig :> Dictionary - """ - FSharpErrorSeverity.Error - 193 - (5, 9, 5, 36) - "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n" + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Error 193, Line 5, Col 9, Line 5, Col 36, + "Type constraint mismatch. The type \n 'IDictionary' \nis not compatible with type\n 'Dictionary' \n") [] let ``Upcast Instead Of Downcast``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() let c = orig :?> IDictionary - """ - [| - FSharpErrorSeverity.Warning, 67, (5, 9, 5, 38), "This type test or downcast will always hold" - FSharpErrorSeverity.Error, 3198, (5, 9, 5, 38), "The conversion from Dictionary to IDictionary is a compile-time safe upcast, not a downcast. Consider using the :> (upcast) operator instead of the :?> (downcast) operator." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 67, Line 5, Col 9, Line 5, Col 38, "This type test or downcast will always hold") + (Error 3198, Line 5, Col 9, Line 5, Col 38, "The conversion from Dictionary to IDictionary is a compile-time safe upcast, not a downcast. Consider using the :> (upcast) operator instead of the :?> (downcast) operator.")] [] let ``Upcast Function Instead Of Downcast``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System.Collections.Generic let orig = Dictionary() let c : IDictionary = downcast orig - """ - [| - FSharpErrorSeverity.Warning, 67, (5, 32, 5, 45), "This type test or downcast will always hold" - FSharpErrorSeverity.Error, 3198, (5, 32, 5, 45), "The conversion from Dictionary to IDictionary is a compile-time safe upcast, not a downcast. Consider using 'upcast' instead of 'downcast'." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 67, Line 5, Col 32, Line 5, Col 45, "This type test or downcast will always hold") + (Error 3198, Line 5, Col 32, Line 5, Col 45, "The conversion from Dictionary to IDictionary is a compile-time safe upcast, not a downcast. Consider using 'upcast' instead of 'downcast'.")] diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs index 565e423407b..28487c97aa6 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WarnExpressionTests.fs @@ -1,62 +1,57 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Warn Expression`` = [] let ``Warn If Expression Result Unused``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ 1 + 2 printfn "%d" 3 - """ - FSharpErrorSeverity.Warning - 20 - (2, 1, 2, 6) - "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 2, Col 1, Line 2, Col 6, + "The result of this expression has type 'int' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn If Possible Assignment``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = "hello" let changeX() = x = 20 y = "test" - """ - FSharpErrorSeverity.Warning - 20 - (6, 5, 6, 11) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to mutate a value, then mark the value 'mutable' and use the '<-' operator e.g. 'x <- expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 11, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to mutate a value, then mark the value 'mutable' and use the '<-' operator e.g. 'x <- expression'.") [] let ``Warn If Possible Assignment To Mutable``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let mutable x = 10 let y = "hello" let changeX() = x = 20 y = "test" - """ - FSharpErrorSeverity.Warning - 20 - (6, 5, 6, 11) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to mutate a value, then use the '<-' operator e.g. 'x <- expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 11, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to mutate a value, then use the '<-' operator e.g. 'x <- expression'.") [] let ``Warn If Possible dotnet Property Setter``() = - CompilerAssert.TypeCheckWithErrors - """ + FSharp """ open System let z = System.Timers.Timer() @@ -65,16 +60,16 @@ let y = "hello" let changeProperty() = z.Enabled = true y = "test" - """ - [| - FSharpErrorSeverity.Warning, 760, (4, 9, 4, 30), "It is recommended that objects supporting the IDisposable interface are created using the syntax 'new Type(args)', rather than 'Type(args)' or 'Type' as a function value representing the constructor, to indicate that resources may be owned by the generated value" - FSharpErrorSeverity.Warning, 20, (8, 5, 8, 21), "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to set a value to a property, then use the '<-' operator e.g. 'z.Enabled <- expression'." - |] + """ + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Warning 760, Line 4, Col 9, Line 4, Col 30, "It is recommended that objects supporting the IDisposable interface are created using the syntax 'new Type(args)', rather than 'Type(args)' or 'Type' as a function value representing the constructor, to indicate that resources may be owned by the generated value") + (Warning 20, Line 8, Col 5, Line 8, Col 21, "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to set a value to a property, then use the '<-' operator e.g. 'z.Enabled <- expression'.")] [] let ``Don't Warn If Property Without Setter``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyClass(property1 : int) = member val Property2 = "" with get @@ -84,33 +79,30 @@ let y = "hello" let changeProperty() = x.Property2 = "22" y = "test" - """ - FSharpErrorSeverity.Warning - 20 - (9, 5, 9, 23) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 9, Col 5, Line 9, Col 23, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'.") [] let ``Warn If Implicitly Discarded``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let x = 10 let y = 20 let changeX() = y * x = 20 y = 30 - """ - FSharpErrorSeverity.Warning - 20 - (6, 5, 6, 15) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 15, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'.") [] let ``Warn If Discarded In List``() = - CompilerAssert.TypeCheckWithErrorsAndOptions - [| "--langversion:4.6" |] - """ + FSharp """ let div _ _ = 1 let subView _ _ = [1; 2] @@ -120,19 +112,16 @@ let view model dispatch = yield! subView model dispatch div [] [] ] - """ - [| - FSharpErrorSeverity.Warning, - 3221, - (9, 8, 9, 17), - "This expression returns a value of type 'int' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield'." - |] + """ + |> withOptions ["--langversion:4.6"] + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3221, Line 9, Col 8, Line 9, Col 17, + "This expression returns a value of type 'int' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield'.") [] let ``Warn If Discarded In List 2``() = - CompilerAssert.TypeCheckWithErrorsAndOptions - [| "--langversion:4.6" |] - """ + FSharp """ // stupid things to make the sample compile let div _ _ = 1 let subView _ _ = [1; 2] @@ -147,19 +136,16 @@ let view model dispatch = | _ -> subView model dispatch ] ] - """ - [| - FSharpErrorSeverity.Warning, - 3222, - (13, 19, 13, 41), - "This expression returns a value of type 'int list' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield!'." - |] + """ + |> withOptions ["--langversion:4.6"] + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 3222, Line 13, Col 19, Line 13, Col 41, + "This expression returns a value of type 'int list' but is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to use the expression as a value in the sequence then use an explicit 'yield!'.") [] let ``Warn If Discarded In List 3``() = - CompilerAssert.TypeCheckWithErrorsAndOptions - [| "--langversion:4.6" |] - """ + FSharp """ // stupid things to make the sample compile let div _ _ = 1 let subView _ _ = true @@ -174,33 +160,30 @@ let view model dispatch = | _ -> subView model dispatch ] ] - """ - [| - FSharpErrorSeverity.Warning, - 20, - (13, 19, 13, 41), - "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." - |] + """ + |> withOptions ["--langversion:4.6"] + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 13, Col 19, Line 13, Col 41, + "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn Only On Last Expression``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ let mutable x = 0 while x < 1 do printfn "unneeded" x <- x + 1 true - """ - FSharpErrorSeverity.Warning - 20 - (6, 5, 6, 9) - "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 6, Col 5, Line 6, Col 9, + "The result of this expression has type 'bool' and is implicitly ignored. Consider using 'ignore' to discard this value explicitly, e.g. 'expr |> ignore', or 'let' to bind the result to a name, e.g. 'let result = expr'.") [] let ``Warn If Possible Property Setter``() = - CompilerAssert.TypeCheckSingleError - """ + FSharp """ type MyClass(property1 : int) = member val Property1 = property1 member val Property2 = "" with get, set @@ -211,17 +194,16 @@ let y = "hello" let changeProperty() = x.Property2 = "20" y = "test" - """ - FSharpErrorSeverity.Warning - 20 - (10, 5, 10, 23) - "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to set a value to a property, then use the '<-' operator e.g. 'x.Property2 <- expression'." + """ + |> typecheck + |> shouldFail + |> withSingleDiagnostic (Warning 20, Line 10, Col 5, Line 10, Col 23, + "The result of this equality expression has type 'bool' and is implicitly discarded. Consider using 'let' to bind the result to a name, e.g. 'let result = expression'. If you intended to set a value to a property, then use the '<-' operator e.g. 'x.Property2 <- expression'.") [] let ``Dont warn external function as unused``() = - CompilerAssert.Pass - """ + FSharp """ open System open System.Runtime.InteropServices @@ -240,4 +222,6 @@ let main _argv = let _ = Test.ExtractIconEx("", 0, [| |], [| |], 0u) 0 - """ + """ + |> typecheck + |> shouldSucceed diff --git a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WrongSyntaxInForLoop.fs b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WrongSyntaxInForLoop.fs index d7cd6068103..89e3707c243 100644 --- a/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WrongSyntaxInForLoop.fs +++ b/tests/FSharp.Compiler.ComponentTests/ErrorMessages/WrongSyntaxInForLoop.fs @@ -1,20 +1,21 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.ErrorMessages.ComponentTests +namespace FSharp.Compiler.ComponentTests.ErrorMessages open Xunit -open FSharp.Test.Utilities -open FSharp.Compiler.SourceCodeServices +open FSharp.Test.Utilities.Compiler module ``Wrong syntax in for loop`` = [] let ``Equals instead of in``() = - CompilerAssert.ParseWithErrors - """ + FSharp """ module X for i = 0 .. 100 do () - """ - [|FSharpErrorSeverity.Error, 3215, (3, 7, 3, 8), "Unexpected symbol '=' in expression. Did you intend to use 'for x in y .. z do' instead?" |] + """ + |> parse + |> shouldFail + |> withSingleDiagnostic (Error 3215, Line 3, Col 7, Line 3, Col 8, + "Unexpected symbol '=' in expression. Did you intend to use 'for x in y .. z do' instead?") diff --git a/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs b/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs index 929eab5c809..0c8b76a9da0 100644 --- a/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Interop/SimpleInteropTests.fs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.Interop.ComponentTests +namespace FSharp.Compiler.ComponentTests.Interop open Xunit -open FSharp.Test.Utilities open FSharp.Test.Utilities.Compiler module ``C# <-> F# basic interop`` = diff --git a/tests/FSharp.Compiler.ComponentTests/Language/CodeQuotationTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/CodeQuotationTests.fs index 16ee721cd0c..03712d32e1a 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/CodeQuotationTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/CodeQuotationTests.fs @@ -1,6 +1,6 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.Language.CodeQuatation +namespace FSharp.Compiler.ComponentTests.Language open Xunit open FSharp.Test.Utilities.Compiler diff --git a/tests/FSharp.Compiler.ComponentTests/Language/CompilerDirectiveTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/CompilerDirectiveTests.fs index 9f9d949667c..32219920d93 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/CompilerDirectiveTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/CompilerDirectiveTests.fs @@ -1,11 +1,9 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. -namespace FSharp.Compiler.Language.ComponentTests +namespace FSharp.Compiler.ComponentTests.Language open Xunit -open FSharp.Test.Utilities open FSharp.Test.Utilities.Compiler -open FSharp.Compiler.SourceCodeServices module ``Test Compiler Directives`` = diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 88c137194ce..b31da1d4866 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -197,11 +197,11 @@ module rec Compiler = let result = compileFSharpCompilation cmpl false match result with | Failure f -> - let message = sprintf "Compilation failed (expected to succeed).\n All errors:\n%A" (f.Errors @ f.Warnings) + let message = sprintf "Operation failed (expected to succeed).\n All errors:\n%A" (f.Errors @ f.Warnings) failwith message | Success s -> match s.OutputPath with - | None -> failwith "Compilation didn't produce any output!" + | None -> failwith "Operation didn't produce any output!" | Some p -> p |> MetadataReference.CreateFromFile | _ -> failwith "Conversion isn't possible" @@ -318,6 +318,31 @@ module rec Compiler = | CS cs -> compileCSharp cs | _ -> failwith "TODO" + let private parseFSharp (fsSource: FSharpCompilationSource) : TestResult = + let source = getSource fsSource.Source + let parseResults = CompilerAssert.Parse source + let failed = parseResults.ParseHadErrors + + let (errors, warnings) = parseResults.Errors |> fromFSharpErrorInfo + + let result = + { OutputPath = None + Dependencies = [] + Adjust = 0 + Warnings = errors + Errors = warnings + Output = None } + + if failed then + Failure result + else + Success result + + let parse (cUnit: CompilationUnit) : TestResult = + match cUnit with + | FS fs -> parseFSharp fs + | _ -> failwith "Parsing only supported for F#." + let private typecheckFSharpWithBaseline (options: string list) (dir: string) (file: string) : TestResult = // Since TypecheckWithErrorsAndOptionsAgainsBaseLine throws if doesn't match expected baseline, // We return a successfull TestResult if it succeeds. @@ -436,12 +461,12 @@ module rec Compiler = match result with | Success _ -> result | Failure r -> - let message = sprintf "Compilation failed (expected to succeed).\n All errors:\n%A" (r.Errors @ r.Warnings) + let message = sprintf "Operation failed (expected to succeed).\n All errors:\n%A" (r.Errors @ r.Warnings) failwith message let shouldFail (result: TestResult) : TestResult = match result with - | Success _ -> failwith "Compilation was \"Success\" (expected: \"Failure\")." + | Success _ -> failwith "Operation was succeeded (expected to fail)." | Failure _ -> result let private assertResultsCategory (what: string) (selector: Output -> ErrorInfo list) (expected: ErrorInfo list) (result: TestResult) : TestResult = diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index e74ef790b16..4077979d91a 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -663,10 +663,13 @@ let main argv = 0""" static member RunScript source expectedErrorMessages = CompilerAssert.RunScriptWithOptions [||] source expectedErrorMessages - static member ParseWithErrors (source: string) expectedParseErrors = + static member Parse (source: string) = let sourceFileName = "test.fs" let parsingOptions = { FSharpParsingOptions.Default with SourceFiles = [| sourceFileName |] } - let parseResults = checker.ParseFile(sourceFileName, SourceText.ofString source, parsingOptions) |> Async.RunSynchronously + checker.ParseFile(sourceFileName, SourceText.ofString source, parsingOptions) |> Async.RunSynchronously + + static member ParseWithErrors (source: string) expectedParseErrors = + let parseResults = CompilerAssert.Parse source Assert.True(parseResults.ParseHadErrors) From 60db04e6415e8343fda470f7c361867d47194b08 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 7 Aug 2020 01:52:27 +0100 Subject: [PATCH 86/87] fix https://github.com/dotnet/fsharp/issues/9893 --- src/fsharp/FSharp.Core/printf.fs | 7 ++- .../Compiler/Language/StringInterpolation.fs | 57 +++++++++++++++++++ 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/src/fsharp/FSharp.Core/printf.fs b/src/fsharp/FSharp.Core/printf.fs index c2034061f87..05ffcce3ea6 100644 --- a/src/fsharp/FSharp.Core/printf.fs +++ b/src/fsharp/FSharp.Core/printf.fs @@ -216,8 +216,9 @@ module internal PrintfImpl = ValueNone // Skip %P() added for hole in "...%d{x}..." - let skipInterpolationHole (fmt: string) (i: byref) = - if i+1 < fmt.Length && fmt.[i] = '%' && fmt.[i+1] = 'P' then + let skipInterpolationHole typeChar (fmt: string) (i: byref) = + if typeChar <> 'P' then + if i+1 < fmt.Length && fmt.[i] = '%' && fmt.[i+1] = 'P' then i <- i + 2 if i+1 < fmt.Length && fmt.[i] = '(' && fmt.[i+1] = ')' then i <- i+2 @@ -1170,7 +1171,7 @@ module internal PrintfImpl = let interpHoleDotnetFormat = FormatString.parseInterpolatedHoleDotNetFormat typeChar fmt &i // Skip %P insertion points added after %d{...} etc. in interpolated strings - FormatString.skipInterpolationHole fmt &i + FormatString.skipInterpolationHole typeChar fmt &i let spec = { TypeChar = typeChar diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index 561966f7054..ed921afd02d 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -41,6 +41,63 @@ check "basic-string-interp9" $"{1} 2" """ + [] + let ``Neighbouring specifiers for string interpolation`` () = + SimpleCheckTest + """ +check "nbr-interp71" $"this is {1} {2} {3} {4} {5} {6} {7}" "this is 1 2 3 4 5 6 7" +check "nbr-interp7b2" $"this is {1}{2}{3}{4}{5}{6}{7}" "this is 1234567" +check "nbr-interp7c3" $"this is {1}{2}{3}{4}{5}{6}" "this is 123456" +check "nbr-interp7c4" $"this is {1}{2}{3}{4}{5}" "this is 12345" +check "nbr-interp7c5" $"this is {1}{2}{3}{4}" "this is 1234" +check "nbr-interp7c6" $"this is {1}{2}{3} {4}" "this is 123 4" +check "nbr-interp7c7" $"this is {1}{2} {3} {4}" "this is 12 3 4" +check "nbr-interp7c8" $"this is {1}{2} {3}{4}" "this is 12 34" +check "nbr-interp7c9" $"this is {1}{2}{3}" "this is 123" +check "nbr-interp7cq" $"this is {1} {2}{3}" "this is 1 23" +check "nbr-interp7cw" $"this is {1}{2} {3}" "this is 12 3" +check "nbr-interp7ce" $"this is {1}{2}" "this is 12" + +check "nbr-interp7r" $"this is %d{1} {2} {3} {4} {5} {6} {7}" "this is 1 2 3 4 5 6 7" +check "nbr-interp7bt" $"this is %d{1}{2}{3}{4}{5}{6}{7}" "this is 1234567" +check "nbr-interp7cy" $"this is %d{1}{2}{3}{4}{5}{6}" "this is 123456" +check "nbr-interp7cu" $"this is %d{1}{2}{3}{4}{5}" "this is 12345" +check "nbr-interp7ci" $"this is %d{1}{2}{3}{4}" "this is 1234" +check "nbr-interp7co" $"this is %d{1}{2}{3} {4}" "this is 123 4" +check "nbr-interp7cp" $"this is %d{1}{2} {3} {4}" "this is 12 3 4" +check "nbr-interp7ca" $"this is %d{1}{2} {3}{4}" "this is 12 34" +check "nbr-interp7cs" $"this is %d{1}{2}{3}" "this is 123" +check "nbr-interp7cd" $"this is %d{1} {2}{3}" "this is 1 23" +check "nbr-interp7cf" $"this is %d{1}{2} {3}" "this is 12 3" +check "nbr-interp7cg" $"this is %d{1}{2}" "this is 12" + +check "nbr-interp7h" $"this is %d{1} %d{2} {3} {4} {5} {6} {7}" "this is 1 2 3 4 5 6 7" +check "nbr-interp7bj" $"this is %d{1}%d{2}{3}{4}{5}{6}{7}" "this is 1234567" +check "nbr-interp7ck" $"this is %d{1}%d{2}{3}{4}{5}{6}" "this is 123456" +check "nbr-interp7cl" $"this is %d{1}%d{2}{3}{4}{5}" "this is 12345" +check "nbr-interp7cz" $"this is %d{1}%d{2}{3}{4}" "this is 1234" +check "nbr-interp7cx" $"this is %d{1}%d{2}{3} {4}" "this is 123 4" +check "nbr-interp7cc" $"this is %d{1}%d{2} {3} {4}" "this is 12 3 4" +check "nbr-interp7cv" $"this is %d{1}%d{2} {3}{4}" "this is 12 34" +check "nbr-interp7cb" $"this is %d{1}%d{2}{3}" "this is 123" +check "nbr-interp7cn" $"this is %d{1} %d{2}{3}" "this is 1 23" +check "nbr-interp7cm" $"this is %d{1}%d{2} {3}" "this is 12 3" +check "nbr-interp7cp" $"this is %d{1}%d{2}" "this is 12" + +check "nbr-interp7" $"this is %d{1} %d{2} %d{3} {4} {5} {6} {7}" "this is 1 2 3 4 5 6 7" +check "nbr-interp7b" $"this is %d{1}%d{2}%d{3}{4}{5}{6}{7}" "this is 1234567" +check "nbr-interp7c" $"this is %d{1}%d{2}%d{3}{4}{5}{6}" "this is 123456" +check "nbr-interp7c" $"this is %d{1}%d{2}%d{3}{4}{5}" "this is 12345" +check "nbr-interp7c" $"this is %d{1}%d{2}%d{3}{4}" "this is 1234" +check "nbr-interp7c" $"this is %d{1}%d{2}%d{3} {4}" "this is 123 4" +check "nbr-interp7c" $"this is %d{1}%d{2} %d{3} {4}" "this is 12 3 4" +check "nbr-interp7c" $"this is %d{1}%d{2} %d{3}{4}" "this is 12 34" +check "nbr-interp7c" $"this is %d{1}%d{2}%d{3}" "this is 123" +check "nbr-interp7c" $"this is %d{1} %d{2}%d{3}" "this is 1 23" +check "nbr-interp7c" $"this is %d{1}%d{2} %d{3}" "this is 12 3" +check "nbr-interp7c" $"this is %d{1}%d{2}" "this is 12" + """ + [] let ``Basic string interpolation verbatim strings`` () = SimpleCheckTest From ba48b5986e35563591ba84b80cf0885e6172061f Mon Sep 17 00:00:00 2001 From: Don Syme Date: Fri, 7 Aug 2020 02:53:43 +0100 Subject: [PATCH 87/87] fix unmantched right brace in interp string --- src/fsharp/FSComp.txt | 1 + src/fsharp/lex.fsl | 36 ++++++++++++++ .../Compiler/Language/StringInterpolation.fs | 48 +++++++++++++++++-- 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 540d27e348b..2733248bea1 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -1532,4 +1532,5 @@ forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable 3379,parsEofInInterpolatedString,"Incomplete interpolated string begun at or before here" 3380,parsEofInInterpolatedVerbatimString,"Incomplete interpolated verbatim string begun at or before here" 3381,parsEofInInterpolatedTripleQuoteString,"Incomplete interpolated triple-quote string begun at or before here" +3382,lexRBraceInInterpolatedString,"A '}}' character must be escaped (by doubling) in an interpolated string." #3501 "This construct is not supported by your version of the F# compiler" CompilerMessage(ExperimentalAttributeMessages.NotSupportedYet, 3501, IsError=true) diff --git a/src/fsharp/lex.fsl b/src/fsharp/lex.fsl index 8e121a76bbf..f5679b2f585 100644 --- a/src/fsharp/lex.fsl +++ b/src/fsharp/lex.fsl @@ -1148,6 +1148,18 @@ and singleQuoteString sargs skip = parse else singleQuoteString sargs skip lexbuf } + | "}" + { let (buf, _fin, m, kind, args) = sargs + let result() = + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.SingleQuote, kind, m)) + else singleQuoteString sargs skip lexbuf + if kind.IsInterpolated then + fail args lexbuf (FSComp.SR.lexRBraceInInterpolatedString()) (result()) + else + addUnicodeString buf (lexeme lexbuf) + (result()) + } + | newline { let (buf, _fin, m, kind, args) = sargs newline lexbuf @@ -1233,6 +1245,18 @@ and verbatimString sargs skip = parse else verbatimString sargs skip lexbuf } + | "}" + { let (buf, _fin, m, kind, args) = sargs + let result() = + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.Verbatim, kind, m)) + else verbatimString sargs skip lexbuf + if kind.IsInterpolated then + fail args lexbuf (FSComp.SR.lexRBraceInInterpolatedString()) (result()) + else + addUnicodeString buf (lexeme lexbuf) + (result()) + } + | ident { let (buf, _fin, m, kind, args) = sargs addUnicodeString buf (lexeme lexbuf) @@ -1317,6 +1341,18 @@ and tripleQuoteString sargs skip = parse else tripleQuoteString sargs skip lexbuf } + | "}" + { let (buf, _fin, m, kind, args) = sargs + let result() = + if not skip then STRING_TEXT (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) + else tripleQuoteString sargs skip lexbuf + if kind.IsInterpolated then + fail args lexbuf (FSComp.SR.lexRBraceInInterpolatedString()) (result()) + else + addUnicodeString buf (lexeme lexbuf) + (result()) + } + | eof { let (_buf, _fin, m, kind, args) = sargs EOF (LexCont.String(args.ifdefStack, args.stringNest, LexerStringStyle.TripleQuote, kind, m)) } diff --git a/tests/fsharp/Compiler/Language/StringInterpolation.fs b/tests/fsharp/Compiler/Language/StringInterpolation.fs index ed921afd02d..83ea5c14078 100644 --- a/tests/fsharp/Compiler/Language/StringInterpolation.fs +++ b/tests/fsharp/Compiler/Language/StringInterpolation.fs @@ -485,13 +485,21 @@ type R2 = { X : int ; Y: int } // Check record expression (parenthesized) check "vcewweh18" $"abc{({contents=1}.contents)}def" "abc1def" -// Check {{ }} expression (un-parenthesized) -check "vcewweh19" $"abc{{contents=1}.contents}def" "abc{contents=1}.contents}def" +// Check record expression (unparenthesized, spaced) +check "vcewweh19a" $"abc{ {X=1} }def" "abc{ X = 1 }def" -// Check record expression (un-parenthesized) +check "vcewweh19b" $"abc{ {X=1} }def" "abc{ X = 1 }def" + +// Check record expression (unparenthesized, spaced ending in token brace then string hole brace) +check "vcewweh19v" $"abc{ {X=1}}def" "abc{ X = 1 }def" + +// Check thing that is not really a record expression (braces are escaped) check "vcewweh20" $"abc{{X=1}}def" "abc{X=1}def" -// Check {{ }} expression (un-parenthesized, multi-line) +// Check thing that is not really a record expression (braces are escaped) +check "vcewweh20b" $"abc{{quack=1}}def" "abc{quack=1}def" + +// Check thing that is not really a record expression (braces are escaped) check "vcewweh21" $"abc{{X=1; Y=2}}def" "abc{X=1; Y=2}def" """ @@ -804,3 +812,35 @@ let TripleInterpolatedInVerbatimInterpolated = $\"123{456}789{$\"\"\"012\"\"\"}3 "Incomplete structured construct at or before this point in binding"); (FSharpErrorSeverity.Error, 3381, (1, 10, 1, 14), "Incomplete interpolated triple-quote string begun at or before here")|] + + [] + let ``String interpolation extra right brace single quote`` () = + let code = "let x1 = $\"}\"" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 3382, (1, 10, 1, 14), + "A '}' character must be escaped (by doubling) in an interpolated string.")|] + + [] + let ``String interpolation extra right brace verbatim`` () = + let code = "let x1 = @$\"}\"" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 3382, (1, 10, 1, 15), + "A '}' character must be escaped (by doubling) in an interpolated string.")|] + + [] + let ``String interpolation extra right brace triple`` () = + let code = "let x1 = $\"\"\"}\"\"\"" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 3382, (1, 10, 1, 18), + "A '}' character must be escaped (by doubling) in an interpolated string.")|] + + [] + let ``String interpolation extra right brace single quote with hole`` () = + let code = "let x1 = $\"{0}}\"" + CompilerAssert.TypeCheckWithErrorsAndOptions [| "--langversion:preview" |] + code + [|(FSharpErrorSeverity.Error, 3382, (1, 14, 1, 17), + "A '}' character must be escaped (by doubling) in an interpolated string.")|]